Merge branch 'master' of git://github.com/OpenMW/openmw into appveyor

coverity_scan
Alexander "Ace" Olofsson 9 years ago
commit fe241be26c

2
.gitignore vendored

@ -5,7 +5,7 @@ CMakeCache.txt
cmake_install.cmake
Makefile
makefile
build
build*
prebuilt
## doxygen

@ -40,8 +40,8 @@ script:
- cd ./build
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then ${ANALYZE}make -j2; fi
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
notifications:
recipients:
- corrmage+travis-ci@gmail.com

@ -13,6 +13,7 @@ Programmers
Marc Zinnschlag (Zini) - Lead Programmer/Project Manager
Adam Hogan (aurix)
Aesylwinn
Aleksandar Jovanov
Alex Haddad (rainChu)
Alex McKibben (WeirdSexy)
@ -74,6 +75,7 @@ Programmers
Marco Melletti (mellotanica)
Marco Schulze
Mateusz Kołaczek (PL_kolek)
Mateusz Malisz (malice)
megaton
Michael Hogan (Xethik)
Michael Mc Donnell
@ -88,6 +90,7 @@ Programmers
Nikolay Kasyanov (corristo)
nobrakal
Nolan Poe (nopoe)
Paul Cercueil (pcercuei)
Paul McElroy (Greendogo)
Pieter van der Kloet (pvdk)
pkubik

@ -1,3 +1,232 @@
0.37.0
------
Bug #385: Light emitting objects have a too short distance of activation
Bug #455: Animation doesn't resize creature's bounding box
Bug #602: Only collision model is updated when modifying objects trough console
Bug #639: Sky horizon at nighttime
Bug #672: incorrect trajectory of the moons
Bug #814: incorrect NPC width
Bug #827: Inaccurate raycasting for dead actors
Bug #996: Can see underwater clearly when at right height/angle
Bug #1317: Erene Llenim in Seyda Neen does not walk around
Bug #1330: Cliff racers fail to hit the player
Bug #1366: Combat AI can't aim down (in order to hit small creatures)
Bug #1511: View distance while under water is much too short
Bug #1563: Terrain positioned incorrectly and appears to vibrate in far-out cells
Bug #1612: First person models clip through walls
Bug #1647: Crash switching from full screen to windows mode - D3D9
Bug #1650: No textures with directx on windows
Bug #1730: Scripts names starting with digit(s) fail to compile
Bug #1738: Socucius Ergalla's greetings are doubled during the tutorial
Bug #1784: First person weapons always in the same position
Bug #1813: Underwater flora lighting up entire area.
Bug #1871: Handle controller extrapolation flags
Bug #1921: Footstep frequency and velocity do not immediately update when speed attribute changes
Bug #2001: OpenMW crashes on start with OpenGL 1.4 drivers
Bug #2014: Antialiasing setting does nothing on Linux
Bug #2037: Some enemies attack the air when spotting the player
Bug #2052: NIF rotation matrices including scales are not supported
Bug #2062: Crank in Old Mournhold: Forgotten Sewer turns about the wrong axis
Bug #2111: Raindrops in front of fire look wrong
Bug #2140: [OpenGL] Water effects, flames and parts of creatures solid black when observed through brazier flame
Bug #2147: Trueflame and Hopesfire flame effects not properly aligned with blade
Bug #2148: Verminous fabricants have little coloured box beneath their feet
Bug #2149: Sparks in Clockwork City should bounce off the floor
Bug #2151: Clockwork City dicer trap doesn't activate when you're too close
Bug #2186: Mini map contains scrambled pixels that cause the mini map to flicker
Bug #2187: NIF file with more than 255 NiBillboardNodes does not load
Bug #2191: Editor: Crash when trying to view cell in render view in OpenCS
Bug #2270: Objects flicker transparently
Bug #2280: Latest 32bit windows build of openmw runns out of vram
Bug #2281: NPCs don't scream when they die
Bug #2286: Jumping animation restarts when equipping mid-air
Bug #2287: Weapon idle animation stops when turning
Bug #2355: Light spell doesn't work in 1st person view
Bug #2362: Lantern glas opaque to flame effect from certain viewing angles
Bug #2364: Light spells are not as bright as in Morrowind
Bug #2383: Remove the alpha testing override list
Bug #2436: Crash on entering cell "Tower of Tel Fyr, Hall of Fyr"
Bug #2457: Player followers should not report crimes
Bug #2458: crash in some fighting situations
Bug #2464: Hiding an emitter node should make that emitter stop firing particles
Bug #2466: Can't load a save created with OpenMW-0.35.0-win64
Bug #2468: music from title screen continues after loading savegame
Bug #2494: Map not consistent between saves
Bug #2504: Dialog scroll should always start at the top
Bug #2506: Editor: Undo/Redo shortcuts do not work in script editor
Bug #2513: Mannequins in mods appear as dead bodies
Bug #2524: Editor: TopicInfo "custom" condition section is missing
Bug #2540: Editor: search and verification result table can not be sorted by clicking on the column names
Bug #2543: Editor: there is a problem with spell effects
Bug #2544: Editor fails to save NPC information correctly.
Bug #2545: Editor: delete record in Objects (referenceables) table messes up data
Bug #2546: Editor: race base attributes and skill boni are not displayed, thus not editable
Bug #2547: Editor: some NPC data is not displayed, thus not editable
Bug #2551: Editor: missing data in cell definition
Bug #2553: Editor: value filter does not work for float values
Bug #2555: Editor: undo leaves the record status as Modified
Bug #2559: Make Detect Enchantment marks appear on top of the player arrow
Bug #2563: position consoling npc doesn't work without cell reload
Bug #2564: Editor: Closing a subview from code does not clean up properly and will lead to crash on opening the next subview
Bug #2568: Editor: Setting default window size is ignored
Bug #2569: Editor: saving from an esp to omwaddon file results in data loss for TopicInfo
Bug #2575: Editor: Deleted record (with Added (ModifiedOnly) status) remains in the Dialog SubView
Bug #2576: Editor: Editor doesn't scroll to a newly opened subview, when ScrollBar Only mode is active
Bug #2578: Editor: changing Level or Reputation of an NPC crashes the editor
Bug #2579: Editor: filters not updated when adding or cloning records
Bug #2580: Editor: omwaddon makes OpenMW crash
Bug #2581: Editor: focus problems in edit subviews single- and multiline input fields
Bug #2582: Editor: object verifier should check for non-existing scripts being referenced
Bug #2583: Editor: applying filter to TopicInfo on mods that have added dialouge makes the Editor crash
Bug #2586: Editor: some dialogue only editable items do not refresh after undo
Bug #2588: Editor: Cancel button exits program
Bug #2589: Editor: Regions table - mapcolor does not change correctly
Bug #2591: Placeatme - spurious 5th parameter raises error
Bug #2593: COC command prints multiple times when GUI is hidden
Bug #2598: Editor: scene view of instances has to be zoomed out to displaying something - center camera instance please
Bug #2607: water behind an invisible NPC becomes invisible as well
Bug #2611: Editor: Sort problem in Objects table when few nested rows are added
Bug #2621: crash when a creature has no model
Bug #2624: Editor: missing columns in tables
Bug #2627: Character sheet doesn't properly update when backing out of CharGen
Bug #2642: Editor: endif without if - is not reported as error when "verify" was executed
Bug #2644: Editor: rebuild the list of available content files when opening the open/new dialogues
Bug #2656: OpenMW & OpenMW-CS: setting "Flies" flag for ghosts has no effect
Bug #2659: OpenMW & OpenMW-CS: savegame load fail due to script attached to NPCs
Bug #2668: Editor: reputation value in the input field is not stored
Bug #2696: Horkers use land idle animations under water
Bug #2705: Editor: Sort by Record Type (Objects table) is incorrect
Bug #2711: Map notes on an exterior cell that shows up with a map marker on the world map do not show up in the tooltip for that cell's marker on the world map
Bug #2714: Editor: Can't reorder rows with the same topic in different letter case
Bug #2720: Head tracking for creatures not implemented
Bug #2722: Alchemy should only include effects shared by at least 2 ingredients
Bug #2723: "ori" console command is not working
Bug #2726: Ashlanders in front of Ghostgate start wandering around
Bug #2727: ESM writer does not handle encoding when saving the TES3 header
Bug #2728: Editor: Incorrect position of an added row in Info tables
Bug #2731: Editor: Deleting a record triggers a Qt warning
Bug #2733: Editor: Undo doesn't restore the Modified status of a record when a nested data is changed
Bug #2734: Editor: The Search doesn't work
Bug #2738: Additive moon blending
Bug #2746: NIF node names should be case insensitive
Bug #2752: Fog depth/density not handled correctly
Bug #2753: Editor: line edit in dialogue subview tables shows after a single click
Bug #2755: Combat AI changes target too frequently
Bug #2761: Can't attack during block animations
Bug #2764: Player doesn't raise arm in 3rd person for weathertype 9
Bug #2768: Current screen resolution not selected in options when starting OpenMW
Bug #2773: Editor: Deleted scripts are editable
Bug #2776: ordinators still think I'm wearing their helm even though Khajiit and argonians can't
Bug #2779: Slider bars continue to move if you don't release mouse button
Bug #2781: sleep interruption is a little off (is this an added feature?)
Bug #2782: erroneously able to ready weapon/magic (+sheathe weapon/magic) while paralyzed
Bug #2785: Editor: Incorrect GMSTs for newly created omwgame files
Bug #2786: Kwama Queen head is inverted under OpenMW
Bug #2788: additem and removeitem incorrect gold behavior
Bug #2790: --start doesn't trace down
Bug #2791: Editor: Listed attributes and skill should not be based on number of NPC objects.
Bug #2792: glitched merchantile/infinite free items
Bug #2794: Need to ignore quotes in names of script function
Bug #2797: Editor: Crash when removing the first row in a nested table
Bug #2800: Show an error message when S3TC support is missing
Bug #2811: Targetted Open spell effect persists.
Bug #2819: Editor: bodypart's race filter not displayed correctly
Bug #2820: Editor: table sorting is inverted
Bug #2821: Editor: undo/redo command labels are incorrect
Bug #2826: locking beds that have been locked via magic psuedo-freezes the game
Bug #2830: Script compiler does not accept IDs as instruction/functions arguments if the ID is also a keyword
Bug #2832: Cell names are not localized on the world map
Bug #2833: [cosmetic] Players swimming at water's surface are slightly too low.
Bug #2840: Save/load menu is not entirely localized
Bug #2853: [exploit/bug] disintegrate weapon incorrectly applying to lockpicks, probes. creates unbreakable lockpicks
Bug #2855: Mouse wheel in journal is not disabled by "Options" panel.
Bug #2856: Heart of Lorkhan doesn't visually respond to attacks
Bug #2863: Inventory highlights wrong category after load
Bug #2864: Illuminated Order 1.0c Bug The teleport amulet is not placed in the PC inventory.
Bug #2866: Editor: use checkbox instead of combobox for boolean values
Bug #2875: special cases of fSleepRandMod not behaving properly.
Bug #2878: Editor: Verify reports "creature has non-positive level" but there is no level setting
Bug #2879: Editor: entered value of field "Buys *" is not saved for a creature
Bug #2880: OpenMW & OpenMW-CS: having a scale value of 0.000 makes the game laggy
Bug #2882: Freeze when entering cell "Guild of Fighters (Ald'ruhn)" after dropping some items inside
Bug #2883: game not playable if mod providing a spell is removed but the list of known spells still contains it
Bug #2884: NPC chats about wrong player race
Bug #2886: Adding custom races breaks existing numbering of PcRace
Bug #2888: Editor: value entered in "AI Wander Idle" is not kept
Bug #2889: Editor: creatures made with the CS (not cloned) are always dead
Bug #2890: Editor: can't make NPC say a specific "Hello" voice-dialouge
Bug #2893: Editor: making a creature use textual dialogue doesn't work.
Bug #2901: Editor: gold for trading can not be set for creatures
Bug #2907: looking from uderwater part of the PC that is below the surface looks like it would be above the water
Bug #2914: Magicka not recalculated on character generation
Bug #2915: When paralyzed, you can still enter and exit sneak
Bug #2917: chameleon does not work for creatures
Bug #2927: Editor: in the automatic script checker local variable caches are not invalidated/updated on modifications of other scripts
Bug #2930: Editor: AIWander Idle can not be set for a creature
Bug #2932: Editor: you can add rows to "Creature Attack" but you can not enter values
Bug #2938: Editor: Can't add a start script.
Bug #2944: Spell chance for power to show as 0 on hud when used
Bug #2953: Editor: rightclick in an empty place in the menu bar shows an unnamed checkbox
Bug #2956: Editor: freezes while editing Filter
Bug #2959: space character in field enchantment (of an amulet) prevents rendering of surroundings
Bug #2962: OpenMW: Assertion `it != invStore.end()' failed
Bug #2964: Recursive script execution can corrupt script runtime data
Bug #2973: Editor: placing a chest in the game world and activating it heavily blurrs the character portrait
Bug #2978: Editor: Cannot edit alchemy ingredient properties
Bug #2980: Editor: Attribute and Skill can be selected for spells that do not require these parameters, leading to non-functional spells
Bug #2990: Compiling a script with warning mode 2 and enabled error downgrading leads to infinite recursion
Bug #2992: [Mod: Great House Dagoth] Killing Dagoth Gares freezes the game
Bug #3007: PlaceItem takes radians instead of degrees + angle reliability
Feature #706: Editor: Script Editor enhancements
Feature #872: Editor: Colour values in tables
Feature #880: Editor: ID auto-complete
Feature #928: Editor: Partial sorting in info tables
Feature #942: Editor: Dialogue for editing/viewing content file meta information
Feature #1057: NiStencilProperty
Feature #1278: Editor: Mouse picking in worldspace widget
Feature #1280: Editor: Cell border arrows
Feature #1401: Editor: Cloning enhancements
Feature #1463: Editor: Fine grained configuration of extended revert/delete commands
Feature #1591: Editor: Make fields in creation bar drop targets where applicable
Feature #1998: Editor: Magic effect record verifier
Feature #1999: Editor Sound Gen record verifier
Feature #2000: Editor: Pathgrid record verifier
Feature #2528: Game Time Tracker
Feature #2534: Editor: global search does not auomatically focus the search input field
Feature #2535: OpenMW: allow comments in openmw.cfg
Feature #2541: Editor: provide a go to the very bottom button for TopicInfo and JournalInfo
Feature #2549: Editor: add a horizontal slider to scroll between opened tables
Feature #2558: Editor: provide a shortcut for closing the subview that has the focus
Feature #2565: Editor: add context menu for dialogue sub view fields with an item matching "Edit 'x'" from the table subview context menu
Feature #2585: Editor: Ignore mouse wheel input for numeric values unless the respective widget has the focus
Feature #2620: Editor: make the verify-view refreshable
Feature #2622: Editor: Make double click behaviour in result tables configurable (see ID tables)
Feature #2717: Editor: Add severity column to report tables
Feature #2729: Editor: Various dialogue button bar improvements
Feature #2739: Profiling overlay
Feature #2740: Resource manager optimizations
Feature #2741: Make NIF files into proper resources
Feature #2742: Use the skinning data in NIF files as-is
Feature #2743: Small feature culling
Feature #2744: Configurable near clip distance
Feature #2745: GUI scaling option
Feature #2747: Support anonymous textures
Feature #2749: Loading screen optimizations
Feature #2751: Character preview optimization
Feature #2804: Editor: Merge Tool
Feature #2818: Editor: allow copying a record ID to the clipboard
Feature #2946: Editor: add script line number in results of search
Feature #2963: Editor: Mouse button bindings in 3D scene
Feature #2983: Sun Glare fader
Feature #2999: Scaling of journal and books
Task #2665: Support building with Qt5
Task #2725: Editor: Remove Display_YesNo
Task #2730: Replace hardcoded column numbers in SimpleDialogueSubView/DialogueSubView
Task #2750: Bullet shape instancing optimization
Task #2793: Replace grid size setting with half grid size setting
Task #3003: Support FFMPEG 2.9 (Debian request)
0.36.1
------

@ -14,7 +14,7 @@ echo "yes" | sudo apt-add-repository ppa:boost-latest/ppa
sudo apt-get update -qq
sudo apt-get install -qq libgtest-dev google-mock
sudo apt-get install -qq libboost-filesystem1.55-dev libboost-program-options1.55-dev libboost-system1.55-dev libboost-thread1.55-dev
sudo apt-get install -qq libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libavresample-dev
sudo apt-get install -qq ffmpeg libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev
sudo apt-get install -qq libbullet-dev libopenscenegraph-dev libmygui-dev libsdl2-dev libunshield-dev libtinyxml-dev libopenal-dev libqt4-dev
sudo apt-get install -qq cmake-data #workaround for broken osgqt cmake script in ubuntu 12.04
if [ "${ANALYZE}" ]; then sudo apt-get install -qq clang-3.6; fi

@ -1,6 +1,6 @@
#!/bin/bash
OUTPUT=$(grep -nRP '\t' --include=\*.{cpp,hpp,c,h} apps components)
OUTPUT=$(grep -nRP '\t' --include=\*.{cpp,hpp,c,h} --exclude=ui_\* apps components)
if [[ $OUTPUT ]] ; then
echo "Error: Tab characters found!"

