1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-26 06:40:22 +00:00
This commit is contained in:
kuyondo 2021-10-04 08:12:56 +08:00
commit 071793eeb3
558 changed files with 49415 additions and 8388 deletions

50
.github/workflows/cmake.yml vendored Normal file
View File

@ -0,0 +1,50 @@
name: CMake
on:
pull_request:
branches: [ master ]
env:
BUILD_TYPE: RelWithDebInfo
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Add OpenMW PPA Dependancies
run: sudo add-apt-repository ppa:openmw/openmw; sudo apt-get update
- name: Install Building Dependancies
run: sudo CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic
- name: Prime ccache
uses: hendrikmuhs/ccache-action@v1
with:
key: ${{ matrix.os }}-${{ env.BUILD_TYPE }}
max-size: 1000M
- name: Configure
run: cmake -S . -B . -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INSTALL_PREFIX=./install -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
run: cmake --build . --config ${{env.BUILD_TYPE}} --parallel 3
- name: Install
shell: bash
run: cmake --install .
- name: Create Artifact
shell: bash
working-directory: install
run: |
ls -laR
7z a ../build_artifact.7z .
- name: Upload Artifact
uses: actions/upload-artifact@v1
with:
path: ./build_artifact.7z
name: build_artifact.7z

View File

@ -31,6 +31,23 @@ stages:
paths:
- build/install/
Clang_Tidy:
extends: .Debian_Image
stage: build
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
before_script:
- CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic clang-tidy clang
script:
- CI/before_script.linux.sh
- cd build
- cmake --build . -- -j $(nproc) openmw esmtool bsatool niftest openmw-wizard openmw-launcher openmw-iniimporter openmw-essimporter
variables:
CC: clang
CXX: clang++
CI_CLANG_TIDY: 1
timeout: 8h
Coverity:
extends: .Debian_Image
stage: build
@ -42,8 +59,8 @@ Coverity:
- tar xfz /tmp/cov-analysis-linux64.tgz
script:
- CI/before_script.linux.sh
# Add more than just `openmw` once we can build everything under 3h
- cov-analysis-linux64-*/bin/cov-build --dir cov-int cmake --build build -- -j $(nproc) openmw
# Remove the specific targets and build everything once we can do it under 3h
- cov-analysis-linux64-*/bin/cov-build --dir cov-int cmake --build build -- -j $(nproc) openmw esmtool bsatool niftest openmw-wizard openmw-launcher openmw-iniimporter openmw-essimporter
after_script:
- tar cfz cov-int.tar.gz cov-int
- curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME
@ -53,7 +70,7 @@ Coverity:
variables:
CC: gcc
CXX: g++
timeout: 8h
artifacts:
Debian_GCC:
extends: .Debian
@ -76,6 +93,15 @@ Debian_GCC_tests:
CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1
Debian_GCC_tests_Debug:
extends: Debian_GCC
cache:
key: Debian_GCC_tests_Debug.v1
variables:
CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1
CMAKE_BUILD_TYPE: Debug
Debian_GCC_Static_Deps:
extends: Debian_GCC
cache:
@ -88,6 +114,7 @@ Debian_GCC_Static_Deps:
- CI/install_debian_deps.sh gcc openmw-deps openmw-deps-static
variables:
CI_OPENMW_USE_STATIC_DEPS: 1
timeout: 3h
Debian_GCC_Static_Deps_tests:
extends: Debian_GCC_Static_Deps
@ -118,6 +145,15 @@ Debian_Clang_tests:
CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1
Debian_Clang_tests_Debug:
extends: Debian_Clang
cache:
key: Debian_Clang_tests_Debug.v1
variables:
CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1
CMAKE_BUILD_TYPE: Debug
.MacOS:
image: macos-11-xcode-12
tags:
@ -187,6 +223,18 @@ variables: &tests-targets
- choco install ninja -y
- choco install python -y
- refreshenv
- |
function Make-SafeFileName {
param(
[Parameter(Mandatory=$true)]
[String]
$FileName
)
[IO.Path]::GetInvalidFileNameChars() | ForEach-Object {
$FileName = $FileName.Replace($_, '_')
}
return $FileName
}
stage: build
script:
- $time = (Get-Date -Format "HH:mm:ss")
@ -198,12 +246,13 @@ variables: &tests-targets
- cmake --build . --config $config --target ($targets.Split(','))
- cd $config
- echo "CI_COMMIT_REF_NAME ${CI_COMMIT_REF_NAME}`nCI_JOB_ID ${CI_JOB_ID}`nCI_COMMIT_SHA ${CI_COMMIT_SHA}" | Out-File -Encoding UTF8 CI-ID.txt
- Get-ChildItem -Recurse *.ilk | Remove-Item
- |
if (Get-ChildItem -Recurse *.pdb) {
7z a -tzip ..\..\OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip '*.pdb' CI-ID.txt
7z a -tzip "..\..\$(Make-SafeFileName("OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" '*.pdb' CI-ID.txt
Get-ChildItem -Recurse *.pdb | Remove-Item
}
- 7z a -tzip ..\..\OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}.zip '*'
- 7z a -tzip "..\..\$(Make-SafeFileName("OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}.zip"))" '*'
- if ($executables) { foreach ($exe in $executables.Split(',')) { & .\$exe } }
after_script:
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
@ -289,6 +338,18 @@ Windows_Ninja_Tests_RelWithDebInfo:
- choco install vswhere -y
- choco install python -y
- refreshenv
- |
function Make-SafeFileName {
param(
[Parameter(Mandatory=$true)]
[String]
$FileName
)
[IO.Path]::GetInvalidFileNameChars() | ForEach-Object {
$FileName = $FileName.Replace($_, '_')
}
return $FileName
}
stage: build
script:
- $time = (Get-Date -Format "HH:mm:ss")
@ -299,12 +360,13 @@ Windows_Ninja_Tests_RelWithDebInfo:
- cmake --build . --config $config --target ($targets.Split(','))
- cd $config
- echo "CI_COMMIT_REF_NAME ${CI_COMMIT_REF_NAME}`nCI_JOB_ID ${CI_JOB_ID}`nCI_COMMIT_SHA ${CI_COMMIT_SHA}" | Out-File -Encoding UTF8 CI-ID.txt
- Get-ChildItem -Recurse *.ilk | Remove-Item
- |
if (Get-ChildItem -Recurse *.pdb) {
7z a -tzip ..\..\OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip '*.pdb' CI-ID.txt
7z a -tzip "..\..\$(Make-SafeFileName("OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" '*.pdb' CI-ID.txt
Get-ChildItem -Recurse *.pdb | Remove-Item
}
- 7z a -tzip ..\..\OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}.zip '*'
- 7z a -tzip "..\..\$(Make-SafeFileName("OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}.zip"))" '*'
- if ($executables) { foreach ($exe in $executables.Split(',')) { & .\$exe } }
after_script:
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
@ -412,3 +474,4 @@ Debian_AndroidNDK_arm64-v8a:
- build/install/
# When CCache doesn't exist (e.g. first build on a fork), build takes more than 1h, which is the default for forks.
timeout: 1h30m

View File

@ -44,6 +44,7 @@ Programmers
Austin Salgat (Salgat)
Ben Shealy (bentsherman)
Berulacks
Bo Svensson
Britt Mathis (galdor557)
Capostrophic
Carl Maxwell
@ -79,6 +80,7 @@ Programmers
Federico Guerra (FedeWar)
Fil Krynicki (filkry)
Finbar Crago(finbar-crago)
Florent Teppe (Tetramir)
Florian Weber (Florianjw)
Frédéric Chardon (fr3dz10)
Gaëtan Dezeiraud (Brouilles)
@ -99,6 +101,7 @@ Programmers
James Stephens (james-h-stephens)
Jan-Peter Nilsson (peppe)
Jan Borsodi (am0s)
JanuarySnow
Jason Hooks (jhooks)
jeaye
jefetienne
@ -159,6 +162,7 @@ Programmers
Nikolay Kasyanov (corristo)
nobrakal
Nolan Poe (nopoe)
Nurivan Gomez (Nuri-G)
Oleg Chkan (mrcheko)
Paul Cercueil (pcercuei)
Paul McElroy (Greendogo)
@ -225,6 +229,7 @@ Programmers
Yuri Krupenin
zelurker
Noah Gooder
Andrew Appuhamy (andrew-app)
Documentation
-------------

View File

@ -1,28 +1,80 @@
0.48.0
------
Bug #1751: Birthsign abilities increase modified attribute values instead of base ones
Bug #3246: ESSImporter: Most NPCs are dead on save load
Bug #3514: Editing a reference's position after loading an esp file makes the reference disappear
Bug #3737: Scripts from The Underground 2 .esp do not play (all patched versions)
Bug #3846: Strings starting with "-" fail to compile if not enclosed in quotes
Bug #3905: Great House Dagoth issues
Bug #4203: Resurrecting an actor should close the loot GUI
Bug #4602: Robert's Bodies: crash inside createInstance()
Bug #4700: Editor: Incorrect command implementation
Bug #4744: Invisible particles must still be processed
Bug #5100: Persuasion doesn't always clamp the resulting disposition
Bug #5120: Scripted object spawning updates physics system
Bug #5207: Loose summons can be present in scene
Bug #5379: Wandering NPCs falling through cantons
Bug #5453: Magic effect VFX are offset for creatures
Bug #5483: AutoCalc flag is not used to calculate spells cost
Bug #5508: Engine binary links to Qt without using it
Bug #5596: Effects in constant spells should not be merged
Bug #5621: Drained stats cannot be restored
Bug #5755: Active grid object paging - disappearing textures
Bug #5766: Active grid object paging - disappearing textures
Bug #5788: Texture editing parses the selected indexes wrongly
Bug #5801: A multi-effect spell with the intervention effects and recall always favors Almsivi intervention
Bug #5842: GetDisposition adds temporary disposition change from different actors
Bug #5863: GetEffect should return true after the player has teleported
Bug #6037: Morrowind Content Language Cannot be Set to English in OpenMW Launcher
Bug #6051: NaN water height in ESM file is not handled gracefully
Bug #6066: addtopic "return" does not work from within script. No errors thrown
Bug #6067: esp loader fails in for certain subrecord orders
Bug #6087: Bound items added directly to the inventory disappear if their corresponding spell effect ends
Bug #6101: Disarming trapped unlocked owned objects isn't considered a crime
Bug #6107: Fatigue is incorrectly recalculated when fortify effect is applied or removed
Bug #6115: Showmap overzealous matching
Bug #6118: Creature landing sound counts as a footstep
Bug #6123: NPC with broken script freezes the game on hello
Bug #6129: Player avatar not displayed correctly for large window sizes when GUI scaling active
Bug #6131: Item selection in the avatar window not working correctly for large window sizes
Bug #6133: Cannot reliably sneak or steal in the sight of the NPCs siding with player
Bug #6143: Capturing a screenshot makes engine to be a temporary unresponsive
Bug #6165: Paralyzed player character can pickup items when the inventory is open
Bug #6172: Some creatures can't open doors
Bug #6174: Spellmaking and Enchanting sliders differences from vanilla
Bug #6184: Command and Calm and Demoralize and Frenzy and Rally magic effects inconsistencies with vanilla
Bug #6197: Infinite Casting Loop
Bug #6223: Some Constant Effect Bound Items inconsistencies
Bug #6273: Respawning NPCs rotation is inconsistent
Bug #6282: Laura craft doesn't follow the player character
Bug #6283: Avis Dorsey follows you after her death
Bug #6289: Keyword search in dialogues expected the text to be all ASCII characters
Bug #6302: Teleporting disabled actor breaks its disabled state
Bug #6307: Pathfinding in Infidelities quest from Tribunal addon is broken
Feature #890: OpenMW-CS: Column filtering
Feature #2554: Modifying an object triggers the instances table to scroll to the corresponding record
Feature #2780: A way to see current OpenMW version in the console
Feature #3616: Allow Zoom levels on the World Map
Feature #4297: Implement APPLIED_ONCE flag for magic effects
Feature #4414: Handle duration of EXTRA SPELL magic effect
Feature #4595: Unique object identifier
Feature #4737: Handle instance move from one cell to another
Feature #5198: Implement "Magic effect expired" event
Feature #5454: Clear active spells from actor when he disappears from scene
Feature #5489: MCP: Telekinesis fix for activators
Feature #5737: Handle instance move from one cell to another
Feature #5996: Support Lua scripts in OpenMW
Feature #6017: Separate persistent and temporary cell references when saving
Feature #6032: Reverse-z depth buffer
Feature #6162: Refactor GUI to use shaders and to be GLES and GL3+ friendly
Feature #6199: Support FBO Rendering
Feature #6249: Alpha testing support for Collada
Feature #6251: OpenMW-CS: Set instance movement based on camera zoom
Feature #6288: Preserve the "blocked" record flag for referenceable objects.
Task #6201: Remove the "Note: No relevant classes found. No output generated" warnings
Task #6264: Remove the old classes in animation.cpp
0.47.0
------
@ -1930,6 +1982,7 @@
Bug #2025: Missing mouse-over text for non affordable items
Bug #2028: [MOD: Tamriel Rebuilt] Crashing when trying to enter interior cell "Ruinous Keep, Great Hall"
Bug #2029: Ienith Brothers Thiev's Guild quest journal entry not adding
Bug #3066: Editor doesn't check if IDs and other strings are longer than their hardcoded field length
Feature #471: Editor: Special case implementation for top-level window with single sub-window
Feature #472: Editor: Sub-Window re-use settings
Feature #704: Font colors import from fallback settings

View File

@ -1,11 +1,15 @@
#!/bin/sh -ex
export HOMEBREW_NO_EMOJI=1
brew update --quiet
# workaround python issue on travis
[ -z "${TRAVIS}" ] && HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies python@3.8 || true
[ -z "${TRAVIS}" ] && HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies python@3.9 || true
[ -z "${TRAVIS}" ] && HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies qt@6 || true
[ -z "${TRAVIS}" ] && brew uninstall --ignore-dependencies python@3.8 || true
[ -z "${TRAVIS}" ] && brew uninstall --ignore-dependencies python@3.9 || true
[ -z "${TRAVIS}" ] && brew uninstall --ignore-dependencies qt@6 || true
# Some of these tools can come from places other than brew, so check before installing
[ -z "${TRAVIS}" ] && brew install fontconfig
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
@ -15,10 +19,6 @@ ccache --version
cmake --version
qmake --version
brew install lua
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20210617.zip -o ~/openmw-deps.zip
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20210716.zip -o ~/openmw-deps.zip
unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
# additional libraries
[ -z "${TRAVIS}" ] && HOMEBREW_NO_AUTO_UPDATE=1 brew install fontconfig

View File

@ -14,6 +14,12 @@ if [[ "${BUILD_TESTS_ONLY}" ]]; then
BUILD_BENCHMARKS=ON
fi
CXX_FLAGS='-Werror -Wno-error=deprecated-declarations -Wno-error=nonnull -Wno-error=deprecated-copy'
if [[ "${CXX}" == 'clang++' ]]; then
CXX_FLAGS="${CXX_FLAGS} -Wno-error=unused-lambda-capture -Wno-error=gnu-zero-variadic-macro-arguments"
fi
declare -a CMAKE_CONF_OPTS=(
-DCMAKE_C_COMPILER="${CC:-/usr/bin/cc}"
-DCMAKE_CXX_COMPILER="${CXX:-/usr/bin/c++}"
@ -24,6 +30,8 @@ declare -a CMAKE_CONF_OPTS=(
-DBUILD_SHARED_LIBS=OFF
-DUSE_SYSTEM_TINYXML=ON
-DCMAKE_INSTALL_PREFIX=install
-DCMAKE_C_FLAGS='-Werror'
-DCMAKE_CXX_FLAGS="${CXX_FLAGS}"
)
if [[ $CI_OPENMW_USE_STATIC_DEPS ]]; then
@ -34,6 +42,19 @@ if [[ $CI_OPENMW_USE_STATIC_DEPS ]]; then
)
fi
if [[ $CI_CLANG_TIDY ]]; then
CMAKE_CONF_OPTS+=(
-DCMAKE_CXX_CLANG_TIDY='clang-tidy;-checks=-*,boost-*,clang-analyzer-*,concurrency-*,performance-*,-header-filter=.*,bugprone-*,misc-definitions-in-headers,misc-misplaced-const,misc-redundant-expression'
)
fi
if [[ "${CMAKE_BUILD_TYPE}" ]]; then
CMAKE_CONF_OPTS+=(
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
)
fi
mkdir -p build
cd build

View File

@ -25,6 +25,5 @@ cmake \
-D BUILD_BSATOOL=TRUE \
-D BUILD_ESSIMPORTER=TRUE \
-D BUILD_NIFTEST=TRUE \
-D USE_LUAJIT=FALSE \
-G"Unix Makefiles" \
..

View File

@ -29,6 +29,7 @@ declare -rA GROUPED_DEPS=(
# These dependencies can alternatively be built and linked statically.
[openmw-deps-dynamic]="libmygui-dev libopenscenegraph-dev"
[coverity]="curl"
[clang-tidy]="clang-tidy"
# Pre-requisites for building MyGUI and OSG for static linking.
#

View File

@ -13,6 +13,11 @@ if(POLICY CMP0083)
cmake_policy(SET CMP0083 NEW)
endif()
# to link with freetype library
if(POLICY CMP0079)
cmake_policy(SET CMP0079 NEW)
endif()
option(OPENMW_GL4ES_MANUAL_INIT "Manually initialize gl4es. This is more reliable on platforms without a windowing system. Requires gl4es to be configured with -DNOEGL=ON -DNO_LOADER=ON -DNO_INIT_CONSTRUCTOR=ON." OFF)
if(OPENMW_GL4ES_MANUAL_INIT)
add_definitions(-DOPENMW_GL4ES_MANUAL_INIT)
@ -202,8 +207,6 @@ if (USE_QT)
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5OpenGL REQUIRED)
# Instruct CMake to run moc automatically when needed.
#set(CMAKE_AUTOMOC ON)
endif()
set(USED_OSG_COMPONENTS
@ -404,11 +407,8 @@ else(USE_LUAJIT)
add_compile_definitions(NO_LUAJIT)
endif(USE_LUAJIT)
# Download sol - C++ library binding to Lua
file(DOWNLOAD
"https://github.com/ThePhD/sol2/releases/download/v3.2.2/sol.hpp" "${OpenMW_BINARY_DIR}/extern/sol3.2.2/sol/sol.hpp"
EXPECTED_MD5 ba113cf458f60672917108e69bb4d958)
set(SOL_INCLUDE_DIRS ${OpenMW_BINARY_DIR}/extern/sol3.2.2 ${OpenMW_SOURCE_DIR}/extern/sol_config)
# C++ library binding to Lua
set(SOL_INCLUDE_DIRS ${OpenMW_SOURCE_DIR}/extern/sol3.2.2 ${OpenMW_SOURCE_DIR}/extern/sol_config)
include_directories(
BEFORE SYSTEM
@ -515,7 +515,7 @@ endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wundef -Wno-unused-parameter -pedantic -Wno-long-long")
set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wundef -Wno-unused-parameter -pedantic -Wno-long-long ${CMAKE_CXX_FLAGS}")
add_definitions( -DBOOST_NO_CXX11_SCOPED_ENUMS=ON )
if (APPLE)
@ -545,7 +545,6 @@ endif()
# Components
add_subdirectory (components)
target_compile_definitions(components PRIVATE OPENMW_DOC_BASEURL="${OPENMW_DOC_BASEURL}")
# Apps and tools
if (BUILD_OPENMW)
@ -681,7 +680,7 @@ if (WIN32)
if (BUILD_OPENMW)
# \bigobj is required:
# 1) for OPENMW_UNITY_BUILD;
# 2) to compile lua binginds, because sol3 is heavily templated.
# 2) to compile lua bindings, because sol3 is heavily templated.
set_target_properties(openmw PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD} /bigobj")
endif()

View File

@ -1,6 +1,7 @@
#include <benchmark/benchmark.h>
#include <components/detournavigator/navmeshtilescache.hpp>
#include <components/esm/loadland.hpp>
#include <algorithm>
#include <random>
@ -15,13 +16,12 @@ namespace
osg::Vec3f mAgentHalfExtents;
TilePosition mTilePosition;
RecastMesh mRecastMesh;
std::vector<OffMeshConnection> mOffMeshConnections;
};
struct Item
{
Key mKey;
NavMeshData mValue;
PreparedNavMeshData mValue;
};
template <typename Random>
@ -31,6 +31,14 @@ namespace
return TilePosition(distribution(random), distribution(random));
}
template <typename Random>
TileBounds generateTileBounds(Random& random)
{
std::uniform_real_distribution<float> distribution(0.0, 1.0);
const osg::Vec2f min(distribution(random), distribution(random));
return TileBounds {min, min + osg::Vec2f(1.0, 1.0)};
}
template <typename Random>
osg::Vec3f generateAgentHalfExtents(float min, float max, Random& random)
{
@ -81,22 +89,55 @@ namespace
template <typename OutputIterator, typename Random>
void generateWater(OutputIterator out, std::size_t count, Random& random)
{
std::uniform_real_distribution<btScalar> distribution(0.0, 1.0);
std::uniform_real_distribution<float> distribution(0.0, 1.0);
std::generate_n(out, count, [&] {
const btVector3 shift(distribution(random), distribution(random), distribution(random));
return RecastMesh::Water {1, btTransform(btMatrix3x3::getIdentity(), shift)};
const osg::Vec3f shift(distribution(random), distribution(random), distribution(random));
return Cell {1, shift};
});
}
template <typename OutputIterator, typename Random>
void generateOffMeshConnection(OutputIterator out, std::size_t count, Random& random)
template <class Random>
Mesh generateMesh(std::size_t triangles, Random& random)
{
std::uniform_real_distribution<btScalar> distribution(0.0, 1.0);
std::generate_n(out, count, [&] {
const osg::Vec3f start(distribution(random), distribution(random), distribution(random));
const osg::Vec3f end(distribution(random), distribution(random), distribution(random));
return OffMeshConnection {start, end, generateAreaType(random)};
std::uniform_real_distribution<float> distribution(0.0, 1.0);
std::vector<float> vertices;
std::vector<int> indices;
std::vector<AreaType> areaTypes;
if (distribution(random) < 0.939)
{
generateVertices(std::back_inserter(vertices), triangles * 2.467, random);
generateIndices(std::back_inserter(indices), static_cast<int>(vertices.size() / 3) - 1, vertices.size() * 1.279, random);
generateAreaTypes(std::back_inserter(areaTypes), indices.size() / 3, random);
}
return Mesh(std::move(indices), std::move(vertices), std::move(areaTypes));
}
template <class Random>
Heightfield generateHeightfield(Random& random)
{
std::uniform_real_distribution<float> distribution(0.0, 1.0);
Heightfield result;
result.mBounds = generateTileBounds(random);
result.mMinHeight = distribution(random);
result.mMaxHeight = result.mMinHeight + 1.0;
result.mShift = osg::Vec3f(distribution(random), distribution(random), distribution(random));
result.mScale = distribution(random);
result.mLength = static_cast<std::uint8_t>(ESM::Land::LAND_SIZE);
std::generate_n(std::back_inserter(result.mHeights), ESM::Land::LAND_NUM_VERTS, [&]
{
return distribution(random);
});
return result;
}
template <class Random>
FlatHeightfield generateFlatHeightfield(Random& random)
{
std::uniform_real_distribution<float> distribution(0.0, 1.0);
FlatHeightfield result;
result.mBounds = generateTileBounds(random);
result.mHeight = distribution(random);
return result;
}
template <class Random>
@ -106,22 +147,15 @@ namespace
const TilePosition tilePosition = generateTilePosition(10000, random);
const std::size_t generation = std::uniform_int_distribution<std::size_t>(0, 100)(random);
const std::size_t revision = std::uniform_int_distribution<std::size_t>(0, 10000)(random);
std::vector<float> vertices;
generateVertices(std::back_inserter(vertices), triangles * 1.98, random);
std::vector<int> indices;
generateIndices(std::back_inserter(indices), static_cast<int>(vertices.size() / 3) - 1, vertices.size() * 1.53, random);
std::vector<AreaType> areaTypes;
generateAreaTypes(std::back_inserter(areaTypes), indices.size() / 3, random);
std::vector<RecastMesh::Water> water;
generateWater(std::back_inserter(water), 2, random);
RecastMesh recastMesh(generation, revision, std::move(indices), std::move(vertices),
std::move(areaTypes), std::move(water));
std::vector<OffMeshConnection> offMeshConnections;
generateOffMeshConnection(std::back_inserter(offMeshConnections), 300, random);
return Key {agentHalfExtents, tilePosition, std::move(recastMesh), std::move(offMeshConnections)};
Mesh mesh = generateMesh(triangles, random);
std::vector<Cell> water;
generateWater(std::back_inserter(water), 1, random);
RecastMesh recastMesh(generation, revision, std::move(mesh), std::move(water),
{generateHeightfield(random)}, {generateFlatHeightfield(random)});
return Key {agentHalfExtents, tilePosition, std::move(recastMesh)};
}
constexpr std::size_t trianglesPerTile = 310;
constexpr std::size_t trianglesPerTile = 239;
template <typename OutputIterator, typename Random>
void generateKeys(OutputIterator out, std::size_t count, Random& random)
@ -137,7 +171,8 @@ namespace
while (true)
{
Key key = generateKey(trianglesPerTile, random);
cache.set(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, key.mOffMeshConnections, NavMeshData());
cache.set(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh,
std::make_unique<PreparedNavMeshData>());
*out++ = std::move(key);
const std::size_t newSize = cache.getStats().mNavMeshCacheSize;
if (size >= newSize)
@ -159,7 +194,7 @@ namespace
while (state.KeepRunning())
{
const auto& key = keys[n++ % keys.size()];
const auto result = cache.get(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, key.mOffMeshConnections);
const auto result = cache.get(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh);
benchmark::DoNotOptimize(result);
}
}
@ -187,7 +222,8 @@ namespace
while (state.KeepRunning())
{
const auto& key = keys[n++ % keys.size()];
const auto result = cache.set(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, key.mOffMeshConnections, NavMeshData());
const auto result = cache.set(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh,
std::make_unique<PreparedNavMeshData>());
benchmark::DoNotOptimize(result);
}
}

View File

@ -236,7 +236,9 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
if(!quiet) std::cout << " References:\n";
bool deleted = false;
while(cell.getNextRef(esm, ref, deleted))
ESM::MovedCellRef movedCellRef;
bool moved = false;
while(cell.getNextRef(esm, ref, deleted, movedCellRef, moved))
{
if (save) {
info.data.mCellRefs[&cell].push_back(std::make_pair(ref, deleted));
@ -244,7 +246,7 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
if(quiet) continue;
std::cout << " Refnum: " << ref.mRefNum.mIndex << '\n';
std::cout << " - Refnum: " << ref.mRefNum.mIndex << '\n';
std::cout << " ID: " << ref.mRefID << '\n';
std::cout << " Position: (" << ref.mPos.pos[0] << ", " << ref.mPos.pos[1] << ", " << ref.mPos.pos[2] << ")\n";
if (ref.mScale != 1.f)
@ -276,6 +278,13 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
if (!ref.mDestCell.empty())
std::cout << " Destination cell: " << ref.mDestCell << '\n';
}
std::cout << " Moved: " << std::boolalpha << moved << std::noboolalpha << '\n';
if (moved)
{
std::cout << " Moved refnum: " << movedCellRef.mRefNum.mIndex << '\n';
std::cout << " Moved content file: " << movedCellRef.mRefNum.mContentFile << '\n';
std::cout << " Target: " << movedCellRef.mTarget[0] << ", " << movedCellRef.mTarget[1] << '\n';
}
}
}

View File

@ -2,6 +2,7 @@
#include <stdexcept>
#include <algorithm>
#include <climits> // INT_MIN
#include <osgDB/WriteFile>
@ -357,7 +358,7 @@ namespace ESSImport
std::string idLower = Misc::StringUtils::lowerCase(out.mRefID);
std::map<std::pair<int, std::string>, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find(
auto npccIt = mContext->mNpcChanges.find(
std::make_pair(refIndex, out.mRefID));
if (npccIt != mContext->mNpcChanges.end())
{
@ -369,6 +370,8 @@ namespace ESSImport
// from the ESM with default values
if (cellref.mHasACDT)
convertACDT(cellref.mACDT, objstate.mCreatureStats);
else
objstate.mCreatureStats.mGoldPool = INT_MIN; // HACK: indicates no ACDT
if (cellref.mHasACSC)
convertACSC(cellref.mACSC, objstate.mCreatureStats);
convertNpcData(cellref, objstate.mNpcStats);
@ -383,7 +386,7 @@ namespace ESSImport
continue;
}
std::map<std::pair<int, std::string>, CNTC>::const_iterator cntcIt = mContext->mContainerChanges.find(
auto cntcIt = mContext->mContainerChanges.find(
std::make_pair(refIndex, out.mRefID));
if (cntcIt != mContext->mContainerChanges.end())
{
@ -398,7 +401,7 @@ namespace ESSImport
continue;
}
std::map<std::pair<int, std::string>, CREC>::const_iterator crecIt = mContext->mCreatureChanges.find(
auto crecIt = mContext->mCreatureChanges.find(
std::make_pair(refIndex, out.mRefID));
if (crecIt != mContext->mCreatureChanges.end())
{
@ -410,6 +413,8 @@ namespace ESSImport
// from the ESM with default values
if (cellref.mHasACDT)
convertACDT(cellref.mACDT, objstate.mCreatureStats);
else
objstate.mCreatureStats.mGoldPool = INT_MIN; // HACK: indicates no ACDT
if (cellref.mHasACSC)
convertACSC(cellref.mACSC, objstate.mCreatureStats);
convertCREC(crecIt->second, objstate);

View File

@ -124,11 +124,9 @@ public:
{
mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt.mLevel;
mContext->mPlayerBase = npc;
ESM::SpellState::SpellParams empty;
// FIXME: player start spells and birthsign spells aren't listed here,
// need to fix openmw to account for this
for (const auto & spell : npc.mSpells.mList)
mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[spell] = empty;
mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells = npc.mSpells.mList;
// Clear the list now that we've written it, this prevents issues cropping up with
// ensureCustomData() in OpenMW tripping over no longer existing spells, where an error would be fatal.
@ -374,7 +372,7 @@ public:
void write(ESM::ESMWriter &esm) override
{
esm.startRecord(ESM::REC_DCOU);
for (std::map<std::string, int>::const_iterator it = mKillCounter.begin(); it != mKillCounter.end(); ++it)
for (auto it = mKillCounter.begin(); it != mKillCounter.end(); ++it)
{
esm.writeHNString("ID__", it->first);
esm.writeHNT ("COUN", it->second);
@ -397,7 +395,7 @@ public:
faction.load(esm, isDeleted);
std::string id = Misc::StringUtils::lowerCase(faction.mId);
for (std::map<std::string, int>::const_iterator it = faction.mReactions.begin(); it != faction.mReactions.end(); ++it)
for (auto it = faction.mReactions.begin(); it != faction.mReactions.end(); ++it)
{
std::string faction2 = Misc::StringUtils::lowerCase(it->first);
mContext->mDialogueState.mChangedFactionReaction[id].insert(std::make_pair(faction2, it->second));
@ -431,7 +429,7 @@ public:
void write(ESM::ESMWriter &esm) override
{
ESM::StolenItems items;
for (std::map<std::string, std::set<Owner> >::const_iterator it = mStolenItems.begin(); it != mStolenItems.end(); ++it)
for (auto it = mStolenItems.begin(); it != mStolenItems.end(); ++it)
{
std::map<std::pair<std::string, bool>, int> owners;
for (const auto & ownerIt : it->second)
@ -487,7 +485,7 @@ public:
}
void write(ESM::ESMWriter &esm) override
{
for (std::map<std::string, DIAL>::const_iterator it = mDials.begin(); it != mDials.end(); ++it)
for (auto it = mDials.begin(); it != mDials.end(); ++it)
{
esm.startRecord(ESM::REC_QUES);
ESM::QuestState state;

View File

@ -385,7 +385,7 @@ namespace ESSImport
// Writing order should be Dynamic Store -> Cells -> Player,
// so that references to dynamic records can be recognized when loading
for (std::map<unsigned int, std::shared_ptr<Converter> >::const_iterator it = converters.begin();
for (auto it = converters.begin();
it != converters.end(); ++it)
{
if (it->second->getStage() != 0)
@ -398,7 +398,7 @@ namespace ESSImport
context.mPlayerBase.save(writer);
writer.endRecord(ESM::REC_NPC_);
for (std::map<unsigned int, std::shared_ptr<Converter> >::const_iterator it = converters.begin();
for (auto it = converters.begin();
it != converters.end(); ++it)
{
if (it->second->getStage() != 1)
@ -423,7 +423,7 @@ namespace ESSImport
writer.endRecord(ESM::REC_ACTC);
// Stage 2 requires cell references to be written / actors IDs assigned
for (std::map<unsigned int, std::shared_ptr<Converter> >::const_iterator it = converters.begin();
for (auto it = converters.begin();
it != converters.end(); ++it)
{
if (it->second->getStage() != 2)

View File

@ -36,23 +36,6 @@ set(LAUNCHER_HEADER
)
# Headers that must be pre-processed
set(LAUNCHER_HEADER_MOC
datafilespage.hpp
graphicspage.hpp
maindialog.hpp
playpage.hpp
textslotmsgbox.hpp
settingspage.hpp
advancedpage.hpp
utils/cellnameloader.hpp
utils/textinputdialog.hpp
utils/profilescombobox.hpp
utils/lineedit.hpp
utils/openalutil.hpp
)
set(LAUNCHER_UI
${CMAKE_SOURCE_DIR}/files/ui/datafilespage.ui
${CMAKE_SOURCE_DIR}/files/ui/graphicspage.ui
@ -74,7 +57,6 @@ if(WIN32)
endif(WIN32)
QT5_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)
QT5_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC})
QT5_WRAP_UI(UI_HDRS ${LAUNCHER_UI})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
@ -99,7 +81,7 @@ endif (WIN32)
target_link_libraries(openmw-launcher
${SDL2_LIBRARY_ONLY}
${OPENAL_LIBRARY}
components
components_qt
)
target_link_libraries(openmw-launcher Qt5::Widgets Qt5::Core)
@ -109,4 +91,7 @@ if (BUILD_WITH_CODE_COVERAGE)
target_link_libraries(openmw-launcher gcov)
endif()
if(USE_QT)
set_property(TARGET openmw-launcher PROPERTY AUTOMOC ON)
endif(USE_QT)

View File

@ -192,6 +192,7 @@ bool Launcher::AdvancedPage::loadSettings()
if (showOwnedIndex >= 0 && showOwnedIndex <= 3)
showOwnedComboBox->setCurrentIndex(showOwnedIndex);
loadSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI");
loadSettingBool(useZoomOnMapCheckBox, "allow zooming", "Map");
loadSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game");
scalingSpinBox->setValue(Settings::Manager::getFloat("scaling factor", "GUI"));
}
@ -352,6 +353,7 @@ void Launcher::AdvancedPage::saveSettings()
if (showOwnedCurrentIndex != Settings::Manager::getInt("show owned", "Game"))
Settings::Manager::setInt("show owned", "Game", showOwnedCurrentIndex);
saveSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI");
saveSettingBool(useZoomOnMapCheckBox, "allow zooming", "Map");
saveSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game");
float uiScalingFactor = scalingSpinBox->value();
if (uiScalingFactor != Settings::Manager::getFloat("scaling factor", "GUI"))

View File

@ -17,7 +17,6 @@
#include <components/config/gamesettings.hpp>
#include <components/config/launchersettings.hpp>
#include <iostream>
#include "utils/textinputdialog.hpp"
#include "utils/profilescombobox.hpp"

View File

@ -14,8 +14,7 @@
#include <SDL_video.h>
#include <numeric>
#include <components/files/configurationmanager.hpp>
#include <array>
QString getAspect(int x, int y)
{

View File

@ -6,7 +6,6 @@
#include <QDate>
#include <QMessageBox>
#include <QFontDatabase>
#include <QInputDialog>
#include <QFileDialog>
#include <QCloseEvent>
#include <QTextCodec>
@ -52,8 +51,8 @@ Launcher::MainDialog::MainDialog(QWidget *parent)
iconWidget->setCurrentRow(0);
iconWidget->setFlow(QListView::LeftToRight);
QPushButton *helpButton = new QPushButton(tr("Help"));
QPushButton *playButton = new QPushButton(tr("Play"));
auto *helpButton = new QPushButton(tr("Help"));
auto *playButton = new QPushButton(tr("Play"));
buttonBox->button(QDialogButtonBox::Close)->setText(tr("Close"));
buttonBox->addButton(helpButton, QDialogButtonBox::HelpRole);
buttonBox->addButton(playButton, QDialogButtonBox::AcceptRole);
@ -79,31 +78,31 @@ void Launcher::MainDialog::createIcons()
if (!QIcon::hasThemeIcon("document-new"))
QIcon::setThemeName("tango");
QListWidgetItem *playButton = new QListWidgetItem(iconWidget);
auto *playButton = new QListWidgetItem(iconWidget);
playButton->setIcon(QIcon(":/images/openmw.png"));
playButton->setText(tr("Play"));
playButton->setTextAlignment(Qt::AlignCenter);
playButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
QListWidgetItem *dataFilesButton = new QListWidgetItem(iconWidget);
auto *dataFilesButton = new QListWidgetItem(iconWidget);
dataFilesButton->setIcon(QIcon(":/images/openmw-plugin.png"));
dataFilesButton->setText(tr("Data Files"));
dataFilesButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
QListWidgetItem *graphicsButton = new QListWidgetItem(iconWidget);
auto *graphicsButton = new QListWidgetItem(iconWidget);
graphicsButton->setIcon(QIcon(":/images/preferences-video.png"));
graphicsButton->setText(tr("Graphics"));
graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute);
graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
QListWidgetItem *settingsButton = new QListWidgetItem(iconWidget);
auto *settingsButton = new QListWidgetItem(iconWidget);
settingsButton->setIcon(QIcon(":/images/preferences.png"));
settingsButton->setText(tr("Settings"));
settingsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
settingsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
QListWidgetItem *advancedButton = new QListWidgetItem(iconWidget);
auto *advancedButton = new QListWidgetItem(iconWidget);
advancedButton->setIcon(QIcon(":/images/preferences-advanced.png"));
advancedButton->setText(tr("Advanced"));
advancedButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);

View File

@ -4,11 +4,6 @@
#include <QMessageBox>
#include <QDir>
#include <components/files/configurationmanager.hpp>
#include <components/config/gamesettings.hpp>
#include <components/config/launchersettings.hpp>
#include "utils/textinputdialog.hpp"
#include "datafilespage.hpp"

View File

@ -1,6 +1,5 @@
#include <cstring>
#include <vector>
#include <memory>
#include <apps/openmw/mwsound/alext.h>

View File

@ -29,7 +29,7 @@ void ProfilesComboBox::setEditEnabled(bool editable)
setEditable(true);
setValidator(mValidator);
ComboBoxLineEdit *edit = new ComboBoxLineEdit(this);
auto *edit = new ComboBoxLineEdit(this);
setLineEdit(edit);
setCompleter(nullptr);

View File

@ -16,7 +16,7 @@ Launcher::TextInputDialog::TextInputDialog(const QString& title, const QString &
mButtonBox->addButton(QDialogButtonBox::Cancel);
mButtonBox->button(QDialogButtonBox::Ok)->setEnabled (false);
QLabel *label = new QLabel(this);
auto *label = new QLabel(this);
label->setText(text);
// Line edit
@ -25,7 +25,7 @@ Launcher::TextInputDialog::TextInputDialog(const QString& title, const QString &
mLineEdit->setValidator(validator);
mLineEdit->setCompleter(nullptr);
QVBoxLayout *dialogLayout = new QVBoxLayout(this);
auto *dialogLayout = new QVBoxLayout(this);
dialogLayout->addWidget(label);
dialogLayout->addWidget(mLineEdit);
dialogLayout->addWidget(mButtonBox);

View File

@ -778,7 +778,7 @@ void MwIniImporter::mergeFallback(multistrmap &cfg, const multistrmap &ini) cons
}
void MwIniImporter::insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value) {
const multistrmap::const_iterator it = cfg.find(key);
const auto it = cfg.find(key);
if(it == cfg.end()) {
cfg.insert(std::make_pair (key, std::vector<std::string>() ));
}
@ -791,7 +791,7 @@ void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) con
std::string archive;
// Search archives listed in ini file
multistrmap::const_iterator it = ini.begin();
auto it = ini.begin();
for(int i=0; it != ini.end(); i++) {
archive = baseArchive;
archive.append(std::to_string(i));
@ -813,7 +813,7 @@ void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) con
// does not appears in the ini file
cfg["fallback-archive"].push_back("Morrowind.bsa");
for(std::vector<std::string>::const_iterator iter=archives.begin(); iter!=archives.end(); ++iter) {
for(auto iter=archives.begin(); iter!=archives.end(); ++iter) {
cfg["fallback-archive"].push_back(*iter);
}
}
@ -886,7 +886,7 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co
dataPaths.push_back(iniFilename.parent_path() /= "Data Files");
multistrmap::const_iterator it = ini.begin();
auto it = ini.begin();
for (int i=0; it != ini.end(); i++)
{
std::string gameFile = baseGameFile;
@ -969,7 +969,7 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co
void MwIniImporter::writeToFile(std::ostream &out, const multistrmap &cfg) {
for(multistrmap::const_iterator it=cfg.begin(); it != cfg.end(); ++it) {
for(std::vector<std::string>::const_iterator entry=it->second.begin(); entry != it->second.end(); ++entry) {
for(auto entry=it->second.begin(); entry != it->second.end(); ++entry) {
out << (it->first) << "=" << (*entry) << std::endl;
}
}

View File

@ -52,11 +52,8 @@ void readVFS(VFS::Archive* anArchive,std::string archivePath = "")
myManager.addArchive(anArchive);
myManager.buildIndex();
std::map<std::string, VFS::File*> files=myManager.getIndex();
for(std::map<std::string, VFS::File*>::const_iterator it=files.begin(); it!=files.end(); ++it)
for(const auto& name : myManager.getRecursiveDirectoryIterator(""))
{
std::string name = it->first;
try{
if(isNIF(name))
{
@ -134,7 +131,7 @@ int main(int argc, char **argv)
Nif::NIFFile::setLoadUnsupportedFiles(true);
// std::cout << "Reading Files" << std::endl;
for(std::vector<std::string>::const_iterator it=files.begin(); it!=files.end(); ++it)
for(auto it=files.begin(); it!=files.end(); ++it)
{
std::string name = *it;

View File

@ -8,29 +8,29 @@ opencs_units (model/doc
document operation saving documentmanager loader runner operationholder
)
opencs_units_noqt (model/doc
opencs_units (model/doc
stage savingstate savingstages blacklist messages
)
opencs_hdrs_noqt (model/doc
opencs_hdrs (model/doc
state
)
opencs_units (model/world
idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel landtexturetableproxymodel
actoradapter
actoradapter idcollection
)
opencs_units_noqt (model/world
opencs_units (model/world
universalid record commands columnbase columnimp scriptcontext cell refidcollection
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope
pathgrid landtexture land nestedtablewrapper nestedcollection nestedcoladapterimp nestedinfocollection
idcompletionmanager metadata defaultgmsts infoselectwrapper commandmacro
)
opencs_hdrs_noqt (model/world
opencs_hdrs (model/world
columnimp idcollection collection info subcellcollection
)
@ -39,14 +39,14 @@ opencs_units (model/tools
tools reportmodel mergeoperation
)
opencs_units_noqt (model/tools
opencs_units (model/tools
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck
startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck
mergestages gmstcheck topicinfocheck journalcheck enchantmentcheck
)
opencs_hdrs_noqt (model/tools
opencs_hdrs (model/tools
mergestate
)
@ -57,11 +57,11 @@ opencs_units (view/doc
)
opencs_units_noqt (view/doc
opencs_units (view/doc
subviewfactory
)
opencs_hdrs_noqt (view/doc
opencs_hdrs (view/doc
subviewfactoryimp
)
@ -71,10 +71,10 @@ opencs_units (view/world
cellcreator pathgridcreator referenceablecreator startscriptcreator referencecreator scenesubview
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator
bodypartcreator landtexturecreator landcreator
bodypartcreator landtexturecreator landcreator tableheadermouseeventhandler
)
opencs_units_noqt (view/world
opencs_units (view/world
subviews enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate
scripthighlighter idvalidator dialoguecreator idcompletiondelegate
colordelegate dragdroputils
@ -92,12 +92,12 @@ opencs_units (view/render
cellwater terraintexturemode actor terrainselection terrainshapemode brushdraw commands
)
opencs_units_noqt (view/render
opencs_units (view/render
lighting lightingday lightingnight lightingbright object cell terrainstorage tagbase
cellarrow cellmarker cellborder pathgrid
)
opencs_hdrs_noqt (view/render
opencs_hdrs (view/render
mask
)
@ -106,7 +106,7 @@ opencs_units (view/tools
reportsubview reporttable searchsubview searchbox merge
)
opencs_units_noqt (view/tools
opencs_units (view/tools
subviews
)
@ -119,11 +119,11 @@ opencs_units (model/prefs
shortcuteventhandler shortcutmanager shortcutsetting modifiersetting stringsetting
)
opencs_units_noqt (model/prefs
opencs_units (model/prefs
category
)
opencs_units_noqt (model/filter
opencs_units (model/filter
node unarynode narynode leafnode booleannode parser andnode ornode notnode textnode valuenode
)
@ -150,7 +150,6 @@ if(WIN32)
endif(WIN32)
qt5_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI})
qt5_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT})
qt5_add_resources(OPENCS_RES_SRC ${OPENCS_RES})
# for compiled .ui files
@ -229,34 +228,9 @@ target_link_libraries(openmw-cs
${Boost_SYSTEM_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
components
components_qt
)
if(OSG_STATIC)
unset(_osg_plugins_static_files)
add_library(openmw_cs_osg_plugins INTERFACE)
foreach(_plugin ${USED_OSG_PLUGINS})
string(TOUPPER ${_plugin} _plugin_uc)
if(OPENMW_USE_SYSTEM_OSG)
list(APPEND _osg_plugins_static_files ${${_plugin_uc}_LIBRARY})
else()
list(APPEND _osg_plugins_static_files $<TARGET_FILE:${${_plugin_uc}_LIBRARY}>)
target_link_libraries(openmw_cs_osg_plugins INTERFACE $<TARGET_PROPERTY:${${_plugin_uc}_LIBRARY},LINK_LIBRARIES>)
add_dependencies(openmw_cs_osg_plugins ${${_plugin_uc}_LIBRARY})
endif()
endforeach()
# We use --whole-archive because OSG plugins use registration.
get_whole_archive_options(_opts ${_osg_plugins_static_files})
target_link_options(openmw_cs_osg_plugins INTERFACE ${_opts})
target_link_libraries(openmw-cs openmw_cs_osg_plugins)
if(OPENMW_USE_SYSTEM_OSG)
# OSG plugin pkgconfig files are missing these dependencies.
# https://github.com/openscenegraph/OpenSceneGraph/issues/1052
target_link_libraries(openmw freetype jpeg png)
endif()
endif(OSG_STATIC)
target_link_libraries(openmw-cs Qt5::Widgets Qt5::Core Qt5::Network Qt5::OpenGL)
if (WIN32)
@ -284,3 +258,7 @@ endif (MSVC)
if(APPLE)
INSTALL(TARGETS openmw-cs BUNDLE DESTINATION "." COMPONENT Bundle)
endif()
if(USE_QT)
set_property(TARGET openmw-cs PROPERTY AUTOMOC ON)
endif(USE_QT)

View File

@ -1,6 +1,7 @@
#include "document.hpp"
#include <cassert>
#include <memory>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
@ -115,10 +116,10 @@ void CSMDoc::Document::addOptionalGmst (const ESM::GameSetting& gmst)
{
if (getData().getGmsts().searchId (gmst.mId)==-1)
{
CSMWorld::Record<ESM::GameSetting> record;
record.mBase = gmst;
record.mState = CSMWorld::RecordBase::State_BaseOnly;
getData().getGmsts().appendRecord (record);
std::unique_ptr<CSMWorld::Record<ESM::GameSetting> > record(new CSMWorld::Record<ESM::GameSetting>);
record->mBase = gmst;
record->mState = CSMWorld::RecordBase::State_BaseOnly;
getData().getGmsts().appendRecord (std::move(record));
}
}
@ -126,10 +127,10 @@ void CSMDoc::Document::addOptionalGlobal (const ESM::Global& global)
{
if (getData().getGlobals().searchId (global.mId)==-1)
{
CSMWorld::Record<ESM::Global> record;
record.mBase = global;
record.mState = CSMWorld::RecordBase::State_BaseOnly;
getData().getGlobals().appendRecord (record);
std::unique_ptr<CSMWorld::Record<ESM::Global> > record(new CSMWorld::Record<ESM::Global>);
record->mBase = global;
record->mState = CSMWorld::RecordBase::State_BaseOnly;
getData().getGlobals().appendRecord (std::move(record));
}
}
@ -137,10 +138,10 @@ void CSMDoc::Document::addOptionalMagicEffect (const ESM::MagicEffect& magicEffe
{
if (getData().getMagicEffects().searchId (magicEffect.mId)==-1)
{
CSMWorld::Record<ESM::MagicEffect> record;
record.mBase = magicEffect;
record.mState = CSMWorld::RecordBase::State_BaseOnly;
getData().getMagicEffects().appendRecord (record);
std::unique_ptr<CSMWorld::Record<ESM::MagicEffect> > record(new CSMWorld::Record<ESM::MagicEffect>);
record->mBase = magicEffect;
record->mState = CSMWorld::RecordBase::State_BaseOnly;
getData().getMagicEffects().appendRecord (std::move(record));
}
}

View File

@ -85,19 +85,19 @@ void CSMDoc::Loader::load()
return;
}
if (iter->second.mFile<size)
if (iter->second.mFile<size) // start loading the files
{
boost::filesystem::path path = document->getContentFiles()[iter->second.mFile];
int steps = document->getData().startLoading (path, iter->second.mFile!=editedIndex, false);
int steps = document->getData().startLoading (path, iter->second.mFile!=editedIndex, /*project*/false);
iter->second.mRecordsLeft = true;
iter->second.mRecordsLoaded = 0;
emit nextStage (document, path.filename().string(), steps);
}
else if (iter->second.mFile==size)
else if (iter->second.mFile==size) // start loading the last (project) file
{
int steps = document->getData().startLoading (document->getProjectPath(), false, true);
int steps = document->getData().startLoading (document->getProjectPath(), /*base*/false, true);
iter->second.mRecordsLeft = true;
iter->second.mRecordsLoaded = 0;

View File

@ -83,6 +83,8 @@ void CSMDoc::Runner::start (bool delayed)
arguments <<
QString::fromUtf8 (("--data=\""+mProjectPath.parent_path().string()+"\"").c_str());
arguments << "--replace=content";
for (std::vector<std::string>::const_iterator iter (mContentFiles.begin());
iter!=mContentFiles.end(); ++iter)
{

View File

@ -114,7 +114,7 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)
{
if (iter->isModified() || iter->mState == CSMWorld::RecordBase::State_Deleted)
if ((*iter)->isModified() || (*iter)->mState == CSMWorld::RecordBase::State_Deleted)
{
infoModified = true;
break;
@ -140,9 +140,9 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message
// write modified selected info records
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)
{
if (iter->isModified() || iter->mState == CSMWorld::RecordBase::State_Deleted)
if ((*iter)->isModified() || (*iter)->mState == CSMWorld::RecordBase::State_Deleted)
{
ESM::DialInfo info = iter->get();
ESM::DialInfo info = (*iter)->get();
info.mId = info.mId.substr (info.mId.find_last_of ('#')+1);
info.mPrev = "";
@ -151,7 +151,7 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message
CSMWorld::InfoCollection::RecordConstIterator prev = iter;
--prev;
info.mPrev = prev->get().mId.substr (prev->get().mId.find_last_of ('#')+1);
info.mPrev = (*prev)->get().mId.substr ((*prev)->get().mId.find_last_of ('#')+1);
}
CSMWorld::InfoCollection::RecordConstIterator next = iter;
@ -160,11 +160,11 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message
info.mNext = "";
if (next!=range.second)
{
info.mNext = next->get().mId.substr (next->get().mId.find_last_of ('#')+1);
info.mNext = (*next)->get().mId.substr ((*next)->get().mId.find_last_of ('#')+1);
}
writer.startRecord (info.sRecordId);
info.save (writer, iter->mState == CSMWorld::RecordBase::State_Deleted);
info.save (writer, (*iter)->mState == CSMWorld::RecordBase::State_Deleted);
writer.endRecord (info.sRecordId);
}
}

View File

@ -108,7 +108,7 @@ namespace CSMDoc
state == CSMWorld::RecordBase::State_ModifiedOnly ||
state == CSMWorld::RecordBase::State_Deleted)
{
writer.startRecord (record.sRecordId);
writer.startRecord (record.sRecordId, record.mRecordFlags);
record.save (writer, state == CSMWorld::RecordBase::State_Deleted);
writer.endRecord (record.sRecordId);
}

View File

@ -35,7 +35,7 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages)
for (CSMWorld::InfoCollection::RecordConstIterator it = range.first; it != range.second; ++it)
{
const CSMWorld::Record<CSMWorld::Info> infoRecord = (*it);
const CSMWorld::Record<CSMWorld::Info> infoRecord = (*it->get());
if (infoRecord.isDeleted())
continue;

View File

@ -103,10 +103,9 @@ void CSMTools::MergeReferencesStage::perform (int stage, CSMDoc::Messages& messa
ref.mRefNum.mContentFile = 0;
ref.mNew = false;
CSMWorld::Record<CSMWorld::CellRef> newRecord (
CSMWorld::RecordBase::State_ModifiedOnly, nullptr, &ref);
mState.mTarget->getData().getReferences().appendRecord (newRecord);
mState.mTarget->getData().getReferences().appendRecord (
std::make_unique<CSMWorld::Record<CSMWorld::CellRef> >(
CSMWorld::Record<CSMWorld::CellRef>(CSMWorld::RecordBase::State_ModifiedOnly, nullptr, &ref)));
}
}
@ -128,7 +127,9 @@ void CSMTools::PopulateLandTexturesMergeStage::perform (int stage, CSMDoc::Messa
if (!record.isDeleted())
{
mState.mTarget->getData().getLandTextures().appendRecord(record);
mState.mTarget->getData().getLandTextures().appendRecord(
std::make_unique<CSMWorld::Record<CSMWorld::LandTexture> >(
CSMWorld::Record<CSMWorld::LandTexture>(CSMWorld::RecordBase::State_ModifiedOnly, nullptr, &record.get())));
}
}
@ -150,7 +151,9 @@ void CSMTools::MergeLandStage::perform (int stage, CSMDoc::Messages& messages)
if (!record.isDeleted())
{
mState.mTarget->getData().getLand().appendRecord (record);
mState.mTarget->getData().getLand().appendRecord (
std::make_unique<CSMWorld::Record<CSMWorld::Land> >(
CSMWorld::Record<CSMWorld::Land>(CSMWorld::RecordBase::State_ModifiedOnly, nullptr, &record.get())));
}
}
@ -185,10 +188,9 @@ void CSMTools::FixLandsAndLandTexturesMergeStage::perform (int stage, CSMDoc::Me
const CSMWorld::Record<CSMWorld::Land>& oldRecord =
mState.mTarget->getData().getLand().getRecord (stage);
CSMWorld::Record<CSMWorld::Land> newRecord(CSMWorld::RecordBase::State_ModifiedOnly,
nullptr, &oldRecord.get());
mState.mTarget->getData().getLand().setRecord(stage, newRecord);
mState.mTarget->getData().getLand().setRecord (stage,
std::make_unique<CSMWorld::Record<CSMWorld::Land> >(
CSMWorld::Record<CSMWorld::Land>(CSMWorld::RecordBase::State_ModifiedOnly, nullptr, &oldRecord.get())));
}
}

View File

@ -3,6 +3,7 @@
#include <algorithm>
#include <map>
#include <memory>
#include <components/to_utf8/to_utf8.hpp>
@ -82,7 +83,8 @@ namespace CSMTools
const CSMWorld::Record<RecordType>& record = source.getRecord (stage);
if (!record.isDeleted())
target.appendRecord (CSMWorld::Record<RecordType> (CSMWorld::RecordBase::State_ModifiedOnly, nullptr, &record.get()));
target.appendRecord (std::make_unique<CSMWorld::Record<RecordType> >(
CSMWorld::Record<RecordType>(CSMWorld::RecordBase::State_ModifiedOnly, nullptr, &record.get())));
}
class MergeRefIdsStage : public CSMDoc::Stage

View File

@ -9,6 +9,9 @@
#include "data.hpp"
#include <string>
#include <string_view>
namespace CSMWorld
{
const std::string& ActorAdapter::RaceData::getId() const
@ -121,7 +124,7 @@ namespace CSMWorld
return SceneUtil::getActorSkeleton(firstPerson, mFemale, beast, werewolf);
}
const std::string ActorAdapter::ActorData::getPart(ESM::PartReferenceType index) const
std::string_view ActorAdapter::ActorData::getPart(ESM::PartReferenceType index) const
{
auto it = mParts.find(index);
if (it == mParts.end())
@ -131,7 +134,7 @@ namespace CSMWorld
if (mFemale)
{
// Note: we should use male parts for females as fallback
const std::string femalePart = mRaceData->getFemalePart(index);
const std::string& femalePart = mRaceData->getFemalePart(index);
if (!femalePart.empty())
return femalePart;
}
@ -139,11 +142,10 @@ namespace CSMWorld
return mRaceData->getMalePart(index);
}
return "";
return {};
}
const std::string& partName = it->second.first;
return partName;
return it->second.first;
}
bool ActorAdapter::ActorData::hasDependency(const std::string& id) const

View File

@ -4,6 +4,8 @@
#include <array>
#include <map>
#include <unordered_set>
#include <string>
#include <string_view>
#include <QObject>
#include <QModelIndex>
@ -93,7 +95,7 @@ namespace CSMWorld
/// Returns the skeleton the actor should use for attaching parts to
std::string getSkeleton() const;
/// Retrieves the associated actor part
const std::string getPart(ESM::PartReferenceType index) const;
std::string_view getPart(ESM::PartReferenceType index) const;
/// Checks if the actor has a data dependency
bool hasDependency(const std::string& id) const;

View File

@ -8,6 +8,8 @@
#include <stdexcept>
#include <string>
#include <functional>
#include <memory>
#include <string_view>
#include <QVariant>
@ -84,7 +86,7 @@ namespace CSMWorld
private:
std::vector<Record<ESXRecordT> > mRecords;
std::vector<std::unique_ptr<Record<ESXRecordT> > > mRecords;
std::map<std::string, int> mIndex;
std::vector<Column<ESXRecordT> *> mColumns;
@ -94,9 +96,7 @@ namespace CSMWorld
protected:
const std::map<std::string, int>& getIdMap() const;
const std::vector<Record<ESXRecordT> >& getRecords() const;
const std::vector<std::unique_ptr<Record<ESXRecordT> > >& getRecords() const;
bool reorderRowsImp (int baseIndex, const std::vector<int>& newOrder);
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
@ -154,16 +154,16 @@ namespace CSMWorld
///< Change the state of a record from base to modified, if it is not already.
/// \return True if the record was changed.
int searchId (const std::string& id) const override;
int searchId(std::string_view id) const override;
////< Search record with \a id.
/// \return index of record (if found) or -1 (not found)
void replace (int index, const RecordBase& record) override;
void replace (int index, std::unique_ptr<RecordBase> record) override;
///< If the record type does not match, an exception is thrown.
///
/// \attention \a record must not change the ID.
void appendRecord (const RecordBase& record,
void appendRecord (std::unique_ptr<RecordBase> record,
UniversalId::Type type = UniversalId::Type_None) override;
///< If the record type does not match, an exception is thrown.
///< \param type Will be ignored, unless the collection supports multiple record types
@ -181,7 +181,7 @@ namespace CSMWorld
///
/// \param listDeleted include deleted record in the list
virtual void insertRecord (const RecordBase& record, int index,
virtual void insertRecord (std::unique_ptr<RecordBase> record, int index,
UniversalId::Type type = UniversalId::Type_None);
///< Insert record before index.
///
@ -198,20 +198,14 @@ namespace CSMWorld
void addColumn (Column<ESXRecordT> *column);
void setRecord (int index, const Record<ESXRecordT>& record);
void setRecord (int index, std::unique_ptr<Record<ESXRecordT> > record);
///< \attention This function must not change the ID.
NestableColumn *getNestableColumn (int column) const;
};
template<typename ESXRecordT, typename IdAccessorT>
const std::map<std::string, int>& Collection<ESXRecordT, IdAccessorT>::getIdMap() const
{
return mIndex;
}
template<typename ESXRecordT, typename IdAccessorT>
const std::vector<Record<ESXRecordT> >& Collection<ESXRecordT, IdAccessorT>::getRecords() const
const std::vector<std::unique_ptr<Record<ESXRecordT> > >& Collection<ESXRecordT, IdAccessorT>::getRecords() const
{
return mRecords;
}
@ -231,15 +225,15 @@ namespace CSMWorld
return false;
// reorder records
std::vector<Record<ESXRecordT> > buffer (size);
std::vector<std::unique_ptr<Record<ESXRecordT> > > buffer (size);
for (int i=0; i<size; ++i)
{
buffer[newOrder[i]] = mRecords [baseIndex+i];
buffer[newOrder[i]].setModified (buffer[newOrder[i]].get());
buffer[newOrder[i]] = std::move(mRecords [baseIndex+i]);
buffer[newOrder[i]]->setModified (buffer[newOrder[i]]->get());
}
std::copy (buffer.begin(), buffer.end(), mRecords.begin()+baseIndex);
std::move (buffer.begin(), buffer.end(), mRecords.begin()+baseIndex);
// adjust index
for (std::map<std::string, int>::iterator iter (mIndex.begin()); iter!=mIndex.end();
@ -255,18 +249,18 @@ namespace CSMWorld
int Collection<ESXRecordT, IdAccessorT>::cloneRecordImp(const std::string& origin,
const std::string& destination, UniversalId::Type type)
{
Record<ESXRecordT> copy;
copy.mModified = getRecord(origin).get();
copy.mState = RecordBase::State_ModifiedOnly;
IdAccessorT().setId(copy.get(), destination);
std::unique_ptr<Record<ESXRecordT> > copy(new Record<ESXRecordT>);
copy->mModified = getRecord(origin).get();
copy->mState = RecordBase::State_ModifiedOnly;
IdAccessorT().setId(copy->get(), destination);
if (type == UniversalId::Type_Reference) {
CSMWorld::CellRef* ptr = (CSMWorld::CellRef*) &copy.mModified;
CSMWorld::CellRef* ptr = (CSMWorld::CellRef*) &copy->mModified;
ptr->mRefNum.mIndex = 0;
}
int index = getAppendIndex(destination, type);
insertRecord(copy, getAppendIndex(destination, type));
insertRecord(std::move(copy), getAppendIndex(destination, type));
return index;
}
@ -275,7 +269,7 @@ namespace CSMWorld
int Collection<ESXRecordT, IdAccessorT>::touchRecordImp(const std::string& id)
{
int index = getIndex(id);
Record<ESXRecordT>& record = mRecords.at(index);
Record<ESXRecordT>& record = *mRecords.at(index);
if (record.isDeleted())
{
throw std::runtime_error("attempt to touch deleted record");
@ -302,7 +296,7 @@ namespace CSMWorld
const std::string& destination, const UniversalId::Type type)
{
int index = cloneRecordImp(origin, destination, type);
mRecords.at(index).get().mPlugin = 0;
mRecords.at(index)->get().mPlugin = 0;
}
template<typename ESXRecordT, typename IdAccessorT>
@ -317,7 +311,7 @@ namespace CSMWorld
int index = touchRecordImp(id);
if (index >= 0)
{
mRecords.at(index).get().mPlugin = 0;
mRecords.at(index)->get().mPlugin = 0;
return true;
}
@ -344,15 +338,15 @@ namespace CSMWorld
if (iter==mIndex.end())
{
Record<ESXRecordT> record2;
record2.mState = Record<ESXRecordT>::State_ModifiedOnly;
record2.mModified = record;
std::unique_ptr<Record<ESXRecordT> > record2(new Record<ESXRecordT>);
record2->mState = Record<ESXRecordT>::State_ModifiedOnly;
record2->mModified = record;
insertRecord (record2, getAppendIndex (id));
insertRecord (std::move(record2), getAppendIndex (id));
}
else
{
mRecords[iter->second].setModified (record);
mRecords[iter->second]->setModified (record);
}
}
@ -365,7 +359,7 @@ namespace CSMWorld
template<typename ESXRecordT, typename IdAccessorT>
std::string Collection<ESXRecordT, IdAccessorT>::getId (int index) const
{
return IdAccessorT().getId (mRecords.at (index).get());
return IdAccessorT().getId (mRecords.at (index)->get());
}
template<typename ESXRecordT, typename IdAccessorT>
@ -388,13 +382,13 @@ namespace CSMWorld
template<typename ESXRecordT, typename IdAccessorT>
QVariant Collection<ESXRecordT, IdAccessorT>::getData (int index, int column) const
{
return mColumns.at (column)->get (mRecords.at (index));
return mColumns.at (column)->get (*mRecords.at (index));
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::setData (int index, int column, const QVariant& data)
{
return mColumns.at (column)->set (mRecords.at (index), data);
return mColumns.at (column)->set (*mRecords.at (index), data);
}
template<typename ESXRecordT, typename IdAccessorT>
@ -421,8 +415,8 @@ namespace CSMWorld
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::merge()
{
for (typename std::vector<Record<ESXRecordT> >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter)
iter->merge();
for (typename std::vector<std::unique_ptr<Record<ESXRecordT> > >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter)
(*iter)->merge();
purge();
}
@ -434,7 +428,7 @@ namespace CSMWorld
while (i<static_cast<int> (mRecords.size()))
{
if (mRecords[i].isErased())
if (mRecords[i]->isErased())
removeRows (i, 1);
else
++i;
@ -475,15 +469,15 @@ namespace CSMWorld
IdAccessorT().setId(record, id);
record.blank();
Record<ESXRecordT> record2;
record2.mState = Record<ESXRecordT>::State_ModifiedOnly;
record2.mModified = record;
std::unique_ptr<Record<ESXRecordT> > record2(new Record<ESXRecordT>);
record2->mState = Record<ESXRecordT>::State_ModifiedOnly;
record2->mModified = record;
insertRecord (record2, getAppendIndex (id, type), type);
insertRecord (std::move(record2), getAppendIndex (id, type), type);
}
template<typename ESXRecordT, typename IdAccessorT>
int Collection<ESXRecordT, IdAccessorT>::searchId (const std::string& id) const
int Collection<ESXRecordT, IdAccessorT>::searchId(std::string_view id) const
{
std::string id2 = Misc::StringUtils::lowerCase(id);
@ -496,18 +490,19 @@ namespace CSMWorld
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::replace (int index, const RecordBase& record)
void Collection<ESXRecordT, IdAccessorT>::replace (int index, std::unique_ptr<RecordBase> record)
{
mRecords.at (index) = dynamic_cast<const Record<ESXRecordT>&> (record);
std::unique_ptr<Record<ESXRecordT> > tmp(static_cast<Record<ESXRecordT>*>(record.release()));
mRecords.at (index) = std::move(tmp);
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::appendRecord (const RecordBase& record,
void Collection<ESXRecordT, IdAccessorT>::appendRecord (std::unique_ptr<RecordBase> record,
UniversalId::Type type)
{
insertRecord (record,
getAppendIndex (IdAccessorT().getId (
dynamic_cast<const Record<ESXRecordT>&> (record).get()), type), type);
int index =
getAppendIndex(IdAccessorT().getId(static_cast<Record<ESXRecordT>*>(record.get())->get()), type);
insertRecord (std::move(record), index, type);
}
template<typename ESXRecordT, typename IdAccessorT>
@ -525,8 +520,8 @@ namespace CSMWorld
for (typename std::map<std::string, int>::const_iterator iter = mIndex.begin();
iter!=mIndex.end(); ++iter)
{
if (listDeleted || !mRecords[iter->second].isDeleted())
ids.push_back (IdAccessorT().getId (mRecords[iter->second].get()));
if (listDeleted || !mRecords[iter->second]->isDeleted())
ids.push_back (IdAccessorT().getId (mRecords[iter->second]->get()));
}
return ids;
@ -536,46 +531,52 @@ namespace CSMWorld
const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (const std::string& id) const
{
int index = getIndex (id);
return mRecords.at (index);
return *mRecords.at (index);
}
template<typename ESXRecordT, typename IdAccessorT>
const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (int index) const
{
return mRecords.at (index);
return *mRecords.at (index);
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::insertRecord (const RecordBase& record, int index,
void Collection<ESXRecordT, IdAccessorT>::insertRecord (std::unique_ptr<RecordBase> record, int index,
UniversalId::Type type)
{
if (index<0 || index>static_cast<int> (mRecords.size()))
int size = static_cast<int>(mRecords.size());
if (index < 0 || index > size)
throw std::runtime_error ("index out of range");
const Record<ESXRecordT>& record2 = dynamic_cast<const Record<ESXRecordT>&> (record);
std::unique_ptr<Record<ESXRecordT> > record2(static_cast<Record<ESXRecordT>*>(record.release()));
std::string lowerId = Misc::StringUtils::lowerCase(IdAccessorT().getId(record2->get()));
mRecords.insert (mRecords.begin()+index, record2);
if (index == size)
mRecords.push_back (std::move(record2));
else
mRecords.insert (mRecords.begin()+index, std::move(record2));
if (index<static_cast<int> (mRecords.size())-1)
if (index < size-1)
{
for (std::map<std::string, int>::iterator iter (mIndex.begin()); iter!=mIndex.end();
++iter)
if (iter->second>=index)
++(iter->second);
for (std::map<std::string, int>::iterator iter (mIndex.begin()); iter!=mIndex.end(); ++iter)
{
if (iter->second >= index)
++(iter->second);
}
}
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (IdAccessorT().getId (
record2.get())), index));
mIndex.insert (std::make_pair (lowerId, index));
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::setRecord (int index, const Record<ESXRecordT>& record)
void Collection<ESXRecordT, IdAccessorT>::setRecord (int index,
std::unique_ptr<Record<ESXRecordT> > record)
{
if (Misc::StringUtils::lowerCase (IdAccessorT().getId (mRecords.at (index).get()))!=
Misc::StringUtils::lowerCase (IdAccessorT().getId (record.get())))
if (Misc::StringUtils::lowerCase (IdAccessorT().getId (mRecords.at (index)->get())) !=
Misc::StringUtils::lowerCase (IdAccessorT().getId (record->get())))
throw std::runtime_error ("attempt to change the ID of a record");
mRecords.at (index) = record;
mRecords.at (index) = std::move(record);
}
template<typename ESXRecordT, typename IdAccessorT>

View File

@ -8,6 +8,11 @@ CSMWorld::CollectionBase::CollectionBase() {}
CSMWorld::CollectionBase::~CollectionBase() {}
int CSMWorld::CollectionBase::getInsertIndex (const std::string& id, UniversalId::Type type, RecordBase *record) const
{
return getAppendIndex(id, type);
}
int CSMWorld::CollectionBase::searchColumnIndex (Columns::ColumnId id) const
{
int columns = getColumns();

View File

@ -3,6 +3,8 @@
#include <string>
#include <vector>
#include <memory>
#include <string_view>
#include "universalid.hpp"
#include "columns.hpp"
@ -60,17 +62,17 @@ namespace CSMWorld
UniversalId::Type type = UniversalId::Type_None) = 0;
///< \param type Will be ignored, unless the collection supports multiple record types
virtual int searchId (const std::string& id) const = 0;
virtual int searchId(std::string_view id) const = 0;
////< Search record with \a id.
/// \return index of record (if found) or -1 (not found)
virtual void replace (int index, const RecordBase& record) = 0;
virtual void replace (int index, std::unique_ptr<RecordBase> record) = 0;
///< If the record type does not match, an exception is thrown.
///
/// \attention \a record must not change the ID.
///< \param type Will be ignored, unless the collection supports multiple record types
virtual void appendRecord (const RecordBase& record,
virtual void appendRecord (std::unique_ptr<RecordBase> record,
UniversalId::Type type = UniversalId::Type_None) = 0;
///< If the record type does not match, an exception is thrown.
@ -99,6 +101,12 @@ namespace CSMWorld
///
/// \return Success?
virtual int getInsertIndex (const std::string& id,
UniversalId::Type type = UniversalId::Type_None,
RecordBase *record = nullptr) const;
///< Works like getAppendIndex unless an overloaded method uses the record pointer
/// to get additional info about the record that results in an alternative index.
int searchColumnIndex (Columns::ColumnId id) const;
///< Return index of column with the given \a id. If no such column exists, -1 is returned.

View File

@ -104,7 +104,8 @@ bool CSMWorld::ColumnBase::isId (Display display)
bool CSMWorld::ColumnBase::isText (Display display)
{
return display==Display_String || display==Display_LongString ||
display==Display_String32 || display==Display_LongString256;
display==Display_String32 || display==Display_String64 ||
display==Display_LongString256;
}
bool CSMWorld::ColumnBase::isScript (Display display)

View File

@ -135,6 +135,7 @@ namespace CSMWorld
Display_InfoCondVar,
Display_InfoCondComp,
Display_String32,
Display_String64,
Display_LongString256,
Display_BookType,
Display_BloodType,

View File

@ -334,7 +334,8 @@ namespace CSMWorld
template<typename ESXRecordT>
struct NameColumn : public Column<ESXRecordT>
{
NameColumn() : Column<ESXRecordT> (Columns::ColumnId_Name, ColumnBase::Display_String) {}
NameColumn(ColumnBase::Display display = ColumnBase::Display_String)
: Column<ESXRecordT> (Columns::ColumnId_Name, display) {}
QVariant get (const Record<ESXRecordT>& record) const override
{

View File

@ -371,6 +371,7 @@ namespace CSMWorld
{ ColumnId_Skill7, "Skill 7" },
{ ColumnId_Persistent, "Persistent" },
{ ColumnId_Blocked, "Blocked" },
{ -1, 0 } // end marker
};
@ -391,7 +392,7 @@ int CSMWorld::Columns::getId (const std::string& name)
std::string name2 = Misc::StringUtils::lowerCase (name);
for (int i=0; sNames[i].mName; ++i)
if (Misc::StringUtils::ciEqual(sNames[i].mName, name2))
if (Misc::StringUtils::ciEqual(std::string_view(sNames[i].mName), name2))
return sNames[i].mId;
return -1;

View File

@ -344,6 +344,7 @@ namespace CSMWorld
ColumnId_FactionAttrib2 = 312,
ColumnId_Persistent = 313,
ColumnId_Blocked = 314,
// Allocated to a separate value range, so we don't get a collision should we ever need
// to extend the number of use values.

View File

@ -141,7 +141,6 @@ void CSMWorld::CommandDispatcher::executeModify (QAbstractItemModel *model, cons
std::unique_ptr<CSMWorld::UpdateCellCommand> modifyCell;
std::unique_ptr<CSMWorld::ModifyCommand> modifyDataRefNum;
int columnId = model->data (index, ColumnBase::Role_ColumnId).toInt();
@ -170,14 +169,8 @@ void CSMWorld::CommandDispatcher::executeModify (QAbstractItemModel *model, cons
if (cellId.find ('#')!=std::string::npos)
{
// Need to recalculate the cell and (if necessary) clear the instance's refNum
// Need to recalculate the cell
modifyCell.reset (new UpdateCellCommand (model2, row));
// Not sure which model this should be applied to
int refNumColumn = model2.searchColumnIndex (Columns::ColumnId_RefNum);
if (refNumColumn!=-1)
modifyDataRefNum.reset (new ModifyCommand(*model, model->index(row, refNumColumn), 0));
}
}
}
@ -191,8 +184,6 @@ void CSMWorld::CommandDispatcher::executeModify (QAbstractItemModel *model, cons
CommandMacro macro (mDocument.getUndoStack());
macro.push (modifyData.release());
macro.push (modifyCell.release());
if (modifyDataRefNum.get())
macro.push (modifyDataRefNum.release());
}
else
mDocument.getUndoStack().push (modifyData.release());

View File

@ -24,11 +24,11 @@ CSMWorld::TouchCommand::TouchCommand(IdTable& table, const std::string& id, QUnd
, mChanged(false)
{
setText(("Touch " + mId).c_str());
mOld.reset(mTable.getRecord(mId).clone());
}
void CSMWorld::TouchCommand::redo()
{
mOld.reset(mTable.getRecord(mId).clone().get());
mChanged = mTable.touchRecord(mId);
}
@ -36,7 +36,7 @@ void CSMWorld::TouchCommand::undo()
{
if (mChanged)
{
mTable.setRecord(mId, *mOld);
mTable.setRecord(mId, std::move(mOld));
mChanged = false;
}
}
@ -159,7 +159,6 @@ CSMWorld::TouchLandCommand::TouchLandCommand(IdTable& landTable, IdTable& ltexTa
, mChanged(false)
{
setText(("Touch " + mId).c_str());
mOld.reset(mLands.getRecord(mId).clone());
}
const std::string& CSMWorld::TouchLandCommand::getOriginId() const
@ -175,13 +174,14 @@ const std::string& CSMWorld::TouchLandCommand::getDestinationId() const
void CSMWorld::TouchLandCommand::onRedo()
{
mChanged = mLands.touchRecord(mId);
if (mChanged) mOld.reset(mLands.getRecord(mId).clone().get());
}
void CSMWorld::TouchLandCommand::onUndo()
{
if (mChanged)
{
mLands.setRecord(mId, *mOld);
mLands.setRecord(mId, std::move(mOld));
mChanged = false;
}
}
@ -190,13 +190,16 @@ CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelI
const QVariant& new_, QUndoCommand* parent)
: QUndoCommand (parent), mModel (&model), mIndex (index), mNew (new_), mHasRecordState(false), mOldRecordState(CSMWorld::RecordBase::State_BaseOnly)
{
if (QAbstractProxyModel *proxy = dynamic_cast<QAbstractProxyModel *> (&model))
if (QAbstractProxyModel *proxy = dynamic_cast<QAbstractProxyModel *> (mModel))
{
// Replace proxy with actual model
mIndex = proxy->mapToSource (index);
mIndex = proxy->mapToSource (mIndex);
mModel = proxy->sourceModel();
}
}
void CSMWorld::ModifyCommand::redo()
{
if (mIndex.parent().isValid())
{
CSMWorld::IdTree* tree = &dynamic_cast<CSMWorld::IdTree&>(*mModel);
@ -223,10 +226,7 @@ CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelI
mRecordStateIndex = table->index(rowIndex, stateColumnIndex);
mOldRecordState = static_cast<CSMWorld::RecordBase::State>(table->data(mRecordStateIndex).toInt());
}
}
void CSMWorld::ModifyCommand::redo()
{
mOld = mModel->data (mIndex, Qt::EditRole);
mModel->setData (mIndex, mNew);
}
@ -291,20 +291,19 @@ void CSMWorld::CreateCommand::undo()
}
CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand* parent)
: QUndoCommand (parent), mModel (model), mId (id), mOld (nullptr)
: QUndoCommand (parent), mModel (model), mId (id), mOld(nullptr)
{
setText (("Revert record " + id).c_str());
mOld = model.getRecord (id).clone();
}
CSMWorld::RevertCommand::~RevertCommand()
{
delete mOld;
}
void CSMWorld::RevertCommand::redo()
{
mOld = mModel.getRecord (mId).clone();
int column = mModel.findColumnIndex (Columns::ColumnId_Modification);
QModelIndex index = mModel.getModelIndex (mId, column);
@ -322,25 +321,24 @@ void CSMWorld::RevertCommand::redo()
void CSMWorld::RevertCommand::undo()
{
mModel.setRecord (mId, *mOld);
mModel.setRecord (mId, std::move(mOld));
}
CSMWorld::DeleteCommand::DeleteCommand (IdTable& model,
const std::string& id, CSMWorld::UniversalId::Type type, QUndoCommand* parent)
: QUndoCommand (parent), mModel (model), mId (id), mOld (nullptr), mType(type)
: QUndoCommand (parent), mModel (model), mId (id), mOld(nullptr), mType(type)
{
setText (("Delete record " + id).c_str());
mOld = model.getRecord (id).clone();
}
CSMWorld::DeleteCommand::~DeleteCommand()
{
delete mOld;
}
void CSMWorld::DeleteCommand::redo()
{
mOld = mModel.getRecord (mId).clone();
int column = mModel.findColumnIndex (Columns::ColumnId_Modification);
QModelIndex index = mModel.getModelIndex (mId, column);
@ -358,7 +356,7 @@ void CSMWorld::DeleteCommand::redo()
void CSMWorld::DeleteCommand::undo()
{
mModel.setRecord (mId, *mOld, mType);
mModel.setRecord (mId, std::move(mOld), mType);
}
@ -424,18 +422,19 @@ void CSMWorld::CreatePathgridCommand::redo()
{
CreateCommand::redo();
Record<Pathgrid> record = static_cast<const Record<Pathgrid>& >(mModel.getRecord(mId));
record.get().blank();
record.get().mCell = mId;
std::unique_ptr<Record<Pathgrid> > record
= std::make_unique<Record<Pathgrid> >(static_cast<const Record<Pathgrid>& >(mModel.getRecord(mId)));
record->get().blank();
record->get().mCell = mId;
std::pair<CellCoordinates, bool> coords = CellCoordinates::fromId(mId);
if (coords.second)
{
record.get().mData.mX = coords.first.getX();
record.get().mData.mY = coords.first.getY();
record->get().mData.mX = coords.first.getX();
record->get().mData.mY = coords.first.getY();
}
mModel.setRecord(mId, record, mType);
mModel.setRecord(mId, std::move(record), mType);
}
CSMWorld::UpdateCellCommand::UpdateCellCommand (IdTable& model, int row, QUndoCommand *parent)
@ -499,8 +498,8 @@ CSMWorld::DeleteNestedCommand::DeleteNestedCommand (IdTree& model,
void CSMWorld::DeleteNestedCommand::redo()
{
QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn);
mModel.removeRows (mNestedRow, 1, parentIndex);
mModifyParentCommand->redo();
mModel.removeRows (mNestedRow, 1, parentIndex);
}
@ -530,8 +529,8 @@ CSMWorld::AddNestedCommand::AddNestedCommand(IdTree& model, const std::string& i
void CSMWorld::AddNestedCommand::redo()
{
QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn);
mModel.addNestedRow (parentIndex, mNewRow);
mModifyParentCommand->redo();
mModel.addNestedRow (parentIndex, mNewRow);
}
void CSMWorld::AddNestedCommand::undo()

View File

@ -203,7 +203,7 @@ namespace CSMWorld
{
IdTable& mModel;
std::string mId;
RecordBase *mOld;
std::unique_ptr<RecordBase> mOld;
// not implemented
RevertCommand (const RevertCommand&);
@ -224,7 +224,7 @@ namespace CSMWorld
{
IdTable& mModel;
std::string mId;
RecordBase *mOld;
std::unique_ptr<RecordBase> mOld;
UniversalId::Type mType;
// not implemented

View File

@ -84,6 +84,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat
defines["preLightEnv"] = "0"; // Apply environment maps after lighting like Morrowind
defines["radialFog"] = "0";
defines["lightingModel"] = "0";
defines["reverseZ"] = "0";
for (const auto& define : shadowDefines)
defines[define.first] = define.second;
mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines);
@ -130,7 +131,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat
mFactions.addColumn (new StringIdColumn<ESM::Faction>);
mFactions.addColumn (new RecordStateColumn<ESM::Faction>);
mFactions.addColumn (new FixedRecordTypeColumn<ESM::Faction> (UniversalId::Type_Faction));
mFactions.addColumn (new NameColumn<ESM::Faction>);
mFactions.addColumn (new NameColumn<ESM::Faction>(ColumnBase::Display_String32));
mFactions.addColumn (new AttributesColumn<ESM::Faction> (0));
mFactions.addColumn (new AttributesColumn<ESM::Faction> (1));
mFactions.addColumn (new HiddenColumn<ESM::Faction>);
@ -339,7 +340,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat
mCells.addColumn (new StringIdColumn<Cell>);
mCells.addColumn (new RecordStateColumn<Cell>);
mCells.addColumn (new FixedRecordTypeColumn<Cell> (UniversalId::Type_Cell));
mCells.addColumn (new NameColumn<Cell>);
mCells.addColumn (new NameColumn<Cell>(ColumnBase::Display_String64));
mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_SleepForbidden, ESM::Cell::NoSleep));
mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_InteriorWater, ESM::Cell::HasWater,
ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh));
@ -917,8 +918,8 @@ const CSMWorld::MetaData& CSMWorld::Data::getMetaData() const
void CSMWorld::Data::setMetaData (const MetaData& metaData)
{
Record<MetaData> record (RecordBase::State_ModifiedOnly, nullptr, &metaData);
mMetaData.setRecord (0, record);
mMetaData.setRecord (0, std::make_unique<Record<MetaData> >(
Record<MetaData>(RecordBase::State_ModifiedOnly, nullptr, &metaData)));
}
QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId& id)
@ -958,6 +959,25 @@ void CSMWorld::Data::merge()
mGlobals.merge();
}
int CSMWorld::Data::getTotalRecords (const std::vector<boost::filesystem::path>& files)
{
int records = 0;
std::unique_ptr<ESM::ESMReader> reader = std::unique_ptr<ESM::ESMReader>(new ESM::ESMReader);
for (unsigned int i = 0; i < files.size(); ++i)
{
if (!boost::filesystem::exists(files[i]))
continue;
reader->open(files[i].string());
records += reader->getRecordCount();
reader->close();
}
return records;
}
int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base, bool project)
{
// Don't delete the Reader yet. Some record types store a reference to the Reader to handle on-demand loading
@ -983,7 +1003,8 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
metaData.mId = "sys::meta";
metaData.load (*mReader);
mMetaData.setRecord (0, Record<MetaData> (RecordBase::State_ModifiedOnly, nullptr, &metaData));
mMetaData.setRecord (0, std::make_unique<Record<MetaData> >(
Record<MetaData> (RecordBase::State_ModifiedOnly, nullptr, &metaData)));
}
return mReader->getRecordCount();
@ -1011,10 +1032,10 @@ void CSMWorld::Data::loadFallbackEntries()
ESM::Static newMarker;
newMarker.mId = marker.first;
newMarker.mModel = marker.second;
CSMWorld::Record<ESM::Static> record;
record.mBase = newMarker;
record.mState = CSMWorld::RecordBase::State_BaseOnly;
mReferenceables.appendRecord (record, CSMWorld::UniversalId::Type_Static);
std::unique_ptr<CSMWorld::Record<ESM::Static> > record(new CSMWorld::Record<ESM::Static>);
record->mBase = newMarker;
record->mState = CSMWorld::RecordBase::State_BaseOnly;
mReferenceables.appendRecord (std::move(record), CSMWorld::UniversalId::Type_Static);
}
}
@ -1025,10 +1046,10 @@ void CSMWorld::Data::loadFallbackEntries()
ESM::Door newMarker;
newMarker.mId = marker.first;
newMarker.mModel = marker.second;
CSMWorld::Record<ESM::Door> record;
record.mBase = newMarker;
record.mState = CSMWorld::RecordBase::State_BaseOnly;
mReferenceables.appendRecord (record, CSMWorld::UniversalId::Type_Door);
std::unique_ptr<CSMWorld::Record<ESM::Door> > record(new CSMWorld::Record<ESM::Door>);
record->mBase = newMarker;
record->mState = CSMWorld::RecordBase::State_BaseOnly;
mReferenceables.appendRecord (std::move(record), CSMWorld::UniversalId::Type_Door);
}
}
}

View File

@ -118,7 +118,7 @@ namespace CSMWorld
const ESM::Dialogue *mDialogue; // last loaded dialogue
bool mBase;
bool mProject;
std::map<std::string, std::map<ESM::RefNum, std::string> > mRefLoadCache;
std::map<std::string, std::map<unsigned int, unsigned int> > mRefLoadCache;
int mReaderIndex;
bool mFsStrict;
@ -292,6 +292,8 @@ namespace CSMWorld
void merge();
///< Merge modified into base.
int getTotalRecords (const std::vector<boost::filesystem::path>& files); // for better loading bar
int startLoading (const boost::filesystem::path& path, bool base, bool project);
///< Begin merging content of a file into base or modified.
///

View File

@ -0,0 +1,43 @@
#include "idcollection.hpp"
namespace CSMWorld
{
template<>
int IdCollection<Pathgrid, IdAccessor<Pathgrid> >::load (ESM::ESMReader& reader, bool base)
{
Pathgrid record;
bool isDeleted = false;
loadRecord (record, reader, isDeleted);
std::string id = IdAccessor<Pathgrid>().getId (record);
int index = this->searchId (id);
if (record.mPoints.empty() || record.mEdges.empty())
isDeleted = true;
if (isDeleted)
{
if (index==-1)
{
// deleting a record that does not exist
// ignore it for now
/// \todo report the problem to the user
return -1;
}
if (base)
{
this->removeRows (index, 1);
return -1;
}
std::unique_ptr<Record<Pathgrid> > baseRecord(new Record<Pathgrid>(this->getRecord(index)));
baseRecord->mState = RecordBase::State_Deleted;
this->setRecord(index, std::move(baseRecord));
return index;
}
return load (record, base, index);
}
}

View File

@ -5,6 +5,7 @@
#include "collection.hpp"
#include "land.hpp"
#include "pathgrid.hpp"
namespace CSMWorld
{
@ -83,9 +84,9 @@ namespace CSMWorld
return -1;
}
Record<ESXRecordT> baseRecord = this->getRecord (index);
baseRecord.mState = RecordBase::State_Deleted;
this->setRecord (index, baseRecord);
std::unique_ptr<Record<ESXRecordT> > baseRecord(new Record<ESXRecordT>(this->getRecord(index)));
baseRecord->mState = RecordBase::State_Deleted;
this->setRecord(index, std::move(baseRecord));
return index;
}
@ -96,30 +97,31 @@ namespace CSMWorld
int IdCollection<ESXRecordT, IdAccessorT>::load (const ESXRecordT& record, bool base,
int index)
{
if (index==-2)
if (index==-2) // index unknown
index = this->searchId (IdAccessorT().getId (record));
if (index==-1)
{
// new record
Record<ESXRecordT> record2;
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
(base ? record2.mBase : record2.mModified) = record;
std::unique_ptr<Record<ESXRecordT> > record2(new Record<ESXRecordT>);
record2->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
(base ? record2->mBase : record2->mModified) = record;
index = this->getSize();
this->appendRecord (record2);
this->appendRecord(std::move(record2));
}
else
{
// old record
Record<ESXRecordT> record2 = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
std::unique_ptr<Record<ESXRecordT> > record2(
new Record<ESXRecordT>(Collection<ESXRecordT, IdAccessorT>::getRecord(index)));
if (base)
record2.mBase = record;
record2->mBase = record;
else
record2.setModified (record);
record2->setModified(record);
this->setRecord (index, record2);
this->setRecord(index, std::move(record2));
}
return index;
@ -133,7 +135,7 @@ namespace CSMWorld
if (index==-1)
return false;
Record<ESXRecordT> record = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
const Record<ESXRecordT>& record = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
if (record.isDeleted())
return false;
@ -144,12 +146,17 @@ namespace CSMWorld
}
else
{
record.mState = RecordBase::State_Deleted;
this->setRecord (index, record);
std::unique_ptr<Record<ESXRecordT> > record2(
new Record<ESXRecordT>(Collection<ESXRecordT, IdAccessorT>::getRecord(index)));
record2->mState = RecordBase::State_Deleted;
this->setRecord(index, std::move(record2));
}
return true;
}
template<>
int IdCollection<Pathgrid, IdAccessor<Pathgrid> >::load(ESM::ESMReader& reader, bool base);
}
#endif

View File

@ -123,6 +123,14 @@ Qt::ItemFlags CSMWorld::IdTable::flags (const QModelIndex & index) const
if (mIdCollection->getColumn (index.column()).isUserEditable())
flags |= Qt::ItemIsEditable;
int blockedColumn = searchColumnIndex(Columns::ColumnId_Blocked);
if (blockedColumn != -1 && blockedColumn != index.column())
{
bool isBlocked = mIdCollection->getData(index.row(), blockedColumn).toInt();
if (isBlocked)
flags = Qt::ItemIsSelectable; // not enabled (to grey out)
}
return flags;
}
@ -191,7 +199,7 @@ void CSMWorld::IdTable::cloneRecord(const std::string& origin,
const std::string& destination,
CSMWorld::UniversalId::Type type)
{
int index = mIdCollection->getAppendIndex (destination);
int index = mIdCollection->getAppendIndex (destination, type);
beginInsertRows (QModelIndex(), index, index);
mIdCollection->cloneRecord(origin, destination, type);
@ -228,23 +236,30 @@ QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column)
return QModelIndex();
}
void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& record, CSMWorld::UniversalId::Type type)
void CSMWorld::IdTable::setRecord (const std::string& id,
std::unique_ptr<RecordBase> record, CSMWorld::UniversalId::Type type)
{
int index = mIdCollection->searchId (id);
if (index==-1)
{
index = mIdCollection->getAppendIndex (id, type);
// For info records, appendRecord may use a different index than the one returned by
// getAppendIndex (because of prev/next links). This can result in the display not
// updating correctly after an undo
//
// Use an alternative method to get the correct index. For non-Info records the
// record pointer is ignored and internally calls getAppendIndex.
int index2 = mIdCollection->getInsertIndex (id, type, record.get());
beginInsertRows (QModelIndex(), index, index);
beginInsertRows (QModelIndex(), index2, index2);
mIdCollection->appendRecord (record, type);
mIdCollection->appendRecord (std::move(record), type);
endInsertRows();
}
else
{
mIdCollection->replace (index, record);
mIdCollection->replace (index, std::move(record));
emit dataChanged (CSMWorld::IdTable::index (index, 0),
CSMWorld::IdTable::index (index, mIdCollection->getColumns()-1));
}

View File

@ -2,6 +2,7 @@
#define CSM_WOLRD_IDTABLE_H
#include <vector>
#include <memory>
#include "idtablebase.hpp"
#include "universalid.hpp"
@ -67,7 +68,7 @@ namespace CSMWorld
QModelIndex getModelIndex (const std::string& id, int column) const override;
void setRecord (const std::string& id, const RecordBase& record,
void setRecord (const std::string& id, std::unique_ptr<RecordBase> record,
UniversalId::Type type = UniversalId::Type_None);
///< Add record or overwrite existing record.

View File

@ -121,8 +121,11 @@ QString CSMWorld::IdTableProxyModel::getRecordId(int sourceRow) const
void CSMWorld::IdTableProxyModel::refreshFilter()
{
updateColumnMap();
invalidateFilter();
if (mFilter)
{
updateColumnMap();
invalidateFilter();
}
}
void CSMWorld::IdTableProxyModel::sourceRowsInserted(const QModelIndex &parent, int /*start*/, int end)

View File

@ -2,12 +2,74 @@
#include <stdexcept>
#include <iterator>
#include <cassert>
#include <components/esm/esmreader.hpp>
#include <components/esm/loaddial.hpp>
#include <components/misc/stringops.hpp>
namespace CSMWorld
{
template<>
void Collection<Info, IdAccessor<Info> >::removeRows (int index, int count)
{
mRecords.erase(mRecords.begin()+index, mRecords.begin()+index+count);
// index map is updated in InfoCollection::removeRows()
}
template<>
void Collection<Info, IdAccessor<Info> >::insertRecord (std::unique_ptr<RecordBase> record,
int index, UniversalId::Type type)
{
int size = static_cast<int>(mRecords.size());
if (index < 0 || index > size)
throw std::runtime_error("index out of range");
std::unique_ptr<Record<Info> > record2(static_cast<Record<Info>*>(record.release()));
if (index == size)
mRecords.push_back(std::move(record2));
else
mRecords.insert(mRecords.begin()+index, std::move(record2));
// index map is updated in InfoCollection::insertRecord()
}
template<>
bool Collection<Info, IdAccessor<Info> >::reorderRowsImp (int baseIndex,
const std::vector<int>& newOrder)
{
if (!newOrder.empty())
{
int size = static_cast<int>(newOrder.size());
// check that all indices are present
std::vector<int> test(newOrder);
std::sort(test.begin(), test.end());
if (*test.begin() != 0 || *--test.end() != size-1)
return false;
// reorder records
std::vector<std::unique_ptr<Record<Info> > > buffer(size);
// FIXME: BUG: undo does not remove modified flag
for (int i = 0; i < size; ++i)
{
buffer[newOrder[i]] = std::move(mRecords[baseIndex+i]);
buffer[newOrder[i]]->setModified(buffer[newOrder[i]]->get());
}
std::move(buffer.begin(), buffer.end(), mRecords.begin()+baseIndex);
// index map is updated in InfoCollection::reorderRows()
}
return true;
}
}
void CSMWorld::InfoCollection::load (const Info& record, bool base)
{
int index = searchId (record.mId);
@ -15,74 +77,96 @@ void CSMWorld::InfoCollection::load (const Info& record, bool base)
if (index==-1)
{
// new record
Record<Info> record2;
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
(base ? record2.mBase : record2.mModified) = record;
std::unique_ptr<Record<Info> > record2(new Record<Info>);
record2->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
(base ? record2->mBase : record2->mModified) = record;
std::string topic = Misc::StringUtils::lowerCase (record2.get().mTopicId);
if (!record2.get().mPrev.empty())
{
index = getInfoIndex (record2.get().mPrev, topic);
if (index!=-1)
++index;
}
if (index==-1 && !record2.get().mNext.empty())
{
index = getInfoIndex (record2.get().mNext, topic);
}
if (index==-1)
{
Range range = getTopicRange (topic);
index = std::distance (getRecords().begin(), range.second);
}
insertRecord (record2, index);
appendRecord(std::move(record2));
}
else
{
// old record
Record<Info> record2 = getRecord (index);
std::unique_ptr<Record<Info> > record2(new Record<Info>(getRecord(index)));
if (base)
record2.mBase = record;
record2->mBase = record;
else
record2.setModified (record);
record2->setModified (record);
setRecord (index, record2);
setRecord (index, std::move(record2));
}
}
int CSMWorld::InfoCollection::getInfoIndex (const std::string& id, const std::string& topic) const
int CSMWorld::InfoCollection::getInfoIndex(std::string_view id, std::string_view topic) const
{
std::string fullId = Misc::StringUtils::lowerCase (topic) + "#" + id;
// find the topic first
std::unordered_map<std::string, std::vector<std::pair<std::string, int> > >::const_iterator iter
= mInfoIndex.find(Misc::StringUtils::lowerCase(topic));
std::pair<RecordConstIterator, RecordConstIterator> range = getTopicRange (topic);
if (iter == mInfoIndex.end())
return -1;
for (; range.first!=range.second; ++range.first)
if (Misc::StringUtils::ciEqual(range.first->get().mId, fullId))
return std::distance (getRecords().begin(), range.first);
// brute force loop
for (std::vector<std::pair<std::string, int> >::const_iterator it = iter->second.begin();
it != iter->second.end(); ++it)
{
if (Misc::StringUtils::ciEqual(it->first, id))
return it->second;
}
return -1;
}
int CSMWorld::InfoCollection::getAppendIndex (const std::string& id, UniversalId::Type type) const
// Calling insertRecord() using index from getInsertIndex() needs to take into account of
// prev/next records; an example is deleting a record then undo
int CSMWorld::InfoCollection::getInsertIndex (const std::string& id,
UniversalId::Type type, RecordBase *record) const
{
std::string::size_type separator = id.find_last_of ('#');
if (record == nullptr)
{
std::string::size_type separator = id.find_last_of('#');
if (separator==std::string::npos)
throw std::runtime_error ("invalid info ID: " + id);
if (separator == std::string::npos)
throw std::runtime_error("invalid info ID: " + id);
std::pair<RecordConstIterator, RecordConstIterator> range = getTopicRange (id.substr (0, separator));
std::pair<RecordConstIterator, RecordConstIterator> range = getTopicRange(id.substr(0, separator));
if (range.first==range.second)
return Collection<Info, IdAccessor<Info> >::getAppendIndex (id, type);
if (range.first == range.second)
return Collection<Info, IdAccessor<Info> >::getAppendIndex(id, type);
return std::distance (getRecords().begin(), range.second);
return std::distance(getRecords().begin(), range.second);
}
int index = -1;
const Info& info = static_cast<Record<Info>*>(record)->get();
std::string topic = info.mTopicId;
// if the record has a prev, find its index value
if (!info.mPrev.empty())
{
index = getInfoIndex(info.mPrev, topic);
if (index != -1)
++index; // if prev exists, set current index to one above prev
}
// if prev doesn't exist or not found and the record has a next, find its index value
if (index == -1 && !info.mNext.empty())
{
// if next exists, use its index as the current index
index = getInfoIndex(info.mNext, topic);
}
// if next doesn't exist or not found (i.e. neither exist yet) then start a new one
if (index == -1)
{
Range range = getTopicRange(topic); // getTopicRange converts topic to lower case first
index = std::distance(getRecords().begin(), range.second);
}
return index;
}
bool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector<int>& newOrder)
@ -99,7 +183,17 @@ bool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector<int
return false;
// reorder
return reorderRowsImp (baseIndex, newOrder);
if (!Collection<Info, IdAccessor<Info> >::reorderRowsImp(baseIndex, newOrder))
return false;
// adjust index
int size = static_cast<int>(newOrder.size());
for (auto& [hash, infos] : mInfoIndex)
for (auto& [a, b] : infos)
if (b >= baseIndex && b < baseIndex + size)
b = newOrder.at(b - baseIndex) + baseIndex;
return true;
}
void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue)
@ -126,9 +220,9 @@ void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ES
}
else
{
Record<Info> record = getRecord (index);
record.mState = RecordBase::State_Deleted;
setRecord (index, record);
std::unique_ptr<Record<Info> > record(new Record<Info>(getRecord(index)));
record->mState = RecordBase::State_Deleted;
setRecord (index, std::move(record));
}
}
else
@ -142,73 +236,54 @@ void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ES
CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const std::string& topic)
const
{
std::string topic2 = Misc::StringUtils::lowerCase (topic);
std::string lowerTopic = Misc::StringUtils::lowerCase (topic);
std::map<std::string, int>::const_iterator iter = getIdMap().lower_bound (topic2);
// find the topic
std::unordered_map<std::string, std::vector<std::pair<std::string, int> > >::const_iterator iter
= mInfoIndex.find(lowerTopic);
// Skip invalid records: The beginning of a topic string could be identical to another topic
// string.
for (; iter!=getIdMap().end(); ++iter)
{
std::string testTopicId =
Misc::StringUtils::lowerCase (getRecord (iter->second).get().mTopicId);
if (testTopicId==topic2)
break;
std::size_t size = topic2.size();
if (testTopicId.size()<size || testTopicId.substr (0, size)!=topic2)
return Range (getRecords().end(), getRecords().end());
}
if (iter==getIdMap().end())
if (iter == mInfoIndex.end())
return Range (getRecords().end(), getRecords().end());
RecordConstIterator begin = getRecords().begin()+iter->second;
while (begin != getRecords().begin())
// topic found, find the starting index
int low = INT_MAX;
for (std::vector<std::pair<std::string, int> >::const_iterator it = iter->second.begin();
it != iter->second.end(); ++it)
{
if (!Misc::StringUtils::ciEqual(begin->get().mTopicId, topic2))
{
// we've gone one too far, go back
++begin;
break;
}
--begin;
low = std::min(low, it->second);
}
// Find end
RecordConstIterator end = begin;
RecordConstIterator begin = getRecords().begin() + low;
for (; end!=getRecords().end(); ++end)
if (!Misc::StringUtils::ciEqual(end->get().mTopicId, topic2))
break;
// Find end (one past the range)
RecordConstIterator end = begin + iter->second.size();
assert(static_cast<size_t>(std::distance(begin, end)) == iter->second.size());
return Range (begin, end);
}
void CSMWorld::InfoCollection::removeDialogueInfos(const std::string& dialogueId)
{
std::string id = Misc::StringUtils::lowerCase(dialogueId);
std::vector<int> erasedRecords;
std::map<std::string, int>::const_iterator current = getIdMap().lower_bound(id);
std::map<std::string, int>::const_iterator end = getIdMap().end();
for (; current != end; ++current)
Range range = getTopicRange(dialogueId); // getTopicRange converts dialogueId to lower case first
for (; range.first != range.second; ++range.first)
{
Record<Info> record = getRecord(current->second);
const Record<Info>& record = **range.first;
if (Misc::StringUtils::ciEqual(dialogueId, record.get().mTopicId))
{
if (record.mState == RecordBase::State_ModifiedOnly)
{
erasedRecords.push_back(current->second);
erasedRecords.push_back(range.first - getRecords().begin());
}
else
{
record.mState = RecordBase::State_Deleted;
setRecord(current->second, record);
std::unique_ptr<Record<Info> > record2(new Record<Info>(record));
record2->mState = RecordBase::State_Deleted;
setRecord(range.first - getRecords().begin(), std::move(record2));
}
}
else
@ -223,3 +298,105 @@ void CSMWorld::InfoCollection::removeDialogueInfos(const std::string& dialogueId
erasedRecords.pop_back();
}
}
// FIXME: removing a record should adjust prev/next and mark those records as modified
// accordingly (also consider undo)
void CSMWorld::InfoCollection::removeRows (int index, int count)
{
Collection<Info, IdAccessor<Info> >::removeRows(index, count); // erase records only
for (std::unordered_map<std::string, std::vector<std::pair<std::string, int> > >::iterator iter
= mInfoIndex.begin(); iter != mInfoIndex.end();)
{
for (std::vector<std::pair<std::string, int> >::iterator it = iter->second.begin();
it != iter->second.end();)
{
if (it->second >= index)
{
if (it->second >= index+count)
{
it->second -= count;
++it;
}
else
iter->second.erase(it);
}
else
++it;
}
// check for an empty vector
if (iter->second.empty())
mInfoIndex.erase(iter++);
else
++iter;
}
}
void CSMWorld::InfoCollection::appendBlankRecord (const std::string& id, UniversalId::Type type)
{
std::unique_ptr<Record<Info> > record2(new Record<Info>);
record2->mState = Record<Info>::State_ModifiedOnly;
record2->mModified.blank();
record2->get().mId = id;
insertRecord(std::move(record2), getInsertIndex(id, type, nullptr), type); // call InfoCollection::insertRecord()
}
int CSMWorld::InfoCollection::searchId(std::string_view id) const
{
std::string::size_type separator = id.find_last_of('#');
if (separator == std::string::npos)
throw std::runtime_error("invalid info ID: " + std::string(id));
return getInfoIndex(id.substr(separator+1), id.substr(0, separator));
}
void CSMWorld::InfoCollection::appendRecord (std::unique_ptr<RecordBase> record, UniversalId::Type type)
{
int index = getInsertIndex(static_cast<Record<Info>*>(record.get())->get().mId, type, record.get());
insertRecord(std::move(record), index, type);
}
void CSMWorld::InfoCollection::insertRecord (std::unique_ptr<RecordBase> record, int index,
UniversalId::Type type)
{
int size = static_cast<int>(getRecords().size());
std::string id = static_cast<Record<Info>*>(record.get())->get().mId;
std::string::size_type separator = id.find_last_of('#');
if (separator == std::string::npos)
throw std::runtime_error("invalid info ID: " + id);
Collection<Info, IdAccessor<Info> >::insertRecord(std::move(record), index, type); // add records only
// adjust index
if (index < size-1)
{
for (std::unordered_map<std::string, std::vector<std::pair<std::string, int> > >::iterator iter
= mInfoIndex.begin(); iter != mInfoIndex.end(); ++iter)
{
for (std::vector<std::pair<std::string, int> >::iterator it = iter->second.begin();
it != iter->second.end(); ++it)
{
if (it->second >= index)
++(it->second);
}
}
}
// get iterator for existing topic or a new topic
std::string lowerId = Misc::StringUtils::lowerCase(id);
std::pair<std::unordered_map<std::string, std::vector<std::pair<std::string, int> > >::iterator, bool> res
= mInfoIndex.insert(
std::make_pair(lowerId.substr(0, separator),
std::vector<std::pair<std::string, int> >())); // empty vector
// insert info and index
res.first->second.push_back(std::make_pair(lowerId.substr(separator+1), index));
}

View File

@ -1,6 +1,9 @@
#ifndef CSM_WOLRD_INFOCOLLECTION_H
#define CSM_WOLRD_INFOCOLLECTION_H
#include <unordered_map>
#include <string_view>
#include "collection.hpp"
#include "info.hpp"
@ -11,27 +14,52 @@ namespace ESM
namespace CSMWorld
{
template<>
void Collection<Info, IdAccessor<Info> >::removeRows (int index, int count);
template<>
void Collection<Info, IdAccessor<Info> >::insertRecord (std::unique_ptr<RecordBase> record,
int index, UniversalId::Type type);
template<>
bool Collection<Info, IdAccessor<Info> >::reorderRowsImp (int baseIndex,
const std::vector<int>& newOrder);
class InfoCollection : public Collection<Info, IdAccessor<Info> >
{
public:
typedef std::vector<Record<Info> >::const_iterator RecordConstIterator;
typedef std::vector<std::unique_ptr<Record<Info> > >::const_iterator RecordConstIterator;
typedef std::pair<RecordConstIterator, RecordConstIterator> Range;
private:
// The general strategy is to keep the records in Collection kept in order (within
// a topic group) while the index lookup maps are not ordered. It is assumed that
// each topic has a small number of infos, which allows the use of vectors for
// iterating through them without too much penalty.
//
// NOTE: topic string as well as id string are stored in lower case.
std::unordered_map<std::string, std::vector<std::pair<std::string, int> > > mInfoIndex;
void load (const Info& record, bool base);
int getInfoIndex (const std::string& id, const std::string& topic) const;
int getInfoIndex(std::string_view id, std::string_view topic) const;
///< Return index for record \a id or -1 (if not present; deleted records are considered)
///
/// \param id info ID without topic prefix
//
/// \attention id and topic are assumed to be in lower case
public:
int getAppendIndex (const std::string& id,
UniversalId::Type type = UniversalId::Type_None) const override;
int getInsertIndex (const std::string& id,
UniversalId::Type type = UniversalId::Type_None,
RecordBase *record = nullptr) const override;
///< \param type Will be ignored, unless the collection supports multiple record types
///
/// Works like getAppendIndex unless an overloaded method uses the record pointer
/// to get additional info about the record that results in an alternative index.
bool reorderRows (int baseIndex, const std::vector<int>& newOrder) override;
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
@ -46,6 +74,20 @@ namespace CSMWorld
/// the given topic.
void removeDialogueInfos(const std::string& dialogueId);
void removeRows (int index, int count) override;
void appendBlankRecord (const std::string& id,
UniversalId::Type type = UniversalId::Type_None) override;
int searchId(std::string_view id) const override;
void appendRecord (std::unique_ptr<RecordBase> record,
UniversalId::Type type = UniversalId::Type_None) override;
void insertRecord (std::unique_ptr<RecordBase> record,
int index,
UniversalId::Type type = UniversalId::Type_None) override;
};
}

View File

@ -90,23 +90,23 @@ namespace CSMWorld
template<typename ESXRecordT, typename IdAccessorT>
void NestedIdCollection<ESXRecordT, IdAccessorT>::addNestedRow(int row, int column, int position)
{
Record<ESXRecordT> record;
record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
std::unique_ptr<Record<ESXRecordT> > record(new Record<ESXRecordT>);
record->assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).addRow(record, position);
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).addRow(*record, position);
Collection<ESXRecordT, IdAccessorT>::setRecord(row, record);
Collection<ESXRecordT, IdAccessorT>::setRecord(row, std::move(record));
}
template<typename ESXRecordT, typename IdAccessorT>
void NestedIdCollection<ESXRecordT, IdAccessorT>::removeNestedRows(int row, int column, int subRow)
{
Record<ESXRecordT> record;
record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
std::unique_ptr<Record<ESXRecordT> > record(new Record<ESXRecordT>);
record->assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).removeRow(record, subRow);
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).removeRow(*record, subRow);
Collection<ESXRecordT, IdAccessorT>::setRecord(row, record);
Collection<ESXRecordT, IdAccessorT>::setRecord(row, std::move(record));
}
template<typename ESXRecordT, typename IdAccessorT>
@ -121,13 +121,13 @@ namespace CSMWorld
void NestedIdCollection<ESXRecordT, IdAccessorT>::setNestedData(int row,
int column, const QVariant& data, int subRow, int subColumn)
{
Record<ESXRecordT> record;
record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
std::unique_ptr<Record<ESXRecordT> > record(new Record<ESXRecordT>);
record->assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).setData(
record, data, subRow, subColumn);
*record, data, subRow, subColumn);
Collection<ESXRecordT, IdAccessorT>::setRecord(row, record);
Collection<ESXRecordT, IdAccessorT>::setRecord(row, std::move(record));
}
template<typename ESXRecordT, typename IdAccessorT>
@ -142,13 +142,13 @@ namespace CSMWorld
void NestedIdCollection<ESXRecordT, IdAccessorT>::setNestedTable(int row,
int column, const CSMWorld::NestedTableWrapperBase& nestedTable)
{
Record<ESXRecordT> record;
record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
std::unique_ptr<Record<ESXRecordT> > record(new Record<ESXRecordT>);
record->assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).setTable(
record, nestedTable);
*record, nestedTable);
Collection<ESXRecordT, IdAccessorT>::setRecord(row, record);
Collection<ESXRecordT, IdAccessorT>::setRecord(row, std::move(record));
}
template<typename ESXRecordT, typename IdAccessorT>

View File

@ -35,22 +35,22 @@ namespace CSMWorld
void NestedInfoCollection::addNestedRow(int row, int column, int position)
{
Record<Info> record;
record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
std::unique_ptr<Record<Info> > record(new Record<Info>);
record->assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).addRow(record, position);
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).addRow(*record, position);
Collection<Info, IdAccessor<Info> >::setRecord(row, record);
Collection<Info, IdAccessor<Info> >::setRecord(row, std::move(record));
}
void NestedInfoCollection::removeNestedRows(int row, int column, int subRow)
{
Record<Info> record;
record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
std::unique_ptr<Record<Info> > record(new Record<Info>);
record->assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).removeRow(record, subRow);
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).removeRow(*record, subRow);
Collection<Info, IdAccessor<Info> >::setRecord(row, record);
Collection<Info, IdAccessor<Info> >::setRecord(row, std::move(record));
}
QVariant NestedInfoCollection::getNestedData (int row,
@ -63,13 +63,13 @@ namespace CSMWorld
void NestedInfoCollection::setNestedData(int row,
int column, const QVariant& data, int subRow, int subColumn)
{
Record<Info> record;
record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
std::unique_ptr<Record<Info> > record(new Record<Info>);
record->assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).setData(
record, data, subRow, subColumn);
*record, data, subRow, subColumn);
Collection<Info, IdAccessor<Info> >::setRecord(row, record);
Collection<Info, IdAccessor<Info> >::setRecord(row, std::move(record));
}
CSMWorld::NestedTableWrapperBase* NestedInfoCollection::nestedTable(int row,
@ -82,13 +82,13 @@ namespace CSMWorld
void NestedInfoCollection::setNestedTable(int row,
int column, const CSMWorld::NestedTableWrapperBase& nestedTable)
{
Record<Info> record;
record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
std::unique_ptr<Record<Info> > record(new Record<Info>);
record->assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).setTable(
record, nestedTable);
*record, nestedTable);
Collection<Info, IdAccessor<Info> >::setRecord(row, record);
Collection<Info, IdAccessor<Info> >::setRecord(row, std::move(record));
}
int NestedInfoCollection::getNestedRowsCount(int row, int column) const

View File

@ -20,7 +20,7 @@ namespace CSMWorld
{
std::string mId;
void load (ESM::ESMReader &esm, bool &isDeleted, const IdCollection<Cell>& cells);
void load (ESM::ESMReader &esm, bool &isDeleted, const IdCollection<Cell, IdAccessor<Cell> >& cells);
void load (ESM::ESMReader &esm, bool &isDeleted);
};
}

View File

@ -1,6 +1,7 @@
#ifndef CSM_WOLRD_RECORD_H
#define CSM_WOLRD_RECORD_H
#include <memory>
#include <stdexcept>
namespace CSMWorld
@ -20,9 +21,9 @@ namespace CSMWorld
virtual ~RecordBase();
virtual RecordBase *clone() const = 0;
virtual std::unique_ptr<RecordBase> clone() const = 0;
virtual RecordBase *modifiedCopy() const = 0;
virtual std::unique_ptr<RecordBase> modifiedCopy() const = 0;
virtual void assign (const RecordBase& record) = 0;
///< Will throw an exception if the types don't match.
@ -45,9 +46,9 @@ namespace CSMWorld
Record(State state,
const ESXRecordT *base = 0, const ESXRecordT *modified = 0);
RecordBase *clone() const override;
std::unique_ptr<RecordBase> clone() const override;
RecordBase *modifiedCopy() const override;
std::unique_ptr<RecordBase> modifiedCopy() const override;
void assign (const RecordBase& record) override;
@ -85,15 +86,16 @@ namespace CSMWorld
}
template <typename ESXRecordT>
RecordBase *Record<ESXRecordT>::modifiedCopy() const
std::unique_ptr<RecordBase> Record<ESXRecordT>::modifiedCopy() const
{
return new Record<ESXRecordT> (State_ModifiedOnly, nullptr, &(this->get()));
return std::make_unique<Record<ESXRecordT> >(
Record<ESXRecordT>(State_ModifiedOnly, nullptr, &(this->get())));
}
template <typename ESXRecordT>
RecordBase *Record<ESXRecordT>::clone() const
std::unique_ptr<RecordBase> Record<ESXRecordT>::clone() const
{
return new Record<ESXRecordT> (*this);
return std::make_unique<Record<ESXRecordT> >(Record<ESXRecordT>(*this));
}
template <typename ESXRecordT>