@ -3,8 +3,9 @@ project(OpenMW)
# If the user doesn't supply a CMAKE_BUILD_TYPE via command line, choose one for them.
IF(NOT CMAKE_BUILD_TYPE)
SET(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING
"Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel."
"Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel."
FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS None Debug Release RelWithDebInfo MinSizeRel)
ENDIF()
if (APPLE)
@ -19,8 +20,8 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 36)
set(OPENMW_VERSION_RELEASE 1)
set(OPENMW_VERSION_MINOR 37)
set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_VERSION_COMMITHASH "")
set(OPENMW_VERSION_TAGHASH "")
@ -45,10 +46,6 @@ endif(EXISTS ${PROJECT_SOURCE_DIR}/.git)
# Macros
include(OpenMWMacros)
if (ANDROID)
set(CMAKE_FIND_ROOT_PATH ${OPENMW_DEPENDENCIES_DIR} "${CMAKE_FIND_ROOT_PATH}")
endif (ANDROID)
# doxygen main page
configure_file ("${OpenMW_SOURCE_DIR}/docs/mainpage.hpp.cmake" "${OpenMW_BINARY_DIR}/docs/mainpage.hpp")
@ -83,13 +80,28 @@ if (MSVC)
option(OPENMW_LTO_BUILD "Build OpenMW with Link-Time Optimization (Needs ~2GB of RAM)" OFF)
endif()
# Location of morrowind data files
# Set up common paths
if (APPLE)
set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files")
set(OPENMW_RESOURCE_FILES "./resources" CACHE PATH "location of OpenMW resources files")
elseif(UNIX)
set(MORROWIND_DATA_FILES "${CMAKE_INSTALL_PREFIX}/share/games/openmw/data/" CACHE PATH "location of Morrowind data files")
set(OPENMW_RESOURCE_FILES "${CMAKE_INSTALL_PREFIX}/share/games/openmw/resources/" CACHE PATH "location of OpenMW resources files")
# Paths
SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries")
SET(LIBDIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "Where to install libraries")
SET(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location")
SET(GLOBAL_DATA_PATH "${DATAROOTDIR}/games/" CACHE PATH "Set data path prefix")
SET(DATADIR "${GLOBAL_DATA_PATH}/openmw" CACHE PATH "Sets the openmw data directories to a non-default location")
SET(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir")
SET(LICDIR "${DATAROOTDIR}/licenses/openmw" CACHE PATH "Sets the openmw license directory to a non-default location.")
IF("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr")
SET(GLOBAL_CONFIG_PATH "/etc/" CACHE PATH "Set config dir prefix")
ELSE()
SET(GLOBAL_CONFIG_PATH "${CMAKE_INSTALL_PREFIX}/etc/" CACHE PATH "Set config dir prefix")
ENDIF()
SET(SYSCONFDIR "${GLOBAL_CONFIG_PATH}/openmw" CACHE PATH "Set config dir")
set(MORROWIND_DATA_FILES "${DATADIR}/data" CACHE PATH "location of Morrowind data files")
set(OPENMW_RESOURCE_FILES "${DATADIR}/resources" CACHE PATH "location of OpenMW resources files")
else()
set(MORROWIND_DATA_FILES "data" CACHE PATH "location of Morrowind data files")
set(OPENMW_RESOURCE_FILES "resources" CACHE PATH "location of OpenMW resources files")
@ -107,27 +119,14 @@ unset(FFMPEG_LIBRARIES CACHE)
find_package(FFmpeg REQUIRED)
set (FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${SWSCALE_LIBRARY})
set (FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${SWSCALE_LIBRARY} ${SWRESAMPLE_LIBRARIES})
if ( NOT AVCODEC_FOUND OR NOT AVFORMAT_FOUND OR NOT AVUTIL_FOUND OR NOT SWSCALE_FOUND )
if ( NOT AVCODEC_FOUND OR NOT AVFORMAT_FOUND OR NOT AVUTIL_FOUND OR NOT SWSCALE_FOUND OR NOT SWRESAMPLE_FOUND)
message(FATAL_ERROR "FFmpeg component required, but not found!")
endif()
set(SOUND_INPUT_INCLUDES ${FFMPEG_INCLUDE_DIRS})
if( SWRESAMPLE_FOUND )
add_definitions(-DHAVE_LIBSWRESAMPLE)
set (FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${SWRESAMPLE_LIBRARIES})
else()
if( AVRESAMPLE_FOUND )
set (FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${AVRESAMPLE_LIBRARIES})
else()
message(FATAL_ERROR "Install either libswresample (FFmpeg) or libavresample (Libav).")
endif()
endif()
# Required for building the FFmpeg headers
add_definitions(-D__STDC_CONSTANT_MACROS)
set(SOUND_INPUT_LIBRARY ${FFMPEG_LIBRARIES})
# TinyXML
option(USE_SYSTEM_TINYXML "Use system TinyXML library instead of internal." OFF)
if(USE_SYSTEM_TINYXML)
@ -157,20 +156,39 @@ if (WIN32)
add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN)
endif()
# Dependencies
if (ANDROID)
set(CMAKE_FIND_ROOT_PATH ${OPENMW_DEPENDENCIES_DIR} "${CMAKE_FIND_ROOT_PATH}")
set(OPENGL_ES TRUE CACHE BOOL "enable opengl es support for android" FORCE)
endif (ANDROID)
option(OPENGL_ES "enable opengl es support" FALSE )
set(DESIRED_QT_VERSION 4 CACHE STRING "The QT version OpenMW should use (4 or 5)")
message(STATUS "Using Qt${DESIRED_QT_VERSION}")
if (OPENGL_ES)
add_definitions(-DOPENGL_ES)
endif(OPENGL_ES)
if (DESIRED_QT_VERSION MATCHES 4)
find_package(Qt4 REQUIRED COMPONENTS QtCore QtGui QtNetwork QtOpenGL)
if (NOT BUILD_LAUNCHER AND NOT BUILD_OPENCS AND NOT BUILD_WIZARD)
set(USE_QT FALSE)
else()
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Core REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5OpenGL REQUIRED)
set(USE_QT TRUE)
endif()
# Dependencies
if (USE_QT)
set(DESIRED_QT_VERSION 4 CACHE STRING "The QT version OpenMW should use (4 or 5)")
set_property(CACHE DESIRED_QT_VERSION PROPERTY STRINGS 4 5)
message(STATUS "Using Qt${DESIRED_QT_VERSION}")
if (DESIRED_QT_VERSION MATCHES 4)
find_package(Qt4 REQUIRED COMPONENTS QtCore QtGui QtNetwork QtOpenGL)
else()
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Core REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5OpenGL REQUIRED)
# Instruct CMake to run moc automatically when needed.
#set(CMAKE_AUTOMOC ON)
endif()
endif()
# Fix for not visible pthreads functions for linker with glibc 2.15
@ -202,7 +220,12 @@ IF(BOOST_STATIC)
set(Boost_USE_STATIC_LIBS ON)
endif()
find_package(OpenSceneGraph 3.2.0 REQUIRED osgDB osgViewer osgText osgGA osgAnimation osgParticle osgQt osgUtil osgFX)
if (USE_QT)
set (OSG_QT osgQt)
endif()
find_package(OpenSceneGraph 3.2.0 REQUIRED osgDB osgViewer osgText osgGA osgAnimation osgParticle ${OSG_QT} osgUtil osgFX)
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
if(OSG_STATIC)
@ -308,8 +331,7 @@ if (APPLE)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${APP_BUNDLE_DIR}/Contents/MacOS")
if (OPENMW_OSX_DEPLOYMENT)
SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
endif()
else (APPLE)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}")
@ -327,8 +349,8 @@ configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
"${OpenMW_BINARY_DIR}/openmw.cfg.install")
configure_file(${OpenMW_SOURCE_DIR}/files/opencs.ini
"${OpenMW_BINARY_DIR}/opencs.ini")
configure_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.cfg
"${OpenMW_BINARY_DIR}/openmw-cs.cfg")
configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters
"${OpenMW_BINARY_DIR}/resources/defaultfilters" COPYONLY)
@ -375,21 +397,7 @@ elseif (MSVC)
endif (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
IF(NOT WIN32 AND NOT APPLE)
# Linux building
# Paths
SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries")
SET(LIBDIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "Where to install libraries")
SET(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location")
SET(GLOBAL_DATA_PATH "${DATAROOTDIR}/games/" CACHE PATH "Set data path prefix")
SET(DATADIR "${GLOBAL_DATA_PATH}/openmw" CACHE PATH "Sets the openmw data directories to a non-default location")
SET(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir")
SET(LICDIR "${DATAROOTDIR}/licenses/openmw" CACHE PATH "Sets the openmw license directory to a non-default location.")
IF("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr")
SET(GLOBAL_CONFIG_PATH "/etc/" CACHE PATH "Set config dir prefix")
ELSE()
SET(GLOBAL_CONFIG_PATH "${CMAKE_INSTALL_PREFIX}/etc/" CACHE PATH "Set config dir prefix")
ENDIF()
SET(SYSCONFDIR "${GLOBAL_CONFIG_PATH}/openmw" CACHE PATH "Set config dir")
# Linux installation
# Install binaries
IF(BUILD_OPENMW)
@ -442,7 +450,7 @@ IF(NOT WIN32 AND NOT APPLE)
INSTALL(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
IF(BUILD_OPENCS)
INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.ini" DESTINATION "${SYSCONFDIR}" COMPONENT "opencs")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "opencs")
ENDIF(BUILD_OPENCS)
# Install resources
@ -475,7 +483,7 @@ if(WIN32)
ENDIF(BUILD_ESSIMPORTER)
IF(BUILD_OPENCS)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-cs.exe" DESTINATION ".")
INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.ini" DESTINATION ".")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION ".")
ENDIF(BUILD_OPENCS)
IF(BUILD_WIZARD)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-wizard.exe" DESTINATION ".")
@ -721,6 +729,18 @@ endif()
# Apple bundling
if (APPLE)
get_property(QT_COCOA_PLUGIN_PATH TARGET Qt5::QCocoaIntegrationPlugin PROPERTY LOCATION_RELEASE)
get_filename_component(QT_COCOA_PLUGIN_DIR "${QT_COCOA_PLUGIN_PATH}" DIRECTORY)
get_filename_component(QT_COCOA_PLUGIN_GROUP "${QT_COCOA_PLUGIN_DIR}" NAME)
get_filename_component(QT_COCOA_PLUGIN_NAME "${QT_COCOA_PLUGIN_PATH}" NAME)
configure_file("${QT_COCOA_PLUGIN_PATH}" "${APP_BUNDLE_DIR}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY)
if (BUILD_OPENCS)
get_property(OPENCS_BUNDLE_NAME_TMP TARGET openmw-cs PROPERTY OUTPUT_NAME)
set(OPENCS_BUNDLE_NAME "${OPENCS_BUNDLE_NAME_TMP}.app")
configure_file("${QT_COCOA_PLUGIN_PATH}" "${OPENCS_BUNDLE_NAME}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY)
endif ()
set(INSTALL_SUBDIR OpenMW)
install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
@ -728,7 +748,7 @@ if (APPLE)
install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/opencs.ini" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
set(CPACK_GENERATOR "DragNDrop")
set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION})
@ -736,22 +756,22 @@ if (APPLE)
set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
set(OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}")
set(OPENCS_BUNDLE_NAME "OpenMW-CS.app")
set(OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}")
set(INSTALLED_OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}")
set(INSTALLED_OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}")
install(CODE "
set(BU_CHMOD_BUNDLE_ITEMS ON)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH})
include(BundleUtilitiesWithRPath)
include(BundleUtilities)
cmake_minimum_required(VERSION 3.1)
" COMPONENT Runtime)
set(ABSOLUTE_PLUGINS "")
set(USED_OSG_PLUGINS
osgdb_tga
osgdb_dds
osgdb_imageio
osgdb_jpeg
osgdb_png
osgdb_tga
)
foreach (PLUGIN_NAME ${USED_OSG_PLUGINS})
@ -759,12 +779,12 @@ if (APPLE)
set(ABSOLUTE_PLUGINS ${PLUGIN_ABS} ${ABSOLUTE_PLUGINS})
endforeach ()
get_filename_component(PLUGIN_PREFIX_DIR "${OSG_PLUGIN_LIB_SEARCH_PATH}" NAME)
get_filename_component(OSG_PLUGIN_PREFIX_DIR "${OSG_PLUGIN_LIB_SEARCH_PATH}" NAME)
# installs used plugins in bundle at given path (bundle_path must be relative to ${CMAKE_INSTALL_PREFIX})
# and returns list of install paths for all installed plugins
function (install_plugins_for_bundle bundle_path plugins_var)
set(RELATIVE_PLUGIN_INSTALL_BASE "${bundle_path}/Contents/PlugIns/${PLUGIN_PREFIX_DIR}")
set(RELATIVE_PLUGIN_INSTALL_BASE "${bundle_path}/Contents/PlugIns/${OSG_PLUGIN_PREFIX_DIR}")
set(PLUGINS "")
set(PLUGIN_INSTALL_BASE "\${CMAKE_INSTALL_PREFIX}/${RELATIVE_PLUGIN_INSTALL_BASE}")
@ -787,19 +807,20 @@ if (APPLE)
install_plugins_for_bundle("${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}" PLUGINS)
install_plugins_for_bundle("${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}" OPENCS_PLUGINS)
set(DIRS "${CMAKE_PREFIX_PATH}/lib")
set(PLUGINS ${PLUGINS} "${INSTALLED_OPENMW_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}")
set(OPENCS_PLUGINS ${OPENCS_PLUGINS} "${INSTALLED_OPENCS_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}")
install(CODE "
function(gp_item_default_embedded_path_override item default_embedded_path_var)
if (\${item} MATCHES ${PLUGIN_PREFIX_DIR})
set(path \"@executable_path/../PlugIns/${PLUGIN_PREFIX_DIR}\")
if (\${item} MATCHES ${OSG_PLUGIN_PREFIX_DIR})
set(path \"@executable_path/../PlugIns/${OSG_PLUGIN_PREFIX_DIR}\")
set(\${default_embedded_path_var} \"\${path}\" PARENT_SCOPE)
endif()
endfunction()
cmake_policy(SET CMP0009 OLD)
fixup_bundle(\"${OPENMW_APP}\" \"${PLUGINS}\" \"${DIRS}\")
fixup_bundle(\"${OPENCS_APP}\" \"${OPENCS_PLUGINS}\" \"${DIRS}\")
fixup_bundle(\"${INSTALLED_OPENMW_APP}\" \"${PLUGINS}\" \"\")
fixup_bundle(\"${INSTALLED_OPENCS_APP}\" \"${OPENCS_PLUGINS}\" \"\")
" COMPONENT Runtime)
include(CPack)
endif (APPLE)

@ -7,7 +7,7 @@ OpenMW is a recreation of the engine for the popular role-playing game Morrowind
OpenMW also comes with OpenMW-CS, a replacement for Morrowind's TES Construction Set.
* Version: 0.36.1
* Version: 0.37.0
* License: GPL (see docs/license/GPL3.txt for more information)
* Website: http://www.openmw.org
* IRC: #openmw on irc.freenode.net

@ -27,7 +27,8 @@ struct ESMData
std::vector<ESM::Header::MasterData> masters;
std::deque<EsmTool::RecordBase *> mRecords;
std::map<ESM::Cell *, std::deque<ESM::CellRef> > mCellRefs;
// Value: (Reference, Deleted flag)
std::map<ESM::Cell *, std::deque<std::pair<ESM::CellRef, bool> > > mCellRefs;
std::map<int, int> mRecordStats;
static const std::set<int> sLabeledRec;
@ -255,7 +256,7 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
while(cell.getNextRef(esm, ref, deleted))
{
if (save) {
info.data.mCellRefs[&cell].push_back(ref);
info.data.mCellRefs[&cell].push_back(std::make_pair(ref, deleted));
}
if(quiet) continue;
@ -352,61 +353,58 @@ int load(Arguments& info)
uint32_t flags;
esm.getRecHeader(flags);
EsmTool::RecordBase *record = EsmTool::RecordBase::create(n);
if (record == 0)
{
if (std::find(skipped.begin(), skipped.end(), n.val) == skipped.end())
{
std::cout << "Skipping " << n.toString() << " records." << std::endl;
skipped.push_back(n.val);
}
esm.skipRecord();
if (quiet) break;
std::cout << " Skipping\n";
continue;
}
record->setFlags(static_cast<int>(flags));
record->setPrintPlain(info.plain_given);
record->load(esm);
// Is the user interested in this record type?
bool interested = true;
if (!info.types.empty())
{
std::vector<std::string>::iterator match;
match = std::find(info.types.begin(), info.types.end(),
n.toString());
match = std::find(info.types.begin(), info.types.end(), n.toString());
if (match == info.types.end()) interested = false;
}
std::string id = esm.getHNOString("NAME");
if (id.empty())
id = esm.getHNOString("INAM");
if (!info.name.empty() && !Misc::StringUtils::ciEqual(info.name, id))
if (!info.name.empty() && !Misc::StringUtils::ciEqual(info.name, record->getId()))
interested = false;
if(!quiet && interested)
std::cout << "\nRecord: " << n.toString()
<< " '" << id << "'\n";
EsmTool::RecordBase *record = EsmTool::RecordBase::create(n);
if (record == 0) {
if (std::find(skipped.begin(), skipped.end(), n.val) == skipped.end())
{
std::cout << "Skipping " << n.toString() << " records." << std::endl;
skipped.push_back(n.val);
}
{
std::cout << "\nRecord: " << n.toString() << " '" << record->getId() << "'\n";
record->print();
}
esm.skipRecord();
if (quiet) break;
std::cout << " Skipping\n";
} else {
if (record->getType().val == ESM::REC_GMST) {
// preset id for GameSetting record
record->cast<ESM::GameSetting>()->get().mId = id;
}
record->setId(id);
record->setFlags((int) flags);
record->setPrintPlain(info.plain_given);
record->load(esm);
if (!quiet && interested) record->print();
if (record->getType().val == ESM::REC_CELL && loadCells && interested) {
loadCell(record->cast<ESM::Cell>()->get(), esm, info);
}
if (record->getType().val == ESM::REC_CELL && loadCells && interested)
{
loadCell(record->cast<ESM::Cell>()->get(), esm, info);
}
if (save) {
info.data.mRecords.push_back(record);
} else {
delete record;
}
++info.data.mRecordStats[n.val];
if (save)
{
info.data.mRecords.push_back(record);
}
else
{
delete record;
}
++info.data.mRecordStats[n.val];
}
} catch(std::exception &e) {
@ -493,28 +491,19 @@ int clone(Arguments& info)
for (Records::iterator it = records.begin(); it != records.end() && i > 0; ++it)
{
EsmTool::RecordBase *record = *it;
name.val = record->getType().val;
esm.startRecord(name.toString(), record->getFlags());
// TODO wrap this with std::set
if (ESMData::sLabeledRec.count(name.val) > 0) {
esm.writeHNCString("NAME", record->getId());
} else {
esm.writeHNOString("NAME", record->getId());
}
record->save(esm);
if (name.val == ESM::REC_CELL) {
ESM::Cell *ptr = &record->cast<ESM::Cell>()->get();
if (!info.data.mCellRefs[ptr].empty()) {
typedef std::deque<ESM::CellRef> RefList;
typedef std::deque<std::pair<ESM::CellRef, bool> > RefList;
RefList &refs = info.data.mCellRefs[ptr];
for (RefList::iterator refIt = refs.begin(); refIt != refs.end(); ++refIt)
{
refIt->save(esm);
refIt->first.save(esm, refIt->second);
}
}
}

@ -405,6 +405,7 @@ void Record<ESM::Activator>::print()
std::cout << " Name: " << mData.mName << std::endl;
std::cout << " Model: " << mData.mModel << std::endl;
std::cout << " Script: " << mData.mScript << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -419,6 +420,7 @@ void Record<ESM::Potion>::print()
std::cout << " Value: " << mData.mData.mValue << std::endl;
std::cout << " AutoCalc: " << mData.mData.mAutoCalc << std::endl;
printEffectList(mData.mEffects);
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -447,6 +449,7 @@ void Record<ESM::Armor>::print()
if (pit->mFemale != "")
std::cout << " Female Name: " << pit->mFemale << std::endl;
}
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -461,6 +464,7 @@ void Record<ESM::Apparatus>::print()
std::cout << " Weight: " << mData.mData.mWeight << std::endl;
std::cout << " Value: " << mData.mData.mValue << std::endl;
std::cout << " Quality: " << mData.mData.mQuality << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -474,6 +478,7 @@ void Record<ESM::BodyPart>::print()
std::cout << " Part: " << meshPartLabel(mData.mData.mPart)
<< " (" << (int)mData.mData.mPart << ")" << std::endl;
std::cout << " Vampire: " << (int)mData.mData.mVampire << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -502,6 +507,7 @@ void Record<ESM::Book>::print()
{
std::cout << " Text: [skipped]" << std::endl;
}
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -513,6 +519,7 @@ void Record<ESM::BirthSign>::print()
std::vector<std::string>::iterator pit;
for (pit = mData.mPowers.mList.begin(); pit != mData.mPowers.mList.end(); ++pit)
std::cout << " Power: " << *pit << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -541,6 +548,7 @@ void Record<ESM::Cell>::print()
std::cout << " Map Color: " << boost::format("0x%08X") % mData.mMapColor << std::endl;
std::cout << " Water Level Int: " << mData.mWaterInt << std::endl;
std::cout << " RefId counter: " << mData.mRefNumCounter << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
@ -563,6 +571,7 @@ void Record<ESM::Class>::print()
for (int i = 0; i != 5; i++)
std::cout << " Major Skill: " << skillLabel(mData.mData.mSkills[i][1])
<< " (" << mData.mData.mSkills[i][1] << ")" << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -589,6 +598,7 @@ void Record<ESM::Clothing>::print()
if (pit->mFemale != "")
std::cout << " Female Name: " << pit->mFemale << std::endl;
}
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -604,6 +614,7 @@ void Record<ESM::Container>::print()
for (cit = mData.mInventory.mList.begin(); cit != mData.mInventory.mList.end(); ++cit)
std::cout << " Inventory: Count: " << boost::format("%4d") % cit->mCount
<< " Item: " << cit->mItem.toString() << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -670,6 +681,7 @@ void Record<ESM::Creature>::print()
std::vector<ESM::AIPackage>::iterator pit;
for (pit = mData.mAiPackage.mList.begin(); pit != mData.mAiPackage.mList.end(); ++pit)
printAIPackage(*pit);
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -677,6 +689,7 @@ void Record<ESM::Dialogue>::print()
{
std::cout << " Type: " << dialogTypeLabel(mData.mType)
<< " (" << (int)mData.mType << ")" << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
// Sadly, there are no DialInfos, because the loader dumps as it
// loads, rather than loading and then dumping. :-( Anyone mind if
// I change this?
@ -693,6 +706,7 @@ void Record<ESM::Door>::print()
std::cout << " Script: " << mData.mScript << std::endl;
std::cout << " OpenSound: " << mData.mOpenSound << std::endl;
std::cout << " CloseSound: " << mData.mCloseSound << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -704,6 +718,7 @@ void Record<ESM::Enchantment>::print()
std::cout << " Charge: " << mData.mData.mCharge << std::endl;
std::cout << " AutoCalc: " << mData.mData.mAutocalc << std::endl;
printEffectList(mData.mEffects);
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -737,12 +752,14 @@ void Record<ESM::Faction>::print()
std::map<std::string, int>::iterator rit;
for (rit = mData.mReactions.begin(); rit != mData.mReactions.end(); ++rit)
std::cout << " Reaction: " << rit->second << " = " << rit->first << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
void Record<ESM::Global>::print()
{
std::cout << " " << mData.mValue << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -809,6 +826,7 @@ void Record<ESM::DialInfo>::print()
std::cout << " Result Script: [skipped]" << std::endl;
}
}
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -832,6 +850,7 @@ void Record<ESM::Ingredient>::print()
std::cout << " Attribute: " << attributeLabel(mData.mData.mAttributes[i])
<< " (" << mData.mData.mAttributes[i] << ")" << std::endl;
}
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -848,6 +867,8 @@ void Record<ESM::Land>::print()
std::cout << " Unknown1: " << data->mUnk1 << std::endl;
std::cout << " Unknown2: " << data->mUnk2 << std::endl;
}
mData.unloadData();
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -860,6 +881,7 @@ void Record<ESM::CreatureLevList>::print()
for (iit = mData.mList.begin(); iit != mData.mList.end(); ++iit)
std::cout << " Creature: Level: " << iit->mLevel
<< " Creature: " << iit->mId << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -872,6 +894,7 @@ void Record<ESM::ItemLevList>::print()
for (iit = mData.mList.begin(); iit != mData.mList.end(); ++iit)
std::cout << " Inventory: Level: " << iit->mLevel
<< " Item: " << iit->mId << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -892,6 +915,7 @@ void Record<ESM::Light>::print()
std::cout << " Duration: " << mData.mData.mTime << std::endl;
std::cout << " Radius: " << mData.mData.mRadius << std::endl;
std::cout << " Color: " << mData.mData.mColor << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -906,6 +930,7 @@ void Record<ESM::Lockpick>::print()
std::cout << " Value: " << mData.mData.mValue << std::endl;
std::cout << " Quality: " << mData.mData.mQuality << std::endl;
std::cout << " Uses: " << mData.mData.mUses << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -920,6 +945,7 @@ void Record<ESM::Probe>::print()
std::cout << " Value: " << mData.mData.mValue << std::endl;
std::cout << " Quality: " << mData.mData.mQuality << std::endl;
std::cout << " Uses: " << mData.mData.mUses << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -934,6 +960,7 @@ void Record<ESM::Repair>::print()
std::cout << " Value: " << mData.mData.mValue << std::endl;
std::cout << " Quality: " << mData.mData.mQuality << std::endl;
std::cout << " Uses: " << mData.mData.mUses << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -942,6 +969,7 @@ void Record<ESM::LandTexture>::print()
std::cout << " Id: " << mData.mId << std::endl;
std::cout << " Index: " << mData.mIndex << std::endl;
std::cout << " Texture: " << mData.mTexture << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -992,6 +1020,7 @@ void Record<ESM::Miscellaneous>::print()
std::cout << " Weight: " << mData.mData.mWeight << std::endl;
std::cout << " Value: " << mData.mData.mValue << std::endl;
std::cout << " Is Key: " << mData.mData.mIsKey << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -1077,6 +1106,8 @@ void Record<ESM::NPC>::print()
std::vector<ESM::AIPackage>::iterator pit;
for (pit = mData.mAiPackage.mList.begin(); pit != mData.mAiPackage.mList.end(); ++pit)
printAIPackage(*pit);
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -1111,6 +1142,8 @@ void Record<ESM::Pathgrid>::print()
std::cout << " BAD POINT IN EDGE!" << std::endl;
i++;
}
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -1151,6 +1184,8 @@ void Record<ESM::Race>::print()
std::vector<std::string>::iterator sit;
for (sit = mData.mPowers.mList.begin(); sit != mData.mPowers.mList.end(); ++sit)
std::cout << " Power: " << *sit << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -1210,6 +1245,8 @@ void Record<ESM::Script>::print()
{
std::cout << " Script: [skipped]" << std::endl;
}
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -1233,6 +1270,7 @@ void Record<ESM::SoundGenerator>::print()
std::cout << " Sound: " << mData.mSound << std::endl;
std::cout << " Type: " << soundTypeLabel(mData.mType)
<< " (" << mData.mType << ")" << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -1243,6 +1281,7 @@ void Record<ESM::Sound>::print()
if (mData.mData.mMinRange != 0 && mData.mData.mMaxRange != 0)
std::cout << " Range: " << (int)mData.mData.mMinRange << " - "
<< (int)mData.mData.mMaxRange << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -1254,13 +1293,15 @@ void Record<ESM::Spell>::print()
std::cout << " Flags: " << spellFlags(mData.mData.mFlags) << std::endl;
std::cout << " Cost: " << mData.mData.mCost << std::endl;
printEffectList(mData.mEffects);
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
void Record<ESM::StartScript>::print()
{
std::cout << "Start Script: " << mData.mId << std::endl;
std::cout << "Start Data: " << mData.mData << std::endl;
std::cout << " Start Script: " << mData.mId << std::endl;
std::cout << " Start Data: " << mData.mData << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
@ -1301,6 +1342,37 @@ void Record<ESM::Weapon>::print()
if (mData.mData.mThrust[0] != 0 && mData.mData.mThrust[1] != 0)
std::cout << " Thrust: " << (int)mData.mData.mThrust[0] << "-"
<< (int)mData.mData.mThrust[1] << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
template<>
std::string Record<ESM::Cell>::getId() const
{
return mData.mName;
}
template<>
std::string Record<ESM::Land>::getId() const
{
return ""; // No ID for Land record
}
template<>
std::string Record<ESM::MagicEffect>::getId() const
{
return ""; // No ID for MagicEffect record
}
template<>
std::string Record<ESM::Pathgrid>::getId() const
{
return ""; // No ID for Pathgrid record
}
template<>
std::string Record<ESM::Skill>::getId() const
{
return ""; // No ID for Skill record
}
} // end namespace

@ -32,13 +32,7 @@ namespace EsmTool
virtual ~RecordBase() {}
const std::string &getId() const {
return mId;
}
void setId(const std::string &id) {
mId = id;
}
virtual std::string getId() const = 0;
uint32_t getFlags() const {
return mFlags;
@ -73,22 +67,37 @@ namespace EsmTool
class Record : public RecordBase
{
T mData;
bool mIsDeleted;
public:
Record()
: mIsDeleted(false)
{}
std::string getId() const {
return mData.mId;
}
T &get() {
return mData;
}
void save(ESM::ESMWriter &esm) {
mData.save(esm);
mData.save(esm, mIsDeleted);
}
void load(ESM::ESMReader &esm) {
mData.load(esm);
mData.load(esm, mIsDeleted);
}
void print();
};
template<> std::string Record<ESM::Cell>::getId() const;
template<> std::string Record<ESM::Land>::getId() const;
template<> std::string Record<ESM::MagicEffect>::getId() const;
template<> std::string Record<ESM::Pathgrid>::getId() const;
template<> std::string Record<ESM::Skill>::getId() const;
template<> void Record<ESM::Activator>::print();
template<> void Record<ESM::Potion>::print();

@ -158,9 +158,9 @@ namespace ESSImport
void ConvertCell::read(ESM::ESMReader &esm)
{
ESM::Cell cell;
std::string id = esm.getHNString("NAME");
cell.mName = id;
cell.load(esm, false);
bool isDeleted = false;
cell.load(esm, isDeleted, false);
// I wonder what 0x40 does?
if (cell.isExterior() && cell.mData.mFlags & 0x20)
@ -169,7 +169,7 @@ namespace ESSImport
}
// note if the player is in a nameless exterior cell, we will assign the cellId later based on player position
if (id == mContext->mPlayerCellName)
if (cell.mName == mContext->mPlayerCellName)
{
mContext->mPlayer.mCellId = cell.getCellId();
}
@ -277,7 +277,7 @@ namespace ESSImport
if (cell.isExterior())
mExtCells[std::make_pair(cell.mData.mX, cell.mData.mY)] = newcell;
else
mIntCells[id] = newcell;
mIntCells[cell.mName] = newcell;
}
void ConvertCell::writeCell(const Cell &cell, ESM::ESMWriter& esm)

@ -54,6 +54,8 @@ public:
void setContext(Context& context) { mContext = &context; }
/// @note The load method of ESM records accept the deleted flag as a parameter.
/// I don't know can the DELE sub-record appear in saved games, so the deleted flag will be ignored.
virtual void read(ESM::ESMReader& esm)
{
}
@ -78,10 +80,11 @@ public:
virtual void read(ESM::ESMReader& esm)
{
std::string id = esm.getHNString("NAME");
T record;
record.load(esm);
mRecords[id] = record;
bool isDeleted = false;
record.load(esm, isDeleted);
mRecords[record.mId] = record;
}
virtual void write(ESM::ESMWriter& esm)
@ -89,7 +92,6 @@ public:
for (typename std::map<std::string, T>::const_iterator it = mRecords.begin(); it != mRecords.end(); ++it)
{
esm.startRecord(T::sRecordId);
esm.writeHNString("NAME", it->first);
it->second.save(esm);
esm.endRecord(T::sRecordId);
}
@ -105,14 +107,15 @@ public:
virtual void read(ESM::ESMReader &esm)
{
ESM::NPC npc;
std::string id = esm.getHNString("NAME");
npc.load(esm);
if (id != "player")
bool isDeleted = false;
npc.load(esm, isDeleted);
if (npc.mId != "player")
{
// Handles changes to the NPC struct, but since there is no index here
// it will apply to ALL instances of the class. seems to be the reason for the
// "feature" in MW where changing AI settings of one guard will change it for all guards of that refID.
mContext->mNpcs[Misc::StringUtils::lowerCase(id)] = npc;
mContext->mNpcs[Misc::StringUtils::lowerCase(npc.mId)] = npc;
}
else
{
@ -142,9 +145,10 @@ public:
{
// See comment in ConvertNPC
ESM::Creature creature;
std::string id = esm.getHNString("NAME");
creature.load(esm);
mContext->mCreatures[Misc::StringUtils::lowerCase(id)] = creature;
bool isDeleted = false;
creature.load(esm, isDeleted);
mContext->mCreatures[Misc::StringUtils::lowerCase(creature.mId)] = creature;
}
};
@ -157,18 +161,19 @@ class ConvertGlobal : public DefaultConverter<ESM::Global>
public:
virtual void read(ESM::ESMReader &esm)
{
std::string id = esm.getHNString("NAME");
ESM::Global global;
global.load(esm);
if (Misc::StringUtils::ciEqual(id, "gamehour"))
bool isDeleted = false;
global.load(esm, isDeleted);
if (Misc::StringUtils::ciEqual(global.mId, "gamehour"))
mContext->mHour = global.mValue.getFloat();
if (Misc::StringUtils::ciEqual(id, "day"))
if (Misc::StringUtils::ciEqual(global.mId, "day"))
mContext->mDay = global.mValue.getInteger();
if (Misc::StringUtils::ciEqual(id, "month"))
if (Misc::StringUtils::ciEqual(global.mId, "month"))
mContext->mMonth = global.mValue.getInteger();
if (Misc::StringUtils::ciEqual(id, "year"))
if (Misc::StringUtils::ciEqual(global.mId, "year"))
mContext->mYear = global.mValue.getInteger();
mRecords[id] = global;
mRecords[global.mId] = global;
}
};
@ -177,14 +182,14 @@ class ConvertClass : public DefaultConverter<ESM::Class>
public:
virtual void read(ESM::ESMReader &esm)
{
std::string id = esm.getHNString("NAME");
ESM::Class class_;
class_.load(esm);
bool isDeleted = false;
if (id == "NEWCLASSID_CHARGEN")
class_.load(esm, isDeleted);
if (class_.mId == "NEWCLASSID_CHARGEN")
mContext->mCustomPlayerClassName = class_.mName;
mRecords[id] = class_;
mRecords[class_.mId] = class_;
}
};
@ -193,13 +198,14 @@ class ConvertBook : public DefaultConverter<ESM::Book>
public:
virtual void read(ESM::ESMReader &esm)
{
std::string id = esm.getHNString("NAME");
ESM::Book book;
book.load(esm);
bool isDeleted = false;
book.load(esm, isDeleted);
if (book.mData.mSkillID == -1)
mContext->mPlayer.mObject.mNpcStats.mUsedIds.push_back(Misc::StringUtils::lowerCase(id));
mContext->mPlayer.mObject.mNpcStats.mUsedIds.push_back(Misc::StringUtils::lowerCase(book.mId));
mRecords[id] = book;
mRecords[book.mId] = book;
}
};
@ -371,11 +377,12 @@ class ConvertFACT : public Converter
public:
virtual void read(ESM::ESMReader& esm)
{
std::string id = esm.getHNString("NAME");
ESM::Faction faction;
faction.load(esm);
bool isDeleted = false;
faction.load(esm, isDeleted);
std::string id = Misc::StringUtils::lowerCase(faction.mId);
Misc::StringUtils::toLower(id);
for (std::map<std::string, int>::const_iterator it = faction.mReactions.begin(); it != faction.mReactions.end(); ++it)
{
std::string faction2 = Misc::StringUtils::lowerCase(it->first);
@ -391,7 +398,7 @@ public:
virtual void read(ESM::ESMReader &esm)
{
std::string itemid = esm.getHNString("NAME");
Misc::StringUtils::toLower(itemid);
Misc::StringUtils::lowerCaseInPlace(itemid);
while (esm.isNextSub("FNAM") || esm.isNextSub("ONAM"))
{

@ -32,7 +32,8 @@ namespace ESSImport
if (esm.isNextSub("MNAM"))
esm.skipHSub();
ESM::CellRef::loadData(esm);
bool isDeleted = false;
ESM::CellRef::loadData(esm, isDeleted);
mHasACDT = false;
if (esm.isNextSub("ACDT"))

@ -394,7 +394,7 @@ namespace ESSImport
}
writer.startRecord(ESM::REC_NPC_);
writer.writeHNString("NAME", "player");
context.mPlayerBase.mId = "player";
context.mPlayerBase.save(writer);
writer.endRecord(ESM::REC_NPC_);

@ -32,7 +32,8 @@ namespace ESSImport
item.mSCRI.load(esm);
// for XSOL and XCHG seen so far, but probably others too
item.ESM::CellRef::loadData(esm);
bool isDeleted = false;
item.ESM::CellRef::loadData(esm, isDeleted);
int charge=-1;
esm.getHNOT(charge, "XHLT");

@ -7,8 +7,6 @@ set(LAUNCHER
textslotmsgbox.cpp
settingspage.cpp
settings/graphicssettings.cpp
utils/profilescombobox.cpp
utils/textinputdialog.cpp
utils/lineedit.cpp
@ -24,8 +22,6 @@ set(LAUNCHER_HEADER
textslotmsgbox.hpp
settingspage.hpp
settings/graphicssettings.hpp
utils/profilescombobox.hpp
utils/textinputdialog.hpp
utils/lineedit.hpp

@ -75,7 +75,7 @@ bool Launcher::DataFilesPage::loadSettings()
QStringList profiles = mLauncherSettings.getContentLists();
QString currentProfile = mLauncherSettings.getCurrentContentListName();
qDebug() << "current profile is: " << currentProfile;
qDebug() << "The current profile is: " << currentProfile;
foreach (const QString &item, profiles)
addProfile (item, false);

@ -18,7 +18,7 @@
#include <components/contentselector/model/naturalsort.hpp>
#include "settings/graphicssettings.hpp"
#include <components/settings/settings.hpp>
QString getAspect(int x, int y)
{
@ -32,10 +32,10 @@ QString getAspect(int x, int y)
return QString(QString::number(xaspect) + ":" + QString::number(yaspect));
}
Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSetting, QWidget *parent)
Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, Settings::Manager &engineSettings, QWidget *parent)
: QWidget(parent)
, mCfgMgr(cfg)
, mGraphicsSettings(graphicsSetting)
, mEngineSettings(engineSettings)
{
setObjectName ("GraphicsPage");
setupUi(this);
@ -80,25 +80,26 @@ bool Launcher::GraphicsPage::loadSettings()
if (!setupSDL())
return false;
if (mGraphicsSettings.value(QString("Video/vsync")) == QLatin1String("true"))
if (mEngineSettings.getBool("vsync", "Video"))
vSyncCheckBox->setCheckState(Qt::Checked);
if (mGraphicsSettings.value(QString("Video/fullscreen")) == QLatin1String("true"))
if (mEngineSettings.getBool("fullscreen", "Video"))
fullScreenCheckBox->setCheckState(Qt::Checked);
if (mGraphicsSettings.value(QString("Video/window border")) == QLatin1String("true"))
if (mEngineSettings.getBool("window border", "Video"))
windowBorderCheckBox->setCheckState(Qt::Checked);
int aaIndex = antiAliasingComboBox->findText(mGraphicsSettings.value(QString("Video/antialiasing")));
// aaValue is the actual value (0, 1, 2, 4, 8, 16)
int aaValue = mEngineSettings.getInt("antialiasing", "Video");
// aaIndex is the index into the allowed values in the pull down.
int aaIndex = antiAliasingComboBox->findText(QString::number(aaValue));
if (aaIndex != -1)
antiAliasingComboBox->setCurrentIndex(aaIndex);
QString width = mGraphicsSettings.value(QString("Video/resolution x"));
QString height = mGraphicsSettings.value(QString("Video/resolution y"));
QString resolution = width + QString(" x ") + height;
QString screen = mGraphicsSettings.value(QString("Video/screen"));
screenComboBox->setCurrentIndex(screen.toInt());
int width = mEngineSettings.getInt("resolution x", "Video");
int height = mEngineSettings.getInt("resolution y", "Video");
QString resolution = QString::number(width) + QString(" x ") + QString::number(height);
screenComboBox->setCurrentIndex(mEngineSettings.getInt("screen", "Video"));
int resIndex = resolutionComboBox->findText(resolution, Qt::MatchStartsWith);
@ -107,9 +108,8 @@ bool Launcher::GraphicsPage::loadSettings()
resolutionComboBox->setCurrentIndex(resIndex);
} else {
customRadioButton->toggle();
customWidthSpinBox->setValue(width.toInt());
customHeightSpinBox->setValue(height.toInt());
customWidthSpinBox->setValue(width);
customHeightSpinBox->setValue(height);
}
return true;
@ -117,31 +117,46 @@ bool Launcher::GraphicsPage::loadSettings()
void Launcher::GraphicsPage::saveSettings()
{
vSyncCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/vsync"), QString("true"))
: mGraphicsSettings.setValue(QString("Video/vsync"), QString("false"));
fullScreenCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/fullscreen"), QString("true"))
: mGraphicsSettings.setValue(QString("Video/fullscreen"), QString("false"));
windowBorderCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/window border"), QString("true"))
: mGraphicsSettings.setValue(QString("Video/window border"), QString("false"));
mGraphicsSettings.setValue(QString("Video/antialiasing"), antiAliasingComboBox->currentText());
// Ensure we only set the new settings if they changed. This is to avoid cluttering the
// user settings file (which by definition should only contain settings the user has touched)
bool cVSync = vSyncCheckBox->checkState();
if (cVSync != mEngineSettings.getBool("vsync", "Video"))
mEngineSettings.setBool("vsync", "Video", cVSync);
bool cFullScreen = fullScreenCheckBox->checkState();
if (cFullScreen != mEngineSettings.getBool("fullscreen", "Video"))
mEngineSettings.setBool("fullscreen", "Video", cFullScreen);
bool cWindowBorder = windowBorderCheckBox->checkState();
if (cWindowBorder != mEngineSettings.getBool("window border", "Video"))
mEngineSettings.setBool("window border", "Video", cWindowBorder);
int cAAValue = antiAliasingComboBox->currentText().toInt();
if (cAAValue != mEngineSettings.getInt("antialiasing", "Video"))
mEngineSettings.setInt("antialiasing", "Video", cAAValue);
int cWidth = 0;
int cHeight = 0;
if (standardRadioButton->isChecked()) {
QRegExp resolutionRe(QString("(\\d+) x (\\d+).*"));
if (resolutionRe.exactMatch(resolutionComboBox->currentText().simplified())) {
mGraphicsSettings.setValue(QString("Video/resolution x"), resolutionRe.cap(1));
mGraphicsSettings.setValue(QString("Video/resolution y"), resolutionRe.cap(2));
cWidth = resolutionRe.cap(1).toInt();
cHeight = resolutionRe.cap(2).toInt();
}
} else {
mGraphicsSettings.setValue(QString("Video/resolution x"), QString::number(customWidthSpinBox->value()));
mGraphicsSettings.setValue(QString("Video/resolution y"), QString::number(customHeightSpinBox->value()));
cWidth = customWidthSpinBox->value();
cHeight = customHeightSpinBox->value();
}
mGraphicsSettings.setValue(QString("Video/screen"), QString::number(screenComboBox->currentIndex()));
if (cWidth != mEngineSettings.getInt("resolution x", "Video"))
mEngineSettings.setInt("resolution x", "Video", cWidth);
if (cHeight != mEngineSettings.getInt("resolution y", "Video"))
mEngineSettings.setInt("resolution y", "Video", cHeight);
int cScreen = screenComboBox->currentIndex();
if (cScreen != mEngineSettings.getInt("screen", "Video"))
mEngineSettings.setInt("screen", "Video", cScreen);
}
QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)

@ -5,6 +5,8 @@
#include "ui_graphicspage.h"
#include <components/settings/settings.hpp>
namespace Files { struct ConfigurationManager; }
namespace Launcher
@ -16,7 +18,7 @@ namespace Launcher
Q_OBJECT
public:
GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0);
GraphicsPage(Files::ConfigurationManager &cfg, Settings::Manager &engineSettings, QWidget *parent = 0);
void saveSettings();
bool loadSettings();
@ -30,7 +32,7 @@ namespace Launcher
private:
Files::ConfigurationManager &mCfgMgr;
GraphicsSettings &mGraphicsSettings;
Settings::Manager &mEngineSettings;
QStringList getAvailableResolutions(int screen);
QRect getMaximumResolution();

@ -41,15 +41,6 @@ int main(int argc, char *argv[])
dir.cdUp();
dir.cdUp();
}
// force Qt to load only LOCAL plugins, don't touch system Qt installation
QDir pluginsPath(QCoreApplication::applicationDirPath());
pluginsPath.cdUp();
pluginsPath.cd("Plugins");
QStringList libraryPaths;
libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath();
app.setLibraryPaths(libraryPaths);
#endif
QDir::setCurrent(dir.absolutePath());

@ -24,6 +24,15 @@
using namespace Process;
void cfgError(const QString& title, const QString& msg) {
QMessageBox msgBox;
msgBox.setWindowTitle(title);
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(msg);
msgBox.exec();
}
Launcher::MainDialog::MainDialog(QWidget *parent)
: QMainWindow(parent), mGameSettings (mCfgMgr)
{
@ -105,7 +114,7 @@ void Launcher::MainDialog::createPages()
{
mPlayPage = new PlayPage(this);
mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
mGraphicsPage = new GraphicsPage(mCfgMgr, mGraphicsSettings, this);
mGraphicsPage = new GraphicsPage(mCfgMgr, mEngineSettings, this);
mSettingsPage = new SettingsPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
// Set the combobox of the play page to imitate the combobox on the datafilespage
@ -248,6 +257,8 @@ void Launcher::MainDialog::changePage(QListWidgetItem *current, QListWidgetItem
bool Launcher::MainDialog::setupLauncherSettings()
{
mLauncherSettings.clear();
mLauncherSettings.setMultiValueEnabled(true);
QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str());
@ -257,18 +268,14 @@ bool Launcher::MainDialog::setupLauncherSettings()
paths.append(userPath + QString(Config::LauncherSettings::sLauncherConfigFileName));
foreach (const QString &path, paths) {
qDebug() << "Loading config file:" << qPrintable(path);
qDebug() << "Loading config file:" << path.toUtf8().constData();
QFile file(path);
if (file.exists()) {
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
cfgError(tr("Error opening OpenMW configuration file"),
tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
return false;
}
QTextStream stream(&file);
@ -284,6 +291,8 @@ bool Launcher::MainDialog::setupLauncherSettings()
bool Launcher::MainDialog::setupGameSettings()
{
mGameSettings.clear();
QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str());
QString globalPath = QString::fromUtf8(mCfgMgr.getGlobalPath().string().c_str());
@ -292,18 +301,14 @@ bool Launcher::MainDialog::setupGameSettings()
QString path = userPath + QLatin1String("openmw.cfg");
QFile file(path);
qDebug() << "Loading config file:" << qPrintable(path);
qDebug() << "Loading config file:" << path.toUtf8().constData();
if (file.exists()) {
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
cfgError(tr("Error opening OpenMW configuration file"),
tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
return false;
}
QTextStream stream(&file);
@ -319,19 +324,15 @@ bool Launcher::MainDialog::setupGameSettings()
paths.append(userPath + QString("openmw.cfg"));
foreach (const QString &path, paths) {
qDebug() << "Loading config file:" << qPrintable(path);
qDebug() << "Loading config file:" << path.toUtf8().constData();
QFile file(path);
if (file.exists()) {
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
cfgError(tr("Error opening OpenMW configuration file"),
tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
return false;
}
QTextStream stream(&file);
@ -383,53 +384,54 @@ bool Launcher::MainDialog::setupGameSettings()
bool Launcher::MainDialog::setupGraphicsSettings()
{
mGraphicsSettings.setMultiValueEnabled(false);
QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str());
QString globalPath = QString::fromUtf8(mCfgMgr.getGlobalPath().string().c_str());
QFile localDefault(QString("settings-default.cfg"));
QFile globalDefault(globalPath + QString("settings-default.cfg"));
if (!localDefault.exists() && !globalDefault.exists()) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error reading OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not find settings-default.cfg</b><br><br> \
The problem may be due to an incomplete installation of OpenMW.<br> \
Reinstalling OpenMW may resolve the problem."));
msgBox.exec();
// This method is almost a copy of OMW::Engine::loadSettings(). They should definitely
// remain consistent, and possibly be merged into a shared component. At the very least
// the filenames should be in the CfgMgr component.
// Ensure to clear previous settings in case we had already loaded settings.
mEngineSettings.clear();
// Create the settings manager and load default settings file
const std::string localDefault = (mCfgMgr.getLocalPath() / "settings-default.cfg").string();
const std::string globalDefault = (mCfgMgr.getGlobalPath() / "settings-default.cfg").string();
std::string defaultPath;
// Prefer the settings-default.cfg in the current directory.
if (boost::filesystem::exists(localDefault))
defaultPath = localDefault;
else if (boost::filesystem::exists(globalDefault))
defaultPath = globalDefault;
// Something's very wrong if we can't find the file at all.
else {
cfgError(tr("Error reading OpenMW configuration file"),
tr("<br><b>Could not find settings-default.cfg</b><br><br> \
The problem may be due to an incomplete installation of OpenMW.<br> \
Reinstalling OpenMW may resolve the problem."));
return false;
}
// Load the default settings, report any parsing errors.
try {
mEngineSettings.loadDefault(defaultPath);
}
catch (std::exception& e) {
std::string msg = std::string("<br><b>Error reading settings-default.cfg</b><br><br>") + e.what();
cfgError(tr("Error reading OpenMW configuration file"), tr(msg.c_str()));
return false;
}
QStringList paths;
paths.append(globalPath + QString("settings-default.cfg"));
paths.append(QString("settings-default.cfg"));
paths.append(userPath + QString("settings.cfg"));
foreach (const QString &path, paths) {
qDebug() << "Loading config file:" << qPrintable(path);
QFile file(path);
if (file.exists()) {
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
return false;
}
QTextStream stream(&file);
stream.setCodec(QTextCodec::codecForName("UTF-8"));
// Load user settings if they exist
const std::string userPath = (mCfgMgr.getUserConfigPath() / "settings.cfg").string();
// User settings are not required to exist, so if they don't we're done.
if (!boost::filesystem::exists(userPath)) return true;
mGraphicsSettings.readFile(stream);
}
file.close();
try {
mEngineSettings.loadUser(userPath);
}
catch (std::exception& e) {
std::string msg = std::string("<br><b>Error reading settings.cfg</b><br><br>") + e.what();
cfgError(tr("Error reading OpenMW configuration file"), tr(msg.c_str()));
return false;
}
return true;
@ -478,15 +480,11 @@ bool Launcher::MainDialog::writeSettings()
if (!dir.exists()) {
if (!dir.mkpath(userPath)) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error creating OpenMW configuration directory"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not create %0</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(userPath));
msgBox.exec();
return false;
cfgError(tr("Error creating OpenMW configuration directory"),
tr("<br><b>Could not create %0</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(userPath));
return false;
}
}
@ -495,15 +493,11 @@ bool Launcher::MainDialog::writeSettings()
if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) {
// File cannot be opened or created
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error writing OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
return false;
cfgError(tr("Error writing OpenMW configuration file"),
tr("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
return false;
}
@ -511,44 +505,30 @@ bool Launcher::MainDialog::writeSettings()
file.close();
// Graphics settings
file.setFileName(userPath + QString("settings.cfg"));
if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) {
// File cannot be opened or created
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error writing OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
return false;
const std::string settingsPath = (mCfgMgr.getUserConfigPath() / "settings.cfg").string();
try {
mEngineSettings.saveUser(settingsPath);
}
catch (std::exception& e) {
std::string msg = "<br><b>Error writing settings.cfg</b><br><br>" +
settingsPath + "<br><br>" + e.what();
cfgError(tr("Error writing user settings file"), tr(msg.c_str()));
return false;
}
QTextStream stream(&file);
stream.setDevice(&file);
stream.setCodec(QTextCodec::codecForName("UTF-8"));
mGraphicsSettings.writeFile(stream);
file.close();
// Launcher settings
file.setFileName(userPath + QString(Config::LauncherSettings::sLauncherConfigFileName));
if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) {
// File cannot be opened or created
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error writing Launcher configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
return false;
cfgError(tr("Error writing Launcher configuration file"),
tr("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
return false;
}
QTextStream stream(&file);
stream.setDevice(&file);
stream.setCodec(QTextCodec::codecForName("UTF-8"));

@ -13,7 +13,7 @@
#include <components/config/gamesettings.hpp>
#include <components/config/launchersettings.hpp>
#include "settings/graphicssettings.hpp"
#include <components/settings/settings.hpp>
#include "ui_mainwindow.h"
@ -93,7 +93,7 @@ namespace Launcher
Files::ConfigurationManager mCfgMgr;
Config::GameSettings mGameSettings;
GraphicsSettings mGraphicsSettings;
Settings::Manager mEngineSettings;
Config::LauncherSettings mLauncherSettings;
};

@ -2,20 +2,11 @@
#include <QListView>
#ifdef Q_OS_MAC
#include <QPlastiqueStyle>
#endif
Launcher::PlayPage::PlayPage(QWidget *parent) : QWidget(parent)
{
setObjectName ("PlayPage");
setupUi(this);
// Hacks to get the stylesheet look properly
#ifdef Q_OS_MAC
QPlastiqueStyle *style = new QPlastiqueStyle;
profilesComboBox->setStyle(style);
#endif
profilesComboBox->setView(new QListView());
connect(profilesComboBox, SIGNAL(activated(int)), this, SIGNAL (signalProfileChanged(int)));

@ -1,44 +0,0 @@
#include "graphicssettings.hpp"
#include <QTextStream>
#include <QString>
#include <QRegExp>
#include <QMap>
Launcher::GraphicsSettings::GraphicsSettings()
{
}
Launcher::GraphicsSettings::~GraphicsSettings()
{
}
bool Launcher::GraphicsSettings::writeFile(QTextStream &stream)
{
QString sectionPrefix;
QRegExp sectionRe("([^/]+)/(.+)$");
QMap<QString, QString> settings = SettingsBase::getSettings();
QMapIterator<QString, QString> i(settings);
while (i.hasNext()) {
i.next();
QString prefix;
QString key;
if (sectionRe.exactMatch(i.key())) {
prefix = sectionRe.cap(1);
key = sectionRe.cap(2);
}
if (sectionPrefix != prefix) {
sectionPrefix = prefix;
stream << "\n[" << prefix << "]\n";
}
stream << key << " = " << i.value() << "\n";
}
return true;
}

@ -1,18 +0,0 @@
#ifndef GRAPHICSSETTINGS_HPP
#define GRAPHICSSETTINGS_HPP
#include <components/config/settingsbase.hpp>
namespace Launcher
{
class GraphicsSettings : public Config::SettingsBase<QMap<QString, QString> >
{
public:
GraphicsSettings();
~GraphicsSettings();
bool writeFile(QTextStream &stream);
};
}
#endif // GRAPHICSSETTINGS_HPP

@ -20,7 +20,6 @@ MwIniImporter::MwIniImporter()
{
const char *map[][2] =
{
{ "fps", "General:Show FPS" },
{ "no-sound", "General:Disable Audio" },
{ 0, 0 }
};
@ -639,6 +638,9 @@ MwIniImporter::MwIniImporter()
"Blood:Texture Name 1",
"Blood:Texture Name 2",
// werewolf (Bloodmoon)
"General:Werewolf FOV",
0
};
@ -847,7 +849,7 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co
for(std::vector<std::string>::const_iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) {
std::string filetype(entry->substr(entry->length()-3));
Misc::StringUtils::toLower(filetype);
Misc::StringUtils::lowerCaseInPlace(filetype);
if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0) {
boost::filesystem::path filepath(gameFilesDir);

@ -106,31 +106,16 @@ opencs_units_noqt (view/tools
subviews
)
opencs_units (view/settings
settingwindow
dialog
page
view
booleanview
textview
listview
rangeview
resizeablestackedwidget
spinbox
opencs_units (view/prefs
dialogue pagebase page
)
opencs_units_noqt (view/settings
frame
opencs_units (model/prefs
state setting intsetting doublesetting boolsetting enumsetting coloursetting
)
opencs_units (model/settings
usersettings
setting
connector
)
opencs_hdrs_noqt (model/settings
support
opencs_units_noqt (model/prefs
category
)
opencs_units_noqt (model/filter
@ -205,7 +190,13 @@ if(APPLE)
endif(APPLE)
target_link_libraries(openmw-cs
${OPENSCENEGRAPH_LIBRARIES}
${OSG_LIBRARIES}
${OPENTHREADS_LIBRARIES}
${OSGUTIL_LIBRARIES}
${OSGVIEWER_LIBRARIES}
${OSGGA_LIBRARIES}
${OSGFX_LIBRARIES}
${OSGQT_LIBRARIES}
${Boost_SYSTEM_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}

@ -18,7 +18,7 @@
#endif
CS::Editor::Editor ()
: mUserSettings (mCfgMgr), mDocumentManager (mCfgMgr),
: mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr),
mViewManager (mDocumentManager), mPid(""),
mLock(), mMerge (mDocumentManager),
mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL)
@ -27,9 +27,6 @@ CS::Editor::Editor ()
setupDataFiles (config.first);
CSMSettings::UserSettings::instance().loadSettings ("opencs.ini");
mSettings.setModel (CSMSettings::UserSettings::instance());
NifOsg::Loader::setShowMarkers(true);
mVFS.reset(new VFS::Manager(mFsStrict));

@ -17,15 +17,16 @@
#include <components/files/multidircollection.hpp>
#include "model/settings/usersettings.hpp"
#include "model/doc/documentmanager.hpp"
#include "model/prefs/state.hpp"
#include "view/doc/viewmanager.hpp"
#include "view/doc/startup.hpp"
#include "view/doc/filedialog.hpp"
#include "view/doc/newgame.hpp"
#include "view/settings/dialog.hpp"
#include "view/prefs/dialogue.hpp"
#include "view/tools/merge.hpp"
@ -49,12 +50,12 @@ namespace CS
std::auto_ptr<VFS::Manager> mVFS;
Files::ConfigurationManager mCfgMgr;
CSMSettings::UserSettings mUserSettings;
CSMPrefs::State mSettingsState;
CSMDoc::DocumentManager mDocumentManager;
CSVDoc::ViewManager mViewManager;
CSVDoc::StartupDialogue mStartup;
CSVDoc::NewGameDialogue mNewGame;
CSVSettings::Dialog mSettings;
CSVPrefs::Dialogue mSettings;
CSVDoc::FileDialog mFileDialog;
boost::filesystem::path mLocal;
boost::filesystem::path mResources;

@ -43,6 +43,10 @@ class Application : public QApplication
int main(int argc, char *argv[])
{
#ifdef Q_OS_MAC
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
#endif
try
{
// To allow background thread drawing in OSG
@ -64,15 +68,6 @@ int main(int argc, char *argv[])
dir.cdUp();
}
QDir::setCurrent(dir.absolutePath());
// force Qt to load only LOCAL plugins, don't touch system Qt installation
QDir pluginsPath(QCoreApplication::applicationDirPath());
pluginsPath.cdUp();
pluginsPath.cd("Plugins");
QStringList libraryPaths;
libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath();
application.setLibraryPaths(libraryPaths);
#endif
application.setWindowIcon (QIcon (":./openmw-cs.png"));

@ -6,7 +6,6 @@
#include <QTimer>
#include "../world/universalid.hpp"
#include "../settings/usersettings.hpp"
#include "state.hpp"
#include "stage.hpp"
@ -23,9 +22,6 @@ void CSMDoc::Operation::prepareStages()
{
iter->second = iter->first->setup();
mTotalSteps += iter->second;
for (std::map<QString, QStringList>::const_iterator iter2 (mSettings.begin()); iter2!=mSettings.end(); ++iter2)
iter->first->updateUserSetting (iter2->first, iter2->second);
}
}
@ -47,7 +43,7 @@ CSMDoc::Operation::~Operation()
void CSMDoc::Operation::run()
{
mTimer->stop();
if (!mConnected)
{
connect (mTimer, SIGNAL (timeout()), this, SLOT (executeStage()));
@ -64,14 +60,6 @@ void CSMDoc::Operation::appendStage (Stage *stage)
mStages.push_back (std::make_pair (stage, 0));
}
void CSMDoc::Operation::configureSettings (const std::vector<QString>& settings)
{
for (std::vector<QString>::const_iterator iter (settings.begin()); iter!=settings.end(); ++iter)
{
mSettings.insert (std::make_pair (*iter, CSMSettings::UserSettings::instance().definitions (*iter)));
}
}
void CSMDoc::Operation::setDefaultSeverity (Message::Severity severity)
{
mDefaultSeverity = severity;
@ -101,14 +89,6 @@ void CSMDoc::Operation::abort()
mCurrentStage = mStages.end();
}
void CSMDoc::Operation::updateUserSetting (const QString& name, const QStringList& value)
{
std::map<QString, QStringList>::iterator iter = mSettings.find (name);
if (iter!=mSettings.end())
iter->second = value;
}
void CSMDoc::Operation::executeStage()
{
if (!mPrepared)
@ -116,7 +96,7 @@ void CSMDoc::Operation::executeStage()
prepareStages();
mPrepared = true;
}
Messages messages (mDefaultSeverity);
while (mCurrentStage!=mStages.end())

@ -34,7 +34,6 @@ namespace CSMDoc
bool mError;
bool mConnected;
QTimer *mTimer;
std::map<QString, QStringList> mSettings;
bool mPrepared;
Message::Severity mDefaultSeverity;
@ -53,11 +52,6 @@ namespace CSMDoc
///
/// \attention Do no call this function while this Operation is running.
/// Specify settings to be passed on to stages.
///
/// \attention Do no call this function while this Operation is running.
void configureSettings (const std::vector<QString>& settings);
/// \attention Do no call this function while this Operation is running.
void setDefaultSeverity (Message::Severity severity);
@ -77,8 +71,6 @@ namespace CSMDoc
void run();
void updateUserSetting (const QString& name, const QStringList& value);
private slots:
void executeStage();

@ -1,7 +1,5 @@
#include "operationholder.hpp"
#include "../settings/usersettings.hpp"
#include "operation.hpp"
CSMDoc::OperationHolder::OperationHolder (Operation *operation) : mRunning (false)
@ -30,9 +28,6 @@ void CSMDoc::OperationHolder::setOperation (Operation *operation)
connect (this, SIGNAL (abortSignal()), mOperation, SLOT (abort()));
connect (&mThread, SIGNAL (started()), mOperation, SLOT (run()));
connect (&CSMSettings::UserSettings::instance(), SIGNAL (userSettingUpdated (const QString&, const QStringList&)),
mOperation, SLOT (updateUserSetting (const QString&, const QStringList&)));
}
bool CSMDoc::OperationHolder::isRunning() const

@ -99,84 +99,77 @@ int CSMDoc::WriteDialogueCollectionStage::setup()
void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& messages)
{
ESM::ESMWriter& writer = mState.getWriter();
const CSMWorld::Record<ESM::Dialogue>& topic = mTopics.getRecord (stage);
CSMWorld::RecordBase::State state = topic.mState;
if (state==CSMWorld::RecordBase::State_Deleted)
if (topic.mState == CSMWorld::RecordBase::State_Deleted)
{
// if the topic is deleted, we do not need to bother with INFO records.
/// \todo wrote record with delete flag
ESM::Dialogue dialogue = topic.get();
writer.startRecord(dialogue.sRecordId);
dialogue.save(writer, true);
writer.endRecord(dialogue.sRecordId);
return;
}
// Test, if we need to save anything associated info records.
bool infoModified = false;
CSMWorld::InfoCollection::Range range = mInfos.getTopicRange (topic.get().mId);
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)
{
CSMWorld::RecordBase::State state = iter->mState;
if (state==CSMWorld::RecordBase::State_Modified ||
state==CSMWorld::RecordBase::State_ModifiedOnly ||
state==CSMWorld::RecordBase::State_Deleted)
if (iter->isModified() || iter->mState == CSMWorld::RecordBase::State_Deleted)
{
infoModified = true;
break;
}
}
if (state==CSMWorld::RecordBase::State_Modified ||
state==CSMWorld::RecordBase::State_ModifiedOnly ||
infoModified)
if (topic.isModified() || infoModified)
{
mState.getWriter().startRecord (topic.mModified.sRecordId);
mState.getWriter().writeHNCString ("NAME", topic.mModified.mId);
topic.mModified.save (mState.getWriter());
mState.getWriter().endRecord (topic.mModified.sRecordId);
if (infoModified && topic.mState != CSMWorld::RecordBase::State_Modified
&& topic.mState != CSMWorld::RecordBase::State_ModifiedOnly)
{
mState.getWriter().startRecord (topic.mBase.sRecordId);
topic.mBase.save (mState.getWriter(), topic.mState == CSMWorld::RecordBase::State_Deleted);
mState.getWriter().endRecord (topic.mBase.sRecordId);
}
else
{
mState.getWriter().startRecord (topic.mModified.sRecordId);
topic.mModified.save (mState.getWriter(), topic.mState == CSMWorld::RecordBase::State_Deleted);
mState.getWriter().endRecord (topic.mModified.sRecordId);
}
// write modified selected info records
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second;
++iter)
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)
{
CSMWorld::RecordBase::State state = iter->mState;
if (state==CSMWorld::RecordBase::State_Deleted)
{
/// \todo wrote record with delete flag
}
else if (state==CSMWorld::RecordBase::State_Modified ||
state==CSMWorld::RecordBase::State_ModifiedOnly)
if (iter->isModified() || iter->mState == CSMWorld::RecordBase::State_Deleted)
{
ESM::DialInfo info = iter->get();
info.mId = info.mId.substr (info.mId.find_last_of ('#')+1);
info.mPrev = "";
if (iter!=range.first)
{
CSMWorld::InfoCollection::RecordConstIterator prev = iter;
--prev;
info.mPrev =
prev->mModified.mId.substr (prev->mModified.mId.find_last_of ('#')+1);
info.mPrev = prev->get().mId.substr (prev->get().mId.find_last_of ('#')+1);
}
CSMWorld::InfoCollection::RecordConstIterator next = iter;
++next;
info.mNext = "";
if (next!=range.second)
{
info.mNext =
next->mModified.mId.substr (next->mModified.mId.find_last_of ('#')+1);
info.mNext = next->get().mId.substr (next->get().mId.find_last_of ('#')+1);
}
mState.getWriter().startRecord (info.sRecordId);
mState.getWriter().writeHNCString ("INAM", info.mId);
info.save (mState.getWriter());
mState.getWriter().endRecord (info.sRecordId);
writer.startRecord (info.sRecordId);
info.save (writer, iter->mState == CSMWorld::RecordBase::State_Deleted);
writer.endRecord (info.sRecordId);
}
}
}
@ -224,9 +217,7 @@ void CSMDoc::CollectionReferencesStage::perform (int stage, Messages& messages)
const CSMWorld::Record<CSMWorld::CellRef>& record =
mDocument.getData().getReferences().getRecord (i);
if (record.mState==CSMWorld::RecordBase::State_Deleted ||
record.mState==CSMWorld::RecordBase::State_Modified ||
record.mState==CSMWorld::RecordBase::State_ModifiedOnly)
if (record.isModified() || record.mState == CSMWorld::RecordBase::State_Deleted)
{
std::string cellId = record.get().mOriginalCell.empty() ?
record.get().mCell : record.get().mOriginalCell;
@ -268,36 +259,34 @@ int CSMDoc::WriteCellCollectionStage::setup()
void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
{
const CSMWorld::Record<CSMWorld::Cell>& cell =
mDocument.getData().getCells().getRecord (stage);
ESM::ESMWriter& writer = mState.getWriter();
const CSMWorld::Record<CSMWorld::Cell>& cell = mDocument.getData().getCells().getRecord (stage);
std::map<std::string, std::deque<int> >::const_iterator references =
mState.getSubRecords().find (Misc::StringUtils::lowerCase (cell.get().mId));
if (cell.mState==CSMWorld::RecordBase::State_Modified ||
cell.mState==CSMWorld::RecordBase::State_ModifiedOnly ||
if (cell.isModified() ||
cell.mState == CSMWorld::RecordBase::State_Deleted ||
references!=mState.getSubRecords().end())
{
bool interior = cell.get().mId.substr (0, 1)!="#";
CSMWorld::Cell cellRecord = cell.get();
bool interior = cellRecord.mId.substr (0, 1)!="#";
// write cell data
mState.getWriter().startRecord (cell.mModified.sRecordId);
mState.getWriter().writeHNOCString ("NAME", cell.get().mName);
ESM::Cell cell2 = cell.get();
writer.startRecord (cellRecord.sRecordId);
if (interior)
cell2.mData.mFlags |= ESM::Cell::Interior;
cellRecord.mData.mFlags |= ESM::Cell::Interior;
else
{
cell2.mData.mFlags &= ~ESM::Cell::Interior;
cellRecord.mData.mFlags &= ~ESM::Cell::Interior;
std::istringstream stream (cell.get().mId.c_str());
std::istringstream stream (cellRecord.mId.c_str());
char ignore;
stream >> ignore >> cell2.mData.mX >> cell2.mData.mY;
stream >> ignore >> cellRecord.mData.mX >> cellRecord.mData.mY;
}
cell2.save (mState.getWriter());
cellRecord.save (writer, cell.mState == CSMWorld::RecordBase::State_Deleted);
// write references
if (references!=mState.getSubRecords().end())
@ -308,24 +297,25 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
const CSMWorld::Record<CSMWorld::CellRef>& ref =
mDocument.getData().getReferences().getRecord (*iter);
if (ref.mState==CSMWorld::RecordBase::State_Modified ||
ref.mState==CSMWorld::RecordBase::State_ModifiedOnly)
if (ref.isModified() || ref.mState == CSMWorld::RecordBase::State_Deleted)
{
CSMWorld::CellRef refRecord = ref.get();
// recalculate the ref's cell location
std::ostringstream stream;
if (!interior)
{
std::pair<int, int> index = ref.get().getCellIndex();
std::pair<int, int> index = refRecord.getCellIndex();
stream << "#" << index.first << " " << index.second;
}
// An empty mOriginalCell is meant to indicate that it is the same as
// the current cell. It is possible that a moved ref is moved again.
if ((ref.get().mOriginalCell.empty() ? ref.get().mCell : ref.get().mOriginalCell)
if ((refRecord.mOriginalCell.empty() ? refRecord.mCell : refRecord.mOriginalCell)
!= stream.str() && !interior)
{
ESM::MovedCellRef moved;
moved.mRefNum = ref.get().mRefNum;
moved.mRefNum = refRecord.mRefNum;
// Need to fill mTarget with the ref's new position.
std::istringstream istream (stream.str().c_str());
@ -333,24 +323,16 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
char ignore;
istream >> ignore >> moved.mTarget[0] >> moved.mTarget[1];
ref.get().mRefNum.save (mState.getWriter(), false, "MVRF");
mState.getWriter().writeHNT ("CNDT", moved.mTarget, 8);
refRecord.mRefNum.save (writer, false, "MVRF");
writer.writeHNT ("CNDT", moved.mTarget, 8);
}
ref.get().save (mState.getWriter());
}
else if (ref.mState==CSMWorld::RecordBase::State_Deleted)
{
/// \todo write record with delete flag
refRecord.save (writer, false, false, ref.mState == CSMWorld::RecordBase::State_Deleted);
}
}
}
mState.getWriter().endRecord (cell.mModified.sRecordId);
}
else if (cell.mState==CSMWorld::RecordBase::State_Deleted)
{
/// \todo write record with delete flag
writer.endRecord (cellRecord.sRecordId);
}
}
@ -367,11 +349,11 @@ int CSMDoc::WritePathgridCollectionStage::setup()
void CSMDoc::WritePathgridCollectionStage::perform (int stage, Messages& messages)
{
const CSMWorld::Record<CSMWorld::Pathgrid>& pathgrid =
ESM::ESMWriter& writer = mState.getWriter();
const CSMWorld::Record<CSMWorld::Pathgrid>& pathgrid =
mDocument.getData().getPathgrids().getRecord (stage);
if (pathgrid.mState==CSMWorld::RecordBase::State_Modified ||
pathgrid.mState==CSMWorld::RecordBase::State_ModifiedOnly)
if (pathgrid.isModified() || pathgrid.mState == CSMWorld::RecordBase::State_Deleted)
{
CSMWorld::Pathgrid record = pathgrid.get();
@ -384,15 +366,9 @@ void CSMDoc::WritePathgridCollectionStage::perform (int stage, Messages& message
else
record.mCell = record.mId;
mState.getWriter().startRecord (record.sRecordId);
record.save (mState.getWriter());
mState.getWriter().endRecord (record.sRecordId);
}
else if (pathgrid.mState==CSMWorld::RecordBase::State_Deleted)
{
/// \todo write record with delete flag
writer.startRecord (record.sRecordId);
record.save (writer, pathgrid.mState == CSMWorld::RecordBase::State_Deleted);
writer.endRecord (record.sRecordId);
}
}
@ -409,26 +385,20 @@ int CSMDoc::WriteLandCollectionStage::setup()
void CSMDoc::WriteLandCollectionStage::perform (int stage, Messages& messages)
{
const CSMWorld::Record<CSMWorld::Land>& land =
ESM::ESMWriter& writer = mState.getWriter();
const CSMWorld::Record<CSMWorld::Land>& land =
mDocument.getData().getLand().getRecord (stage);
if (land.mState==CSMWorld::RecordBase::State_Modified ||
land.mState==CSMWorld::RecordBase::State_ModifiedOnly)
if (land.isModified() || land.mState == CSMWorld::RecordBase::State_Deleted)
{
const CSMWorld::Land& record = land.get();
mState.getWriter().startRecord (record.sRecordId);
record.save (mState.getWriter());
CSMWorld::Land record = land.get();
writer.startRecord (record.sRecordId);
record.save (writer, land.mState == CSMWorld::RecordBase::State_Deleted);
if (const ESM::Land::LandData *data = record.getLandData (record.mDataTypes))
data->save (mState.getWriter());
mState.getWriter().endRecord (record.sRecordId);
}
else if (land.mState==CSMWorld::RecordBase::State_Deleted)
{
/// \todo write record with delete flag
writer.endRecord (record.sRecordId);
}
}
@ -445,25 +415,16 @@ int CSMDoc::WriteLandTextureCollectionStage::setup()
void CSMDoc::WriteLandTextureCollectionStage::perform (int stage, Messages& messages)
{
const CSMWorld::Record<CSMWorld::LandTexture>& landTexture =
ESM::ESMWriter& writer = mState.getWriter();
const CSMWorld::Record<CSMWorld::LandTexture>& landTexture =
mDocument.getData().getLandTextures().getRecord (stage);
if (landTexture.mState==CSMWorld::RecordBase::State_Modified ||
landTexture.mState==CSMWorld::RecordBase::State_ModifiedOnly)
if (landTexture.isModified() || landTexture.mState == CSMWorld::RecordBase::State_Deleted)
{
CSMWorld::LandTexture record = landTexture.get();
mState.getWriter().startRecord (record.sRecordId);
mState.getWriter().writeHNString("NAME", record.mId);
record.save (mState.getWriter());
mState.getWriter().endRecord (record.sRecordId);
}
else if (landTexture.mState==CSMWorld::RecordBase::State_Deleted)
{
/// \todo write record with delete flag
writer.startRecord (record.sRecordId);
record.save (writer, landTexture.mState == CSMWorld::RecordBase::State_Deleted);
writer.endRecord (record.sRecordId);
}
}

@ -100,26 +100,17 @@ namespace CSMDoc
if (CSMWorld::getScopeFromId (mCollection.getRecord (stage).get().mId)!=mScope)
return;
ESM::ESMWriter& writer = mState.getWriter();
CSMWorld::RecordBase::State state = mCollection.getRecord (stage).mState;
typename CollectionT::ESXRecord record = mCollection.getRecord (stage).get();
if (state==CSMWorld::RecordBase::State_Modified ||
state==CSMWorld::RecordBase::State_ModifiedOnly)
if (state == CSMWorld::RecordBase::State_Modified ||
state == CSMWorld::RecordBase::State_ModifiedOnly ||
state == CSMWorld::RecordBase::State_Deleted)
{
// FIXME: A quick Workaround to support records which should not write
// NAME, including SKIL, MGEF and SCPT. If there are many more
// idcollection records that doesn't use NAME then a more generic
// solution may be required.
uint32_t name = mCollection.getRecord (stage).mModified.sRecordId;
mState.getWriter().startRecord (name);
if(name != ESM::REC_SKIL && name != ESM::REC_MGEF && name != ESM::REC_SCPT)
mState.getWriter().writeHNCString ("NAME", mCollection.getId (stage));
mCollection.getRecord (stage).mModified.save (mState.getWriter());
mState.getWriter().endRecord (mCollection.getRecord (stage).mModified.sRecordId);
}
else if (state==CSMWorld::RecordBase::State_Deleted)
{
/// \todo write record with delete flag
writer.startRecord (record.sRecordId);
record.save (writer, state == CSMWorld::RecordBase::State_Deleted);
writer.endRecord (record.sRecordId);
}
}

@ -1,5 +1,3 @@
#include "stage.hpp"
CSMDoc::Stage::~Stage() {}
void CSMDoc::Stage::updateUserSetting (const QString& name, const QStringList& value) {}

@ -23,11 +23,7 @@ namespace CSMDoc
virtual void perform (int stage, Messages& messages) = 0;
///< Messages resulting from this stage will be appended to \a messages.
/// Default-implementation: ignore
virtual void updateUserSetting (const QString& name, const QStringList& value);
};
}
#endif

@ -0,0 +1,47 @@
#include "boolsetting.hpp"
#include <QCheckBox>
#include <QMutexLocker>
#include <components/settings/settings.hpp>
#include "category.hpp"
#include "state.hpp"
CSMPrefs::BoolSetting::BoolSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label, bool default_)
: Setting (parent, values, mutex, key, label), mDefault (default_)
{}
CSMPrefs::BoolSetting& CSMPrefs::BoolSetting::setTooltip (const std::string& tooltip)
{
mTooltip = tooltip;
return *this;
}
std::pair<QWidget *, QWidget *> CSMPrefs::BoolSetting::makeWidgets (QWidget *parent)
{
QCheckBox *widget = new QCheckBox (QString::fromUtf8 (getLabel().c_str()), parent);
widget->setCheckState (mDefault ? Qt::Checked : Qt::Unchecked);
if (!mTooltip.empty())
{
QString tooltip = QString::fromUtf8 (mTooltip.c_str());
widget->setToolTip (tooltip);
}
connect (widget, SIGNAL (stateChanged (int)), this, SLOT (valueChanged (int)));
return std::make_pair (static_cast<QWidget *> (0), widget);
}
void CSMPrefs::BoolSetting::valueChanged (int value)
{
{
QMutexLocker lock (getMutex());
getValues().setBool (getKey(), getParent()->getKey(), value);
}
getParent()->getState()->update (*this);
}

@ -0,0 +1,31 @@
#ifndef CSM_PREFS_BOOLSETTING_H
#define CSM_PREFS_BOOLSETTING_H
#include "setting.hpp"
namespace CSMPrefs
{
class BoolSetting : public Setting
{
Q_OBJECT
std::string mTooltip;
bool mDefault;
public:
BoolSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label, bool default_);
BoolSetting& setTooltip (const std::string& tooltip);
/// Return label, input widget.
virtual std::pair<QWidget *, QWidget *> makeWidgets (QWidget *parent);
private slots:
void valueChanged (int value);
};
}
#endif

@ -0,0 +1,51 @@
#include "category.hpp"
#include <stdexcept>
#include "setting.hpp"
#include "state.hpp"
CSMPrefs::Category::Category (State *parent, const std::string& key)
: mParent (parent), mKey (key)
{}
const std::string& CSMPrefs::Category::getKey() const
{
return mKey;
}
CSMPrefs::State *CSMPrefs::Category::getState() const
{
return mParent;
}
void CSMPrefs::Category::addSetting (Setting *setting)
{
mSettings.push_back (setting);
}
CSMPrefs::Category::Iterator CSMPrefs::Category::begin()
{
return mSettings.begin();
}
CSMPrefs::Category::Iterator CSMPrefs::Category::end()
{
return mSettings.end();
}
CSMPrefs::Setting& CSMPrefs::Category::operator[] (const std::string& key)
{
for (Iterator iter = mSettings.begin(); iter!=mSettings.end(); ++iter)
if ((*iter)->getKey()==key)
return **iter;
throw std::logic_error ("Invalid user setting: " + key);
}
void CSMPrefs::Category::update()
{
for (Iterator iter = mSettings.begin(); iter!=mSettings.end(); ++iter)
mParent->update (**iter);
}

@ -0,0 +1,45 @@
#ifndef CSM_PREFS_CATEGORY_H
#define CSM_PREFS_CATEGORY_H
#include <string>
#include <vector>
namespace CSMPrefs
{
class State;
class Setting;
class Category
{
public:
typedef std::vector<Setting *> Container;
typedef Container::iterator Iterator;
private:
State *mParent;
std::string mKey;
Container mSettings;
public:
Category (State *parent, const std::string& key);
const std::string& getKey() const;
State *getState() const;
void addSetting (Setting *setting);
Iterator begin();
Iterator end();
Setting& operator[] (const std::string& key);
void update();
};
}
#endif

@ -0,0 +1,52 @@
#include "coloursetting.hpp"
#include <QLabel>
#include <QMutexLocker>
#include <components/settings/settings.hpp>
#include "../../view/widget/coloreditor.hpp"
#include "category.hpp"
#include "state.hpp"
CSMPrefs::ColourSetting::ColourSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label, QColor default_)
: Setting (parent, values, mutex, key, label), mDefault (default_)
{}
CSMPrefs::ColourSetting& CSMPrefs::ColourSetting::setTooltip (const std::string& tooltip)
{
mTooltip = tooltip;
return *this;
}
std::pair<QWidget *, QWidget *> CSMPrefs::ColourSetting::makeWidgets (QWidget *parent)
{
QLabel *label = new QLabel (QString::fromUtf8 (getLabel().c_str()), parent);
CSVWidget::ColorEditor *widget = new CSVWidget::ColorEditor (mDefault, parent);
if (!mTooltip.empty())
{
QString tooltip = QString::fromUtf8 (mTooltip.c_str());
label->setToolTip (tooltip);
widget->setToolTip (tooltip);
}
connect (widget, SIGNAL (pickingFinished()), this, SLOT (valueChanged()));
return std::make_pair (label, widget);
}
void CSMPrefs::ColourSetting::valueChanged()
{
CSVWidget::ColorEditor& widget = dynamic_cast<CSVWidget::ColorEditor&> (*sender());
{
QMutexLocker lock (getMutex());
getValues().setString (getKey(), getParent()->getKey(), widget.color().name().toUtf8().data());
}
getParent()->getState()->update (*this);
}

@ -0,0 +1,34 @@
#ifndef CSM_PREFS_COLOURSETTING_H
#define CSM_PREFS_COLOURSETTING_H
#include "setting.hpp"
#include <QColor>
namespace CSMPrefs
{
class ColourSetting : public Setting
{
Q_OBJECT
std::string mTooltip;
QColor mDefault;
public:
ColourSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label,
QColor default_);
ColourSetting& setTooltip (const std::string& tooltip);
/// Return label, input widget.
virtual std::pair<QWidget *, QWidget *> makeWidgets (QWidget *parent);
private slots:
void valueChanged();
};
}
#endif

@ -0,0 +1,75 @@
#include "doublesetting.hpp"
#include <limits>
#include <QLabel>
#include <QDoubleSpinBox>
#include <QMutexLocker>
#include <components/settings/settings.hpp>
#include "category.hpp"
#include "state.hpp"
CSMPrefs::DoubleSetting::DoubleSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label, double default_)
: Setting (parent, values, mutex, key, label),
mMin (0), mMax (std::numeric_limits<double>::max()),
mDefault (default_)
{}
CSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setRange (double min, double max)
{
mMin = min;
mMax = max;
return *this;
}
CSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setMin (double min)
{
mMin = min;
return *this;
}
CSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setMax (double max)
{
mMax = max;
return *this;
}
CSMPrefs::DoubleSetting& CSMPrefs::DoubleSetting::setTooltip (const std::string& tooltip)
{
mTooltip = tooltip;
return *this;
}
std::pair<QWidget *, QWidget *> CSMPrefs::DoubleSetting::makeWidgets (QWidget *parent)
{
QLabel *label = new QLabel (QString::fromUtf8 (getLabel().c_str()), parent);
QDoubleSpinBox *widget = new QDoubleSpinBox (parent);
widget->setRange (mMin, mMax);
widget->setValue (mDefault);
if (!mTooltip.empty())
{
QString tooltip = QString::fromUtf8 (mTooltip.c_str());
label->setToolTip (tooltip);
widget->setToolTip (tooltip);
}
connect (widget, SIGNAL (valueChanged (double)), this, SLOT (valueChanged (double)));
return std::make_pair (label, widget);
}
void CSMPrefs::DoubleSetting::valueChanged (double value)
{
{
QMutexLocker lock (getMutex());
getValues().setFloat (getKey(), getParent()->getKey(), value);
}
getParent()->getState()->update (*this);
}

@ -0,0 +1,41 @@
#ifndef CSM_PREFS_DOUBLESETTING_H
#define CSM_PREFS_DOUBLESETTING_H
#include "setting.hpp"
namespace CSMPrefs
{
class DoubleSetting : public Setting
{
Q_OBJECT
double mMin;
double mMax;
std::string mTooltip;
double mDefault;
public:
DoubleSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label,
double default_);
// defaults to [0, std::numeric_limits<double>::max()]
DoubleSetting& setRange (double min, double max);
DoubleSetting& setMin (double min);
DoubleSetting& setMax (double max);
DoubleSetting& setTooltip (const std::string& tooltip);
/// Return label, input widget.
virtual std::pair<QWidget *, QWidget *> makeWidgets (QWidget *parent);
private slots:
void valueChanged (double value);
};
}
#endif

@ -0,0 +1,112 @@
#include "enumsetting.hpp"
#include <QLabel>
#include <QComboBox>
#include <QMutexLocker>
#include <components/settings/settings.hpp>
#include "category.hpp"
#include "state.hpp"
CSMPrefs::EnumValue::EnumValue (const std::string& value, const std::string& tooltip)
: mValue (value), mTooltip (tooltip)
{}
CSMPrefs::EnumValue::EnumValue (const char *value)
: mValue (value)
{}
CSMPrefs::EnumValues& CSMPrefs::EnumValues::add (const EnumValues& values)
{
mValues.insert (mValues.end(), values.mValues.begin(), values.mValues.end());
return *this;
}
CSMPrefs::EnumValues& CSMPrefs::EnumValues::add (const EnumValue& value)
{
mValues.push_back (value);
return *this;
}
CSMPrefs::EnumValues& CSMPrefs::EnumValues::add (const std::string& value, const std::string& tooltip)
{
mValues.push_back (EnumValue (value, tooltip));
return *this;
}
CSMPrefs::EnumSetting::EnumSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label, const EnumValue& default_)
: Setting (parent, values, mutex, key, label), mDefault (default_)
{}
CSMPrefs::EnumSetting& CSMPrefs::EnumSetting::setTooltip (const std::string& tooltip)
{
mTooltip = tooltip;
return *this;
}
CSMPrefs::EnumSetting& CSMPrefs::EnumSetting::addValues (const EnumValues& values)
{
mValues.add (values);
return *this;
}
CSMPrefs::EnumSetting& CSMPrefs::EnumSetting::addValue (const EnumValue& value)
{
mValues.add (value);
return *this;
}
CSMPrefs::EnumSetting& CSMPrefs::EnumSetting::addValue (const std::string& value, const std::string& tooltip)
{
mValues.add (value, tooltip);
return *this;
}
std::pair<QWidget *, QWidget *> CSMPrefs::EnumSetting::makeWidgets (QWidget *parent)
{
QLabel *label = new QLabel (QString::fromUtf8 (getLabel().c_str()), parent);
QComboBox *widget = new QComboBox (parent);
int index = 0;
for (int i=0; i<static_cast<int> (mValues.mValues.size()); ++i)
{
if (mDefault.mValue==mValues.mValues[i].mValue)
index = i;
widget->addItem (QString::fromUtf8 (mValues.mValues[i].mValue.c_str()));
if (!mValues.mValues[i].mTooltip.empty())
widget->setItemData (i, QString::fromUtf8 (mValues.mValues[i].mTooltip.c_str()),
Qt::ToolTipRole);
}
widget->setCurrentIndex (index);
if (!mTooltip.empty())
{
QString tooltip = QString::fromUtf8 (mTooltip.c_str());
label->setToolTip (tooltip);
}
connect (widget, SIGNAL (currentIndexChanged (int)), this, SLOT (valueChanged (int)));
return std::make_pair (label, widget);
}
void CSMPrefs::EnumSetting::valueChanged (int value)
{
{
QMutexLocker lock (getMutex());
getValues().setString (getKey(), getParent()->getKey(), mValues.mValues.at (value).mValue);
}
getParent()->getState()->update (*this);
}

@ -0,0 +1,62 @@
#ifndef CSM_PREFS_ENUMSETTING_H
#define CSM_PREFS_ENUMSETTING_H
#include <vector>
#include "setting.hpp"
namespace CSMPrefs
{
struct EnumValue
{
std::string mValue;
std::string mTooltip;
EnumValue (const std::string& value, const std::string& tooltip = "");
EnumValue (const char *value);
};
struct EnumValues
{
std::vector<EnumValue> mValues;
EnumValues& add (const EnumValues& values);
EnumValues& add (const EnumValue& value);
EnumValues& add (const std::string& value, const std::string& tooltip);
};
class EnumSetting : public Setting
{
Q_OBJECT
std::string mTooltip;
EnumValue mDefault;
EnumValues mValues;
public:
EnumSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label,
const EnumValue& default_);
EnumSetting& setTooltip (const std::string& tooltip);
EnumSetting& addValues (const EnumValues& values);
EnumSetting& addValue (const EnumValue& value);
EnumSetting& addValue (const std::string& value, const std::string& tooltip);
/// Return label, input widget.
virtual std::pair<QWidget *, QWidget *> makeWidgets (QWidget *parent);
private slots:
void valueChanged (int value);
};
}
#endif

@ -0,0 +1,74 @@
#include "intsetting.hpp"
#include <limits>
#include <QLabel>
#include <QSpinBox>
#include <QMutexLocker>
#include <components/settings/settings.hpp>
#include "category.hpp"
#include "state.hpp"
CSMPrefs::IntSetting::IntSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label, int default_)
: Setting (parent, values, mutex, key, label), mMin (0), mMax (std::numeric_limits<int>::max()),
mDefault (default_)
{}
CSMPrefs::IntSetting& CSMPrefs::IntSetting::setRange (int min, int max)
{
mMin = min;
mMax = max;
return *this;
}
CSMPrefs::IntSetting& CSMPrefs::IntSetting::setMin (int min)
{
mMin = min;
return *this;
}
CSMPrefs::IntSetting& CSMPrefs::IntSetting::setMax (int max)
{
mMax = max;
return *this;
}
CSMPrefs::IntSetting& CSMPrefs::IntSetting::setTooltip (const std::string& tooltip)
{
mTooltip = tooltip;
return *this;
}
std::pair<QWidget *, QWidget *> CSMPrefs::IntSetting::makeWidgets (QWidget *parent)
{
QLabel *label = new QLabel (QString::fromUtf8 (getLabel().c_str()), parent);
QSpinBox *widget = new QSpinBox (parent);
widget->setRange (mMin, mMax);
widget->setValue (mDefault);
if (!mTooltip.empty())
{
QString tooltip = QString::fromUtf8 (mTooltip.c_str());
label->setToolTip (tooltip);
widget->setToolTip (tooltip);
}
connect (widget, SIGNAL (valueChanged (int)), this, SLOT (valueChanged (int)));
return std::make_pair (label, widget);
}
void CSMPrefs::IntSetting::valueChanged (int value)
{
{
QMutexLocker lock (getMutex());
getValues().setInt (getKey(), getParent()->getKey(), value);
}
getParent()->getState()->update (*this);
}

@ -0,0 +1,40 @@
#ifndef CSM_PREFS_INTSETTING_H
#define CSM_PREFS_INTSETTING_H
#include "setting.hpp"
namespace CSMPrefs
{
class IntSetting : public Setting
{
Q_OBJECT
int mMin;
int mMax;
std::string mTooltip;
int mDefault;
public:
IntSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label, int default_);
// defaults to [0, std::numeric_limits<int>::max()]
IntSetting& setRange (int min, int max);
IntSetting& setMin (int min);
IntSetting& setMax (int max);
IntSetting& setTooltip (const std::string& tooltip);
/// Return label, input widget.
virtual std::pair<QWidget *, QWidget *> makeWidgets (QWidget *parent);
private slots:
void valueChanged (int value);
};
}
#endif

@ -0,0 +1,97 @@
#include "setting.hpp"
#include <QColor>
#include <QMutexLocker>
#include "category.hpp"
#include "state.hpp"
Settings::Manager& CSMPrefs::Setting::getValues()
{
return *mValues;
}
QMutex *CSMPrefs::Setting::getMutex()
{
return mMutex;
}
CSMPrefs::Setting::Setting (Category *parent, Settings::Manager *values, QMutex *mutex,
const std::string& key, const std::string& label)
: QObject (parent->getState()), mParent (parent), mValues (values), mMutex (mutex), mKey (key),
mLabel (label)
{}
CSMPrefs::Setting:: ~Setting() {}
std::pair<QWidget *, QWidget *> CSMPrefs::Setting::makeWidgets (QWidget *parent)
{
return std::pair<QWidget *, QWidget *> (0, 0);
}
const CSMPrefs::Category *CSMPrefs::Setting::getParent() const
{
return mParent;
}
const std::string& CSMPrefs::Setting::getKey() const
{
return mKey;
}
const std::string& CSMPrefs::Setting::getLabel() const
{
return mLabel;
}
int CSMPrefs::Setting::toInt() const
{
QMutexLocker lock (mMutex);
return mValues->getInt (mKey, mParent->getKey());
}
double CSMPrefs::Setting::toDouble() const
{
QMutexLocker lock (mMutex);
return mValues->getFloat (mKey, mParent->getKey());
}
std::string CSMPrefs::Setting::toString() const
{
QMutexLocker lock (mMutex);
return mValues->getString (mKey, mParent->getKey());
}
bool CSMPrefs::Setting::isTrue() const
{
QMutexLocker lock (mMutex);
return mValues->getBool (mKey, mParent->getKey());
}
QColor CSMPrefs::Setting::toColor() const
{
// toString() handles lock
return QColor (QString::fromUtf8 (toString().c_str()));
}
bool CSMPrefs::operator== (const Setting& setting, const std::string& key)
{
std::string fullKey = setting.getParent()->getKey() + "/" + setting.getKey();
return fullKey==key;
}
bool CSMPrefs::operator== (const std::string& key, const Setting& setting)
{
return setting==key;
}
bool CSMPrefs::operator!= (const Setting& setting, const std::string& key)
{
return !(setting==key);
}
bool CSMPrefs::operator!= (const std::string& key, const Setting& setting)
{
return !(key==setting);
}

@ -0,0 +1,74 @@
#ifndef CSM_PREFS_SETTING_H
#define CSM_PREFS_SETTING_H
#include <string>
#include <utility>
#include <QObject>
class QWidget;
class QColor;
class QMutex;
namespace Settings
{
class Manager;
}
namespace CSMPrefs
{
class Category;
class Setting : public QObject
{
Q_OBJECT
Category *mParent;
Settings::Manager *mValues;
QMutex *mMutex;
std::string mKey;
std::string mLabel;
protected:
Settings::Manager& getValues();
QMutex *getMutex();
public:
Setting (Category *parent, Settings::Manager *values, QMutex *mutex, const std::string& key, const std::string& label);
virtual ~Setting();
/// Return label, input widget.
///
/// \note first can be a 0-pointer, which means that the label is part of the input
/// widget.
virtual std::pair<QWidget *, QWidget *> makeWidgets (QWidget *parent);
const Category *getParent() const;
const std::string& getKey() const;
const std::string& getLabel() const;
int toInt() const;
double toDouble() const;
std::string toString() const;
bool isTrue() const;
QColor toColor() const;
};
// note: fullKeys have the format categoryKey/settingKey
bool operator== (const Setting& setting, const std::string& fullKey);
bool operator== (const std::string& fullKey, const Setting& setting);
bool operator!= (const Setting& setting, const std::string& fullKey);
bool operator!= (const std::string& fullKey, const Setting& setting);
}
#endif

@ -0,0 +1,392 @@
#include "state.hpp"
#include <stdexcept>
#include <algorithm>
#include <sstream>
#include "intsetting.hpp"
#include "doublesetting.hpp"
#include "boolsetting.hpp"
#include "coloursetting.hpp"
CSMPrefs::State *CSMPrefs::State::sThis = 0;
void CSMPrefs::State::load()
{
// default settings file
boost::filesystem::path local = mConfigurationManager.getLocalPath() / mConfigFile;
boost::filesystem::path global = mConfigurationManager.getGlobalPath() / mConfigFile;
if (boost::filesystem::exists (local))
mSettings.loadDefault (local.string());
else if (boost::filesystem::exists (global))
mSettings.loadDefault (global.string());
else
throw std::runtime_error ("No default settings file found! Make sure the file \"openmw-cs.cfg\" was properly installed.");
// user settings file
boost::filesystem::path user = mConfigurationManager.getUserConfigPath() / mConfigFile;
if (boost::filesystem::exists (user))
mSettings.loadUser (user.string());
}
void CSMPrefs::State::declare()
{
declareCategory ("Windows");
declareInt ("default-width", "Default window width", 800).
setTooltip ("Newly opened top-level windows will open with this width.").
setMin (80);
declareInt ("default-height", "Default window height", 600).
setTooltip ("Newly opened top-level windows will open with this height.").
setMin (80);
declareBool ("show-statusbar", "Show Status Bar", true).
setTooltip ("If a newly open top level window is showing status bars or not. "
" Note that this does not affect existing windows.");
declareSeparator();
declareBool ("reuse", "Reuse Subviews", true).
setTooltip ("When a new subview is requested and a matching subview already "
" exist, do not open a new subview and use the existing one instead.");
declareInt ("max-subviews", "Maximum number of subviews per top-level window", 256).
setTooltip ("If the maximum number is reached and a new subview is opened "
"it will be placed into a new top-level window.").
setRange (1, 256);
declareBool ("hide-subview", "Hide single subview", false).
setTooltip ("When a view contains only a single subview, hide the subview title "
"bar and if this subview is closed also close the view (unless it is the last "
"view for this document)");
declareInt ("minimum-width", "Minimum subview width", 325).
setTooltip ("Minimum width of subviews.").
setRange (50, 10000);
declareSeparator();
EnumValue scrollbarOnly ("Scrollbar Only", "Simple addition of scrollbars, the view window "
"does not grow automatically.");
declareEnum ("mainwindow-scrollbar", "Horizontal scrollbar mode for main window.", scrollbarOnly).
addValue (scrollbarOnly).
addValue ("Grow Only", "The view window grows as subviews are added. No scrollbars.").
addValue ("Grow then Scroll", "The view window grows. The scrollbar appears once it cannot grow any further.");
declareBool ("grow-limit", "Grow Limit Screen", false).
setTooltip ("When \"Grow then Scroll\" option is selected, the window size grows to"
" the width of the virtual desktop. \nIf this option is selected the the window growth"
"is limited to the current screen.");
declareCategory ("Records");
EnumValue iconAndText ("Icon and Text");
EnumValues recordValues;
recordValues.add (iconAndText).add ("Icon Only").add ("Text Only");
declareEnum ("status-format", "Modification status display format", iconAndText).
addValues (recordValues);
declareEnum ("type-format", "ID type display format", iconAndText).
addValues (recordValues);
declareCategory ("ID Tables");
EnumValue inPlaceEdit ("Edit in Place", "Edit the clicked cell");
EnumValue editRecord ("Edit Record", "Open a dialogue subview for the clicked record");
EnumValue view ("View", "Open a scene subview for the clicked record (not available everywhere)");
EnumValue editRecordAndClose ("Edit Record and Close");
EnumValues doubleClickValues;
doubleClickValues.add (inPlaceEdit).add (editRecord).add (view).add ("Revert").
add ("Delete").add (editRecordAndClose).
add ("View and Close", "Open a scene subview for the clicked record and close the table subview");
declareEnum ("double", "Double Click", inPlaceEdit).addValues (doubleClickValues);
declareEnum ("double-s", "Shift Double Click", editRecord).addValues (doubleClickValues);
declareEnum ("double-c", "Control Double Click", view).addValues (doubleClickValues);
declareEnum ("double-sc", "Shift Control Double Click", editRecordAndClose).addValues (doubleClickValues);
declareSeparator();
EnumValue jumpAndSelect ("Jump and Select", "Scroll new record into view and make it the selection");
declareEnum ("jump-to-added", "Action on adding or cloning a record", jumpAndSelect).
addValue (jumpAndSelect).
addValue ("Jump Only", "Scroll new record into view").
addValue ("No Jump", "No special action");
declareBool ("extended-config",
"Manually specify affected record types for an extended delete/revert", false).
setTooltip ("Delete and revert commands have an extended form that also affects "
"associated records.\n\n"
"If this option is enabled, types of affected records are selected "
"manually before a command execution.\nOtherwise, all associated "
"records are deleted/reverted immediately.");
declareCategory ("ID Dialogues");
declareBool ("toolbar", "Show toolbar", true);
declareCategory ("Reports");
EnumValue actionNone ("None");
EnumValue actionEdit ("Edit", "Open a table or dialogue suitable for addressing the listed report");
EnumValue actionRemove ("Remove", "Remove the report from the report table");
EnumValue actionEditAndRemove ("Edit And Remove", "Open a table or dialogue suitable for addressing the listed report, then remove the report from the report table");
EnumValues reportValues;
reportValues.add (actionNone).add (actionEdit).add (actionRemove).add (actionEditAndRemove);
declareEnum ("double", "Double Click", actionEdit).addValues (reportValues);
declareEnum ("double-s", "Shift Double Click", actionRemove).addValues (reportValues);
declareEnum ("double-c", "Control Double Click", actionEditAndRemove).addValues (reportValues);
declareEnum ("double-sc", "Shift Control Double Click", actionNone).addValues (reportValues);
declareCategory ("Search & Replace");
declareInt ("char-before", "Characters before search string", 10).
setTooltip ("Maximum number of character to display in search result before the searched text");
declareInt ("char-after", "Characters after search string", 10).
setTooltip ("Maximum number of character to display in search result after the searched text");
declareBool ("auto-delete", "Delete row from result table after a successful replace", true);
declareCategory ("Scripts");
declareBool ("show-linenum", "Show Line Numbers", true).
setTooltip ("Show line numbers to the left of the script editor window."
"The current row and column numbers of the text cursor are shown at the bottom.");
declareBool ("mono-font", "Use monospace font", true);
EnumValue warningsNormal ("Normal", "Report warnings as warning");
declareEnum ("warnings", "Warning Mode", warningsNormal).
addValue ("Ignore", "Do not report warning").
addValue (warningsNormal).
addValue ("Strcit", "Promote warning to an error");
declareBool ("toolbar", "Show toolbar", true);
declareInt ("compile-delay", "Delay between updating of source errors", 100).
setTooltip ("Delay in milliseconds").
setRange (0, 10000);
declareInt ("error-height", "Initial height of the error panel", 100).
setRange (100, 10000);
declareSeparator();
declareColour ("colour-int", "Highlight Colour: Integer Literals", QColor ("darkmagenta"));
declareColour ("colour-float", "Highlight Colour: Float Literals", QColor ("magenta"));
declareColour ("colour-name", "Highlight Colour: Names", QColor ("grey"));
declareColour ("colour-keyword", "Highlight Colour: Keywords", QColor ("red"));
declareColour ("colour-special", "Highlight Colour: Special Characters", QColor ("darkorange"));
declareColour ("colour-comment", "Highlight Colour: Comments", QColor ("green"));
declareColour ("colour-id", "Highlight Colour: IDs", QColor ("blue"));
declareCategory ("General Input");
declareBool ("cycle", "Cyclic next/previous", false).
setTooltip ("When using next/previous functions at the last/first item of a "
"list go to the first/last item");
declareCategory ("3D Scene Input");
EnumValue left ("Left Mouse-Button");
EnumValue cLeft ("Ctrl-Left Mouse-Button");
EnumValue right ("Right Mouse-Button");
EnumValue cRight ("Ctrl-Right Mouse-Button");
EnumValue middle ("Middle Mouse-Button");
EnumValue cMiddle ("Ctrl-Middle Mouse-Button");
EnumValues inputButtons;
inputButtons.add (left).add (cLeft).add (right).add (cRight).add (middle).add (cMiddle);
declareEnum ("p-navi", "Primary Camera Navigation Button", left).addValues (inputButtons);
declareEnum ("s-navi", "Secondary Camera Navigation Button", cLeft).addValues (inputButtons);
declareEnum ("p-edit", "Primary Editing Button", right).addValues (inputButtons);
declareEnum ("s-edit", "Secondary Editing Button", cRight).addValues (inputButtons);
declareEnum ("p-select", "Primary Selection Button", middle).addValues (inputButtons);
declareEnum ("s-select", "Secondary Selection Button", cMiddle).addValues (inputButtons);
declareSeparator();
declareBool ("context-select", "Context Sensitive Selection", false);
declareDouble ("drag-factor", "Mouse sensitivity during drag operations", 1.0).
setRange (0.001, 100.0);
declareDouble ("drag-wheel-factor", "Mouse wheel sensitivity during drag operations", 1.0).
setRange (0.001, 100.0);
declareDouble ("drag-shift-factor",
"Shift-acceleration factor during drag operations", 4.0).
setTooltip ("Acceleration factor during drag operations while holding down shift").
setRange (0.001, 100.0);
declareCategory ("Tooltips");
declareBool ("scene", "Show Tooltips in 3D scenes", true);
declareBool ("scene-hide-basic", "Hide basic 3D scenes tooltips", false);
declareInt ("scene-delay", "Tooltip delay in milliseconds", 500).
setMin (1);
}
void CSMPrefs::State::declareCategory (const std::string& key)
{
std::map<std::string, Category>::iterator iter = mCategories.find (key);
if (iter!=mCategories.end())
{
mCurrentCategory = iter;
}
else
{
mCurrentCategory =
mCategories.insert (std::make_pair (key, Category (this, key))).first;
}
}
CSMPrefs::IntSetting& CSMPrefs::State::declareInt (const std::string& key,
const std::string& label, int default_)
{
if (mCurrentCategory==mCategories.end())
throw std::logic_error ("no category for setting");
std::ostringstream stream;
stream << default_;
setDefault (key, stream.str());
default_ = mSettings.getInt (key, mCurrentCategory->second.getKey());
CSMPrefs::IntSetting *setting =
new CSMPrefs::IntSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label,
default_);
mCurrentCategory->second.addSetting (setting);
return *setting;
}
CSMPrefs::DoubleSetting& CSMPrefs::State::declareDouble (const std::string& key,
const std::string& label, double default_)
{
if (mCurrentCategory==mCategories.end())
throw std::logic_error ("no category for setting");
std::ostringstream stream;
stream << default_;
setDefault (key, stream.str());
default_ = mSettings.getFloat (key, mCurrentCategory->second.getKey());
CSMPrefs::DoubleSetting *setting =
new CSMPrefs::DoubleSetting (&mCurrentCategory->second, &mSettings, &mMutex,
key, label, default_);
mCurrentCategory->second.addSetting (setting);
return *setting;
}
CSMPrefs::BoolSetting& CSMPrefs::State::declareBool (const std::string& key,
const std::string& label, bool default_)
{
if (mCurrentCategory==mCategories.end())
throw std::logic_error ("no category for setting");
setDefault (key, default_ ? "true" : "false");
default_ = mSettings.getBool (key, mCurrentCategory->second.getKey());
CSMPrefs::BoolSetting *setting =
new CSMPrefs::BoolSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label,
default_);
mCurrentCategory->second.addSetting (setting);
return *setting;
}
CSMPrefs::EnumSetting& CSMPrefs::State::declareEnum (const std::string& key,
const std::string& label, EnumValue default_)
{
if (mCurrentCategory==mCategories.end())
throw std::logic_error ("no category for setting");
setDefault (key, default_.mValue);
default_.mValue = mSettings.getString (key, mCurrentCategory->second.getKey());
CSMPrefs::EnumSetting *setting =
new CSMPrefs::EnumSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label,
default_);
mCurrentCategory->second.addSetting (setting);
return *setting;
}
CSMPrefs::ColourSetting& CSMPrefs::State::declareColour (const std::string& key,
const std::string& label, QColor default_)
{
if (mCurrentCategory==mCategories.end())
throw std::logic_error ("no category for setting");
setDefault (key, default_.name().toUtf8().data());
default_.setNamedColor (QString::fromUtf8 (mSettings.getString (key, mCurrentCategory->second.getKey()).c_str()));
CSMPrefs::ColourSetting *setting =
new CSMPrefs::ColourSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label,
default_);
mCurrentCategory->second.addSetting (setting);
return *setting;
}
void CSMPrefs::State::declareSeparator()
{
if (mCurrentCategory==mCategories.end())
throw std::logic_error ("no category for setting");
CSMPrefs::Setting *setting =
new CSMPrefs::Setting (&mCurrentCategory->second, &mSettings, &mMutex, "", "");
mCurrentCategory->second.addSetting (setting);
}
void CSMPrefs::State::setDefault (const std::string& key, const std::string& default_)
{
Settings::CategorySetting fullKey (mCurrentCategory->second.getKey(), key);
Settings::CategorySettingValueMap::iterator iter =
mSettings.mDefaultSettings.find (fullKey);
if (iter==mSettings.mDefaultSettings.end())
mSettings.mDefaultSettings.insert (std::make_pair (fullKey, default_));
}
CSMPrefs::State::State (const Files::ConfigurationManager& configurationManager)
: mConfigFile ("openmw-cs.cfg"), mConfigurationManager (configurationManager),
mCurrentCategory (mCategories.end())
{
if (sThis)
throw std::logic_error ("An instance of CSMPRefs::State already exists");
load();
declare();
sThis = this;
}
CSMPrefs::State::~State()
{
sThis = 0;
}
void CSMPrefs::State::save()
{
boost::filesystem::path user = mConfigurationManager.getUserConfigPath() / mConfigFile;
mSettings.saveUser (user.string());
}
CSMPrefs::State::Iterator CSMPrefs::State::begin()
{
return mCategories.begin();
}
CSMPrefs::State::Iterator CSMPrefs::State::end()
{
return mCategories.end();
}
CSMPrefs::Category& CSMPrefs::State::operator[] (const std::string& key)
{
Iterator iter = mCategories.find (key);
if (iter==mCategories.end())
throw std::logic_error ("Invalid user settings category: " + key);
return iter->second;
}
void CSMPrefs::State::update (const Setting& setting)
{
emit (settingChanged (&setting));
}
CSMPrefs::State& CSMPrefs::State::get()
{
if (!sThis)
throw std::logic_error ("No instance of CSMPrefs::State");
return *sThis;
}
CSMPrefs::State& CSMPrefs::get()
{
return State::get();
}

@ -0,0 +1,105 @@
#ifndef CSV_PREFS_STATE_H
#define CSM_PREFS_STATE_H
#include <map>
#include <string>
#include <QObject>
#include <QMutex>
#ifndef Q_MOC_RUN
#include <components/files/configurationmanager.hpp>
#endif
#include <components/settings/settings.hpp>
#include "category.hpp"
#include "setting.hpp"
#include "enumsetting.hpp"
class QColor;
namespace CSMPrefs
{
class IntSetting;
class DoubleSetting;
class BoolSetting;
class ColourSetting;
/// \brief User settings state
///
/// \note Access to the user settings is thread-safe once all declarations and loading has
/// been completed.
class State : public QObject
{
Q_OBJECT
static State *sThis;
public:
typedef std::map<std::string, Category> Collection;
typedef Collection::iterator Iterator;
private:
const std::string mConfigFile;
const Files::ConfigurationManager& mConfigurationManager;
Settings::Manager mSettings;
Collection mCategories;
Iterator mCurrentCategory;
QMutex mMutex;
// not implemented
State (const State&);
State& operator= (const State&);
private:
void load();
void declare();
void declareCategory (const std::string& key);
IntSetting& declareInt (const std::string& key, const std::string& label, int default_);
DoubleSetting& declareDouble (const std::string& key, const std::string& label, double default_);
BoolSetting& declareBool (const std::string& key, const std::string& label, bool default_);
EnumSetting& declareEnum (const std::string& key, const std::string& label, EnumValue default_);
ColourSetting& declareColour (const std::string& key, const std::string& label, QColor default_);
void declareSeparator();
void setDefault (const std::string& key, const std::string& default_);
public:
State (const Files::ConfigurationManager& configurationManager);
~State();
void save();
Iterator begin();
Iterator end();
Category& operator[](const std::string& key);
void update (const Setting& setting);
static State& get();
signals:
void settingChanged (const CSMPrefs::Setting *setting);
};
// convenience function
State& get();
}
#endif

@ -1,128 +0,0 @@
#include "connector.hpp"
#include "../../view/settings/view.hpp"
#include "../../view/settings/page.hpp"
CSMSettings::Connector::Connector(CSVSettings::View *master,
QObject *parent)
: QObject(parent), mMasterView (master)
{}
void CSMSettings::Connector::addSlaveView (CSVSettings::View *view,
QList <QStringList> &masterProxyValues)
{
mSlaveViews.append (view);
mProxyListMap[view->viewKey()].append (masterProxyValues);
}
QList <QStringList> CSMSettings::Connector::getSlaveViewValues() const
{
QList <QStringList> list;
foreach (const CSVSettings::View *view, mSlaveViews)
list.append (view->selectedValues());
return list;
}
bool CSMSettings::Connector::proxyListsMatch (
const QList <QStringList> &list1,
const QList <QStringList> &list2) const
{
bool success = true;
for (int i = 0; i < list1.size(); i++)
{
success = stringListsMatch (list1.at(i), list2.at(i));
if (!success)
break;
}
return success;
}
void CSMSettings::Connector::slotUpdateMaster() const
{
//list of the current values for each slave.
QList <QStringList> slaveValueList = getSlaveViewValues();
int masterColumn = -1;
/*
* A row in the master view is one of the values in the
* master view's data model. This corresponds directly to the number of
* values in a proxy list contained in the ProxyListMap member.
* Thus, we iterate each "column" in the master proxy list
* (one for each vlaue in the master. Each column represents
* one master value's corresponding list of slave values. We examine
* each master value's list, comparing it to the current slave value list,
* stopping when we find a match using proxyListsMatch().
*
* If no match is found, clear the master view's value
*/
for (int i = 0; i < mMasterView->rowCount(); i++)
{
QList <QStringList> proxyValueList;
foreach (const QString &settingKey, mProxyListMap.keys())
{
// append the proxy value list stored in the i'th column
// for each setting key. A setting key is the id of the setting
// in page.name format.
proxyValueList.append (mProxyListMap.value(settingKey).at(i));
}
if (proxyListsMatch (slaveValueList, proxyValueList))
{
masterColumn = i;
break;
}
}
QString masterValue = mMasterView->value (masterColumn);
mMasterView->setSelectedValue (masterValue);
}
void CSMSettings::Connector::slotUpdateSlaves() const
{
int row = mMasterView->currentIndex();
if (row == -1)
return;
//iterate the proxy lists for the chosen master index
//and pass the list to each slave for updating
for (int i = 0; i < mSlaveViews.size(); i++)
{
QList <QStringList> proxyList =
mProxyListMap.value(mSlaveViews.at(i)->viewKey());
mSlaveViews.at(i)->setSelectedValues (proxyList.at(row));
}
}
bool CSMSettings::Connector::stringListsMatch (
const QStringList &list1,
const QStringList &list2) const
{
//returns a "sloppy" match, verifying that each list contains all the same
//items, though not necessarily in the same order.
if (list1.size() != list2.size())
return false;
QStringList tempList(list2);
//iterate each value in the list, removing one occurrence of the value in
//the other list. If no corresponding value is found, test fails
foreach (const QString &value, list1)
{
if (!tempList.contains(value))
return false;
tempList.removeOne(value);
}
return true;
}

@ -1,67 +0,0 @@
#ifndef CSMSETTINGS_CONNECTOR_HPP
#define CSMSETTINGS_CONNECTOR_HPP
#include <QObject>
#include <QList>
#include <QMap>
#include <QStringList>
#include "support.hpp"
namespace CSVSettings {
class View;
}
namespace CSMSettings {
class Connector : public QObject
{
Q_OBJECT
CSVSettings::View *mMasterView;
///map using the view pointer as a key to it's index value
QList <CSVSettings::View *> mSlaveViews;
///list of proxy values for each master value.
///value list order is indexed to the master value index.
QMap < QString, QList <QStringList> > mProxyListMap;
public:
explicit Connector(CSVSettings::View *master,
QObject *parent = 0);
///Set the view which acts as a proxy for other setting views
void setMasterView (CSVSettings::View *view);
///Add a view to be updated / update to the master
void addSlaveView (CSVSettings::View *view,
QList <QStringList> &masterProxyValues);
private:
///loosely matches lists of proxy values across registered slaves
///against a proxy value list for a given master value
bool proxyListsMatch (const QList <QStringList> &list1,
const QList <QStringList> &list2) const;
///loosely matches two string lists
bool stringListsMatch (const QStringList &list1,
const QStringList &list2) const;
///retrieves current values of registered slave views
QList <QStringList> getSlaveViewValues() const;
public slots:
///updates slave views with proxy values associated with current
///master value
void slotUpdateSlaves() const;
///updates master value associated with the currently selected
///slave values, if applicable.
void slotUpdateMaster() const;
};
}
#endif // CSMSETTINGS_CONNECTOR_HPP

@ -1,414 +0,0 @@
#include "setting.hpp"
#include "support.hpp"
CSMSettings::Setting::Setting(SettingType typ, const QString &settingName,
const QString &pageName, const QString& label)
: mIsEditorSetting (true)
{
buildDefaultSetting();
int settingType = static_cast <int> (typ);
//even-numbered setting types are multi-valued
if ((settingType % 2) == 0)
setProperty (Property_IsMultiValue, QVariant(true).toString());
//view type is related to setting type by an order of magnitude
setProperty (Property_SettingType, QVariant (settingType).toString());
setProperty (Property_Page, pageName);
setProperty (Property_Name, settingName);
setProperty (Property_Label, label.isEmpty() ? settingName : label);
}
void CSMSettings::Setting::buildDefaultSetting()
{
int arrLen = sizeof(sPropertyDefaults) / sizeof (*sPropertyDefaults);
for (int i = 0; i < arrLen; i++)
{
QStringList propertyList;
if (i <Property_DefaultValues)
propertyList.append (sPropertyDefaults[i]);
mProperties.append (propertyList);
}
}
void CSMSettings::Setting::addProxy (const Setting *setting,
const QStringList &vals)
{
if (serializable())
setSerializable (false);
QList <QStringList> list;
foreach (const QString &val, vals)
list << (QStringList() << val);
mProxies [setting->page() + '/' + setting->name()] = list;
}
void CSMSettings::Setting::addProxy (const Setting *setting,
const QList <QStringList> &list)
{
if (serializable())
setProperty (Property_Serializable, false);
mProxies [setting->page() + '/' + setting->name()] = list;
}
void CSMSettings::Setting::setColumnSpan (int value)
{
setProperty (Property_ColumnSpan, value);
}
int CSMSettings::Setting::columnSpan() const
{
return property (Property_ColumnSpan).at(0).toInt();
}
void CSMSettings::Setting::setDeclaredValues (QStringList list)
{
setProperty (Property_DeclaredValues, list);
}
QStringList CSMSettings::Setting::declaredValues() const
{
return property (Property_DeclaredValues);
}
QStringList CSMSettings::Setting::property (SettingProperty prop) const
{
if (prop >= mProperties.size())
return QStringList();
return mProperties.at(prop);
}
void CSMSettings::Setting::setDefaultValue (int value)
{
setDefaultValues (QStringList() << QVariant (value).toString());
}
void CSMSettings::Setting::setDefaultValue (double value)
{
setDefaultValues (QStringList() << QVariant (value).toString());
}
void CSMSettings::Setting::setDefaultValue (const QString &value)
{
setDefaultValues (QStringList() << value);
}
void CSMSettings::Setting::setDefaultValues (const QStringList &values)
{
setProperty (Property_DefaultValues, values);
}
QStringList CSMSettings::Setting::defaultValues() const
{
return property (Property_DefaultValues);
}
void CSMSettings::Setting::setDelimiter (const QString &value)
{
setProperty (Property_Delimiter, value);
}
QString CSMSettings::Setting::delimiter() const
{
return property (Property_Delimiter).at(0);
}
void CSMSettings::Setting::setEditorSetting(bool state)
{
mIsEditorSetting = true;
}
bool CSMSettings::Setting::isEditorSetting() const
{
return mIsEditorSetting;
}
void CSMSettings::Setting::setIsMultiLine (bool state)
{
setProperty (Property_IsMultiLine, state);
}
bool CSMSettings::Setting::isMultiLine() const
{
return (property (Property_IsMultiLine).at(0) == "true");
}
void CSMSettings::Setting::setIsMultiValue (bool state)
{
setProperty (Property_IsMultiValue, state);
}
bool CSMSettings::Setting::isMultiValue() const
{
return (property (Property_IsMultiValue).at(0) == "true");
}
const CSMSettings::ProxyValueMap &CSMSettings::Setting::proxyLists() const
{
return mProxies;
}
void CSMSettings::Setting::setSerializable (bool state)
{
setProperty (Property_Serializable, state);
}
bool CSMSettings::Setting::serializable() const
{
return (property (Property_Serializable).at(0) == "true");
}
void CSMSettings::Setting::setSpecialValueText(const QString &text)
{
setProperty (Property_SpecialValueText, text);
}
QString CSMSettings::Setting::specialValueText() const
{
return property (Property_SpecialValueText).at(0);
}
void CSMSettings::Setting::setName (const QString &value)
{
setProperty (Property_Name, value);
}
QString CSMSettings::Setting::name() const
{
return property (Property_Name).at(0);
}
void CSMSettings::Setting::setPage (const QString &value)
{
setProperty (Property_Page, value);
}
QString CSMSettings::Setting::page() const
{
return property (Property_Page).at(0);
}
void CSMSettings::Setting::setStyleSheet (const QString &value)
{
setProperty (Property_StyleSheet, value);
}
QString CSMSettings::Setting::styleSheet() const
{
return property (Property_StyleSheet).at(0);
}
void CSMSettings::Setting::setPrefix (const QString &value)
{
setProperty (Property_Prefix, value);
}
QString CSMSettings::Setting::prefix() const
{
return property (Property_Prefix).at(0);
}
void CSMSettings::Setting::setRowSpan (const int value)
{
setProperty (Property_RowSpan, value);
}
int CSMSettings::Setting::rowSpan () const
{
return property (Property_RowSpan).at(0).toInt();
}
void CSMSettings::Setting::setSingleStep (int value)
{
setProperty (Property_SingleStep, value);
}
void CSMSettings::Setting::setSingleStep (double value)
{
setProperty (Property_SingleStep, value);
}
QString CSMSettings::Setting::singleStep() const
{
return property (Property_SingleStep).at(0);
}
void CSMSettings::Setting::setSuffix (const QString &value)
{
setProperty (Property_Suffix, value);
}
QString CSMSettings::Setting::suffix() const
{
return property (Property_Suffix).at(0);
}
void CSMSettings::Setting::setTickInterval (int value)
{
setProperty (Property_TickInterval, value);
}
int CSMSettings::Setting::tickInterval () const
{
return property (Property_TickInterval).at(0).toInt();
}
void CSMSettings::Setting::setTicksAbove (bool state)
{
setProperty (Property_TicksAbove, state);
}
bool CSMSettings::Setting::ticksAbove() const
{
return (property (Property_TicksAbove).at(0) == "true");
}
void CSMSettings::Setting::setTicksBelow (bool state)
{
setProperty (Property_TicksBelow, state);
}
bool CSMSettings::Setting::ticksBelow() const
{
return (property (Property_TicksBelow).at(0) == "true");
}
void CSMSettings::Setting::setType (int settingType)
{
setProperty (Property_SettingType, settingType);
}
CSMSettings::SettingType CSMSettings::Setting::type() const
{
return static_cast <CSMSettings::SettingType> ( property (
Property_SettingType).at(0).toInt());
}
void CSMSettings::Setting::setRange (int min, int max)
{
setProperty (Property_Minimum, min);
setProperty (Property_Maximum, max);
}
void CSMSettings::Setting::setRange (double min, double max)
{
setProperty (Property_Minimum, min);
setProperty (Property_Maximum, max);
}
QString CSMSettings::Setting::maximum() const
{
return property (Property_Maximum).at(0);
}
QString CSMSettings::Setting::minimum() const
{
return property (Property_Minimum).at(0);
}
CSVSettings::ViewType CSMSettings::Setting::viewType() const
{
return static_cast <CSVSettings::ViewType> ( property (
Property_SettingType).at(0).toInt() / 10);
}
void CSMSettings::Setting::setViewColumn (int value)
{
setProperty (Property_ViewColumn, value);
}
int CSMSettings::Setting::viewColumn() const
{
return property (Property_ViewColumn).at(0).toInt();
}
void CSMSettings::Setting::setViewLocation (int row, int column)
{
setViewRow (row);
setViewColumn (column);
}
void CSMSettings::Setting::setViewRow (int value)
{
setProperty (Property_ViewRow, value);
}
int CSMSettings::Setting::viewRow() const
{
return property (Property_ViewRow).at(0).toInt();
}
void CSMSettings::Setting::setWidgetWidth (int value)
{
setProperty (Property_WidgetWidth, value);
}
int CSMSettings::Setting::widgetWidth() const
{
return property (Property_WidgetWidth).at(0).toInt();
}
void CSMSettings::Setting::setWrapping (bool state)
{
setProperty (Property_Wrapping, state);
}
bool CSMSettings::Setting::wrapping() const
{
return (property (Property_Wrapping).at(0) == "true");
}
void CSMSettings::Setting::setLabel (const QString& label)
{
setProperty (Property_Label, label);
}
QString CSMSettings::Setting::getLabel() const
{
return property (Property_Label).at (0);
}
void CSMSettings::Setting::setToolTip (const QString& toolTip)
{
setProperty (Property_ToolTip, toolTip);
}
QString CSMSettings::Setting::getToolTip() const
{
return property (Property_ToolTip).at (0);
}
void CSMSettings::Setting::setProperty (SettingProperty prop, bool value)
{
setProperty (prop, QStringList() << QVariant (value).toString());
}
void CSMSettings::Setting::setProperty (SettingProperty prop, int value)
{
setProperty (prop, QStringList() << QVariant (value).toString());
}
void CSMSettings::Setting::setProperty (SettingProperty prop, double value)
{
setProperty (prop, QStringList() << QVariant (value).toString());
}
void CSMSettings::Setting::setProperty (SettingProperty prop,
const QString &value)
{
setProperty (prop, QStringList() << value);
}
void CSMSettings::Setting::setProperty (SettingProperty prop,
const QStringList &value)
{
if (prop < mProperties.size())
mProperties.replace (prop, value);
}

@ -1,159 +0,0 @@
#ifndef CSMSETTINGS_SETTING_HPP
#define CSMSETTINGS_SETTING_HPP
#include <QStringList>
#include <QMap>
#include "support.hpp"
namespace CSMSettings
{
//QString is the setting id in the form of "page/name"
//QList is a list of stringlists of proxy values.
//Order is important! Proxy stringlists are matched against
//master values by their position in the QList.
typedef QMap <QString, QList <QStringList> > ProxyValueMap;
///Setting class is the interface for the User Settings. It contains
///a great deal of boiler plate to provide the core API functions, as
///well as the property() functions which use enumeration to be iterable.
///This makes the Setting class capable of being manipulated by script.
///See CSMSettings::support.hpp for enumerations / string values.
class Setting
{
QList <QStringList> mProperties;
QStringList mDefaults;
bool mIsEditorSetting;
ProxyValueMap mProxies;
public:
Setting(SettingType typ, const QString &settingName,
const QString &pageName, const QString& label = "");
void addProxy (const Setting *setting, const QStringList &vals);
void addProxy (const Setting *setting, const QList <QStringList> &list);
const QList <QStringList> &properties() const { return mProperties; }
const ProxyValueMap &proxies() const { return mProxies; }
void setColumnSpan (int value);
int columnSpan() const;
void setDeclaredValues (QStringList list);
QStringList declaredValues() const;
void setDefaultValue (int value);
void setDefaultValue (double value);
void setDefaultValue (const QString &value);
void setDefaultValues (const QStringList &values);
QStringList defaultValues() const;
void setDelimiter (const QString &value);
QString delimiter() const;
void setEditorSetting (bool state);
bool isEditorSetting() const;
void setIsMultiLine (bool state);
bool isMultiLine() const;
void setIsMultiValue (bool state);
bool isMultiValue() const;
void setMask (const QString &value);
QString mask() const;
void setRange (int min, int max);
void setRange (double min, double max);
QString maximum() const;
QString minimum() const;
void setName (const QString &value);
QString name() const;
void setPage (const QString &value);
QString page() const;
void setStyleSheet (const QString &value);
QString styleSheet() const;
void setPrefix (const QString &value);
QString prefix() const;
void setRowSpan (const int value);
int rowSpan() const;
const ProxyValueMap &proxyLists() const;
void setSerializable (bool state);
bool serializable() const;
void setSpecialValueText (const QString &text);
QString specialValueText() const;
void setSingleStep (int value);
void setSingleStep (double value);
QString singleStep() const;
void setSuffix (const QString &value);
QString suffix() const;
void setTickInterval (int value);
int tickInterval() const;
void setTicksAbove (bool state);
bool ticksAbove() const;
void setTicksBelow (bool state);
bool ticksBelow() const;
void setViewColumn (int value);
int viewColumn() const;
void setViewLocation (int row = -1, int column = -1);
void setViewRow (int value);
int viewRow() const;
void setType (int settingType);
CSMSettings::SettingType type() const;
CSVSettings::ViewType viewType() const;
void setWrapping (bool state);
bool wrapping() const;
void setWidgetWidth (int value);
int widgetWidth() const;
/// This is the text the user gets to see.
void setLabel (const QString& label);
QString getLabel() const;
void setToolTip (const QString& toolTip);
QString getToolTip() const;
///returns the specified property value
QStringList property (SettingProperty prop) const;
///boilerplate code to convert setting values of common types
void setProperty (SettingProperty prop, bool value);
void setProperty (SettingProperty prop, int value);
void setProperty (SettingProperty prop, double value);
void setProperty (SettingProperty prop, const QString &value);
void setProperty (SettingProperty prop, const QStringList &value);
void addProxy (Setting* setting,
QMap <QString, QStringList> &proxyMap);
protected:
void buildDefaultSetting();
};
}
#endif // CSMSETTINGS_SETTING_HPP

@ -1,149 +0,0 @@
#ifndef SETTING_SUPPORT_HPP
#define SETTING_SUPPORT_HPP
#include <Qt>
#include <QPair>
#include <QList>
#include <QVariant>
#include <QStringList>
//Enums
namespace CSMSettings
{
///Enumerated properties for scripting
enum SettingProperty
{
Property_Name = 0,
Property_Page = 1,
Property_SettingType = 2,
Property_IsMultiValue = 3,
Property_IsMultiLine = 4,
Property_WidgetWidth = 5,
Property_ViewRow = 6,
Property_ViewColumn = 7,
Property_Delimiter = 8,
Property_Serializable = 9,
Property_ColumnSpan = 10,
Property_RowSpan = 11,
Property_Minimum = 12,
Property_Maximum = 13,
Property_SpecialValueText = 14,
Property_Prefix = 15,
Property_Suffix = 16,
Property_SingleStep = 17,
Property_Wrapping = 18,
Property_TickInterval = 19,
Property_TicksAbove = 20,
Property_TicksBelow = 21,
Property_StyleSheet = 22,
Property_Label = 23,
Property_ToolTip = 24,
//Stringlists should always be the last items
Property_DefaultValues = 25,
Property_DeclaredValues = 26,
Property_DefinedValues = 27,
Property_Proxies = 28
};
///Basic setting widget types.
enum SettingType
{
/*
* 0 - 9 - Boolean widgets
* 10-19 - List widgets
* 21-29 - Range widgets
* 31-39 - Text widgets
*
* Each range corresponds to a View_Type enum by a factor of 10.
*
* Even-numbered values are single-value widgets
* Odd-numbered values are multi-valued widgets
*/
Type_CheckBox = 0,
Type_RadioButton = 1,
Type_ListView = 10,
Type_ComboBox = 11,
Type_SpinBox = 21,
Type_DoubleSpinBox = 23,
Type_Slider = 25,
Type_Dial = 27,
Type_TextArea = 30,
Type_LineEdit = 31,
Type_Undefined = 40
};
}
namespace CSVSettings
{
///Categorical view types which encompass the setting widget types
enum ViewType
{
ViewType_Boolean = 0,
ViewType_List = 1,
ViewType_Range = 2,
ViewType_Text = 3,
ViewType_Undefined = 4
};
}
namespace CSMSettings
{
///used to construct default settings in the Setting class
struct PropertyDefaultValues
{
int id;
QString name;
QVariant value;
};
///strings which correspond to setting values. These strings represent
///the script language keywords which would be used to declare setting
///views for 3rd party addons
const QString sPropertyNames[] =
{
"name", "page", "setting_type", "is_multi_value",
"is_multi_line", "widget_width", "view_row", "view_column", "delimiter",
"is_serializable","column_span", "row_span", "minimum", "maximum",
"special_value_text", "prefix", "suffix", "single_step", "wrapping",
"tick_interval", "ticks_above", "ticks_below", "stylesheet",
"defaults", "declarations", "definitions", "proxies"
};
///Default values for a setting. Used in setting creation.
const QString sPropertyDefaults[] =
{
"", //name
"", //page
"40", //setting type
"false", //multivalue
"false", //multiline
"7", //widget width
"-1", //view row
"-1", //view column
",", //delimiter
"true", //serialized
"1", //column span
"1", //row span
"0", //value range
"0", //value minimum
"0", //value maximum
"", //special text
"", //prefix
"", //suffix
"false", //wrapping
"1", //tick interval
"false", //ticks above
"true", //ticks below
"", //StyleSheet
"", //default values
"", //declared values
"", //defined values
"" //proxy values
};
}
#endif // VIEW_SUPPORT_HPP

@ -1,801 +0,0 @@
#include "usersettings.hpp"
#include <QSettings>
#include <QFile>
#include <components/files/configurationmanager.hpp>
#include <components/settings/settings.hpp>
#include <boost/version.hpp>
#include "setting.hpp"
#include "support.hpp"
#include <QTextCodec>
#include <QDebug>
/**
* Workaround for problems with whitespaces in paths in older versions of Boost library
*/
#if (BOOST_VERSION <= 104600)
namespace boost
{
template<>
inline boost::filesystem::path lexical_cast<boost::filesystem::path, std::string>(const std::string& arg)
{
return boost::filesystem::path(arg);
}
} /* namespace boost */
#endif /* (BOOST_VERSION <= 104600) */
CSMSettings::UserSettings *CSMSettings::UserSettings::sUserSettingsInstance = 0;
CSMSettings::UserSettings::UserSettings (const Files::ConfigurationManager& configurationManager)
: mCfgMgr (configurationManager)
, mSettingDefinitions(NULL)
{
assert(!sUserSettingsInstance);
sUserSettingsInstance = this;
buildSettingModelDefaults();
}
void CSMSettings::UserSettings::buildSettingModelDefaults()
{
/*
declareSection ("3d-render", "3D Rendering");
{
Setting *farClipDist = createSetting (Type_DoubleSpinBox, "far-clip-distance", "Far clipping distance");
farClipDist->setDefaultValue (300000);
farClipDist->setRange (0, 1000000);
farClipDist->setToolTip ("The maximum distance objects are still rendered at.");
QString defaultValue = "None";
Setting *antialiasing = createSetting (Type_ComboBox, "antialiasing", "Antialiasing");
antialiasing->setDeclaredValues (QStringList()
<< defaultValue << "MSAA 2" << "MSAA 4" << "MSAA 8" << "MSAA 16");
antialiasing->setDefaultValue (defaultValue);
}
*/
/*
declareSection ("scene-input", "Scene Input");
{
Setting *fastFactor = createSetting (Type_SpinBox, "fast-factor",
"Fast movement factor");
fastFactor->setDefaultValue (4);
fastFactor->setRange (1, 100);
fastFactor->setToolTip (
"Factor by which movement is speed up while the shift key is held down.");
}
*/
declareSection ("window", "Window");
{
Setting *preDefined = createSetting (Type_ComboBox, "pre-defined",
"Default window size");
preDefined->setEditorSetting (false);
preDefined->setDeclaredValues (
QStringList() << "640 x 480" << "800 x 600" << "1024 x 768" << "1440 x 900");
preDefined->setViewLocation (1, 1);
preDefined->setColumnSpan (2);
preDefined->setToolTip ("Newly opened top-level windows will open with this size "
"(picked from a list of pre-defined values)");
Setting *width = createSetting (Type_LineEdit, "default-width",
"Default window width");
width->setDefaultValues (QStringList() << "1024");
width->setViewLocation (2, 1);
width->setColumnSpan (1);
width->setToolTip ("Newly opened top-level windows will open with this width.");
preDefined->addProxy (width, QStringList() << "640" << "800" << "1024" << "1440");
Setting *height = createSetting (Type_LineEdit, "default-height",
"Default window height");
height->setDefaultValues (QStringList() << "768");
height->setViewLocation (2, 2);
height->setColumnSpan (1);
height->setToolTip ("Newly opened top-level windows will open with this height.");
preDefined->addProxy (height, QStringList() << "480" << "600" << "768" << "900");
Setting *reuse = createSetting (Type_CheckBox, "reuse", "Reuse Subviews");
reuse->setDefaultValue ("true");
reuse->setToolTip ("When a new subview is requested and a matching subview already "
" exist, do not open a new subview and use the existing one instead.");
Setting *statusBar = createSetting (Type_CheckBox, "show-statusbar", "Show Status Bar");
statusBar->setDefaultValue ("true");
statusBar->setToolTip ("If a newly open top level window is showing status bars or not. "
" Note that this does not affect existing windows.");
Setting *maxSubView = createSetting (Type_SpinBox, "max-subviews",
"Maximum number of subviews per top-level window");
maxSubView->setDefaultValue (256);
maxSubView->setRange (1, 256);
maxSubView->setToolTip ("If the maximum number is reached and a new subview is opened "
"it will be placed into a new top-level window.");
Setting *hide = createSetting (Type_CheckBox, "hide-subview", "Hide single subview");
hide->setDefaultValue ("false");
hide->setToolTip ("When a view contains only a single subview, hide the subview title "
"bar and if this subview is closed also close the view (unless it is the last "
"view for this document)");
Setting *minWidth = createSetting (Type_SpinBox, "minimum-width",
"Minimum subview width");
minWidth->setDefaultValue (325);
minWidth->setRange (50, 10000);
minWidth->setToolTip ("Minimum width of subviews.");
QString defaultScroll = "Scrollbar Only";
QStringList scrollValues = QStringList() << defaultScroll << "Grow Only" << "Grow then Scroll";
Setting *mainwinScroll = createSetting (Type_RadioButton, "mainwindow-scrollbar",
"Add a horizontal scrollbar to the main view window.");
mainwinScroll->setDefaultValue (defaultScroll);
mainwinScroll->setDeclaredValues (scrollValues);
mainwinScroll->setToolTip ("Scrollbar Only: Simple addition of scrollbars, the view window does not grow"
" automatically.\n"
"Grow Only: Original Editor behaviour. The view window grows as subviews are added. No scrollbars.\n"
"Grow then Scroll: The view window grows. The scrollbar appears once it cannot grow any further.");
Setting *grow = createSetting (Type_CheckBox, "grow-limit", "Grow Limit Screen");
grow->setDefaultValue ("false");
grow->setToolTip ("When \"Grow then Scroll\" option is selected, the window size grows to"
" the width of the virtual desktop. \nIf this option is selected the the window growth"
"is limited to the current screen.");
}
declareSection ("records", "Records");
{
QString defaultValue = "Icon and Text";
QStringList values = QStringList() << defaultValue << "Icon Only" << "Text Only";
Setting *rsd = createSetting (Type_RadioButton, "status-format",
"Modification status display format");
rsd->setDefaultValue (defaultValue);
rsd->setDeclaredValues (values);
Setting *ritd = createSetting (Type_RadioButton, "type-format",
"ID type display format");
ritd->setDefaultValue (defaultValue);
ritd->setDeclaredValues (values);
}
declareSection ("table-input", "ID Tables");
{
QString inPlaceEdit ("Edit in Place");
QString editRecord ("Edit Record");
QString view ("View");
QString editRecordAndClose ("Edit Record and Close");
QStringList values;
values
<< "None" << inPlaceEdit << editRecord << view << "Revert" << "Delete"
<< editRecordAndClose << "View and Close";
QString toolTip = "<ul>"
"<li>None</li>"
"<li>Edit in Place: Edit the clicked cell</li>"
"<li>Edit Record: Open a dialogue subview for the clicked record</li>"
"<li>View: Open a scene subview for the clicked record (not available everywhere)</li>"
"<li>Revert: Revert record</li>"
"<li>Delete: Delete recordy</li>"
"<li>Edit Record and Close: Open a dialogue subview for the clicked record and close the table subview</li>"
"<li>View And Close: Open a scene subview for the clicked record and close the table subview</li>"
"</ul>";
Setting *doubleClick = createSetting (Type_ComboBox, "double", "Double Click");
doubleClick->setDeclaredValues (values);
doubleClick->setDefaultValue (inPlaceEdit);
doubleClick->setToolTip ("Action on double click in table:<p>" + toolTip);
Setting *shiftDoubleClick = createSetting (Type_ComboBox, "double-s",
"Shift Double Click");
shiftDoubleClick->setDeclaredValues (values);
shiftDoubleClick->setDefaultValue (editRecord);
shiftDoubleClick->setToolTip ("Action on shift double click in table:<p>" + toolTip);
Setting *ctrlDoubleClick = createSetting (Type_ComboBox, "double-c",
"Control Double Click");
ctrlDoubleClick->setDeclaredValues (values);
ctrlDoubleClick->setDefaultValue (view);
ctrlDoubleClick->setToolTip ("Action on control double click in table:<p>" + toolTip);
Setting *shiftCtrlDoubleClick = createSetting (Type_ComboBox, "double-sc",
"Shift Control Double Click");
shiftCtrlDoubleClick->setDeclaredValues (values);
shiftCtrlDoubleClick->setDefaultValue (editRecordAndClose);
shiftCtrlDoubleClick->setToolTip ("Action on shift control double click in table:<p>" + toolTip);
QString defaultValue = "Jump and Select";
QStringList jumpValues = QStringList() << defaultValue << "Jump Only" << "No Jump";
Setting *jumpToAdded = createSetting (Type_RadioButton, "jump-to-added",
"Jump to the added or cloned record.");
jumpToAdded->setDefaultValue (defaultValue);
jumpToAdded->setDeclaredValues (jumpValues);
Setting *extendedConfig = createSetting (Type_CheckBox, "extended-config",
"Manually specify affected record types for an extended delete/revert");
extendedConfig->setDefaultValue("false");
extendedConfig->setToolTip("Delete and revert commands have an extended form that also affects "
"associated records.\n\n"
"If this option is enabled, types of affected records are selected "
"manually before a command execution.\nOtherwise, all associated "
"records are deleted/reverted immediately.");
}
declareSection ("dialogues", "ID Dialogues");
{
Setting *toolbar = createSetting (Type_CheckBox, "toolbar", "Show toolbar");
toolbar->setDefaultValue ("true");
}
declareSection ("report-input", "Reports");
{
QString none ("None");
QString edit ("Edit");
QString remove ("Remove");
QString editAndRemove ("Edit And Remove");
QStringList values;
values << none << edit << remove << editAndRemove;
QString toolTip = "<ul>"
"<li>None</li>"
"<li>Edit: Open a table or dialogue suitable for addressing the listed report</li>"
"<li>Remove: Remove the report from the report table</li>"
"<li>Edit and Remove: Open a table or dialogue suitable for addressing the listed report, then remove the report from the report table</li>"
"</ul>";
Setting *doubleClick = createSetting (Type_ComboBox, "double", "Double Click");
doubleClick->setDeclaredValues (values);
doubleClick->setDefaultValue (edit);
doubleClick->setToolTip ("Action on double click in report table:<p>" + toolTip);
Setting *shiftDoubleClick = createSetting (Type_ComboBox, "double-s",
"Shift Double Click");
shiftDoubleClick->setDeclaredValues (values);
shiftDoubleClick->setDefaultValue (remove);
shiftDoubleClick->setToolTip ("Action on shift double click in report table:<p>" + toolTip);
Setting *ctrlDoubleClick = createSetting (Type_ComboBox, "double-c",
"Control Double Click");
ctrlDoubleClick->setDeclaredValues (values);
ctrlDoubleClick->setDefaultValue (editAndRemove);
ctrlDoubleClick->setToolTip ("Action on control double click in report table:<p>" + toolTip);
Setting *shiftCtrlDoubleClick = createSetting (Type_ComboBox, "double-sc",
"Shift Control Double Click");
shiftCtrlDoubleClick->setDeclaredValues (values);
shiftCtrlDoubleClick->setDefaultValue (none);
shiftCtrlDoubleClick->setToolTip ("Action on shift control double click in report table:<p>" + toolTip);
}
declareSection ("search", "Search & Replace");
{
Setting *before = createSetting (Type_SpinBox, "char-before",
"Characters before search string");
before->setDefaultValue (10);
before->setRange (0, 1000);
before->setToolTip ("Maximum number of character to display in search result before the searched text");
Setting *after = createSetting (Type_SpinBox, "char-after",
"Characters after search string");
after->setDefaultValue (10);
after->setRange (0, 1000);
after->setToolTip ("Maximum number of character to display in search result after the searched text");
Setting *autoDelete = createSetting (Type_CheckBox, "auto-delete", "Delete row from result table after a successful replace");
autoDelete->setDefaultValue ("true");
}
declareSection ("script-editor", "Scripts");
{
Setting *lineNum = createSetting (Type_CheckBox, "show-linenum", "Show Line Numbers");
lineNum->setDefaultValue ("true");
lineNum->setToolTip ("Show line numbers to the left of the script editor window."
"The current row and column numbers of the text cursor are shown at the bottom.");
Setting *monoFont = createSetting (Type_CheckBox, "mono-font", "Use monospace font");
monoFont->setDefaultValue ("true");
monoFont->setToolTip ("Whether to use monospaced fonts on script edit subview.");
QString tooltip =
"\n#RGB (each of R, G, and B is a single hex digit)"
"\n#RRGGBB"
"\n#RRRGGGBBB"
"\n#RRRRGGGGBBBB"
"\nA name from the list of colors defined in the list of SVG color keyword names."
"\nX11 color names may also work.";
QString modeNormal ("Normal");
QStringList modes;
modes << "Ignore" << modeNormal << "Strict";
Setting *warnings = createSetting (Type_ComboBox, "warnings",
"Warning Mode");
warnings->setDeclaredValues (modes);
warnings->setDefaultValue (modeNormal);
warnings->setToolTip ("<ul>How to handle warning messages during compilation:<p>"
"<li>Ignore: Do not report warning</li>"
"<li>Normal: Report warning as a warning</li>"
"<li>Strict: Promote warning to an error</li>"
"</ul>");
Setting *toolbar = createSetting (Type_CheckBox, "toolbar", "Show toolbar");
toolbar->setDefaultValue ("true");
Setting *delay = createSetting (Type_SpinBox, "compile-delay",
"Delay between updating of source errors");
delay->setDefaultValue (100);
delay->setRange (0, 10000);
delay->setToolTip ("Delay in milliseconds");
Setting *formatInt = createSetting (Type_LineEdit, "colour-int", "Highlight Colour: Int");
formatInt->setDefaultValues (QStringList() << "Dark magenta");
formatInt->setToolTip ("(Default: Green) Use one of the following formats:" + tooltip);
Setting *formatFloat = createSetting (Type_LineEdit, "colour-float", "Highlight Colour: Float");
formatFloat->setDefaultValues (QStringList() << "Magenta");
formatFloat->setToolTip ("(Default: Magenta) Use one of the following formats:" + tooltip);
Setting *formatName = createSetting (Type_LineEdit, "colour-name", "Highlight Colour: Name");
formatName->setDefaultValues (QStringList() << "Gray");
formatName->setToolTip ("(Default: Gray) Use one of the following formats:" + tooltip);
Setting *formatKeyword = createSetting (Type_LineEdit, "colour-keyword", "Highlight Colour: Keyword");
formatKeyword->setDefaultValues (QStringList() << "Red");
formatKeyword->setToolTip ("(Default: Red) Use one of the following formats:" + tooltip);
Setting *formatSpecial = createSetting (Type_LineEdit, "colour-special", "Highlight Colour: Special");
formatSpecial->setDefaultValues (QStringList() << "Dark yellow");
formatSpecial->setToolTip ("(Default: Dark yellow) Use one of the following formats:" + tooltip);
Setting *formatComment = createSetting (Type_LineEdit, "colour-comment", "Highlight Colour: Comment");
formatComment->setDefaultValues (QStringList() << "Green");
formatComment->setToolTip ("(Default: Green) Use one of the following formats:" + tooltip);
Setting *formatId = createSetting (Type_LineEdit, "colour-id", "Highlight Colour: Id");
formatId->setDefaultValues (QStringList() << "Blue");
formatId->setToolTip ("(Default: Blue) Use one of the following formats:" + tooltip);
}
declareSection ("general-input", "General Input");
{
Setting *cycle = createSetting (Type_CheckBox, "cycle", "Cyclic next/previous");
cycle->setDefaultValue ("false");
cycle->setToolTip ("When using next/previous functions at the last/first item of a "
"list go to the first/last item");
}
declareSection ("scene-input", "3D Scene Input");
{
QString left ("Left Mouse-Button");
QString cLeft ("Ctrl-Left Mouse-Button");
QString right ("Right Mouse-Button");
QString cRight ("Ctrl-Right Mouse-Button");
QString middle ("Middle Mouse-Button");
QString cMiddle ("Ctrl-Middle Mouse-Button");
QStringList values;
values << left << cLeft << right << cRight << middle << cMiddle;
Setting *primaryNavigation = createSetting (Type_ComboBox, "p-navi", "Primary Camera Navigation Button");
primaryNavigation->setDeclaredValues (values);
primaryNavigation->setDefaultValue (left);
Setting *secondaryNavigation = createSetting (Type_ComboBox, "s-navi", "Secondary Camera Navigation Button");
secondaryNavigation->setDeclaredValues (values);
secondaryNavigation->setDefaultValue (cLeft);
Setting *primaryEditing = createSetting (Type_ComboBox, "p-edit", "Primary Editing Button");
primaryEditing->setDeclaredValues (values);
primaryEditing->setDefaultValue (right);
Setting *secondaryEditing = createSetting (Type_ComboBox, "s-edit", "Secondary Editing Button");
secondaryEditing->setDeclaredValues (values);
secondaryEditing->setDefaultValue (cRight);
Setting *selection = createSetting (Type_ComboBox, "select", "Selection Button");
selection->setDeclaredValues (values);
selection->setDefaultValue (middle);
Setting *contextSensitive = createSetting (Type_CheckBox, "context-select", "Context Sensitive Selection");
contextSensitive->setDefaultValue ("false");
Setting *dragMouseSensitivity = createSetting (Type_DoubleSpinBox, "drag-factor",
"Mouse sensitivity during drag operations");
dragMouseSensitivity->setDefaultValue (1.0);
dragMouseSensitivity->setRange (0.001, 100.0);
Setting *dragWheelSensitivity = createSetting (Type_DoubleSpinBox, "drag-wheel-factor",
"Mouse wheel sensitivity during drag operations");
dragWheelSensitivity->setDefaultValue (1.0);
dragWheelSensitivity->setRange (0.001, 100.0);
Setting *dragShiftFactor = createSetting (Type_DoubleSpinBox, "drag-shift-factor",
"Acceleration factor during drag operations while holding down shift");
dragShiftFactor->setDefaultValue (4.0);
dragShiftFactor->setRange (0.001, 100.0);
}
{
/******************************************************************
* There are three types of values:
*
* Declared values
*
* Pre-determined values, typically for
* combobox drop downs and boolean (radiobutton / checkbox) labels.
* These values represent the total possible list of values that
* may define a setting. No other values are allowed.
*
* Defined values
*
* Values which represent the actual, current value of
* a setting. For settings with declared values, this must be one
* or several declared values, as appropriate.
*
* Proxy values
* Values the proxy master updates the proxy slave when
* it's own definition is set / changed. These are definitions for
* proxy slave settings, but must match any declared values the
* proxy slave has, if any.
*******************************************************************/
/*
//create setting objects, specifying the basic widget type,
//the page name, and the view name
Setting *masterBoolean = createSetting (Type_RadioButton, section,
"Master Proxy");
Setting *slaveBoolean = createSetting (Type_CheckBox, section,
"Proxy Checkboxes");
Setting *slaveSingleText = createSetting (Type_LineEdit, section,
"Proxy TextBox 1");
Setting *slaveMultiText = createSetting (Type_LineEdit, section,
"ProxyTextBox 2");
Setting *slaveAlphaSpinbox = createSetting (Type_SpinBox, section,
"Alpha Spinbox");
Setting *slaveIntegerSpinbox = createSetting (Type_SpinBox, section,
"Int Spinbox");
Setting *slaveDoubleSpinbox = createSetting (Type_DoubleSpinBox,
section, "Double Spinbox");
Setting *slaveSlider = createSetting (Type_Slider, section, "Slider");
Setting *slaveDial = createSetting (Type_Dial, section, "Dial");
//set declared values for selected views
masterBoolean->setDeclaredValues (QStringList()
<< "Profile One" << "Profile Two"
<< "Profile Three" << "Profile Four");
slaveBoolean->setDeclaredValues (QStringList()
<< "One" << "Two" << "Three" << "Four" << "Five");
slaveAlphaSpinbox->setDeclaredValues (QStringList()
<< "One" << "Two" << "Three" << "Four");
masterBoolean->addProxy (slaveBoolean, QList <QStringList>()
<< (QStringList() << "One" << "Three")
<< (QStringList() << "One" << "Three")
<< (QStringList() << "One" << "Three" << "Five")
<< (QStringList() << "Two" << "Four")
);
masterBoolean->addProxy (slaveSingleText, QList <QStringList>()
<< (QStringList() << "Text A")
<< (QStringList() << "Text B")
<< (QStringList() << "Text A")
<< (QStringList() << "Text C")
);
masterBoolean->addProxy (slaveMultiText, QList <QStringList>()
<< (QStringList() << "One" << "Three")
<< (QStringList() << "One" << "Three")
<< (QStringList() << "One" << "Three" << "Five")
<< (QStringList() << "Two" << "Four")
);
masterBoolean->addProxy (slaveAlphaSpinbox, QList <QStringList>()
<< (QStringList() << "Four")
<< (QStringList() << "Three")
<< (QStringList() << "Two")
<< (QStringList() << "One"));
masterBoolean->addProxy (slaveIntegerSpinbox, QList <QStringList> ()
<< (QStringList() << "0")
<< (QStringList() << "7")
<< (QStringList() << "14")
<< (QStringList() << "21"));
masterBoolean->addProxy (slaveDoubleSpinbox, QList <QStringList> ()
<< (QStringList() << "0.17")
<< (QStringList() << "0.34")
<< (QStringList() << "0.51")
<< (QStringList() << "0.68"));
masterBoolean->addProxy (slaveSlider, QList <QStringList> ()
<< (QStringList() << "25")
<< (QStringList() << "50")
<< (QStringList() << "75")
<< (QStringList() << "100")
);
masterBoolean->addProxy (slaveDial, QList <QStringList> ()
<< (QStringList() << "25")
<< (QStringList() << "50")
<< (QStringList() << "75")
<< (QStringList() << "100")
);
//settings with proxies are not serialized by default
//other settings non-serialized for demo purposes
slaveBoolean->setSerializable (false);
slaveSingleText->setSerializable (false);
slaveMultiText->setSerializable (false);
slaveAlphaSpinbox->setSerializable (false);
slaveIntegerSpinbox->setSerializable (false);
slaveDoubleSpinbox->setSerializable (false);
slaveSlider->setSerializable (false);
slaveDial->setSerializable (false);
slaveBoolean->setDefaultValues (QStringList()
<< "One" << "Three" << "Five");
slaveSingleText->setDefaultValue ("Text A");
slaveMultiText->setDefaultValues (QStringList()
<< "One" << "Three" << "Five");
slaveSingleText->setWidgetWidth (24);
slaveMultiText->setWidgetWidth (24);
slaveAlphaSpinbox->setDefaultValue ("Two");
slaveAlphaSpinbox->setWidgetWidth (20);
//slaveAlphaSpinbox->setPrefix ("No. ");
//slaveAlphaSpinbox->setSuffix ("!");
slaveAlphaSpinbox->setWrapping (true);
slaveIntegerSpinbox->setDefaultValue (14);
slaveIntegerSpinbox->setMinimum (0);
slaveIntegerSpinbox->setMaximum (58);
slaveIntegerSpinbox->setPrefix ("$");
slaveIntegerSpinbox->setSuffix (".00");
slaveIntegerSpinbox->setWidgetWidth (10);
slaveIntegerSpinbox->setSpecialValueText ("Nothing!");
slaveDoubleSpinbox->setDefaultValue (0.51);
slaveDoubleSpinbox->setSingleStep(0.17);
slaveDoubleSpinbox->setMaximum(4.0);
slaveSlider->setMinimum (0);
slaveSlider->setMaximum (100);
slaveSlider->setDefaultValue (75);
slaveSlider->setWidgetWidth (100);
slaveSlider->setTicksAbove (true);
slaveSlider->setTickInterval (25);
slaveDial->setMinimum (0);
slaveDial->setMaximum (100);
slaveDial->setSingleStep (5);
slaveDial->setDefaultValue (75);
slaveDial->setTickInterval (25);
*/
}
}
CSMSettings::UserSettings::~UserSettings()
{
sUserSettingsInstance = 0;
}
void CSMSettings::UserSettings::loadSettings (const QString &fileName)
{
QString userFilePath = QString::fromUtf8
(mCfgMgr.getUserConfigPath().string().c_str());
QString globalFilePath = QString::fromUtf8
(mCfgMgr.getGlobalPath().string().c_str());
QString otherFilePath = globalFilePath;
//test for local only if global fails (uninstalled copy)
if (!QFile (globalFilePath + fileName).exists())
{
//if global is invalid, use the local path
otherFilePath = QString::fromUtf8
(mCfgMgr.getLocalPath().string().c_str());
}
QSettings::setPath
(QSettings::IniFormat, QSettings::UserScope, userFilePath);
QSettings::setPath
(QSettings::IniFormat, QSettings::SystemScope, otherFilePath);
mSettingDefinitions = new QSettings
(QSettings::IniFormat, QSettings::UserScope, "opencs", QString(), this);
}
// if the key is not found create one with a default value
QString CSMSettings::UserSettings::setting(const QString &viewKey, const QString &value)
{
if(mSettingDefinitions->contains(viewKey))
return settingValue(viewKey);
else if(value != QString())
{
mSettingDefinitions->setValue (viewKey, QStringList() << value);
return value;
}
return QString();
}
bool CSMSettings::UserSettings::hasSettingDefinitions (const QString &viewKey) const
{
return (mSettingDefinitions->contains (viewKey));
}
void CSMSettings::UserSettings::setDefinitions
(const QString &key, const QStringList &list)
{
mSettingDefinitions->setValue (key, list);
}
void CSMSettings::UserSettings::saveDefinitions() const
{
mSettingDefinitions->sync();
}
QString CSMSettings::UserSettings::settingValue (const QString &settingKey)
{
QStringList defs;
if (!mSettingDefinitions->contains (settingKey))
return QString();
defs = mSettingDefinitions->value (settingKey).toStringList();
if (defs.isEmpty())
return QString();
return defs.at(0);
}
CSMSettings::UserSettings& CSMSettings::UserSettings::instance()
{
assert(sUserSettingsInstance);
return *sUserSettingsInstance;
}
void CSMSettings::UserSettings::updateUserSetting(const QString &settingKey,
const QStringList &list)
{
mSettingDefinitions->setValue (settingKey ,list);
emit userSettingUpdated (settingKey, list);
}
CSMSettings::Setting *CSMSettings::UserSettings::findSetting
(const QString &pageName, const QString &settingName)
{
foreach (Setting *setting, mSettings)
{
if (setting->name() == settingName)
{
if (setting->page() == pageName)
return setting;
}
}
return 0;
}
void CSMSettings::UserSettings::removeSetting
(const QString &pageName, const QString &settingName)
{
if (mSettings.isEmpty())
return;
QList <Setting *>::iterator removeIterator = mSettings.begin();
while (removeIterator != mSettings.end())
{
if ((*removeIterator)->name() == settingName)
{
if ((*removeIterator)->page() == pageName)
{
mSettings.erase (removeIterator);
break;
}
}
removeIterator++;
}
}
CSMSettings::SettingPageMap CSMSettings::UserSettings::settingPageMap() const
{
SettingPageMap pageMap;
foreach (Setting *setting, mSettings)
{
SettingPageMap::iterator iter = pageMap.find (setting->page());
if (iter==pageMap.end())
{
QPair<QString, QList <Setting *> > value;
std::map<QString, QString>::const_iterator iter2 =
mSectionLabels.find (setting->page());
value.first = iter2!=mSectionLabels.end() ? iter2->second : "";
iter = pageMap.insert (setting->page(), value);
}
iter->second.append (setting);
}
return pageMap;
}
CSMSettings::Setting *CSMSettings::UserSettings::createSetting
(CSMSettings::SettingType type, const QString &name, const QString& label)
{
Setting *setting = new Setting (type, name, mSection, label);
// set useful defaults
int row = 1;
if (!mSettings.empty())
row = mSettings.back()->viewRow()+1;
setting->setViewLocation (row, 1);
setting->setColumnSpan (3);
int width = 10;
if (type==Type_CheckBox)
width = 40;
setting->setWidgetWidth (width);
if (type==Type_CheckBox)
setting->setStyleSheet ("QGroupBox { border: 0px; }");
if (type==Type_CheckBox)
setting->setDeclaredValues(QStringList() << "true" << "false");
if (type==Type_CheckBox)
setting->setSpecialValueText (setting->getLabel());
//add declaration to the model
mSettings.append (setting);
return setting;
}
void CSMSettings::UserSettings::declareSection (const QString& page, const QString& label)
{
mSection = page;
mSectionLabels[page] = label;
}
QStringList CSMSettings::UserSettings::definitions (const QString &viewKey) const
{
if (mSettingDefinitions->contains (viewKey))
return mSettingDefinitions->value (viewKey).toStringList();
return QStringList();
}

@ -1,107 +0,0 @@
#ifndef USERSETTINGS_HPP
#define USERSETTINGS_HPP
#include <map>
#include <QList>
#include <QStringList>
#include <QString>
#include <QMap>
#include <QPair>
#include <boost/filesystem/path.hpp>
#include "support.hpp"
#ifndef Q_MOC_RUN
#include <components/files/configurationmanager.hpp>
#endif
namespace Files { typedef std::vector<boost::filesystem::path> PathContainer;
struct ConfigurationManager;}
class QFile;
class QSettings;
namespace CSMSettings {
class Setting;
typedef QMap <QString, QPair<QString, QList <Setting *> > > SettingPageMap;
class UserSettings: public QObject
{
Q_OBJECT
static UserSettings *sUserSettingsInstance;
const Files::ConfigurationManager& mCfgMgr;
QSettings *mSettingDefinitions;
QList <Setting *> mSettings;
QString mSection;
std::map<QString, QString> mSectionLabels;
public:
/// Singleton implementation
static UserSettings& instance();
UserSettings (const Files::ConfigurationManager& configurationManager);
~UserSettings();
UserSettings (UserSettings const &); //not implemented
UserSettings& operator= (UserSettings const &); //not implemented
/// Retrieves the settings file at all three levels (global, local and user).
void loadSettings (const QString &fileName);
/// Updates QSettings and syncs with the ini file
void setDefinitions (const QString &key, const QStringList &defs);
QString settingValue (const QString &settingKey);
///retrieve a setting object from a given page and setting name
Setting *findSetting
(const QString &pageName, const QString &settingName = QString());
///remove a setting from the list
void removeSetting
(const QString &pageName, const QString &settingName);
///Retrieve a map of the settings, keyed by page name
SettingPageMap settingPageMap() const;
///Returns a string list of defined vlaues for the specified setting
///in "page/name" format.
QStringList definitions (const QString &viewKey) const;
///Test to indicate whether or not a setting has any definitions
bool hasSettingDefinitions (const QString &viewKey) const;
///Save any unsaved changes in the QSettings object
void saveDefinitions() const;
QString setting(const QString &viewKey, const QString &value = QString());
private:
void buildSettingModelDefaults();
///add a new setting to the model and return it
Setting *createSetting (CSMSettings::SettingType type, const QString &name,
const QString& label);
/// Set the section for createSetting calls.
///
/// Sections can be declared multiple times.
void declareSection (const QString& page, const QString& label);
signals:
void userSettingUpdated (const QString &, const QStringList &);
public slots:
void updateUserSetting (const QString &, const QStringList &);
};
}
#endif // USERSETTINGS_HPP

@ -397,6 +397,9 @@ void CSMTools::ReferenceableCheckStage::containerCheck(
//checking for name
if (container.mName.empty())
messages.push_back (std::make_pair (id, container.mId + " has an empty name"));
//checking contained items
inventoryListCheck(container.mInventory.mList, messages, id.toString());
// Check that mentioned scripts exist
scriptCheck<ESM::Container>(container, messages, id.toString());
@ -468,6 +471,12 @@ void CSMTools::ReferenceableCheckStage::creatureCheck (
if (creature.mData.mGold < 0) //It seems that this is for gold in merchant creatures
messages.push_back (std::make_pair (id, creature.mId + " has negative gold "));
if (creature.mScale == 0)
messages.push_back (std::make_pair (id, creature.mId + " has zero scale value"));
// Check inventory
inventoryListCheck(creature.mInventory.mList, messages, id.toString());
// Check that mentioned scripts exist
scriptCheck<ESM::Creature>(creature, messages, id.toString());
}
@ -739,6 +748,9 @@ void CSMTools::ReferenceableCheckStage::npcCheck (
//TODO: reputation, Disposition, rank, everything else
// Check inventory
inventoryListCheck(npc.mInventory.mList, messages, id.toString());
// Check that mentioned scripts exist
scriptCheck<ESM::NPC>(npc, messages, id.toString());
}
@ -888,6 +900,45 @@ void CSMTools::ReferenceableCheckStage::finalCheck (CSMDoc::Messages& messages)
"There is no player record"));
}
void CSMTools::ReferenceableCheckStage::inventoryListCheck(
const std::vector<ESM::ContItem>& itemList,
CSMDoc::Messages& messages,
const std::string& id)
{
for (size_t i = 0; i < itemList.size(); ++i)
{
std::string itemName = itemList[i].mItem.toString();
CSMWorld::RefIdData::LocalIndex localIndex = mReferencables.searchId(itemName);
if (localIndex.first == -1)
messages.push_back (std::make_pair (id,
id + " contains non-existing item (" + itemName + ")"));
else
{
// Needs to accomodate Containers, Creatures, and NPCs
switch (localIndex.second)
{
case CSMWorld::UniversalId::Type_Potion:
case CSMWorld::UniversalId::Type_Apparatus:
case CSMWorld::UniversalId::Type_Armor:
case CSMWorld::UniversalId::Type_Book:
case CSMWorld::UniversalId::Type_Clothing:
case CSMWorld::UniversalId::Type_Ingredient:
case CSMWorld::UniversalId::Type_Light:
case CSMWorld::UniversalId::Type_Lockpick:
case CSMWorld::UniversalId::Type_Miscellaneous:
case CSMWorld::UniversalId::Type_Probe:
case CSMWorld::UniversalId::Type_Repair:
case CSMWorld::UniversalId::Type_Weapon:
case CSMWorld::UniversalId::Type_ItemLevelledList:
break;
default:
messages.push_back (std::make_pair(id,
id + " contains item of invalid type (" + itemName + ")"));
}
}
}
}
//Templates begins here

@ -47,7 +47,9 @@ namespace CSMTools
//FINAL CHECK
void finalCheck (CSMDoc::Messages& messages);
//TEMPLATE CHECKS
//Convenience functions
void inventoryListCheck(const std::vector<ESM::ContItem>& itemList, CSMDoc::Messages& messages, const std::string& id);
template<typename ITEM> void inventoryItemCheck(const ITEM& someItem,
CSMDoc::Messages& messages,
const std::string& someID,

@ -10,6 +10,8 @@
#include "../world/data.hpp"
#include "../prefs/state.hpp"
CSMDoc::Message::Severity CSMTools::ScriptCheckStage::getSeverity (Type type)
{
switch (type)
@ -46,7 +48,7 @@ void CSMTools::ScriptCheckStage::report (const std::string& message, Type type)
std::ostringstream stream;
stream << "script " << mFile << ": " << message;
mMessages->add (id, stream.str(), "", getSeverity (type));
}
@ -62,6 +64,15 @@ CSMTools::ScriptCheckStage::ScriptCheckStage (const CSMDoc::Document& document)
int CSMTools::ScriptCheckStage::setup()
{
std::string warnings = CSMPrefs::get()["Scripts"]["warnings"].toString();
if (warnings=="Ignore")
mWarningMode = Mode_Ignore;
else if (warnings=="Normal")
mWarningMode = Mode_Normal;
else if (warnings=="Strict")
mWarningMode = Mode_Strict;
mContext.clear();
mMessages = 0;
mId.clear();
@ -110,22 +121,9 @@ void CSMTools::ScriptCheckStage::perform (int stage, CSMDoc::Messages& messages)
std::ostringstream stream;
stream << "script " << mFile << ": " << error.what();
messages.add (id, stream.str(), "", CSMDoc::Message::Severity_SeriousError);
}
mMessages = 0;
}
void CSMTools::ScriptCheckStage::updateUserSetting (const QString& name, const QStringList& value)
{
if (name=="script-editor/warnings" && !value.isEmpty())
{
if (value.at (0)=="Ignore")
mWarningMode = Mode_Ignore;
else if (value.at (0)=="Normal")
mWarningMode = Mode_Normal;
else if (value.at (0)=="Strict")
mWarningMode = Mode_Strict;
}
}

@ -34,7 +34,7 @@ namespace CSMTools
WarningMode mWarningMode;
CSMDoc::Message::Severity getSeverity (Type type);
virtual void report (const std::string& message, const Compiler::TokenLoc& loc, Type type);
///< Report error to the user.
@ -50,8 +50,6 @@ namespace CSMTools
virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this tage will be appended to \a messages.
virtual void updateUserSetting (const QString& name, const QStringList& value);
};
}