View File

@ -2,7 +2,7 @@
#include "cellcoordinates.hpp"
CSMWorld::CellRef::CellRef() : mNew (true)
CSMWorld::CellRef::CellRef() : mNew (true), mIdNum(0)
{
mRefNum.mIndex = 0;
mRefNum.mContentFile = 0;

View File

@ -14,6 +14,7 @@ namespace CSMWorld
std::string mCell;
std::string mOriginalCell;
bool mNew; // new reference, not counted yet, ref num not assigned yet
unsigned int mIdNum;
CellRef();

View File

@ -7,8 +7,39 @@
#include "universalid.hpp"
#include "record.hpp"
#include <string_view>
namespace CSMWorld
{
template<>
void Collection<CellRef, IdAccessor<CellRef> >::removeRows (int index, int count)
{
mRecords.erase(mRecords.begin()+index, mRecords.begin()+index+count);
// index map is updated in RefCollection::removeRows()
}
template<>
void Collection<CellRef, IdAccessor<CellRef> >::insertRecord (std::unique_ptr<RecordBase> record, int index,
UniversalId::Type type)
{
int size = static_cast<int>(mRecords.size());
if (index < 0 || index > size)
throw std::runtime_error("index out of range");
std::unique_ptr<Record<CellRef> > record2(static_cast<Record<CellRef>*>(record.release()));
if (index == size)
mRecords.push_back(std::move(record2));
else
mRecords.insert(mRecords.begin()+index, std::move(record2));
// index map is updated in RefCollection::insertRecord()
}
}
void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base,
std::map<ESM::RefNum, std::string>& cache, CSMDoc::Messages& messages)
std::map<unsigned int, unsigned int>& cache, CSMDoc::Messages& messages)
{
Record<Cell> cell = mCells.getRecord (cellIndex);
@ -19,8 +50,9 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
ESM::MovedCellRef mref;
mref.mRefNum.mIndex = 0;
bool isDeleted = false;
bool isMoved = false;
while (ESM::Cell::getNextRef(reader, ref, isDeleted, true, &mref))
while (ESM::Cell::getNextRef(reader, ref, isDeleted, mref, isMoved))
{
// Keep mOriginalCell empty when in modified (as an indicator that the
// original cell will always be equal the current cell).
@ -34,7 +66,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
ref.mCell = "#" + std::to_string(index.first) + " " + std::to_string(index.second);
// Handle non-base moved references
if (!base && mref.mRefNum.mIndex != 0)
if (!base && isMoved)
{
// Moved references must have a link back to their original cell
// See discussion: https://forum.openmw.org/viewtopic.php?f=6&t=577&start=30
@ -59,17 +91,47 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
else
ref.mCell = cell2.mId;
mref.mRefNum.mIndex = 0;
// ignore content file number
std::map<ESM::RefNum, std::string>::iterator iter = cache.begin();
unsigned int thisIndex = ref.mRefNum.mIndex & 0x00ffffff;
if (ref.mRefNum.mContentFile != -1 && !base) ref.mRefNum.mContentFile = ref.mRefNum.mIndex >> 24;
for (; iter != cache.end(); ++iter)
if (ref.mRefNum.mContentFile != -1 && !base)
{
if (thisIndex == iter->first.mIndex)
break;
ref.mRefNum.mContentFile = ref.mRefNum.mIndex >> 24;
ref.mRefNum.mIndex &= 0x00ffffff;
}
unsigned int refNum = (ref.mRefNum.mIndex & 0x00ffffff) |
(ref.mRefNum.hasContentFile() ? ref.mRefNum.mContentFile : 0xff) << 24;
std::map<unsigned int, unsigned int>::iterator iter = cache.find(refNum);
if (isMoved)
{
if (iter == cache.end())
{
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Cell,
mCells.getId(cellIndex));
messages.add(id, "Attempt to move a non-existent reference - RefNum index "
+ std::to_string(ref.mRefNum.mIndex) + ", refID " + ref.mRefID + ", content file index "
+ std::to_string(ref.mRefNum.mContentFile),
/*hint*/"",
CSMDoc::Message::Severity_Warning);
continue;
}
int index = getIntIndex(iter->second);
// ensure we have the same record id for setRecord()
ref.mId = getRecord(index).get().mId;
ref.mIdNum = extractIdNum(ref.mId);
std::unique_ptr<Record<CellRef> > record(new Record<CellRef>);
// TODO: check whether a base record be moved
record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
(base ? record->mBase : record->mModified) = std::move(ref);
// overwrite original record
setRecord(index, std::move(record));
continue; // NOTE: assumed moved references are not deleted at the same time
}
if (isDeleted)
@ -79,13 +141,15 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Cell,
mCells.getId (cellIndex));
messages.add (id, "Attempt to delete a non-existent reference");
messages.add (id, "Attempt to delete a non-existent reference - RefNum index "
+ std::to_string(ref.mRefNum.mIndex) + ", refID " + ref.mRefID + ", content file index "
+ std::to_string(ref.mRefNum.mContentFile),
/*hint*/"",
CSMDoc::Message::Severity_Warning);
continue;
}
int index = getIndex (iter->second);
Record<CellRef> record = getRecord (index);
int index = getIntIndex (iter->second);
if (base)
{
@ -94,8 +158,9 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
}
else
{
record.mState = RecordBase::State_Deleted;
setRecord (index, record);
std::unique_ptr<Record<CellRef> > record(new Record<CellRef>(getRecord(index)));
record->mState = RecordBase::State_Deleted;
setRecord(index, std::move(record));
}
continue;
@ -104,30 +169,47 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
if (iter==cache.end())
{
// new reference
ref.mIdNum = mNextId; // FIXME: fragile
ref.mId = getNewId();
Record<CellRef> record;
record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
const ESM::RefNum refNum = ref.mRefNum;
std::string refId = ref.mId;
(base ? record.mBase : record.mModified) = std::move(ref);
cache.emplace(refNum, ref.mIdNum);
appendRecord (record);
std::unique_ptr<Record<CellRef> > record(new Record<CellRef>);
record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
(base ? record->mBase : record->mModified) = std::move(ref);
cache.emplace(refNum, std::move(refId));
appendRecord(std::move(record));
}
else
{
// old reference -> merge
ref.mId = iter->second;
int index = getIntIndex(iter->second);
#if 0
// ref.mRefNum.mIndex : the key
// iter->second : previously cached idNum for the key
// index : position of the record for that idNum
// getRecord(index).get() : record in the index position
assert(iter->second != getRecord(index).get().mIdNum); // sanity check
int index = getIndex (ref.mId);
// check if the plugin used the same RefNum index for a different record
if (ref.mRefID != getRecord(index).get().mRefID)
{
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Cell, mCells.getId(cellIndex));
messages.add(id,
"RefNum renamed from RefID \"" + getRecord(index).get().mRefID + "\" to \""
+ ref.mRefID + "\" (RefNum index " + std::to_string(ref.mRefNum.mIndex) + ")",
/*hint*/"",
CSMDoc::Message::Severity_Info);
}
#endif
ref.mId = getRecord(index).get().mId;
ref.mIdNum = extractIdNum(ref.mId);
Record<CellRef> record = getRecord (index);
record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_Modified;
(base ? record.mBase : record.mModified) = std::move(ref);
std::unique_ptr<Record<CellRef> > record(new Record<CellRef>(getRecord(index)));
record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_Modified;
(base ? record->mBase : record->mModified) = std::move(ref);
setRecord (index, record);
setRecord(index, std::move(record));
}
}
}
@ -136,3 +218,117 @@ std::string CSMWorld::RefCollection::getNewId()
{
return "ref#" + std::to_string(mNextId++);
}
unsigned int CSMWorld::RefCollection::extractIdNum(std::string_view id) const
{
std::string::size_type separator = id.find_last_of('#');
if (separator == std::string::npos)
throw std::runtime_error("invalid ref ID: " + std::string(id));
return static_cast<unsigned int>(std::stoi(std::string(id.substr(separator+1))));
}
int CSMWorld::RefCollection::getIntIndex (unsigned int id) const
{
int index = searchId(id);
if (index == -1)
throw std::runtime_error("invalid RefNum: " + std::to_string(id));
return index;
}
int CSMWorld::RefCollection::searchId (unsigned int id) const
{
std::map<unsigned int, int>::const_iterator iter = mRefIndex.find(id);
if (iter == mRefIndex.end())
return -1;
return iter->second;
}
void CSMWorld::RefCollection::removeRows (int index, int count)
{
Collection<CellRef, IdAccessor<CellRef> >::removeRows(index, count); // erase records only
std::map<unsigned int, int>::iterator iter = mRefIndex.begin();
while (iter != mRefIndex.end())
{
if (iter->second>=index)
{
if (iter->second >= index+count)
{
iter->second -= count;
++iter;
}
else
mRefIndex.erase(iter++);
}
else
++iter;
}
}
void CSMWorld::RefCollection::appendBlankRecord (const std::string& id, UniversalId::Type type)
{
std::unique_ptr<Record<CellRef> > record(new Record<CellRef>);
record->mState = Record<CellRef>::State_ModifiedOnly;
record->mModified.blank();
record->get().mId = id;
record->get().mIdNum = extractIdNum(id);
Collection<CellRef, IdAccessor<CellRef> >::appendRecord(std::move(record));
}
void CSMWorld::RefCollection::cloneRecord (const std::string& origin,
const std::string& destination,
const UniversalId::Type type)
{
std::unique_ptr<Record<CellRef> > copy(new Record<CellRef>);
copy->mModified = getRecord(origin).get();
copy->mState = RecordBase::State_ModifiedOnly;
copy->get().mId = destination;
copy->get().mIdNum = extractIdNum(destination);
insertRecord(std::move(copy), getAppendIndex(destination, type)); // call RefCollection::insertRecord()
}
int CSMWorld::RefCollection::searchId(std::string_view id) const
{
return searchId(extractIdNum(id));
}
void CSMWorld::RefCollection::appendRecord (std::unique_ptr<RecordBase> record, UniversalId::Type type)
{
int index = getAppendIndex(/*id*/"", type); // for CellRef records id is ignored
mRefIndex.insert(std::make_pair(static_cast<Record<CellRef>*>(record.get())->get().mIdNum, index));
Collection<CellRef, IdAccessor<CellRef> >::insertRecord(std::move(record), index, type); // add records only
}
void CSMWorld::RefCollection::insertRecord (std::unique_ptr<RecordBase> record, int index,
UniversalId::Type type)
{
int size = getAppendIndex(/*id*/"", type); // for CellRef records id is ignored
unsigned int idNum = static_cast<Record<CellRef>*>(record.get())->get().mIdNum;
Collection<CellRef, IdAccessor<CellRef> >::insertRecord(std::move(record), index, type); // add records only
if (index < size-1)
{
for (std::map<unsigned int, int>::iterator iter(mRefIndex.begin()); iter != mRefIndex.end(); ++iter)
{
if (iter->second >= index)
++(iter->second);
}
}
mRefIndex.insert(std::make_pair(idNum, index));
}

View File

@ -2,6 +2,7 @@
#define CSM_WOLRD_REFCOLLECTION_H
#include <map>
#include <string_view>
#include "../doc/stage.hpp"
@ -14,12 +15,27 @@ namespace CSMWorld
struct Cell;
class UniversalId;
template<>
void Collection<CellRef, IdAccessor<CellRef> >::removeRows (int index, int count);
template<>
void Collection<CellRef, IdAccessor<CellRef> >::insertRecord (std::unique_ptr<RecordBase> record, int index,
UniversalId::Type type);
/// \brief References in cells
class RefCollection : public Collection<CellRef>
{
Collection<Cell>& mCells;
std::map<unsigned int, int> mRefIndex; // CellRef index keyed by CSMWorld::CellRef::mIdNum
int mNextId;
unsigned int extractIdNum(std::string_view id) const;
int getIntIndex (unsigned int id) const;
int searchId (unsigned int id) const;
public:
// MSVC needs the constructor for a class inheriting a template to be defined in header
RefCollection (Collection<Cell>& cells)
@ -27,10 +43,28 @@ namespace CSMWorld
{}
void load (ESM::ESMReader& reader, int cellIndex, bool base,
std::map<ESM::RefNum, std::string>& cache, CSMDoc::Messages& messages);
std::map<unsigned int, unsigned int>& cache, CSMDoc::Messages& messages);
///< Load a sequence of references.
std::string getNewId();
virtual void removeRows (int index, int count);
virtual void appendBlankRecord (const std::string& id,
UniversalId::Type type = UniversalId::Type_None);
virtual void cloneRecord (const std::string& origin,
const std::string& destination,
const UniversalId::Type type);
virtual int searchId(std::string_view id) const;
virtual void appendRecord (std::unique_ptr<RecordBase> record,
UniversalId::Type type = UniversalId::Type_None);
virtual void insertRecord (std::unique_ptr<RecordBase> record,
int index,
UniversalId::Type type = UniversalId::Type_None);
};
}