@ -53,11 +53,6 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier()
{
mVerifierOperation = new CSMDoc::Operation (CSMDoc::State_Verifying, false);
std::vector<QString> settings;
settings.push_back ("script-editor/warnings");
mVerifierOperation->configureSettings (settings);
connect (&mVerifier, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int)));
connect (&mVerifier, SIGNAL (done (int, bool)), this, SIGNAL (done (int, bool)));
connect (&mVerifier, SIGNAL (reportMessage (const CSMDoc::Message&, int)),

@ -2,18 +2,15 @@
#include <sstream>
void CSMWorld::Cell::load (ESM::ESMReader &esm)
void CSMWorld::Cell::load (ESM::ESMReader &esm, bool &isDeleted)
{
mName = mId;
ESM::Cell::load (esm, isDeleted, false);
ESM::Cell::load (esm, false);
if (!(mData.mFlags & Interior))
mId = mName;
if (isExterior())
{
std::ostringstream stream;
stream << "#" << mData.mX << " " << mData.mY;
mId = stream.str();
}
}

@ -16,7 +16,7 @@ namespace CSMWorld
{
std::string mId;
void load (ESM::ESMReader &esm);
void load (ESM::ESMReader &esm, bool &isDeleted);
};
}

@ -43,6 +43,12 @@ namespace CSMWorld
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
class Collection : public CollectionBase
{
public:
typedef ESXRecordT ESXRecord;
private:
std::vector<Record<ESXRecordT> > mRecords;
std::map<std::string, int> mIndex;
std::vector<Column<ESXRecordT> *> mColumns;

@ -86,6 +86,10 @@ bool CSMWorld::ColumnBase::isId (Display display)
Display_InfoCondVar,
Display_InfoCondComp,
Display_EffectSkill,
Display_EffectAttribute,
Display_IngredEffectId,
Display_None
};