View File

@ -25,6 +25,9 @@ namespace CSMWorld
const RefIdColumn *mId;
const RefIdColumn *mModified;
const RefIdColumn *mType;
const RefIdColumn *mBlocked;
BaseColumns () : mBlocked(nullptr) {}
};
/// \brief Base adapter for all refereceable record types
@ -90,6 +93,9 @@ namespace CSMWorld
if (column==mBase.mType)
return static_cast<int> (mType);
if (column==mBase.mBlocked)
return (record.get().mRecordFlags & ESM::FLAG_Blocked) != 0;
return QVariant();
}
@ -102,6 +108,17 @@ namespace CSMWorld
if (column==mBase.mModified)
record.mState = static_cast<RecordBase::State> (value.toInt());
else if (column==mBase.mBlocked)
{
RecordT record2 = record.get();
if (value.toInt() != 0)
record2.mRecordFlags |= ESM::FLAG_Blocked;
else
record2.mRecordFlags &= ~ESM::FLAG_Blocked;
record.setModified(record2);
}
}
template<typename RecordT>
@ -110,6 +127,14 @@ namespace CSMWorld
return mType;
}
// NOTE: Body Part should not have persistence (but BodyPart is not listed in the Objects
// table at the moment).
//
// Spellmaking - not persistent - currently not part of objects table
// Enchanting - not persistent - currently not part of objects table
//
// Leveled Creature - no model, so not persistent
// Leveled Item - no model, so not persistent
struct ModelColumns : public BaseColumns
{

View File

@ -2,6 +2,7 @@
#include <stdexcept>
#include <memory>
#include <string_view>
#include <components/esm/esmreader.hpp>
@ -49,17 +50,22 @@ CSMWorld::RefIdCollection::RefIdCollection()
mColumns.emplace_back(Columns::ColumnId_RecordType, ColumnBase::Display_RefRecordType,
ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false);
baseColumns.mType = &mColumns.back();
mColumns.emplace_back(Columns::ColumnId_Blocked, ColumnBase::Display_Boolean,
ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh);
baseColumns.mBlocked = &mColumns.back();
ModelColumns modelColumns (baseColumns);
mColumns.emplace_back(Columns::ColumnId_Model, ColumnBase::Display_Mesh);
modelColumns.mModel = &mColumns.back();
mColumns.emplace_back(Columns::ColumnId_Persistent, ColumnBase::Display_Boolean);
modelColumns.mPersistence = &mColumns.back();
mColumns.emplace_back(Columns::ColumnId_Model, ColumnBase::Display_Mesh);
modelColumns.mModel = &mColumns.back();
NameColumns nameColumns (modelColumns);
mColumns.emplace_back(Columns::ColumnId_Name, ColumnBase::Display_String);
// Only items that can be placed in a container have the 32 character limit, but enforce
// that for all referenceable types for now.
mColumns.emplace_back(Columns::ColumnId_Name, ColumnBase::Display_String32);
nameColumns.mName = &mColumns.back();
mColumns.emplace_back(Columns::ColumnId_Script, ColumnBase::Display_Script);
nameColumns.mScript = &mColumns.back();
@ -231,9 +237,9 @@ CSMWorld::RefIdCollection::RefIdCollection()
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_AiWanderRepeat, CSMWorld::ColumnBase::Display_Boolean));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_AiActivateName, CSMWorld::ColumnBase::Display_String));
new RefIdColumn (Columns::ColumnId_AiActivateName, CSMWorld::ColumnBase::Display_String32));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_AiTargetId, CSMWorld::ColumnBase::Display_String));
new RefIdColumn (Columns::ColumnId_AiTargetId, CSMWorld::ColumnBase::Display_String32));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_AiTargetCell, CSMWorld::ColumnBase::Display_String));
mColumns.back().addColumn(
@ -479,6 +485,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
mColumns.emplace_back(Columns::ColumnId_Class, ColumnBase::Display_Class);
npcColumns.mClass = &mColumns.back();
// NAME32 enforced in IdCompletionDelegate::createEditor()
mColumns.emplace_back(Columns::ColumnId_Faction, ColumnBase::Display_Faction);
npcColumns.mFaction = &mColumns.back();
@ -764,7 +771,6 @@ void CSMWorld::RefIdCollection::setNestedData(int row, int column, const QVarian
const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second);
nestedAdapter.setNestedData(&mColumns.at (column), mData, localIndex.first, data, subRow, subColumn);
return;
}
void CSMWorld::RefIdCollection::removeRows (int index, int count)
@ -778,7 +784,6 @@ void CSMWorld::RefIdCollection::removeNestedRows(int row, int column, int subRow
const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second);
nestedAdapter.removeNestedRow(&mColumns.at (column), mData, localIndex.first, subRow);
return;
}
void CSMWorld::RefIdCollection::appendBlankRecord (const std::string& id, UniversalId::Type type)
@ -786,7 +791,7 @@ void CSMWorld::RefIdCollection::appendBlankRecord (const std::string& id, Univer
mData.appendRecord (type, id, false);
}
int CSMWorld::RefIdCollection::searchId (const std::string& id) const
int CSMWorld::RefIdCollection::searchId(std::string_view id) const
{
RefIdData::LocalIndex localIndex = mData.searchId (id);
@ -796,18 +801,18 @@ int CSMWorld::RefIdCollection::searchId (const std::string& id) const
return mData.localToGlobalIndex (localIndex);
}
void CSMWorld::RefIdCollection::replace (int index, const RecordBase& record)
void CSMWorld::RefIdCollection::replace (int index, std::unique_ptr<RecordBase> record)
{
mData.getRecord (mData.globalToLocalIndex (index)).assign (record);
mData.getRecord (mData.globalToLocalIndex (index)).assign (*record.release());
}
void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin,
const std::string& destination,
const CSMWorld::UniversalId::Type type)
{
std::unique_ptr<RecordBase> newRecord(mData.getRecord(mData.searchId(origin)).modifiedCopy());
std::unique_ptr<RecordBase> newRecord = mData.getRecord(mData.searchId(origin)).modifiedCopy();
mAdapters.find(type)->second->setId(*newRecord, destination);
mData.insertRecord(*newRecord, type, destination);
mData.insertRecord(std::move(newRecord), type, destination);
}
bool CSMWorld::RefIdCollection::touchRecord(const std::string& id)
@ -816,16 +821,16 @@ bool CSMWorld::RefIdCollection::touchRecord(const std::string& id)
return false;
}
void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record,
void CSMWorld::RefIdCollection::appendRecord (std::unique_ptr<RecordBase> record,
UniversalId::Type type)
{
std::string id = findAdapter (type).getId (record);
std::string id = findAdapter (type).getId (*record.get());
int index = mData.getAppendIndex (type);
mData.appendRecord (type, id, false);
mData.getRecord (mData.globalToLocalIndex (index)).assign (record);
mData.getRecord (mData.globalToLocalIndex (index)).assign (*record.release());
}
const CSMWorld::RecordBase& CSMWorld::RefIdCollection::getRecord (const std::string& id) const
@ -895,7 +900,6 @@ void CSMWorld::RefIdCollection::addNestedRow(int row, int col, int position)
const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(col), localIndex.second);
nestedAdapter.addNestedRow(&mColumns.at(col), mData, localIndex.first, position);
return;
}
void CSMWorld::RefIdCollection::setNestedTable(int row, int column, const CSMWorld::NestedTableWrapperBase& nestedTable)
@ -904,7 +908,6 @@ void CSMWorld::RefIdCollection::setNestedTable(int row, int column, const CSMWor
const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second);
nestedAdapter.setNestedTable(&mColumns.at(column), mData, localIndex.first, nestedTable);
return;
}
CSMWorld::NestedTableWrapperBase* CSMWorld::RefIdCollection::nestedTable(int row, int column) const