@ -14,6 +14,13 @@ namespace CSMWorld
{
struct ColumnBase
{
enum TableEditModes
{
TableEdit_None, // no editing
TableEdit_Full, // edit cells and add/remove rows
TableEdit_FixedRows // edit cells only
};
enum Roles
{
Role_Flags = Qt::UserRole,
@ -124,6 +131,10 @@ namespace CSMWorld
Display_String32,
Display_LongString256,
Display_EffectSkill, // must display at least one, unlike Display_Skill
Display_EffectAttribute, // must display at least one, unlike Display_Attribute
Display_IngredEffectId, // display none allowed, unlike Display_EffectId
//top level columns that nest other columns
Display_NestedHeader
};
@ -186,8 +197,8 @@ namespace CSMWorld
template<typename ESXRecordT>
struct NestedParentColumn : public Column<ESXRecordT>
{
NestedParentColumn (int id, int flags = ColumnBase::Flag_Dialogue) : Column<ESXRecordT> (id,
ColumnBase::Display_NestedHeader, flags)
NestedParentColumn (int id, int flags = ColumnBase::Flag_Dialogue, bool fixedRows = false)
: Column<ESXRecordT> (id, ColumnBase::Display_NestedHeader, flags), mFixedRows(fixedRows)
{}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
@ -198,13 +209,20 @@ namespace CSMWorld
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return true; // required by IdTree::hasChildren()
// by default editable; also see IdTree::hasChildren()
if (mFixedRows)
return QVariant::fromValue(ColumnBase::TableEdit_FixedRows);
else
return QVariant::fromValue(ColumnBase::TableEdit_Full);
}
virtual bool isEditable() const
{
return true;
}
private:
bool mFixedRows;
};
struct NestedChildColumn : public NestableColumn
@ -219,4 +237,6 @@ namespace CSMWorld
};
}
Q_DECLARE_METATYPE(CSMWorld::ColumnBase::TableEditModes)
#endif

@ -93,7 +93,7 @@ void CSMWorld::CommandDispatcher::setEditLock (bool locked)
void CSMWorld::CommandDispatcher::setSelection (const std::vector<std::string>& selection)
{
mSelection = selection;
std::for_each (mSelection.begin(), mSelection.end(), Misc::StringUtils::toLower);
std::for_each (mSelection.begin(), mSelection.end(), Misc::StringUtils::lowerCaseInPlace);
std::sort (mSelection.begin(), mSelection.end());
}

@ -136,7 +136,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mRaces.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_SpellId, ColumnBase::Display_Spell));
// Race attributes
mRaces.addColumn (new NestedParentColumn<ESM::Race> (Columns::ColumnId_RaceAttributes));
mRaces.addColumn (new NestedParentColumn<ESM::Race> (Columns::ColumnId_RaceAttributes,
ColumnBase::Flag_Dialogue, true)); // fixed rows table
index = mRaces.getColumns()-1;
mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new RaceAttributeAdapter()));
mRaces.getNestableColumn(index)->addColumn(
@ -147,7 +148,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mRaces.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_Female, ColumnBase::Display_Integer));
// Race skill bonus
mRaces.addColumn (new NestedParentColumn<ESM::Race> (Columns::ColumnId_RaceSkillBonus));
mRaces.addColumn (new NestedParentColumn<ESM::Race> (Columns::ColumnId_RaceSkillBonus,
ColumnBase::Flag_Dialogue, true)); // fixed rows table
index = mRaces.getColumns()-1;
mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new RaceSkillsBonusAdapter()));
mRaces.getNestableColumn(index)->addColumn(
@ -213,9 +215,9 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mSpells.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_EffectId, ColumnBase::Display_EffectId));
mSpells.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_Skill, ColumnBase::Display_SkillId));
new NestedChildColumn (Columns::ColumnId_Skill, ColumnBase::Display_EffectSkill));
mSpells.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_Attribute));
new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_EffectAttribute));
mSpells.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_EffectRange, ColumnBase::Display_EffectRange));
mSpells.getNestableColumn(index)->addColumn(
@ -329,9 +331,9 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mEnchantments.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_EffectId, ColumnBase::Display_EffectId));
mEnchantments.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_Skill, ColumnBase::Display_SkillId));
new NestedChildColumn (Columns::ColumnId_Skill, ColumnBase::Display_EffectSkill));
mEnchantments.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_Attribute));
new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_EffectAttribute));
mEnchantments.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_EffectRange, ColumnBase::Display_EffectRange));
mEnchantments.getNestableColumn(index)->addColumn(
@ -1008,41 +1010,43 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
case ESM::REC_DIAL:
{
std::string id = mReader->getHNOString ("NAME");
ESM::Dialogue record;
record.mId = id;
record.load (*mReader);
bool isDeleted = false;
if (record.mType==ESM::Dialogue::Journal)
{
mJournals.load (record, mBase);
mDialogue = &mJournals.getRecord (id).get();
}
else if (record.mType==ESM::Dialogue::Deleted)
record.load (*mReader, isDeleted);
if (isDeleted)
{
mDialogue = 0; // record vector can be shuffled around which would make pointer
// to record invalid
// record vector can be shuffled around which would make pointer to record invalid
mDialogue = 0;
if (mJournals.tryDelete (id))
if (mJournals.tryDelete (record.mId))
{
/// \todo handle info records
mJournalInfos.removeDialogueInfos(record.mId);
}
else if (mTopics.tryDelete (id))
else if (mTopics.tryDelete (record.mId))
{
/// \todo handle info records
mTopicInfos.removeDialogueInfos(record.mId);
}
else
{
messages.add (UniversalId::Type_None,
"Trying to delete dialogue record " + id + " which does not exist",
"Trying to delete dialogue record " + record.mId + " which does not exist",
"", CSMDoc::Message::Severity_Warning);
}
}
else
{
mTopics.load (record, mBase);
mDialogue = &mTopics.getRecord (id).get();
if (record.mType == ESM::Dialogue::Journal)
{
mJournals.load (record, mBase);
mDialogue = &mJournals.getRecord (record.mId).get();
}
else
{
mTopics.load (record, mBase);
mDialogue = &mTopics.getRecord (record.mId).get();
}
}
break;

@ -11,7 +11,7 @@ namespace CSMWorld
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
class IdCollection : public Collection<ESXRecordT, IdAccessorT>
{
virtual void loadRecord (ESXRecordT& record, ESM::ESMReader& reader);
virtual void loadRecord (ESXRecordT& record, ESM::ESMReader& reader, bool& isDeleted);
public:
@ -33,77 +33,46 @@ namespace CSMWorld
template<typename ESXRecordT, typename IdAccessorT>
void IdCollection<ESXRecordT, IdAccessorT>::loadRecord (ESXRecordT& record,
ESM::ESMReader& reader)
ESM::ESMReader& reader,
bool& isDeleted)
{
record.load (reader);
record.load (reader, isDeleted);
}
template<typename ESXRecordT, typename IdAccessorT>
int IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base)
{
std::string id = reader.getHNOString ("NAME");
ESXRecordT record;
bool isDeleted = false;
if (reader.isNextSub ("DELE"))
{
int index = Collection<ESXRecordT, IdAccessorT>::searchId (id);
loadRecord (record, reader, isDeleted);
reader.skipRecord();
std::string id = IdAccessorT().getId (record);
int index = this->searchId (id);
if (isDeleted)
{
if (index==-1)
{
// deleting a record that does not exist
// ignore it for now
/// \todo report the problem to the user
}
else if (base)
{
Collection<ESXRecordT, IdAccessorT>::removeRows (index, 1);
}
else
{
Record<ESXRecordT> record = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
record.mState = RecordBase::State_Deleted;
this->setRecord (index, record);
return -1;
}
return -1;
}
else
{
ESXRecordT record;
// Sometimes id (i.e. NAME of the cell) may be different to the id we stored
// earlier. e.g. NAME == "Vivec, Arena" but id == "#-4 11". Sometime NAME is
// missing altogether for scripts or cells.
//
// In such cases the returned index will be -1. We then try updating the
// IdAccessor's id manually (e.g. set mId of the record to "Vivec, Arena")
// and try getting the index once more after loading the record. The mId of the
// record would have changed to "#-4 11" after the load, and searchId() should find
// it (if this is a modify)
int index = this->searchId (id);
if (index==-1)
IdAccessorT().getId (record) = id;
else
{
record = this->getRecord (index).get();
}
loadRecord (record, reader);
if (index==-1)
if (base)
{
std::string newId = IdAccessorT().getId(record);
int newIndex = this->searchId(newId);
if (newIndex != -1 && id != newId)
index = newIndex;
this->removeRows (index, 1);
return -1;
}
return load (record, base, index);
Record<ESXRecordT> baseRecord = this->getRecord (index);
baseRecord.mState = RecordBase::State_Deleted;
this->setRecord (index, baseRecord);
return index;
}
return load (record, base, index);
}
template<typename ESXRecordT, typename IdAccessorT>

@ -106,21 +106,20 @@ bool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector<int
void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue)
{
std::string id = Misc::StringUtils::lowerCase (dialogue.mId) + "#" +
reader.getHNOString ("INAM");
Info info;
bool isDeleted = false;
if (reader.isNextSub ("DELE"))
info.load (reader, isDeleted);
std::string id = Misc::StringUtils::lowerCase (dialogue.mId) + "#" + info.mId;
if (isDeleted)
{
int index = searchId (id);
reader.skipRecord();
if (index==-1)
{
// deleting a record that does not exist
// ignore it for now
/// \todo report the problem to the user
}
else if (base)
@ -136,12 +135,9 @@ void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ES
}
else
{
Info record;
record.mTopicId = dialogue.mId;
record.mId = id;
record.load (reader);
load (record, base);
info.mTopicId = dialogue.mId;
info.mId = id;
load (info, base);
}
}
@ -193,3 +189,39 @@ CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const s
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)
{
Record<Info> record = getRecord(current->second);
if (Misc::StringUtils::ciEqual(dialogueId, record.get().mTopicId))
{
if (record.mState == RecordBase::State_ModifiedOnly)
{
erasedRecords.push_back(current->second);
}
else
{
record.mState = RecordBase::State_Deleted;
setRecord(current->second, record);
}
}
else
{
break;
}
}
while (!erasedRecords.empty())
{
removeRows(erasedRecords.back(), 1);
erasedRecords.pop_back();
}
}