View File

@ -4,6 +4,7 @@
#include <vector>
#include <map>
#include <deque>
#include <string_view>
#include "columnbase.hpp"
#include "collectionbase.hpp"
@ -85,16 +86,16 @@ namespace CSMWorld
void appendBlankRecord (const std::string& id, UniversalId::Type type) override;
///< \param type Will be ignored, unless the collection supports multiple record types
int searchId (const std::string& id) const override;
int searchId(std::string_view id) const override;
////< Search record with \a id.
/// \return index of record (if found) or -1 (not found)
void replace (int index, const RecordBase& record) override;
void replace (int index, std::unique_ptr<RecordBase> record) override;
///< If the record type does not match, an exception is thrown.
///
/// \attention \a record must not change the ID.
void appendRecord (const RecordBase& record, UniversalId::Type type) override;
void appendRecord (std::unique_ptr<RecordBase> record, UniversalId::Type type) override;
///< If the record type does not match, an exception is thrown.
///
///< \param type Will be ignored, unless the collection supports multiple record types

View File

@ -2,6 +2,7 @@
#include <cassert>
#include <memory>
#include <string_view>
CSMWorld::RefIdDataContainerBase::~RefIdDataContainerBase() {}
@ -74,8 +75,7 @@ int CSMWorld::RefIdData::localToGlobalIndex (const LocalIndex& index)
return globalIndex;
}
CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::searchId (
const std::string& id) const
CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::searchId(std::string_view id) const
{
std::string id2 = Misc::StringUtils::lowerCase (id);
@ -400,7 +400,7 @@ const CSMWorld::RefIdDataContainer< ESM::Static >& CSMWorld::RefIdData::getStati
return mStatics;
}
void CSMWorld::RefIdData::insertRecord (CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, const std::string& id)
void CSMWorld::RefIdData::insertRecord (std::unique_ptr<CSMWorld::RecordBase> record, CSMWorld::UniversalId::Type type, const std::string& id)
{
std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =
mRecordContainers.find (type);
@ -408,7 +408,7 @@ void CSMWorld::RefIdData::insertRecord (CSMWorld::RecordBase& record, CSMWorld::
if (iter==mRecordContainers.end())
throw std::logic_error ("invalid local index type");
iter->second->insertRecord(record);
iter->second->insertRecord(std::move(record));
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id),
LocalIndex (iter->second->getSize()-1, type)));
@ -420,9 +420,7 @@ void CSMWorld::RefIdData::copyTo (int index, RefIdData& target) const
RefIdDataContainerBase *source = mRecordContainers.find (localIndex.second)->second;
std::string id = source->getId (localIndex.first);
std::unique_ptr<CSMWorld::RecordBase> newRecord (source->getRecord (localIndex.first).modifiedCopy());
target.insertRecord (*newRecord, localIndex.second, id);
target.insertRecord(source->getRecord(localIndex.first).modifiedCopy(),
localIndex.second,
source->getId(localIndex.first));
}