@ -44,6 +44,8 @@ namespace CSMWorld
Range getTopicRange (const std::string& topic) const;
///< Return iterators that point to the beginning and past the end of the range for
/// the given topic.
void removeDialogueInfos(const std::string& dialogueId);
};
}

@ -4,13 +4,12 @@
namespace CSMWorld
{
void Land::load(ESM::ESMReader &esm)
void Land::load(ESM::ESMReader &esm, bool &isDeleted)
{
ESM::Land::load(esm);
ESM::Land::load(esm, isDeleted);
std::ostringstream stream;
stream << "#" << mX << " " << mY;
mId = stream.str();
}
}

@ -10,13 +10,12 @@ namespace CSMWorld
/// \brief Wrapper for Land record. Encodes X and Y cell index in the ID.
///
/// \todo Add worldspace support to the Land record.
/// \todo Add a proper copy constructor (currently worked around using shared_ptr)
struct Land : public ESM::Land
{
std::string mId;
/// Loads the metadata and ID
void load (ESM::ESMReader &esm);
void load (ESM::ESMReader &esm, bool &isDeleted);
};
}

@ -4,10 +4,9 @@
namespace CSMWorld
{
void LandTexture::load(ESM::ESMReader &esm)
void LandTexture::load(ESM::ESMReader &esm, bool &isDeleted)
{
ESM::LandTexture::load(esm);
ESM::LandTexture::load(esm, isDeleted);
mPluginIndex = esm.getIndex();
}

@ -12,7 +12,7 @@ namespace CSMWorld
{
int mPluginIndex;
void load (ESM::ESMReader &esm);
void load (ESM::ESMReader &esm, bool &isDeleted);
};
}