View File

@ -3,6 +3,9 @@
#include <vector>
#include <map>
#include <memory>
#include <cassert>
#include <string_view>
#include <components/esm/loadacti.hpp>
#include <components/esm/loadalch.hpp>
@ -51,7 +54,7 @@ namespace CSMWorld
virtual void appendRecord (const std::string& id, bool base) = 0;
virtual void insertRecord (RecordBase& record) = 0;
virtual void insertRecord (std::unique_ptr<RecordBase> record) = 0;
virtual int load (ESM::ESMReader& reader, bool base) = 0;
///< \return index of a loaded record or -1 if no record was loaded
@ -66,7 +69,7 @@ namespace CSMWorld
template<typename RecordT>
struct RefIdDataContainer : public RefIdDataContainerBase
{
std::vector<Record<RecordT> > mContainer;
std::vector<std::unique_ptr<Record<RecordT> > > mContainer;
int getSize() const override;
@ -78,7 +81,7 @@ namespace CSMWorld
void appendRecord (const std::string& id, bool base) override;
void insertRecord (RecordBase& record) override;
void insertRecord (std::unique_ptr<RecordBase> record) override;
int load (ESM::ESMReader& reader, bool base) override;
///< \return index of a loaded record or -1 if no record was loaded
@ -91,10 +94,13 @@ namespace CSMWorld
};
template<typename RecordT>
void RefIdDataContainer<RecordT>::insertRecord(RecordBase& record)
void RefIdDataContainer<RecordT>::insertRecord(std::unique_ptr<RecordBase> record)
{
Record<RecordT>& newRecord = dynamic_cast<Record<RecordT>& >(record);
mContainer.push_back(newRecord);
assert(record != nullptr);
// convert base pointer to record type pointer
std::unique_ptr<Record<RecordT>> typedRecord(&dynamic_cast<Record<RecordT>&>(*record));
record.release();
mContainer.push_back(std::move(typedRecord));
}
template<typename RecordT>
@ -106,33 +112,33 @@ namespace CSMWorld
template<typename RecordT>
const RecordBase& RefIdDataContainer<RecordT>::getRecord (int index) const
{
return mContainer.at (index);
return *mContainer.at (index);
}
template<typename RecordT>
RecordBase& RefIdDataContainer<RecordT>::getRecord (int index)
{
return mContainer.at (index);
return *mContainer.at (index);
}
template<typename RecordT>
unsigned int RefIdDataContainer<RecordT>::getRecordFlags (int index) const
{
return mContainer.at (index).get().mRecordFlags;
return mContainer.at (index)->get().mRecordFlags;
}
template<typename RecordT>
void RefIdDataContainer<RecordT>::appendRecord (const std::string& id, bool base)
{
Record<RecordT> record;
std::unique_ptr<Record<RecordT> > record(new Record<RecordT>);
record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
record.mBase.mId = id;
record.mModified.mId = id;
(base ? record.mBase : record.mModified).blank();
record->mBase.mId = id;
record->mModified.mId = id;
(base ? record->mBase : record->mModified).blank();
mContainer.push_back (record);
mContainer.push_back (std::move(record));
}
template<typename RecordT>
@ -147,7 +153,7 @@ namespace CSMWorld
int numRecords = static_cast<int>(mContainer.size());
for (; index < numRecords; ++index)
{
if (Misc::StringUtils::ciEqual(mContainer[index].get().mId, record.mId))
if (Misc::StringUtils::ciEqual(mContainer[index]->get().mId, record.mId))
{
break;
}
@ -165,7 +171,7 @@ namespace CSMWorld
// Flag the record as Deleted even for a base content file.
// RefIdData is responsible for its erasure.
mContainer[index].mState = RecordBase::State_Deleted;
mContainer[index]->mState = RecordBase::State_Deleted;
}
else
{
@ -174,22 +180,22 @@ namespace CSMWorld
appendRecord(record.mId, base);
if (base)
{
mContainer.back().mBase = record;
mContainer.back()->mBase = record;
}
else
{
mContainer.back().mModified = record;
mContainer.back()->mModified = record;
}
}
else if (!base)
{
mContainer[index].setModified(record);
mContainer[index]->setModified(record);
}
else
{
// Overwrite
mContainer[index].setModified(record);
mContainer[index].merge();
mContainer[index]->setModified(record);
mContainer[index]->merge();
}
}
@ -208,13 +214,13 @@ namespace CSMWorld
template<typename RecordT>
std::string RefIdDataContainer<RecordT>::getId (int index) const
{
return mContainer.at (index).get().mId;
return mContainer.at (index)->get().mId;
}
template<typename RecordT>
void RefIdDataContainer<RecordT>::save (int index, ESM::ESMWriter& writer) const
{
Record<RecordT> record = mContainer.at(index);
const Record<RecordT>& record = *mContainer.at(index);
if (record.isModified() || record.mState == RecordBase::State_Deleted)
{
@ -272,11 +278,11 @@ namespace CSMWorld
int localToGlobalIndex (const LocalIndex& index) const;
LocalIndex searchId (const std::string& id) const;
LocalIndex searchId(std::string_view id) const;
void erase (int index, int count);
void insertRecord (CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type,
void insertRecord (std::unique_ptr<RecordBase> record, CSMWorld::UniversalId::Type type,
const std::string& id);
const RecordBase& getRecord (const LocalIndex& index) const;

View File

@ -3,6 +3,7 @@
#include <sstream>
#include <stdexcept>
#include <algorithm>
#include <string_view>
#include <components/vfs/manager.hpp>
@ -22,10 +23,8 @@ void CSMWorld::Resources::recreate(const VFS::Manager* vfs, const char * const *
size_t baseSize = mBaseDirectory.size();
const std::map<std::string, VFS::File*>& index = vfs->getIndex();
for (std::map<std::string, VFS::File*>::const_iterator it = index.begin(); it != index.end(); ++it)
for (const auto& filepath : vfs->getRecursiveDirectoryIterator(""))
{
std::string filepath = it->first;
if (filepath.size()<baseSize+1 ||
filepath.substr (0, baseSize)!=mBaseDirectory ||
(filepath[baseSize]!='/' && filepath[baseSize]!='\\'))
@ -83,7 +82,7 @@ int CSMWorld::Resources::getIndex (const std::string& id) const
return index;
}
int CSMWorld::Resources::searchId (const std::string& id) const
int CSMWorld::Resources::searchId(std::string_view id) const
{
std::string id2 = Misc::StringUtils::lowerCase (id);

View File

@ -4,6 +4,7 @@
#include <string>
#include <map>
#include <vector>
#include <string_view>
#include "universalid.hpp"
@ -35,7 +36,7 @@ namespace CSMWorld
int getIndex (const std::string& id) const;
int searchId (const std::string& id) const;
int searchId(std::string_view id) const;
UniversalId::Type getType() const;
};

View File

@ -17,7 +17,7 @@ void CSVDoc::LoadingDocument::closeEvent (QCloseEvent *event)
}
CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document)
: mDocument (document), mAborted (false), mMessages (nullptr), mTotalRecords (0)
: mDocument (document), mTotalRecordsLabel (0), mRecordsLabel (0), mAborted (false), mMessages (nullptr), mRecords(0)
{
setWindowTitle (QString::fromUtf8((std::string("Opening ") + document->getSavePath().filename().string()).c_str()));
@ -25,26 +25,25 @@ CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document)
mLayout = new QVBoxLayout (this);
// file progress
mFile = new QLabel (this);
// total progress
mTotalRecordsLabel = new QLabel (this);
mLayout->addWidget (mFile);
mLayout->addWidget (mTotalRecordsLabel);
mFileProgress = new QProgressBar (this);
mTotalProgress = new QProgressBar (this);
mLayout->addWidget (mFileProgress);
mLayout->addWidget (mTotalProgress);
int size = static_cast<int> (document->getContentFiles().size())+1;
if (document->isNew())
--size;
mTotalProgress->setMinimum (0);
mTotalProgress->setMaximum (document->getData().getTotalRecords(document->getContentFiles()));
mTotalProgress->setTextVisible (true);
mTotalProgress->setValue (0);
mTotalRecords = 0;
mFileProgress->setMinimum (0);
mFileProgress->setMaximum (size);
mFileProgress->setTextVisible (true);
mFileProgress->setValue (0);
mFilesLoaded = 0;
// record progress
mLayout->addWidget (mRecords = new QLabel ("Records", this));
mLayout->addWidget (mRecordsLabel = new QLabel ("Records", this));
mRecordProgress = new QProgressBar (this);
@ -74,29 +73,32 @@ CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document)
connect (mButtons, SIGNAL (rejected()), this, SLOT (cancel()));
}
void CSVDoc::LoadingDocument::nextStage (const std::string& name, int totalRecords)
void CSVDoc::LoadingDocument::nextStage (const std::string& name, int fileRecords)
{
mFile->setText (QString::fromUtf8 (("Loading: " + name).c_str()));
++mFilesLoaded;
size_t numFiles = mDocument->getContentFiles().size();
mFileProgress->setValue (mFileProgress->value()+1);
mTotalRecordsLabel->setText (QString::fromUtf8 (("Loading: "+name
+" ("+std::to_string(mFilesLoaded)+" of "+std::to_string((numFiles))+")").c_str()));
mTotalRecords = mTotalProgress->value();
mRecordProgress->setValue (0);
mRecordProgress->setMaximum (totalRecords>0 ? totalRecords : 1);
mRecordProgress->setMaximum (fileRecords>0 ? fileRecords : 1);
mTotalRecords = totalRecords;
mRecords = fileRecords;
}
void CSVDoc::LoadingDocument::nextRecord (int records)
{
if (records<=mTotalRecords)
if (records <= mRecords)
{
mRecordProgress->setValue (records);
mTotalProgress->setValue (mTotalRecords+records);
std::ostringstream stream;
mRecordProgress->setValue(records);
stream << "Records: " << records << " of " << mTotalRecords;
mRecords->setText (QString::fromUtf8 (stream.str().c_str()));
mRecordsLabel->setText(QString::fromStdString(
"Records: "+std::to_string(records)+" of "+std::to_string(mRecords)));
}
}
@ -176,12 +178,12 @@ void CSVDoc::Loader::loadingStopped (CSMDoc::Document *document, bool completed,
}
void CSVDoc::Loader::nextStage (CSMDoc::Document *document, const std::string& name,
int totalRecords)
int fileRecords)
{
std::map<CSMDoc::Document *, LoadingDocument *>::iterator iter = mDocuments.find (document);
if (iter!=mDocuments.end())
iter->second->nextStage (name, totalRecords);
iter->second->nextStage (name, fileRecords);
}
void CSVDoc::Loader::nextRecord (CSMDoc::Document *document, int records)

View File

@ -25,16 +25,18 @@ namespace CSVDoc
Q_OBJECT
CSMDoc::Document *mDocument;
QLabel *mFile;
QLabel *mRecords;
QProgressBar *mFileProgress;
QLabel *mTotalRecordsLabel;
QLabel *mRecordsLabel;
QProgressBar *mTotalProgress;
QProgressBar *mRecordProgress;
bool mAborted;
QDialogButtonBox *mButtons;
QLabel *mError;
QListWidget *mMessages;
QVBoxLayout *mLayout;
int mRecords;
int mTotalRecords;
int mFilesLoaded;
private:

View File

@ -66,6 +66,8 @@ void CSVDoc::View::setupFileMenu()
connect (save, SIGNAL (triggered()), this, SLOT (save()));
mSave = save;
file->addSeparator();
QAction* verify = createMenuEntry("Verify", ":./menu-verify.png", file, "document-file-verify");
connect (verify, SIGNAL (triggered()), this, SLOT (verify()));
mVerify = verify;
@ -80,6 +82,8 @@ void CSVDoc::View::setupFileMenu()
QAction* meta = createMenuEntry(CSMWorld::UniversalId::Type_MetaDatas, file, "document-file-metadata");
connect (meta, SIGNAL (triggered()), this, SLOT (addMetaDataSubView()));
file->addSeparator();
QAction* close = createMenuEntry("Close", ":./menu-close.png", file, "document-file-close");
connect (close, SIGNAL (triggered()), this, SLOT (close()));
@ -156,18 +160,17 @@ void CSVDoc::View::setupWorldMenu()
{
QMenu *world = menuBar()->addMenu (tr ("World"));
QAction* regions = createMenuEntry(CSMWorld::UniversalId::Type_Regions, world, "document-world-regions");
connect (regions, SIGNAL (triggered()), this, SLOT (addRegionsSubView()));
QAction* cells = createMenuEntry(CSMWorld::UniversalId::Type_Cells, world, "document-world-cells");
connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView()));
QAction* referenceables = createMenuEntry(CSMWorld::UniversalId::Type_Referenceables, world, "document-world-referencables");
connect (referenceables, SIGNAL (triggered()), this, SLOT (addReferenceablesSubView()));
QAction* references = createMenuEntry(CSMWorld::UniversalId::Type_References, world, "document-world-references");
connect (references, SIGNAL (triggered()), this, SLOT (addReferencesSubView()));
world->addSeparator();
QAction* cells = createMenuEntry(CSMWorld::UniversalId::Type_Cells, world, "document-world-cells");
connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView()));
QAction *lands = createMenuEntry(CSMWorld::UniversalId::Type_Lands, world, "document-world-lands");
connect (lands, SIGNAL (triggered()), this, SLOT (addLandsSubView()));
@ -177,7 +180,10 @@ void CSVDoc::View::setupWorldMenu()
QAction *grid = createMenuEntry(CSMWorld::UniversalId::Type_Pathgrids, world, "document-world-pathgrid");
connect (grid, SIGNAL (triggered()), this, SLOT (addPathgridSubView()));
world->addSeparator(); // items that don't represent single record lists follow here
world->addSeparator();
QAction* regions = createMenuEntry(CSMWorld::UniversalId::Type_Regions, world, "document-world-regions");
connect (regions, SIGNAL (triggered()), this, SLOT (addRegionsSubView()));
QAction *regionMap = createMenuEntry(CSMWorld::UniversalId::Type_RegionMap, world, "document-world-regionmap");
connect (regionMap, SIGNAL (triggered()), this, SLOT (addRegionMapSubView()));
@ -187,14 +193,19 @@ void CSVDoc::View::setupMechanicsMenu()
{
QMenu *mechanics = menuBar()->addMenu (tr ("Mechanics"));
QAction* scripts = createMenuEntry(CSMWorld::UniversalId::Type_Scripts, mechanics, "document-mechanics-scripts");
connect (scripts, SIGNAL (triggered()), this, SLOT (addScriptsSubView()));
QAction* startScripts = createMenuEntry(CSMWorld::UniversalId::Type_StartScripts, mechanics, "document-mechanics-startscripts");
connect (startScripts, SIGNAL (triggered()), this, SLOT (addStartScriptsSubView()));
QAction* globals = createMenuEntry(CSMWorld::UniversalId::Type_Globals, mechanics, "document-mechanics-globals");
connect (globals, SIGNAL (triggered()), this, SLOT (addGlobalsSubView()));
QAction* gmsts = createMenuEntry(CSMWorld::UniversalId::Type_Gmsts, mechanics, "document-mechanics-gamesettings");
connect (gmsts, SIGNAL (triggered()), this, SLOT (addGmstsSubView()));
QAction* scripts = createMenuEntry(CSMWorld::UniversalId::Type_Scripts, mechanics, "document-mechanics-scripts");
connect (scripts, SIGNAL (triggered()), this, SLOT (addScriptsSubView()));
mechanics->addSeparator();
QAction* spells = createMenuEntry(CSMWorld::UniversalId::Type_Spells, mechanics, "document-mechanics-spells");
connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView()));
@ -204,9 +215,6 @@ void CSVDoc::View::setupMechanicsMenu()
QAction* magicEffects = createMenuEntry(CSMWorld::UniversalId::Type_MagicEffects, mechanics, "document-mechanics-magiceffects");
connect (magicEffects, SIGNAL (triggered()), this, SLOT (addMagicEffectsSubView()));
QAction* startScripts = createMenuEntry(CSMWorld::UniversalId::Type_StartScripts, mechanics, "document-mechanics-startscripts");
connect (startScripts, SIGNAL (triggered()), this, SLOT (addStartScriptsSubView()));
}
void CSVDoc::View::setupCharacterMenu()
@ -228,20 +236,24 @@ void CSVDoc::View::setupCharacterMenu()
QAction* birthsigns = createMenuEntry(CSMWorld::UniversalId::Type_Birthsigns, characters, "document-character-birthsigns");
connect (birthsigns, SIGNAL (triggered()), this, SLOT (addBirthsignsSubView()));
QAction* bodyParts = createMenuEntry(CSMWorld::UniversalId::Type_BodyParts, characters, "document-character-bodyparts");
connect (bodyParts, SIGNAL (triggered()), this, SLOT (addBodyPartsSubView()));
characters->addSeparator();
QAction* topics = createMenuEntry(CSMWorld::UniversalId::Type_Topics, characters, "document-character-topics");
connect (topics, SIGNAL (triggered()), this, SLOT (addTopicsSubView()));
QAction* journals = createMenuEntry(CSMWorld::UniversalId::Type_Journals, characters, "document-character-journals");
connect (journals, SIGNAL (triggered()), this, SLOT (addJournalsSubView()));
QAction* topicInfos = createMenuEntry(CSMWorld::UniversalId::Type_TopicInfos, characters, "document-character-topicinfos");
connect (topicInfos, SIGNAL (triggered()), this, SLOT (addTopicInfosSubView()));
characters->addSeparator();
QAction* journals = createMenuEntry(CSMWorld::UniversalId::Type_Journals, characters, "document-character-journals");
connect (journals, SIGNAL (triggered()), this, SLOT (addJournalsSubView()));
QAction* journalInfos = createMenuEntry(CSMWorld::UniversalId::Type_JournalInfos, characters, "document-character-journalinfos");
connect (journalInfos, SIGNAL (triggered()), this, SLOT (addJournalInfosSubView()));
QAction* bodyParts = createMenuEntry(CSMWorld::UniversalId::Type_BodyParts, characters, "document-character-bodyparts");
connect (bodyParts, SIGNAL (triggered()), this, SLOT (addBodyPartsSubView()));
}
void CSVDoc::View::setupAssetsMenu()

View File

@ -96,7 +96,7 @@ namespace CSVRender
for (int i = 0; i < ESM::PRT_Count; ++i)
{
auto type = (ESM::PartReferenceType) i;
std::string partId = mActorData->getPart(type);
const std::string_view partId = mActorData->getPart(type);
attachBodyPart(type, getBodyPartMesh(partId));
}
}
@ -115,7 +115,7 @@ namespace CSVRender
}
}
std::string Actor::getBodyPartMesh(const std::string& bodyPartId)
std::string Actor::getBodyPartMesh(std::string_view bodyPartId)
{
const auto& bodyParts = mData.getBodyParts();

View File

@ -2,6 +2,7 @@
#define OPENCS_VIEW_RENDER_ACTOR_H
#include <string>
#include <string_view>
#include <osg/ref_ptr>
@ -54,7 +55,7 @@ namespace CSVRender
void loadBodyParts();
void attachBodyPart(ESM::PartReferenceType, const std::string& mesh);
std::string getBodyPartMesh(const std::string& bodyPartId);
std::string getBodyPartMesh(std::string_view bodyPartId);
static const std::string MeshPrefix;

View File

@ -18,6 +18,8 @@ CSVRender::BrushDraw::BrushDraw(osg::ref_ptr<osg::Group> parentNode, bool textur
mBrushDrawNode = new osg::Group();
mGeometry = new osg::Geometry();
mBrushDrawNode->addChild(mGeometry);
mBrushDrawNode->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
mBrushDrawNode->getOrCreateStateSet()->setRenderBinDetails(11, "RenderBin");
mParentNode->addChild(mBrushDrawNode);
if (mTextureMode)
mLandSizeFactor = static_cast<float>(ESM::Land::REAL_SIZE) / static_cast<float>(ESM::Land::LAND_TEXTURE_SIZE);
@ -122,7 +124,14 @@ void CSVRender::BrushDraw::buildSquareGeometry(const float& radius, const osg::V
const float brushOutlineHeight (1.0f);
float diameter = radius * 2;
int resolution = static_cast<int>(2.f * diameter / mLandSizeFactor); //half a vertex resolution
float resAdjustedLandSizeFactor = mLandSizeFactor / 2;
float resAdjustedLandSizeFactor = mLandSizeFactor / 2; //128
if (resolution > 128) // limit accuracy for performance
{
resolution = 128;
resAdjustedLandSizeFactor = diameter / resolution;
}
osg::Vec4f lineColor(1.0f, 1.0f, 1.0f, 0.6f);
for (int i = 0; i < resolution; i++)
@ -215,7 +224,8 @@ void CSVRender::BrushDraw::buildCircleGeometry(const float& radius, const osg::V
osg::ref_ptr<osg::Geometry> geom (new osg::Geometry());
osg::ref_ptr<osg::Vec3Array> vertices (new osg::Vec3Array());
osg::ref_ptr<osg::Vec4Array> colors (new osg::Vec4Array());
const int amountOfPoints = (osg::PI * 2.0f) * radius / 20;
const int amountOfPoints = 128;
const float step ((osg::PI * 2.0f) / static_cast<float>(amountOfPoints));
const float brushOutlineHeight (1.0f);
osg::Vec4f lineColor(1.0f, 1.0f, 1.0f, 0.6f);

View File

@ -60,7 +60,7 @@ void CSVRender::CellArrow::adjustTransform()
{
// position
const int cellSize = Constants::CellSizeInUnits;
const int offset = cellSize / 2 + 800;
const int offset = cellSize / 2 + 600;
int x = mCoordinates.getX()*cellSize + cellSize/2;
int y = mCoordinates.getY()*cellSize + cellSize/2;
@ -92,9 +92,9 @@ void CSVRender::CellArrow::buildShape()
{
osg::ref_ptr<osg::Geometry> geometry (new osg::Geometry);
const int arrowWidth = 4000;
const int arrowLength = 1500;
const int arrowHeight = 500;
const int arrowWidth = 2700;
const int arrowLength = 1350;
const int arrowHeight = 300;
osg::Vec3Array *vertices = new osg::Vec3Array;
for (int i2=0; i2<2; ++i2)

View File

@ -1,19 +1,44 @@
#include "commands.hpp"
#include <QPointer>
#include <components/debug/debuglog.hpp>
#include <components/esm/loadland.hpp>
#include "editmode.hpp"
#include "terrainselection.hpp"
#include "terrainshapemode.hpp"
#include "terraintexturemode.hpp"
#include "worldspacewidget.hpp"
CSVRender::DrawTerrainSelectionCommand::DrawTerrainSelectionCommand(TerrainSelection& terrainSelection, QUndoCommand* parent)
: mTerrainSelection(terrainSelection)
CSVRender::DrawTerrainSelectionCommand::DrawTerrainSelectionCommand(WorldspaceWidget* worldspaceWidget, QUndoCommand* parent)
: mWorldspaceWidget(worldspaceWidget)
{ }
void CSVRender::DrawTerrainSelectionCommand::redo()
{
mTerrainSelection.update();
tryUpdate();
}
void CSVRender::DrawTerrainSelectionCommand::undo()
{
mTerrainSelection.update();
tryUpdate();
}
void CSVRender::DrawTerrainSelectionCommand::tryUpdate()
{
if (!mWorldspaceWidget)
{
Log(Debug::Verbose) << "Can't update terrain selection, no WorldspaceWidget found!";
return;
}
auto terrainMode = dynamic_cast<CSVRender::TerrainShapeMode*>(mWorldspaceWidget->getEditMode());
if (!terrainMode)
{
Log(Debug::Verbose) << "Can't update terrain selection in current EditMode";
return;
}
terrainMode->getTerrainSelection()->update();
}

View File

@ -1,8 +1,12 @@
#ifndef CSV_RENDER_COMMANDS_HPP
#define CSV_RENDER_COMMANDS_HPP
#include <QPointer>
#include <QUndoCommand>
#include "worldspacewidget.hpp"
namespace CSVRender
{
class TerrainSelection;
@ -21,14 +25,17 @@ namespace CSVRender
*/
class DrawTerrainSelectionCommand : public QUndoCommand
{
private:
TerrainSelection& mTerrainSelection;
QPointer<WorldspaceWidget> mWorldspaceWidget;
public:
DrawTerrainSelectionCommand(TerrainSelection& terrainSelection, QUndoCommand* parent = nullptr);
DrawTerrainSelectionCommand(WorldspaceWidget* worldspaceWidget, QUndoCommand* parent = nullptr);
void redo() override;
void undo() override;
void tryUpdate();
};
}

View File

@ -297,6 +297,8 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (const QPoint& pos)
return false;
}
mObjectsAtDragStart.clear();
for (std::vector<osg::ref_ptr<TagBase> >::iterator iter (selection.begin());
iter!=selection.end(); ++iter)
{
@ -305,6 +307,12 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (const QPoint& pos)
if (mSubModeId == "move")
{
objectTag->mObject->setEdited (Object::Override_Position);
float x = objectTag->mObject->getPosition().pos[0];
float y = objectTag->mObject->getPosition().pos[1];
float z = objectTag->mObject->getPosition().pos[2];
osg::Vec3f thisPoint(x, y, z);
mDragStart = getMousePlaneCoords(pos, getProjectionSpaceCoords(thisPoint));
mObjectsAtDragStart.emplace_back(thisPoint);
mDragMode = DragMode_Move;
}
else if (mSubModeId == "rotate")
@ -392,29 +400,7 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou
std::vector<osg::ref_ptr<TagBase> > selection = getWorldspaceWidget().getEdited (Mask_Reference);
if (mDragMode == DragMode_Move)
{
osg::Vec3f eye, centre, up;
getWorldspaceWidget().getCamera()->getViewMatrix().getLookAt (eye, centre, up);
if (diffY)
{
offset += up * diffY * speedFactor;
}
if (diffX)
{
offset += ((centre-eye) ^ up) * diffX * speedFactor;
}
if (mDragAxis!=-1)
{
for (int i=0; i<3; ++i)
{
if (i!=mDragAxis)
offset[i] = 0;
}
}
}
if (mDragMode == DragMode_Move) {}
else if (mDragMode == DragMode_Rotate)
{
osg::Vec3f eye, centre, up;
@ -514,17 +500,32 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou
return;
}
int i = 0;
// Apply
for (std::vector<osg::ref_ptr<TagBase> >::iterator iter (selection.begin()); iter!=selection.end(); ++iter)
for (std::vector<osg::ref_ptr<TagBase> >::iterator iter (selection.begin()); iter!=selection.end(); ++iter, i++)
{
if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (iter->get()))
{
if (mDragMode == DragMode_Move)
{
ESM::Position position = objectTag->mObject->getPosition();
for (int i=0; i<3; ++i)
osg::Vec3f mousePos = getMousePlaneCoords(pos, getProjectionSpaceCoords(mDragStart));
float addToX = mousePos.x() - mDragStart.x();
float addToY = mousePos.y() - mDragStart.y();
float addToZ = mousePos.z() - mDragStart.z();
position.pos[0] = mObjectsAtDragStart[i].x() + addToX;
position.pos[1] = mObjectsAtDragStart[i].y() + addToY;
position.pos[2] = mObjectsAtDragStart[i].z() + addToZ;
// XYZ-locking
if (mDragAxis != -1)
{
position.pos[i] += offset[i];
for (int j = 0; j < 3; ++j)
{
if (j != mDragAxis)
position.pos[j] = mObjectsAtDragStart[i][j];
}
}
objectTag->mObject->setPosition(position.pos);
@ -608,6 +609,7 @@ void CSVRender::InstanceMode::dragCompleted(const QPoint& pos)
}
}
mObjectsAtDragStart.clear();
mDragMode = DragMode_None;
}
@ -634,8 +636,10 @@ void CSVRender::InstanceMode::dragWheel (int diff, double speedFactor)
std::vector<osg::ref_ptr<TagBase> > selection =
getWorldspaceWidget().getEdited (Mask_Reference);
int j = 0;
for (std::vector<osg::ref_ptr<TagBase> >::iterator iter (selection.begin());
iter!=selection.end(); ++iter)
iter!=selection.end(); ++iter, j++)
{
if (CSVRender::ObjectTag *objectTag = dynamic_cast<CSVRender::ObjectTag *> (iter->get()))
{
@ -643,6 +647,9 @@ void CSVRender::InstanceMode::dragWheel (int diff, double speedFactor)
for (int i=0; i<3; ++i)
position.pos[i] += offset[i];
objectTag->mObject->setPosition (position.pos);
osg::Vec3f thisPoint(position.pos[0], position.pos[1], position.pos[2]);
mDragStart = getMousePlaneCoords(getWorldspaceWidget().mapFromGlobal(QCursor::pos()), getProjectionSpaceCoords(thisPoint));
mObjectsAtDragStart[j] = thisPoint;
}
}
}

View File

@ -45,6 +45,8 @@ namespace CSVRender
bool mLocked;
float mUnitScaleDist;
osg::ref_ptr<osg::Group> mParentNode;
osg::Vec3f mDragStart;
std::vector<osg::Vec3f> mObjectsAtDragStart;
int getSubModeFromId (const std::string& id) const;

View File

@ -682,18 +682,20 @@ void CSVRender::Object::apply (CSMWorld::CommandMacro& commands)
int cellColumn = collection.findColumnIndex (static_cast<CSMWorld::Columns::ColumnId> (
CSMWorld::Columns::ColumnId_Cell));
int refNumColumn = collection.findColumnIndex (static_cast<CSMWorld::Columns::ColumnId> (
CSMWorld::Columns::ColumnId_RefNum));
int origCellColumn = collection.findColumnIndex(static_cast<CSMWorld::Columns::ColumnId> (
CSMWorld::Columns::ColumnId_OriginalCell));
if (cellIndex != originalIndex)
{
/// \todo figure out worldspace (not important until multiple worldspaces are supported)
std::string origCellId = CSMWorld::CellCoordinates(originalIndex).getId("");
std::string cellId = CSMWorld::CellCoordinates (cellIndex).getId ("");
commands.push (new CSMWorld::ModifyCommand (*model,
model->index (recordIndex, cellColumn), QString::fromUtf8 (cellId.c_str())));
commands.push (new CSMWorld::ModifyCommand( *model,
model->index (recordIndex, refNumColumn), 0));
model->index (recordIndex, origCellColumn), QString::fromUtf8 (origCellId.c_str())));
commands.push(new CSMWorld::ModifyCommand(*model,
model->index(recordIndex, cellColumn), QString::fromUtf8(cellId.c_str())));
// NOTE: refnum is not modified for moving a reference to another cell
}
}

View File

@ -287,7 +287,7 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges()
undoStack.beginMacro ("Edit shape and normal records");
// One command at the beginning of the macro for redrawing the terrain-selection grid when undoing the changes.
undoStack.push(new DrawTerrainSelectionCommand(*mTerrainShapeSelection));
undoStack.push(new DrawTerrainSelectionCommand(&getWorldspaceWidget()));
for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells)
{
@ -358,7 +358,7 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges()
pushNormalsEditToCommand(landNormalsNew, document, landTable, cellId);
}
// One command at the end of the macro for redrawing the terrain-selection grid when redoing the changes.
undoStack.push(new DrawTerrainSelectionCommand(*mTerrainShapeSelection));
undoStack.push(new DrawTerrainSelectionCommand(&getWorldspaceWidget()));
undoStack.endMacro();
clearTransientEdits();
@ -1444,6 +1444,11 @@ void CSVRender::TerrainShapeMode::mouseMoveEvent (QMouseEvent *event)
mBrushDraw->hide();
}
std::shared_ptr<CSVRender::TerrainSelection> CSVRender::TerrainShapeMode::getTerrainSelection()
{
return mTerrainShapeSelection;
}
void CSVRender::TerrainShapeMode::setBrushSize(int brushSize)
{
mBrushSize = brushSize;

View File

@ -92,6 +92,8 @@ namespace CSVRender
void dragMoveEvent (QDragMoveEvent *event) override;
void mouseMoveEvent (QMouseEvent *event) override;
std::shared_ptr<TerrainSelection> getTerrainSelection();
private:
/// Remove duplicates and sort mAlteredCells, then limitAlteredHeights forward and reverse
@ -176,7 +178,7 @@ namespace CSVRender
int mDragMode = InteractionType_None;
osg::Group* mParentNode;
bool mIsEditing = false;
std::unique_ptr<TerrainSelection> mTerrainShapeSelection;
std::shared_ptr<TerrainSelection> mTerrainShapeSelection;
int mTotalDiffY = 0;
std::vector<CSMWorld::CellCoordinates> mAlteredCells;
osg::Vec3d mEditingPos;

View File

@ -712,6 +712,11 @@ void CSVRender::TerrainTextureMode::mouseMoveEvent (QMouseEvent *event)
mBrushDraw->hide();
}
std::shared_ptr<CSVRender::TerrainSelection> CSVRender::TerrainTextureMode::getTerrainSelection()
{
return mTerrainTextureSelection;
}
void CSVRender::TerrainTextureMode::setBrushSize(int brushSize)
{

View File

@ -85,6 +85,8 @@ namespace CSVRender
void mouseMoveEvent (QMouseEvent *event) override;
std::shared_ptr<TerrainSelection> getTerrainSelection();
private:
/// \brief Handle brush mechanics, maths regarding worldspace hit etc.
void editTerrainTextureGrid (const WorldspaceHitResult& hit);
@ -115,7 +117,7 @@ namespace CSVRender
int mDragMode;
osg::Group* mParentNode;
bool mIsEditing;
std::unique_ptr<TerrainSelection> mTerrainTextureSelection;
std::shared_ptr<TerrainSelection> mTerrainTextureSelection;
const int cellSize {ESM::Land::REAL_SIZE};
const int landTextureSize {ESM::Land::LAND_TEXTURE_SIZE};

View File

@ -452,6 +452,11 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick (const QPo
return hit;
}
CSVRender::EditMode *CSVRender::WorldspaceWidget::getEditMode()
{
return dynamic_cast<CSVRender::EditMode *> (mEditMode->getCurrent());
}
void CSVRender::WorldspaceWidget::abortDrag()
{
if (mDragging)
@ -697,11 +702,6 @@ void CSVRender::WorldspaceWidget::handleInteractionPress (const WorldspaceHitRes
editMode.primaryOpenPressed (hit);
}
CSVRender::EditMode *CSVRender::WorldspaceWidget::getEditMode()
{
return dynamic_cast<CSVRender::EditMode *> (mEditMode->getCurrent());
}
void CSVRender::WorldspaceWidget::primaryOpen(bool activate)
{
handleInteraction(InteractionType_PrimaryOpen, activate);

View File

@ -189,6 +189,8 @@ namespace CSVRender
/// Erase all overrides and restore the visual representation to its true state.
virtual void reset (unsigned int elementMask) = 0;
EditMode *getEditMode();
protected:
/// Visual elements in a scene
@ -215,8 +217,6 @@ namespace CSVRender
void settingChanged (const CSMPrefs::Setting *setting) override;
EditMode *getEditMode();
bool getSpeedMode();
private:

View File

@ -1,5 +1,5 @@
#ifndef CSV_TOOLS_REPORTTABLE_H
#define CSV_TOOLS_REPORTTABLE_H
#ifndef CSV_TOOLS_MERGE_H
#define CSV_TOOLS_MERGE_H
#include <QWidget>

View File

@ -36,7 +36,7 @@ void CSVTools::SearchSubView::replace (bool selection)
// in a single string.
for (std::vector<int>::const_reverse_iterator iter (indices.rbegin()); iter!=indices.rend(); ++iter)
{
CSMWorld::UniversalId id = model.getUniversalId (*iter);
const CSMWorld::UniversalId& id = model.getUniversalId (*iter);
CSMWorld::UniversalId::Type type = CSMWorld::UniversalId::getParentType (id.getType());

View File

@ -179,10 +179,10 @@ void CSVWidget::TextureBrushWindow::setBrushTexture(std::string brushTexture)
undoStack.endMacro();
}
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
if (index != -1 && !landtexturesCollection.getRecord(rowInNew).isDeleted())
{
mBrushTextureLabel = "Selected texture: " + newBrushTextureId + " ";
mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel) + landtexturesCollection.getData(index, landTextureFilename).value<QString>());
mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel) + landtexturesCollection.getData(rowInNew, landTextureFilename).value<QString>());
} else
{
newBrushTextureId = "";

View File

@ -84,6 +84,13 @@ void CSVWorld::CellCreator::setType (int index)
mYLabel->setVisible (index==1);
mY->setVisible (index==1);
// The cell name is limited to 64 characters. (ESM::Header::GMDT::mCurrentCell)
std::string text = mType->currentText().toStdString();
if (text == "Interior Cell")
GenericCreator::setEditorMaxLength (64);
else
GenericCreator::setEditorMaxLength (32767);
update();
}

View File

@ -538,6 +538,9 @@ void CSVWorld::EditWidget::remake(int row)
mainLayout->addLayout(tablesLayout, QSizePolicy::Preferred);
mainLayout->addStretch(1);
int blockedColumn = mTable->searchColumnIndex(CSMWorld::Columns::ColumnId_Blocked);
bool isBlocked = mTable->data(mTable->index(row, blockedColumn)).toInt();
int unlocked = 0;
int locked = 0;
const int columns = mTable->columnCount();
@ -583,6 +586,8 @@ void CSVWorld::EditWidget::remake(int row)
NestedTable* table =
new NestedTable(mDocument, id, mNestedModels.back(), this, editable, fixedRows);
table->resizeColumnsToContents();
if (isBlocked)
table->setEditTriggers(QAbstractItemView::NoEditTriggers);
int rows = mTable->rowCount(mTable->index(row, i));
int rowHeight = (rows == 0) ? table->horizontalHeader()->height() : table->rowHeight(0);
@ -617,7 +622,9 @@ void CSVWorld::EditWidget::remake(int row)
label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed);
editor->setSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
if (! (mTable->flags (mTable->index (row, i)) & Qt::ItemIsEditable))
// HACK: the blocked checkbox needs to keep the same position
// FIXME: unfortunately blocked record displays a little differently to unblocked one
if (!(mTable->flags (mTable->index (row, i)) & Qt::ItemIsEditable) || i == blockedColumn)
{
lockedLayout->addWidget (label, locked, 0);
lockedLayout->addWidget (editor, locked, 1);
@ -639,7 +646,7 @@ void CSVWorld::EditWidget::remake(int row)
createEditorContextMenu(editor, display, row);
}
}
else
else // Flag_Dialogue_List
{
CSMWorld::IdTree *tree = static_cast<CSMWorld::IdTree *>(mTable);
mNestedTableMapper = new QDataWidgetMapper (this);
@ -686,7 +693,10 @@ void CSVWorld::EditWidget::remake(int row)
label->setEnabled(false);
}
createEditorContextMenu(editor, display, row);
if (!isBlocked)
createEditorContextMenu(editor, display, row);
else
editor->setEnabled(false);
}
}
mNestedTableMapper->setCurrentModelIndex(tree->index(0, 0, tree->index(row, i)));

View File

@ -79,6 +79,8 @@ QWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent, const QStyleOptio
iter!=mValues.end(); ++iter)
comboBox->addItem (iter->second);
comboBox->setMaxVisibleItems(20);
return comboBox;
}

View File

@ -184,6 +184,11 @@ CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undo
connect (&mData, SIGNAL (idListChanged()), this, SLOT (dataIdListChanged()));
}
void CSVWorld::GenericCreator::setEditorMaxLength (int length)
{
mId->setMaxLength (length);
}
void CSVWorld::GenericCreator::setEditLock (bool locked)
{
mLocked = locked;

View File

@ -84,6 +84,8 @@ namespace CSVWorld
std::string getNamespace() const;
void setEditorMaxLength(int length);
private:
void updateNamespace();

View File

@ -81,6 +81,23 @@ QWidget *CSVWorld::IdCompletionDelegate::createEditor(QWidget *parent,
CSMWorld::IdCompletionManager &completionManager = getDocument().getIdCompletionManager();
CSVWidget::DropLineEdit *editor = new CSVWidget::DropLineEdit(display, parent);
editor->setCompleter(completionManager.getCompleter(display).get());
// The savegame format limits the player faction string to 32 characters.
// The region sound name is limited to 32 characters. (ESM::Region::SoundRef::mSound)
// The script name is limited to 32 characters. (ESM::Script::SCHD::mName)
// The cell name is limited to 64 characters. (ESM::Header::GMDT::mCurrentCell)
if (display == CSMWorld::ColumnBase::Display_Faction ||
display == CSMWorld::ColumnBase::Display_Sound ||
display == CSMWorld::ColumnBase::Display_Script ||
display == CSMWorld::ColumnBase::Display_Referenceable)
{
editor->setMaxLength (32);
}
else if (display == CSMWorld::ColumnBase::Display_Cell)
{
editor->setMaxLength (64);
}
return editor;
}

View File

@ -22,6 +22,7 @@ CSVWorld::ReferenceableCreator::ReferenceableCreator (CSMWorld::Data& data, QUnd
std::vector<CSMWorld::UniversalId::Type> types = CSMWorld::UniversalId::listReferenceableTypes();
mType = new QComboBox (this);
mType->setMaxVisibleItems(20);
for (std::vector<CSMWorld::UniversalId::Type>::const_iterator iter (types.begin());
iter!=types.end(); ++iter)
@ -32,7 +33,11 @@ CSVWorld::ReferenceableCreator::ReferenceableCreator (CSMWorld::Data& data, QUnd
static_cast<int> (id2.getType()));
}
mType->model()->sort(0);
insertBeforeButtons (mType, false);
connect (mType, SIGNAL (currentIndexChanged (int)), this, SLOT (setType (int)));
}
void CSVWorld::ReferenceableCreator::reset()
@ -41,6 +46,30 @@ void CSVWorld::ReferenceableCreator::reset()
GenericCreator::reset();
}
void CSVWorld::ReferenceableCreator::setType (int index)
{
// container items have name limit of 32 characters
std::string text = mType->currentText().toStdString();
if (text == "Potion" ||
text == "Apparatus" ||
text == "Armor" ||
text == "Book" ||
text == "Clothing" ||
text == "Ingredient" ||
text == "ItemLevelledList" ||
text == "Light" ||
text == "Lockpick" ||
text == "Miscellaneous" ||
text == "Probe" ||
text == "Repair" ||
text == "Weapon")
{
GenericCreator::setEditorMaxLength (32);
}
else
GenericCreator::setEditorMaxLength (32767);
}
void CSVWorld::ReferenceableCreator::cloneMode (const std::string& originId,
const CSMWorld::UniversalId::Type type)
{

View File

@ -29,6 +29,9 @@ namespace CSVWorld
void toggleWidgets(bool active = true) override;
private slots:
void setType (int index);
};
}

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