@ -606,7 +606,7 @@ namespace CSMWorld
funcMap["09"] = "PC Fatigue";
funcMap["10"] = "PC Strength";
funcMap["11"] = "PC Block";
funcMap["12"] = "PC Armoror";
funcMap["12"] = "PC Armorer";
funcMap["13"] = "PC Medium Armor";
funcMap["14"] = "PC Heavy Armor";
funcMap["15"] = "PC Blunt Weapon";

@ -317,8 +317,34 @@ namespace CSMWorld
else
throw std::runtime_error("Magic effects ID unexpected value");
}
case 1: return effect.mSkill;
case 2: return effect.mAttribute;
case 1:
{
switch (effect.mEffectID)
{
case ESM::MagicEffect::DrainSkill:
case ESM::MagicEffect::DamageSkill:
case ESM::MagicEffect::RestoreSkill:
case ESM::MagicEffect::FortifySkill:
case ESM::MagicEffect::AbsorbSkill:
return effect.mSkill;
default:
return QVariant();
}
}
case 2:
{
switch (effect.mEffectID)
{
case ESM::MagicEffect::DrainAttribute:
case ESM::MagicEffect::DamageAttribute:
case ESM::MagicEffect::RestoreAttribute:
case ESM::MagicEffect::FortifyAttribute:
case ESM::MagicEffect::AbsorbAttribute:
return effect.mAttribute;
default:
return QVariant();
}
}
case 3:
{
if (effect.mRange >=0 && effect.mRange <=2)

@ -4,33 +4,28 @@
#include <sstream>
void CSMWorld::Pathgrid::load (ESM::ESMReader &esm, const IdCollection<Cell>& cells)
void CSMWorld::Pathgrid::load (ESM::ESMReader &esm, bool &isDeleted, const IdCollection<Cell>& cells)
{
load (esm);
load (esm, isDeleted);
// correct ID
if (!mId.empty() && mId[0]!='#' && cells.searchId (mId)==-1)
{
std::ostringstream stream;
stream << "#" << mData.mX << " " << mData.mY;
mId = stream.str();
}
}
void CSMWorld::Pathgrid::load (ESM::ESMReader &esm)
void CSMWorld::Pathgrid::load (ESM::ESMReader &esm, bool &isDeleted)
{
ESM::Pathgrid::load (esm);
ESM::Pathgrid::load (esm, isDeleted);
mId = mCell;
if (mCell.empty())
{
std::ostringstream stream;
stream << "#" << mData.mX << " " << mData.mY;
mId = stream.str();
}
else
mId = mCell;
}

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

@ -19,12 +19,11 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
Cell& cell2 = base ? cell.mBase : cell.mModified;
CellRef ref;
bool deleted = false;
ESM::MovedCellRef mref;
bool isDeleted = false;
// hack to initialise mindex
while (!(mref.mRefNum.mIndex = 0) && ESM::Cell::getNextRef(reader, ref, deleted, true, &mref))
while (!(mref.mRefNum.mIndex = 0) && ESM::Cell::getNextRef(reader, ref, isDeleted, true, &mref))
{
// Keep mOriginalCell empty when in modified (as an indicator that the
// original cell will always be equal the current cell).
@ -49,17 +48,6 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
// https://forum.openmw.org/viewtopic.php?f=6&t=577&start=30
ref.mOriginalCell = cell2.mId;
if (deleted)
{
// FIXME: how to mark the record deleted?
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Cell,
mCells.getId (cellIndex));
messages.add (id, "Moved reference "+ref.mRefID+" is in DELE state");
continue;
}
// It is not always possibe to ignore moved references sub-record and
// calculate from coordinates. Some mods may place the ref in positions
// outside normal bounds, resulting in non sensical cell id's. This often
@ -91,7 +79,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
break;
}
if (deleted)
if (isDeleted)
{
if (iter==cache.end())
{
@ -99,7 +87,6 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
mCells.getId (cellIndex));
messages.add (id, "Attempt to delete a non-existing reference");
continue;
}
@ -107,7 +94,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
Record<CellRef> record = getRecord (index);
if (record.mState==RecordBase::State_BaseOnly)
if (base)
{
removeRows (index, 1);
cache.erase (iter);

@ -5,6 +5,8 @@
#include <utility>
#include <components/esm/loadcont.hpp>
#include <components/esm/loadmgef.hpp>
#include "nestedtablewrapper.hpp"
CSMWorld::PotionColumns::PotionColumns (const InventoryColumns& columns)
@ -25,8 +27,9 @@ QVariant CSMWorld::PotionRefIdAdapter::getData (const RefIdColumn *column, const
if (column==mAutoCalc)
return record.get().mData.mAutoCalc!=0;
// to show nested tables in dialogue subview, see IdTree::hasChildren()
if (column==mColumns.mEffects)
return true; // to show nested tables in dialogue subview, see IdTree::hasChildren()
return QVariant::fromValue(ColumnBase::TableEdit_Full);
return InventoryRefIdAdapter<ESM::Potion>::getData (column, data, index);
}
@ -52,6 +55,156 @@ void CSMWorld::PotionRefIdAdapter::setData (const RefIdColumn *column, RefIdData
}
CSMWorld::IngredientColumns::IngredientColumns (const InventoryColumns& columns)
: InventoryColumns (columns) {}
CSMWorld::IngredientRefIdAdapter::IngredientRefIdAdapter (const IngredientColumns& columns)
: InventoryRefIdAdapter<ESM::Ingredient> (UniversalId::Type_Ingredient, columns),
mColumns(columns)
{}
QVariant CSMWorld::IngredientRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data,
int index) const
{
if (column==mColumns.mEffects)
return QVariant::fromValue(ColumnBase::TableEdit_FixedRows);
return InventoryRefIdAdapter<ESM::Ingredient>::getData (column, data, index);
}
void CSMWorld::IngredientRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const
{
InventoryRefIdAdapter<ESM::Ingredient>::setData (column, data, index, value);
return;
}
CSMWorld::IngredEffectRefIdAdapter::IngredEffectRefIdAdapter()
: mType(UniversalId::Type_Ingredient)
{}
CSMWorld::IngredEffectRefIdAdapter::~IngredEffectRefIdAdapter()
{}
void CSMWorld::IngredEffectRefIdAdapter::addNestedRow (const RefIdColumn *column,
RefIdData& data, int index, int position) const
{
// Do nothing, this table cannot be changed by the user
}
void CSMWorld::IngredEffectRefIdAdapter::removeNestedRow (const RefIdColumn *column,
RefIdData& data, int index, int rowToRemove) const
{
// Do nothing, this table cannot be changed by the user
}
void CSMWorld::IngredEffectRefIdAdapter::setNestedTable (const RefIdColumn* column,
RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const
{
Record<ESM::Ingredient>& record =
static_cast<Record<ESM::Ingredient>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));
ESM::Ingredient ingredient = record.get();
ingredient.mData =
static_cast<const NestedTableWrapper<std::vector<typename ESM::Ingredient::IRDTstruct> >&>(nestedTable).mNestedTable.at(0);
record.setModified (ingredient);
}
CSMWorld::NestedTableWrapperBase* CSMWorld::IngredEffectRefIdAdapter::nestedTable (const RefIdColumn* column,
const RefIdData& data, int index) const
{
const Record<ESM::Ingredient>& record =
static_cast<const Record<ESM::Ingredient>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));
// return the whole struct
std::vector<typename ESM::Ingredient::IRDTstruct> wrap;
wrap.push_back(record.get().mData);
// deleted by dtor of NestedTableStoring
return new NestedTableWrapper<std::vector<typename ESM::Ingredient::IRDTstruct> >(wrap);
}
QVariant CSMWorld::IngredEffectRefIdAdapter::getNestedData (const RefIdColumn *column,
const RefIdData& data, int index, int subRowIndex, int subColIndex) const
{
const Record<ESM::Ingredient>& record =
static_cast<const Record<ESM::Ingredient>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));
if (subRowIndex < 0 || subRowIndex >= 4)
throw std::runtime_error ("index out of range");
switch (subColIndex)
{
case 0: return record.get().mData.mEffectID[subRowIndex];
case 1:
{
switch (record.get().mData.mEffectID[subRowIndex])
{
case ESM::MagicEffect::DrainSkill:
case ESM::MagicEffect::DamageSkill:
case ESM::MagicEffect::RestoreSkill:
case ESM::MagicEffect::FortifySkill:
case ESM::MagicEffect::AbsorbSkill:
return record.get().mData.mSkills[subRowIndex];
default:
return QVariant();
}
}
case 2:
{
switch (record.get().mData.mEffectID[subRowIndex])
{
case ESM::MagicEffect::DrainAttribute:
case ESM::MagicEffect::DamageAttribute:
case ESM::MagicEffect::RestoreAttribute:
case ESM::MagicEffect::FortifyAttribute:
case ESM::MagicEffect::AbsorbAttribute:
return record.get().mData.mAttributes[subRowIndex];
default:
return QVariant();
}
}
default:
throw std::runtime_error("Trying to access non-existing column in the nested table!");
}
}
void CSMWorld::IngredEffectRefIdAdapter::setNestedData (const RefIdColumn *column,
RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const
{
Record<ESM::Ingredient>& record =
static_cast<Record<ESM::Ingredient>&> (data.getRecord (RefIdData::LocalIndex (row, mType)));
ESM::Ingredient ingredient = record.get();
if (subRowIndex < 0 || subRowIndex >= 4)
throw std::runtime_error ("index out of range");
switch(subColIndex)
{
case 0: ingredient.mData.mEffectID[subRowIndex] = value.toInt(); break;
case 1: ingredient.mData.mSkills[subRowIndex] = value.toInt(); break;
case 2: ingredient.mData.mAttributes[subRowIndex] = value.toInt(); break;
default:
throw std::runtime_error("Trying to access non-existing column in the nested table!");
}
record.setModified (ingredient);
}
int CSMWorld::IngredEffectRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const
{
return 3; // effect, skill, attribute
}
int CSMWorld::IngredEffectRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const
{
return 4; // up to 4 effects
}
CSMWorld::ApparatusRefIdAdapter::ApparatusRefIdAdapter (const InventoryColumns& columns,
const RefIdColumn *type, const RefIdColumn *quality)
: InventoryRefIdAdapter<ESM::Apparatus> (UniversalId::Type_Apparatus, columns),
@ -118,7 +271,7 @@ QVariant CSMWorld::ArmorRefIdAdapter::getData (const RefIdColumn *column,
return record.get().mData.mArmor;
if (column==mPartRef)
return true; // to show nested tables in dialogue subview, see IdTree::hasChildren()
return QVariant::fromValue(ColumnBase::TableEdit_Full);
return EnchantableRefIdAdapter<ESM::Armor>::getData (column, data, index);
}
@ -206,7 +359,7 @@ QVariant CSMWorld::ClothingRefIdAdapter::getData (const RefIdColumn *column,
return record.get().mData.mType;
if (column==mPartRef)
return true; // to show nested tables in dialogue subview, see IdTree::hasChildren()
return QVariant::fromValue(ColumnBase::TableEdit_Full);
return EnchantableRefIdAdapter<ESM::Clothing>::getData (column, data, index);
}
@ -254,7 +407,7 @@ QVariant CSMWorld::ContainerRefIdAdapter::getData (const RefIdColumn *column,
return (record.get().mFlags & ESM::Container::Respawn)!=0;
if (column==mContent)
return true; // Required to show nested tables in dialogue subview
return QVariant::fromValue(ColumnBase::TableEdit_Full);
return NameRefIdAdapter<ESM::Container>::getData (column, data, index);
}
@ -323,13 +476,13 @@ QVariant CSMWorld::CreatureRefIdAdapter::getData (const RefIdColumn *column, con
return QString::fromUtf8 (record.get().mOriginal.c_str());
if (column==mColumns.mAttributes)
return true; // Required to show nested tables in dialogue subview
return QVariant::fromValue(ColumnBase::TableEdit_FixedRows);
if (column==mColumns.mAttacks)
return true; // Required to show nested tables in dialogue subview
return QVariant::fromValue(ColumnBase::TableEdit_FixedRows);
if (column==mColumns.mMisc)
return true; // Required to show nested items in dialogue subview
return QVariant::fromValue(ColumnBase::TableEdit_Full);
std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
mColumns.mFlags.find (column);
@ -569,13 +722,13 @@ QVariant CSMWorld::NpcRefIdAdapter::getData (const RefIdColumn *column, const Re
if (column==mColumns.mAttributes || column==mColumns.mSkills)
{
if ((record.get().mFlags & ESM::NPC::Autocalc) != 0)
return QVariant(QVariant::UserType);
return QVariant::fromValue(ColumnBase::TableEdit_None);
else
return true;
return QVariant::fromValue(ColumnBase::TableEdit_FixedRows);
}
if (column==mColumns.mMisc)
return true;
return QVariant::fromValue(ColumnBase::TableEdit_Full);
std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
mColumns.mFlags.find (column);

@ -11,6 +11,7 @@
#include <components/esm/loadnpc.hpp>
#include <components/esm/loadcrea.hpp>
#include "columnbase.hpp"
#include "record.hpp"
#include "refiddata.hpp"
#include "universalid.hpp"
@ -340,6 +341,66 @@ namespace CSMWorld
///< If the data type does not match an exception is thrown.
};
struct IngredientColumns : public InventoryColumns
{
const RefIdColumn *mEffects;
IngredientColumns (const InventoryColumns& columns);
};
class IngredientRefIdAdapter : public InventoryRefIdAdapter<ESM::Ingredient>
{
IngredientColumns mColumns;
public:
IngredientRefIdAdapter (const IngredientColumns& columns);
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)
const;
virtual void setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const;
///< If the data type does not match an exception is thrown.
};
class IngredEffectRefIdAdapter : public NestedRefIdAdapterBase
{
UniversalId::Type mType;
// not implemented
IngredEffectRefIdAdapter (const IngredEffectRefIdAdapter&);
IngredEffectRefIdAdapter& operator= (const IngredEffectRefIdAdapter&);
public:
IngredEffectRefIdAdapter();
virtual ~IngredEffectRefIdAdapter();
virtual void addNestedRow (const RefIdColumn *column,
RefIdData& data, int index, int position) const;
virtual void removeNestedRow (const RefIdColumn *column,
RefIdData& data, int index, int rowToRemove) const;
virtual void setNestedTable (const RefIdColumn* column,
RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const;
virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column,
const RefIdData& data, int index) const;
virtual QVariant getNestedData (const RefIdColumn *column,
const RefIdData& data, int index, int subRowIndex, int subColIndex) const;
virtual void setNestedData (const RefIdColumn *column,
RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const;
virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const;
virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const;
};
struct EnchantableColumns : public InventoryColumns
{
const RefIdColumn *mEnchantment;
@ -536,16 +597,16 @@ namespace CSMWorld
return record.get().mAiData.mAlarm;
if (column==mActors.mInventory)
return true; // to show nested tables in dialogue subview, see IdTree::hasChildren()
return QVariant::fromValue(ColumnBase::TableEdit_Full);
if (column==mActors.mSpells)
return true; // to show nested tables in dialogue subview, see IdTree::hasChildren()
return QVariant::fromValue(ColumnBase::TableEdit_Full);
if (column==mActors.mDestinations)
return true; // to show nested tables in dialogue subview, see IdTree::hasChildren()
return QVariant::fromValue(ColumnBase::TableEdit_Full);
if (column==mActors.mAiPackages)
return true; // to show nested tables in dialogue subview, see IdTree::hasChildren()
return QVariant::fromValue(ColumnBase::TableEdit_Full);
std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
mActors.mServices.find (column);
@ -2020,7 +2081,7 @@ namespace CSMWorld
int index) const
{
if (column==mLevList.mLevList || column == mLevList.mNestedListLevList)
return true; // to show nested tables in dialogue subview, see IdTree::hasChildren()
return QVariant::fromValue(ColumnBase::TableEdit_Full);
return BaseRefIdAdapter<RecordT>::getData (column, data, index);
}

@ -71,6 +71,21 @@ CSMWorld::RefIdCollection::RefIdCollection()
mColumns.push_back (RefIdColumn (Columns::ColumnId_CoinValue, ColumnBase::Display_Integer));
inventoryColumns.mValue = &mColumns.back();
IngredientColumns ingredientColumns (inventoryColumns);
mColumns.push_back (RefIdColumn (Columns::ColumnId_EffectList,
ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue));
ingredientColumns.mEffects = &mColumns.back();
std::map<UniversalId::Type, NestedRefIdAdapterBase*> ingredientEffectsMap;
ingredientEffectsMap.insert(std::make_pair(UniversalId::Type_Ingredient,
new IngredEffectRefIdAdapter ()));
mNestedAdapters.push_back (std::make_pair(&mColumns.back(), ingredientEffectsMap));
mColumns.back().addColumn(
new NestedChildColumn (Columns::ColumnId_EffectId, ColumnBase::Display_IngredEffectId));
mColumns.back().addColumn(
new NestedChildColumn (Columns::ColumnId_Skill, ColumnBase::Display_EffectSkill));
mColumns.back().addColumn(
new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_EffectAttribute));
// nested table
PotionColumns potionColumns (inventoryColumns);
mColumns.push_back (RefIdColumn (Columns::ColumnId_EffectList,
@ -83,9 +98,9 @@ CSMWorld::RefIdCollection::RefIdCollection()
mColumns.back().addColumn(
new NestedChildColumn (Columns::ColumnId_EffectId, ColumnBase::Display_EffectId));
mColumns.back().addColumn(
new NestedChildColumn (Columns::ColumnId_Skill, ColumnBase::Display_SkillId));
new NestedChildColumn (Columns::ColumnId_Skill, ColumnBase::Display_EffectSkill));
mColumns.back().addColumn(
new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_Attribute));
new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_EffectAttribute));
mColumns.back().addColumn(
new NestedChildColumn (Columns::ColumnId_EffectRange, ColumnBase::Display_EffectRange));
mColumns.back().addColumn(
@ -651,7 +666,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
mAdapters.insert (std::make_pair (UniversalId::Type_Door,
new DoorRefIdAdapter (nameColumns, openSound, closeSound)));
mAdapters.insert (std::make_pair (UniversalId::Type_Ingredient,
new InventoryRefIdAdapter<ESM::Ingredient> (UniversalId::Type_Ingredient, inventoryColumns)));
new IngredientRefIdAdapter (ingredientColumns)));
mAdapters.insert (std::make_pair (UniversalId::Type_CreatureLevelledList,
new LevelledListRefIdAdapter<ESM::CreatureLevList> (
UniversalId::Type_CreatureLevelledList, levListColumns)));
@ -822,61 +837,7 @@ const CSMWorld::RecordBase& CSMWorld::RefIdCollection::getRecord (int index) con
void CSMWorld::RefIdCollection::load (ESM::ESMReader& reader, bool base, UniversalId::Type type)
{
std::string id = reader.getHNOString ("NAME");
int index = searchId (id);
if (reader.isNextSub ("DELE"))
{
reader.skipRecord();
if (index==-1)
{
// deleting a record that does not exist
// ignore it for now
/// \todo report the problem to the user
}
else if (base)
{
mData.erase (index, 1);
}
else
{
mData.getRecord (mData.globalToLocalIndex (index)).mState = RecordBase::State_Deleted;
}
}
else
{
if (index==-1)
{
// new record
int index = mData.getAppendIndex (type);
mData.appendRecord (type, id, base);
RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index);
mData.load (localIndex, reader, base);
mData.getRecord (localIndex).mState =
base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
}
else
{
// old record
RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index);
if (!base)
if (mData.getRecord (localIndex).mState==RecordBase::State_Erased)
throw std::logic_error ("attempt to access a deleted record");
mData.load (localIndex, reader, base);
if (!base)
mData.getRecord (localIndex).mState = RecordBase::State_Modified;
}
}
mData.load(reader, base, type);
}
int CSMWorld::RefIdCollection::getAppendIndex (const std::string& id, UniversalId::Type type) const

@ -3,10 +3,20 @@
#include <cassert>
#include <memory>
#include <components/misc/stringops.hpp>
CSMWorld::RefIdDataContainerBase::~RefIdDataContainerBase() {}
std::string CSMWorld::RefIdData::getRecordId(const CSMWorld::RefIdData::LocalIndex &index) const
{
std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator found =
mRecordContainers.find (index.second);
if (found == mRecordContainers.end())
throw std::logic_error ("invalid local index type");
return found->second->getId(index.first);
}
CSMWorld::RefIdData::RefIdData()
{
mRecordContainers.insert (std::make_pair (UniversalId::Type_Activator, &mActivators));
@ -161,15 +171,27 @@ int CSMWorld::RefIdData::getAppendIndex (UniversalId::Type type) const
return index;
}
void CSMWorld::RefIdData::load (const LocalIndex& index, ESM::ESMReader& reader, bool base)
void CSMWorld::RefIdData::load (ESM::ESMReader& reader, bool base, CSMWorld::UniversalId::Type type)
{
std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =
mRecordContainers.find (index.second);
std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator found =
mRecordContainers.find (type);
if (iter==mRecordContainers.end())
throw std::logic_error ("invalid local index type");
if (found == mRecordContainers.end())
throw std::logic_error ("Invalid Referenceable ID type");
iter->second->load (index.first, reader, base);
int index = found->second->load(reader, base);
if (index != -1)
{
LocalIndex localIndex = LocalIndex(index, type);
if (base && getRecord(localIndex).mState == RecordBase::State_Deleted)
{
erase(localIndex, 1);
}
else
{
mIndex[Misc::StringUtils::lowerCase(getRecordId(localIndex))] = localIndex;
}
}
}
void CSMWorld::RefIdData::erase (const LocalIndex& index, int count)

@ -25,6 +25,8 @@
#include <components/esm/loadmisc.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/misc/stringops.hpp>
#include "record.hpp"
#include "universalid.hpp"
@ -49,7 +51,8 @@ namespace CSMWorld
virtual void insertRecord (RecordBase& record) = 0;
virtual void load (int index, ESM::ESMReader& reader, bool base) = 0;
virtual int load (ESM::ESMReader& reader, bool base) = 0;
///< \return index of a loaded record or -1 if no record was loaded
virtual void erase (int index, int count) = 0;
@ -73,7 +76,8 @@ namespace CSMWorld
virtual void insertRecord (RecordBase& record);
virtual void load (int index, ESM::ESMReader& reader, bool base);
virtual int load (ESM::ESMReader& reader, bool base);
///< \return index of a loaded record or -1 if no record was loaded
virtual void erase (int index, int count);
@ -122,9 +126,58 @@ namespace CSMWorld
}
template<typename RecordT>
void RefIdDataContainer<RecordT>::load (int index, ESM::ESMReader& reader, bool base)
int RefIdDataContainer<RecordT>::load (ESM::ESMReader& reader, bool base)
{
(base ? mContainer.at (index).mBase : mContainer.at (index).mModified).load (reader);
RecordT record;
bool isDeleted = false;
record.load(reader, isDeleted);
int index = 0;
int numRecords = static_cast<int>(mContainer.size());
for (; index < numRecords; ++index)
{
if (Misc::StringUtils::ciEqual(mContainer[index].get().mId, record.mId))
{
break;
}
}
if (isDeleted)
{
if (index == numRecords)
{
// deleting a record that does not exist
// ignore it for now
/// \todo report the problem to the user
return -1;
}
// Flag the record as Deleted even for a base content file.
// RefIdData is responsible for its erasure.
mContainer[index].mState = RecordBase::State_Deleted;
}
else
{
if (index == numRecords)
{
appendRecord(record.mId, base);
if (base)
{
mContainer.back().mBase = record;
}
else
{
mContainer.back().mModified = record;
}
}
else if (!base)
{
mContainer[index].setModified(record);
}
}
return index;
}
template<typename RecordT>
@ -145,19 +198,14 @@ namespace CSMWorld
template<typename RecordT>
void RefIdDataContainer<RecordT>::save (int index, ESM::ESMWriter& writer) const
{
CSMWorld::RecordBase::State state = mContainer.at (index).mState;
Record<RecordT> record = mContainer.at(index);
if (state==CSMWorld::RecordBase::State_Modified ||
state==CSMWorld::RecordBase::State_ModifiedOnly)
if (record.isModified() || record.mState == RecordBase::State_Deleted)
{
writer.startRecord (mContainer.at (index).mModified.sRecordId);
writer.writeHNCString ("NAME", getId (index));
mContainer.at (index).mModified.save (writer);
writer.endRecord (mContainer.at (index).mModified.sRecordId);
}
else if (state==CSMWorld::RecordBase::State_Deleted)
{
/// \todo write record with delete flag
RecordT esmRecord = record.get();
writer.startRecord(esmRecord.sRecordId);
esmRecord.save(writer, record.mState == RecordBase::State_Deleted);
writer.endRecord(esmRecord.sRecordId);
}
}
@ -198,6 +246,8 @@ namespace CSMWorld
void erase (const LocalIndex& index, int count);
///< Must not spill over into another type.
std::string getRecordId(const LocalIndex &index) const;
public:
RefIdData();
@ -221,7 +271,7 @@ namespace CSMWorld
int getAppendIndex (UniversalId::Type type) const;
void load (const LocalIndex& index, ESM::ESMReader& reader, bool base);
void load (ESM::ESMReader& reader, bool base, UniversalId::Type type);
int getSize() const;

@ -168,7 +168,7 @@ void CSMWorld::RegionMap::updateRegions (const std::vector<std::string>& regions
{
std::vector<std::string> regions2 (regions);
std::for_each (regions2.begin(), regions2.end(), &Misc::StringUtils::lowerCase);
std::for_each (regions2.begin(), regions2.end(), Misc::StringUtils::lowerCaseInPlace);
std::sort (regions2.begin(), regions2.end());
for (std::map<CellCoordinates, CellDescription>::const_iterator iter (mMap.begin());

@ -19,7 +19,9 @@ void CSMWorld::ResourcesManager::setVFS(const VFS::Manager *vfs)
mVFS = vfs;
mResources.clear();
static const char * const sMeshTypes[] = { "nif", 0 };
// maybe we could go over the osgDB::Registry to list all supported node formats
static const char * const sMeshTypes[] = { "nif", "osg", "osgt", "osgb", "osgx", "osg2", 0 };
addResources (Resources (vfs, "meshes", UniversalId::Type_Mesh, sMeshTypes));
addResources (Resources (vfs, "icons", UniversalId::Type_Icon));

@ -93,7 +93,7 @@ bool CSMWorld::ScriptContext::isId (const std::string& name) const
{
mIds = mData.getIds();
std::for_each (mIds.begin(), mIds.end(), &Misc::StringUtils::toLower);
std::for_each (mIds.begin(), mIds.end(), &Misc::StringUtils::lowerCaseInPlace);
std::sort (mIds.begin(), mIds.end());
mIdsUpdated = true;

@ -20,7 +20,7 @@ namespace CSMWorld
{
const IdCollection<Cell>& mCells;
virtual void loadRecord (ESXRecordT& record, ESM::ESMReader& reader);
virtual void loadRecord (ESXRecordT& record, ESM::ESMReader& reader, bool& isDeleted);
public:
@ -29,9 +29,10 @@ namespace CSMWorld
template<typename ESXRecordT, typename IdAccessorT>
void SubCellCollection<ESXRecordT, IdAccessorT>::loadRecord (ESXRecordT& record,
ESM::ESMReader& reader)
ESM::ESMReader& reader,
bool& isDeleted)
{
record.load (reader, mCells);
record.load (reader, isDeleted, mCells);
}
template<typename ESXRecordT, typename IdAccessorT>

@ -179,7 +179,7 @@ CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::Univers
}
}
throw std::runtime_error ("TableMimeData object does not hold object of the seeked type");
throw std::runtime_error ("TableMimeData object does not hold object of the sought type");
}
CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnBase::Display type) const
@ -201,7 +201,7 @@ CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnB
}
}
throw std::runtime_error ("TableMimeData object does not hold object of the seeked type");
throw std::runtime_error ("TableMimeData object does not hold object of the sought type");
}
bool CSMWorld::TableMimeData::fromDocument (const CSMDoc::Document& document) const

@ -16,7 +16,6 @@ CSVDoc::FileWidget::FileWidget (QWidget *parent) : QWidget (parent), mAddon (fal
QHBoxLayout *layout = new QHBoxLayout (this);
mInput = new QLineEdit (this);
mInput->setValidator (new QRegExpValidator(QRegExp("^[a-zA-Z0-9_-\\s]*$")));
layout->addWidget (mInput, 1);

@ -104,14 +104,16 @@ CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2)
layout->addWidget (createButtons());
layout->addWidget (createTools());
/// \todo remove this label once loading and saving are fully implemented
QLabel *warning = new QLabel ("<font color=Red>WARNING:<p>OpenCS is in alpha stage.<br>The code for loading and saving is incomplete.<br>This version of OpenCS is only a preview.<br>Do NOT use it for real editing!<br>You will lose records both on loading and on saving.<p>Please note:<br>If you lose data and come to the OpenMW forum to complain,<br>we will mock you.</font color>");
/// \todo remove this label once we are feature complete and convinced that this thing is
/// working properly.
QLabel *warning = new QLabel ("<font color=Red>WARNING: OpenMW-CS is in alpha stage.<p>The editor is not feature complete and not sufficiently tested.<br>In theory your data should be safe. But we strongly advice to make backups regularly if you are working with live data.</font color>");
QFont font;
font.setPointSize (12);
font.setBold (true);
warning->setFont (font);
warning->setWordWrap (true);
layout->addWidget (warning, 1);

@ -16,7 +16,7 @@ bool CSVDoc::SubView::event (QEvent *event)
emit closeRequest();
return true;
}
return QDockWidget::event (event);
}
@ -38,9 +38,6 @@ void CSVDoc::SubView::setStatusBar (bool show) {}
void CSVDoc::SubView::useHint (const std::string& hint) {}
void CSVDoc::SubView::updateUserSetting (const QString &, const QStringList &)
{}
void CSVDoc::SubView::setUniversalId (const CSMWorld::UniversalId& id)
{
mUniversalId = id;

@ -52,8 +52,6 @@ namespace CSVDoc
virtual std::string getTitle() const;
virtual void updateUserSetting (const QString& name, const QStringList& value);
private:
void closeEvent (QCloseEvent *event);

@ -15,7 +15,7 @@
#include <QScrollBar>
#include "../../model/doc/document.hpp"
#include "../../model/settings/usersettings.hpp"
#include "../../model/prefs/state.hpp"
#include "../../model/world/idtable.hpp"
@ -121,10 +121,9 @@ void CSVDoc::View::setupViewMenu()
mShowStatusBar = new QAction (tr ("Show Status Bar"), this);
mShowStatusBar->setCheckable (true);
connect (mShowStatusBar, SIGNAL (toggled (bool)), this, SLOT (toggleShowStatusBar (bool)));
std::string showStatusBar =
CSMSettings::UserSettings::instance().settingValue("window/show-statusbar").toStdString();
if(showStatusBar == "true")
mShowStatusBar->setChecked(true);
mShowStatusBar->setChecked (CSMPrefs::get()["Windows"]["show-statusbar"].isTrue());
view->addAction (mShowStatusBar);
QAction *filters = new QAction (tr ("Filters"), this);
@ -333,9 +332,9 @@ void CSVDoc::View::updateTitle()
if (mViewTotal>1)
stream << " [" << (mViewIndex+1) << "/" << mViewTotal << "]";
CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance();
CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"];
bool hideTitle = userSettings.setting ("window/hide-subview", QString ("false"))=="true" &&
bool hideTitle = windows["hide-subview"].isTrue() &&
mSubViews.size()==1 && !mSubViews.at (0)->isFloating();
if (hideTitle)
@ -346,19 +345,18 @@ void CSVDoc::View::updateTitle()
void CSVDoc::View::updateSubViewIndicies(SubView *view)
{
CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"];
if(view && mSubViews.contains(view))
{
mSubViews.removeOne(view);
// adjust (reduce) the scroll area (even floating), except when it is "Scrollbar Only"
CSMSettings::UserSettings &settings = CSMSettings::UserSettings::instance();
if(settings.settingValue ("window/mainwindow-scrollbar") == "Grow then Scroll")
if (windows["mainwindow-scrollbar"].toString() == "Grow then Scroll")
updateScrollbar();
}
CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance();
bool hideTitle = userSettings.setting ("window/hide-subview", QString ("false"))=="true" &&
bool hideTitle = windows["hide-subview"].isTrue() &&
mSubViews.size()==1 && !mSubViews.at (0)->isFloating();
updateTitle();
@ -406,21 +404,16 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to
: mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1),
mViewTotal (totalViews), mScroll(0), mScrollbarOnly(false)
{
int width = CSMSettings::UserSettings::instance().settingValue
("window/default-width").toInt();
int height = CSMSettings::UserSettings::instance().settingValue
("window/default-height").toInt();
CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"];
width = std::max(width, 300);
height = std::max(height, 300);
int width = std::max (windows["default-width"].toInt(), 300);
int height = std::max (windows["default-height"].toInt(), 300);
resize (width, height);
mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks);
CSMSettings::UserSettings &settings = CSMSettings::UserSettings::instance();
if(settings.settingValue ("window/mainwindow-scrollbar") == "Grow Only")
if (windows["mainwindow-scrollbar"].toString() == "Grow Only")
{
setCentralWidget (&mSubViewWindow);
}
@ -449,6 +442,9 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to
mSubViewFactory.add (CSMWorld::UniversalId::Type_RunLog, new SubViewFactory<RunLogSubView>);
connect (mOperations, SIGNAL (abortOperation (int)), this, SLOT (abortOperation (int)));
connect (&CSMPrefs::State::get(), SIGNAL (settingChanged (const CSMPrefs::Setting *)),
this, SLOT (settingChanged (const CSMPrefs::Setting *)));
}
CSVDoc::View::~View()
@ -503,14 +499,12 @@ void CSVDoc::View::updateProgress (int current, int max, int type, int threads)
void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::string& hint)
{
CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance();
CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"];
bool isReferenceable = id.getClass() == CSMWorld::UniversalId::Class_RefRecord;
// User setting to reuse sub views (on a per top level view basis)
bool reuse =
userSettings.setting ("window/reuse", QString("true")) == "true" ? true : false;
if(reuse)
if (windows["reuse"].isTrue())
{
foreach(SubView *sb, mSubViews)
{
@ -538,8 +532,7 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin
//
// If the sub view limit setting is one, the sub view title bar is hidden and the
// text in the main title bar is adjusted accordingly
int maxSubView = userSettings.setting("window/max-subviews", QString("256")).toInt();
if(mSubViews.size() >= maxSubView) // create a new top level view
if(mSubViews.size() >= windows["max-subviews"].toInt()) // create a new top level view
{
mViewManager.addView(mDocument, id, hint);
@ -559,8 +552,8 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin
view->setParent(this);
mSubViews.append(view); // only after assert
int minWidth = userSettings.setting ("window/minimum-width", QString("325")).toInt();
view->setMinimumWidth(minWidth);
int minWidth = windows["minimum-width"].toInt();
view->setMinimumWidth (minWidth);
view->setStatusBar (mShowStatusBar->isChecked());
@ -575,13 +568,11 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin
// should become visible)
// - Move the scroll bar to the newly added subview
//
CSMSettings::UserSettings &settings = CSMSettings::UserSettings::instance();
QString mainwinScroll = settings.settingValue ("window/mainwindow-scrollbar");
mScrollbarOnly = mainwinScroll.isEmpty() || mainwinScroll == "Scrollbar Only";
mScrollbarOnly = windows["mainwindow-scrollbar"].toString() == "Scrollbar Only";
QDesktopWidget *dw = QApplication::desktop();
QRect rect;
if(settings.settingValue ("window/grow-limit") == "true")
if (windows["grow-limit"].isTrue())
rect = dw->screenGeometry(this);
else
rect = dw->screenGeometry(dw->screen(dw->screenNumber(this)));
@ -637,6 +628,45 @@ void CSVDoc::View::moveScrollBarToEnd(int min, int max)
}
}
void CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting)
{
if (*setting=="Windows/hide-subview")
updateSubViewIndicies (0);
else if (*setting=="Windows/mainwindow-scrollbar")
{
if (setting->toString()!="Grow Only")
{
if (mScroll)
{
if (setting->toString()=="Scrollbar Only")
{
mScrollbarOnly = true;
mSubViewWindow.setMinimumWidth(0);
}
else if (mScrollbarOnly)
{
mScrollbarOnly = false;
updateScrollbar();
}
}
else
{
mScroll = new QScrollArea(this);
mScroll->setWidgetResizable(true);
mScroll->setWidget(&mSubViewWindow);
setCentralWidget(mScroll);
}
}
else if (mScroll)
{
mScroll->takeWidget();
setCentralWidget (&mSubViewWindow);
mScroll->deleteLater();
mScroll = 0;
}
}
}
void CSVDoc::View::newView()
{
mViewManager.addView (mDocument);
@ -860,59 +890,6 @@ void CSVDoc::View::resizeViewHeight (int height)
resize (geometry().width(), height);
}
void CSVDoc::View::updateUserSetting (const QString &name, const QStringList &list)
{
if (name=="window/hide-subview")
updateSubViewIndicies (0);
foreach (SubView *subView, mSubViews)
{
subView->updateUserSetting (name, list);
}
if (name=="window/mainwindow-scrollbar")
{
if(list.at(0) != "Grow Only")
{
if (mScroll)
{
if (list.at(0).isEmpty() || list.at(0) == "Scrollbar Only")
{
mScrollbarOnly = true;
mSubViewWindow.setMinimumWidth(0);
}
else
{
if(!mScrollbarOnly)
return;
mScrollbarOnly = false;
updateScrollbar();
}
}
else
{
mScroll = new QScrollArea(this);
mScroll->setWidgetResizable(true);
mScroll->setWidget(&mSubViewWindow);
setCentralWidget(mScroll);
}
}
else
{
if (mScroll)
{
mScroll->takeWidget();
setCentralWidget (&mSubViewWindow);
mScroll->deleteLater();
mScroll = 0;
}
else
return;
}
}
}
void CSVDoc::View::toggleShowStatusBar (bool show)
{
foreach (QObject *view, mSubViewWindow.children())
@ -944,10 +921,9 @@ void CSVDoc::View::stop()
void CSVDoc::View::closeRequest (SubView *subView)
{
CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance();
CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"];
if (mSubViews.size()>1 || mViewTotal<=1 ||
userSettings.setting ("window/hide-subview", QString ("false"))!="true")
if (mSubViews.size()>1 || mViewTotal<=1 || !windows["hide-subview"].isTrue())
{
subView->deleteLater();
mSubViews.removeOne (subView);

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

Loading…
Cancel
Save