Merge branch 'cc9cii' into Feature-1278

Conflicts:
	apps/opencs/CMakeLists.txt
	apps/opencs/model/world/nestedcoladapterimp.cpp
	apps/opencs/view/render/cell.cpp
	apps/opencs/view/render/worldspacewidget.cpp
pull/1955/head
cc9cii 9 years ago
commit 9716b671f1

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

@ -19,6 +19,7 @@ Programmers
Alexander Nadeau (wareya)
Alexander Olofsson (Ace)
Artem Kotsynyak (greye)
artemutin
Arthur Moore (EmperorArthur)
athile
Bret Curtis (psi29a)
@ -59,6 +60,7 @@ Programmers
Julien Voisin (jvoisin/ap0)
Karl-Felix Glatzer (k1ll)
Kevin Poitra (PuppyKevin)
Koncord
Lars Söderberg (Lazaroth)
lazydev
Leon Saunders (emoose)
@ -109,6 +111,7 @@ Programmers
viadanna
Vincent Heuken
vocollapse
zelurker
Manual
------

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

@ -98,25 +98,29 @@ endif()
cmake_minimum_required(VERSION 2.6)
# Sound setup
set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE SWRESAMPLE AVRESAMPLE)
unset(FFMPEG_LIBRARIES CACHE)
find_package(FFmpeg)
find_package(FFmpeg REQUIRED)
set (FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${SWSCALE_LIBRARY})
if ( NOT AVCODEC_FOUND OR NOT AVFORMAT_FOUND OR NOT AVUTIL_FOUND OR NOT SWSCALE_FOUND )
message(FATAL_ERROR "FFmpeg component required, but not found!")
endif()
set(SOUND_INPUT_INCLUDES ${FFMPEG_INCLUDE_DIRS})
set(SOUND_INPUT_LIBRARY ${FFMPEG_LIBRARIES} ${SWSCALE_LIBRARIES})
if( SWRESAMPLE_FOUND )
add_definitions(-DHAVE_LIBSWRESAMPLE)
set(SOUND_INPUT_LIBRARY ${FFMPEG_LIBRARIES} ${SWRESAMPLE_LIBRARIES})
set (FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${SWRESAMPLE_LIBRARIES})
else()
if( AVRESAMPLE_FOUND )
set(SOUND_INPUT_LIBRARY ${FFMPEG_LIBRARIES} ${AVRESAMPLE_LIBRARIES})
set (FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${AVRESAMPLE_LIBRARIES})
else()
message(FATAL_ERROR "Install either libswresample (FFmpeg) or libavresample (Libav).")
endif()
endif()
set(SOUND_INPUT_LIBRARY ${FFMPEG_LIBRARIES})
# TinyXML
option(USE_SYSTEM_TINYXML "Use system TinyXML library instead of internal." OFF)
if(USE_SYSTEM_TINYXML)
@ -151,6 +155,19 @@ endif()
# Dependencies
set(DESIRED_QT_VERSION 4 CACHE STRING "The QT version OpenMW should use (4 or 5)")
message(STATUS "Using Qt${DESIRED_QT_VERSION}")
if (DESIRED_QT_VERSION MATCHES 4)
find_package(Qt4 REQUIRED COMPONENTS QtCore QtGui QtNetwork)
else()
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Core REQUIRED)
find_package(Qt5Network REQUIRED)
# Instruct CMake to run moc automatically when needed.
#set(CMAKE_AUTOMOC ON)
endif()
# Fix for not visible pthreads functions for linker with glibc 2.15
if (UNIX AND NOT APPLE)
find_package (Threads)
@ -171,7 +188,7 @@ if (HAVE_UNORDERED_MAP)
endif ()
set(BOOST_COMPONENTS system filesystem program_options)
set(BOOST_COMPONENTS system filesystem program_options thread wave)
if(WIN32)
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale)
endif(WIN32)
@ -347,6 +364,8 @@ configure_file(${OpenMW_SOURCE_DIR}/files/gamecontrollerdb.txt
if (NOT WIN32 AND NOT APPLE)
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop
"${OpenMW_BINARY_DIR}/openmw.desktop")
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.appdata.xml
"${OpenMW_BINARY_DIR}/openmw.appdata.xml")
configure_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.desktop
"${OpenMW_BINARY_DIR}/openmw-cs.desktop")
endif()
@ -404,6 +423,9 @@ IF(NOT WIN32 AND NOT APPLE)
IF(BUILD_ESMTOOL)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" )
ENDIF(BUILD_ESMTOOL)
IF(BUILD_NIFTEST)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/niftest" DESTINATION "${BINDIR}" )
ENDIF(BUILD_NIFTEST)
IF(BUILD_MWINIIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-iniimporter" DESTINATION "${BINDIR}" )
ENDIF(BUILD_MWINIIMPORTER)
@ -430,6 +452,7 @@ IF(NOT WIN32 AND NOT APPLE)
# Install icon and desktop file
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "openmw")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}" COMPONENT "openmw")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.appdata.xml" DESTINATION "${DATAROOTDIR}/appdata" COMPONENT "openmw")
IF(BUILD_OPENCS)
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "opencs")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/opencs/openmw-cs.png" DESTINATION "${ICONDIR}" COMPONENT "opencs")
@ -600,6 +623,10 @@ if (BUILD_WIZARD)
add_subdirectory(apps/wizard)
endif()
if (BUILD_NIFTEST)
add_subdirectory(apps/niftest)
endif(BUILD_NIFTEST)
# UnitTests
if (BUILD_UNITTESTS)
add_subdirectory( apps/openmw_test_suite )

@ -0,0 +1,16 @@
Description
===========
Your pull request description should include (if applicable):
* A link back to the bug report or forum discussion that prompted the change
* Summary of the changes made
* Reasoning / motivation behind the change
* What testing you have carried out to verify the change
Other notes
===========
* Separate your work into multiple pull requests whenever possible. As a rule of thumb, each feature and each bugfix should go into a separate PR, unless they are closely related or dependent upon each other. Small pull requests are easier to review, and are less likely to require further changes before we can merge them. A "mega" pull request with lots of unrelated commits in it is likely to get held up in review for a long time.
* Feel free to submit incomplete pull requests. Even if the work can not be merged yet, pull requests are a great place to collect early feedback. Just make sure to mark it as *[Incomplete]* or *[Do not merge yet]* in the title.
* If you plan on contributing often, please read the [Developer Reference](https://wiki.openmw.org/index.php?title=Developer_Reference) on our wiki, especially the [Policies and Standards](https://wiki.openmw.org/index.php?title=Policies_and_Standards).

@ -9,7 +9,8 @@ add_executable(bsatool
)
target_link_libraries(bsatool
${Boost_LIBRARIES}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
components
)

@ -1,4 +1,5 @@
#include <iostream>
#include <iomanip>
#include <vector>
#include <exception>

@ -13,7 +13,7 @@ add_executable(esmtool
)
target_link_libraries(esmtool
${Boost_LIBRARIES}
${Boost_PROGRAM_OPTIONS_LIBRARY}
components
)

@ -119,7 +119,7 @@ std::string clothingTypeLabel(int idx)
}
std::string armorTypeLabel(int idx)
{
{
if (idx >= 0 && idx <= 10)
{
static const char *armorTypeLabels[] = {
@ -645,7 +645,7 @@ std::string ruleFunction(int idx)
else
return "Invalid";
}
// The "unused flag bits" should probably be defined alongside the
// defined bits in the ESM component. The names of the flag bits are
// very inconsistent.
@ -653,7 +653,7 @@ std::string ruleFunction(int idx)
std::string bodyPartFlags(int flags)
{
std::string properties = "";
if (flags == 0) properties += "[None] ";
if (flags == 0) properties += "[None] ";
if (flags & ESM::BodyPart::BPF_Female) properties += "Female ";
if (flags & ESM::BodyPart::BPF_NotPlayable) properties += "NotPlayable ";
int unused = (0xFFFFFFFF ^
@ -667,7 +667,7 @@ std::string bodyPartFlags(int flags)
std::string cellFlags(int flags)
{
std::string properties = "";
if (flags == 0) properties += "[None] ";
if (flags == 0) properties += "[None] ";
if (flags & ESM::Cell::HasWater) properties += "HasWater ";
if (flags & ESM::Cell::Interior) properties += "Interior ";
if (flags & ESM::Cell::NoSleep) properties += "NoSleep ";
@ -830,12 +830,12 @@ std::string npcFlags(int flags)
std::string properties = "";
if (flags == 0) properties += "[None] ";
// Mythicmods and the ESM component differ. Mythicmods says
// 0x8=None and 0x10=AutoCalc, while our code defines 0x8 as
// AutoCalc. The former seems to be correct. All Bethesda
// records have bit 0x8 set. A suspiciously large portion of
// females have autocalc turned off.
if (flags & ESM::NPC::Autocalc) properties += "Unknown ";
if (flags & 0x00000010) properties += "Autocalc ";
// 0x8=None and 0x10=AutoCalc, while our code previously defined
// 0x8 as AutoCalc. The former seems to be correct. All Bethesda
// records have bit 0x8 set. Previously, suspiciously large portion
// of females had autocalc turned off.
if (flags & 0x00000008) properties += "Unknown ";
if (flags & ESM::NPC::Autocalc) properties += "Autocalc ";
if (flags & ESM::NPC::Female) properties += "Female ";
if (flags & ESM::NPC::Respawn) properties += "Respawn ";
if (flags & ESM::NPC::Essential) properties += "Essential ";
@ -847,8 +847,8 @@ std::string npcFlags(int flags)
// however the only unknown bit occurs on ALL records, and
// relatively few NPCs have this bit set.
int unused = (0xFFFFFFFF ^
(ESM::NPC::Autocalc|
0x00000010|
(0x00000008|
ESM::NPC::Autocalc|
ESM::NPC::Female|
ESM::NPC::Respawn|
ESM::NPC::Essential|

@ -493,14 +493,14 @@ void Record<ESM::Book>::print()
std::cout << " Enchantment Points: " << mData.mData.mEnchant << std::endl;
if (mPrintPlain)
{
std::cout << " Text:" << std::endl;
std::cout << "START--------------------------------------" << std::endl;
std::cout << mData.mText << std::endl;
std::cout << "END----------------------------------------" << std::endl;
std::cout << " Text:" << std::endl;
std::cout << "START--------------------------------------" << std::endl;
std::cout << mData.mText << std::endl;
std::cout << "END----------------------------------------" << std::endl;
}
else
{
std::cout << " Text: [skipped]" << std::endl;
std::cout << " Text: [skipped]" << std::endl;
}
}
@ -799,14 +799,14 @@ void Record<ESM::DialInfo>::print()
{
if (mPrintPlain)
{
std::cout << " Result Script:" << std::endl;
std::cout << "START--------------------------------------" << std::endl;
std::cout << mData.mResultScript << std::endl;
std::cout << "END----------------------------------------" << std::endl;
std::cout << " Result Script:" << std::endl;
std::cout << "START--------------------------------------" << std::endl;
std::cout << mData.mResultScript << std::endl;
std::cout << "END----------------------------------------" << std::endl;
}
else
{
std::cout << " Result Script: [skipped]" << std::endl;
std::cout << " Result Script: [skipped]" << std::endl;
}
}
}
@ -841,19 +841,13 @@ void Record<ESM::Land>::print()
std::cout << " Flags: " << landFlags(mData.mFlags) << std::endl;
std::cout << " DataTypes: " << mData.mDataTypes << std::endl;
// Seems like this should done with reference counting in the
// loader to me. But I'm not really knowledgable about this
// record type yet. --Cory
bool wasLoaded = (mData.mDataLoaded != 0);
if (mData.mDataTypes) mData.loadData(mData.mDataTypes);
if (mData.mDataLoaded)
if (const ESM::Land::LandData *data = mData.getLandData (mData.mDataTypes))
{
std::cout << " Height Offset: " << mData.mLandData->mHeightOffset << std::endl;
std::cout << " Height Offset: " << data->mHeightOffset << std::endl;
// Lots of missing members.
std::cout << " Unknown1: " << mData.mLandData->mUnk1 << std::endl;
std::cout << " Unknown2: " << mData.mLandData->mUnk2 << std::endl;
std::cout << " Unknown1: " << data->mUnk1 << std::endl;
std::cout << " Unknown2: " << data->mUnk2 << std::endl;
}
if (!wasLoaded) mData.unloadData();
}
template<>
@ -1207,14 +1201,14 @@ void Record<ESM::Script>::print()
if (mPrintPlain)
{
std::cout << " Script:" << std::endl;
std::cout << "START--------------------------------------" << std::endl;
std::cout << mData.mScriptText << std::endl;
std::cout << "END----------------------------------------" << std::endl;
std::cout << " Script:" << std::endl;
std::cout << "START--------------------------------------" << std::endl;
std::cout << mData.mScriptText << std::endl;
std::cout << "END----------------------------------------" << std::endl;
}
else
{
std::cout << " Script: [skipped]" << std::endl;
std::cout << " Script: [skipped]" << std::endl;
}
}

@ -53,7 +53,7 @@ namespace EsmTool
}
void setPrintPlain(bool plain) {
mPrintPlain = plain;
mPrintPlain = plain;
}
virtual void load(ESM::ESMReader &esm) = 0;

@ -33,7 +33,8 @@ add_executable(openmw-essimporter
)
target_link_libraries(openmw-essimporter
${Boost_LIBRARIES}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
components
)

@ -41,9 +41,9 @@ namespace ESSImport
{
for (int i=0; i<ESM::Skill::Length; ++i)
{
npcStats.mSkills[i].mRegular.mMod = actorData.mSkills[i][1];
npcStats.mSkills[i].mRegular.mCurrent = actorData.mSkills[i][1];
npcStats.mSkills[i].mRegular.mBase = actorData.mSkills[i][0];
npcStats.mSkills[i].mMod = actorData.mSkills[i][1];
npcStats.mSkills[i].mCurrent = actorData.mSkills[i][1];
npcStats.mSkills[i].mBase = actorData.mSkills[i][0];
}
npcStats.mTimeToStartDrowning = actorData.mACDT.mBreathMeter;

@ -18,7 +18,7 @@ namespace ESSImport
for (int i=0; i<8; ++i)
out.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i];
for (int i=0; i<27; ++i)
out.mObject.mNpcStats.mSkills[i].mRegular.mProgress = pcdt.mPNAM.mSkillProgress[i];
out.mObject.mNpcStats.mSkills[i].mProgress = pcdt.mPNAM.mSkillProgress[i];
out.mObject.mNpcStats.mLevelProgress = pcdt.mPNAM.mLevelProgress;
if (pcdt.mPNAM.mDrawState & PCDT::DrawState_Weapon)

@ -1,7 +1,11 @@
#include "importer.hpp"
#include <iomanip>
#include <boost/shared_ptr.hpp>
#include <OgreRoot.h>
#include <OgreLogManager.h>
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
@ -292,7 +296,7 @@ namespace ESSImport
ESM::ESMWriter writer;
writer.setFormat (ESM::Header::CurrentFormat);
writer.setFormat (ESM::SavedGame::sCurrentFormat);
std::ofstream stream(mOutFile.c_str(), std::ios::binary);
// all unused

@ -57,7 +57,6 @@ set(LAUNCHER_UI
source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER})
find_package(Qt4 REQUIRED)
set(QT_USE_QTGUI 1)
# Set some platform specific settings
@ -66,12 +65,17 @@ if(WIN32)
set(QT_USE_QTMAIN TRUE)
endif(WIN32)
QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)
QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC})
QT4_WRAP_UI(UI_HDRS ${LAUNCHER_UI})
if (DESIRED_QT_VERSION MATCHES 4)
include(${QT_USE_FILE})
QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)
QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC})
QT4_WRAP_UI(UI_HDRS ${LAUNCHER_UI})
else()
QT5_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)
QT5_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC})
QT5_WRAP_UI(UI_HDRS ${LAUNCHER_UI})
endif()
include(${QT_USE_FILE})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
if(NOT WIN32)
include_directories(${LIBUNSHIELD_INCLUDE_DIR})
@ -88,17 +92,27 @@ add_executable(openmw-launcher
)
target_link_libraries(openmw-launcher
${Boost_LIBRARIES}
${OGRE_LIBRARIES}
${OGRE_STATIC_PLUGINS}
${SDL2_LIBRARY_ONLY}
${QT_LIBRARIES}
components
)
if (DESIRED_QT_VERSION MATCHES 4)
target_link_libraries(openmw-launcher ${QT_QTGUI_LIBRARY} ${QT_QTCORE_LIBRARY})
if(WIN32)
target_link_libraries(openmw-launcher ${QT_QTMAIN_LIBRARY})
endif(WIN32)
else()
qt5_use_modules(openmw-launcher Widgets Core)
if (WIN32)
target_link_libraries(Qt5::WinMain)
endif()
endif()
if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage)
target_link_libraries(openmw-launcher gcov)
endif()

@ -54,9 +54,6 @@ int main(int argc, char *argv[])
QDir::setCurrent(dir.absolutePath());
// Support non-latin characters
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
Launcher::MainDialog mainWin;
Launcher::FirstRunDialogResult result = mainWin.showFirstRunDialog();

@ -309,11 +309,11 @@ bool Launcher::MainDialog::setupGameSettings()
mGameSettings.readUserFile(stream);
}
// Now the rest
// Now the rest - priority: user > local > global
QStringList paths;
paths.append(userPath + QString("openmw.cfg"));
paths.append(QString("openmw.cfg"));
paths.append(globalPath + QString("openmw.cfg"));
paths.append(QString("openmw.cfg"));
paths.append(userPath + QString("openmw.cfg"));
foreach (const QString &path, paths) {
qDebug() << "Loading config file:" << qPrintable(path);

@ -14,10 +14,16 @@ add_executable(openmw-iniimporter
)
target_link_libraries(openmw-iniimporter
${Boost_LIBRARIES}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
components
)
if (WIN32)
target_link_libraries(openmw-iniimporter
${Boost_LOCALE_LIBRARY})
endif()
if (MINGW)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -municode")
endif()

@ -0,0 +1,19 @@
set(NIFTEST
niftest.cpp
)
source_group(components\\nif\\tests FILES ${NIFTEST})
# Main executable
add_executable(niftest
${NIFTEST}
)
target_link_libraries(niftest
${Boost_FILESYSTEM_LIBRARY}
components
)
if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage)
target_link_libraries(niftest gcov)
endif()

@ -0,0 +1,165 @@
///Program to test .nif files both on the FileSystem and in BSA archives.
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <components/nif/niffile.hpp>
#include <components/files/constrainedfilestream.hpp>
#include <components/vfs/manager.hpp>
#include <components/vfs/bsaarchive.hpp>
#include <components/vfs/filesystemarchive.hpp>
#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>
// Create local aliases for brevity
namespace bpo = boost::program_options;
namespace bfs = boost::filesystem;
///See if the file has the named extension
bool hasExtension(std::string filename, std::string extensionToFind)
{
std::string extension = filename.substr(filename.find_last_of(".")+1);
//Convert strings to lower case for comparison
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
std::transform(extensionToFind.begin(), extensionToFind.end(), extensionToFind.begin(), ::tolower);
if(extension == extensionToFind)
return true;
else
return false;
}
///See if the file has the "nif" extension.
bool isNIF(std::string filename)
{
return hasExtension(filename,"nif");
}
///See if the file has the "bsa" extension.
bool isBSA(std::string filename)
{
return hasExtension(filename,"bsa");
}
/// Check all the nif files in a given VFS::Archive
/// \note Takes ownership!
/// \note Can not read a bsa file inside of a bsa file.
void readVFS(VFS::Archive* anArchive,std::string archivePath = "")
{
VFS::Manager myManager(true);
myManager.addArchive(anArchive);
myManager.buildIndex();
std::map<std::string, VFS::File*> files=myManager.getIndex();
for(std::map<std::string, VFS::File*>::const_iterator it=files.begin(); it!=files.end(); ++it)
{
std::string name = it->first;
try{
if(isNIF(name))
{
// std::cout << "Decoding: " << name << std::endl;
Nif::NIFFile temp_nif(myManager.get(name),archivePath+name);
}
else if(isBSA(name))
{
if(!archivePath.empty() && !isBSA(archivePath))
{
// std::cout << "Reading BSA File: " << name << std::endl;
readVFS(new VFS::BsaArchive(archivePath+name),archivePath+name+"/");
// std::cout << "Done with BSA File: " << name << std::endl;
}
}
}
catch (std::exception& e)
{
std::cerr << "ERROR, an exception has occurred: " << e.what() << std::endl;
}
}
}
std::vector<std::string> parseOptions (int argc, char** argv)
{
bpo::options_description desc("Ensure that OpenMW can use the provided NIF and BSA files\n\n"
"Usages:\n"
" niftool <nif files, BSA files, or directories>\n"
" Scan the file or directories for nif errors.\n\n"
"Allowed options");
desc.add_options()
("help,h", "print help message.")
("input-file", bpo::value< std::vector<std::string> >(), "input file")
;
//Default option if none provided
bpo::positional_options_description p;
p.add("input-file", -1);
bpo::variables_map variables;
try
{
bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv).
options(desc).positional(p).run();
bpo::store(valid_opts, variables);
}
catch(std::exception &e)
{
std::cout << "ERROR parsing arguments: " << e.what() << "\n\n"
<< desc << std::endl;
exit(1);
}
bpo::notify(variables);
if (variables.count ("help"))
{
std::cout << desc << std::endl;
exit(1);
}
if (variables.count("input-file"))
{
return variables["input-file"].as< std::vector<std::string> >();
}
std::cout << "No input files or directories specified!" << std::endl;
std::cout << desc << std::endl;
exit(1);
}
int main(int argc, char **argv)
{
std::vector<std::string> files = parseOptions (argc, argv);
// std::cout << "Reading Files" << std::endl;
for(std::vector<std::string>::const_iterator it=files.begin(); it!=files.end(); ++it)
{
std::string name = *it;
try{
if(isNIF(name))
{
//std::cout << "Decoding: " << name << std::endl;
Nif::NIFFile temp_nif(Files::openConstrainedFileStream(name.c_str()),name);
}
else if(isBSA(name))
{
// std::cout << "Reading BSA File: " << name << std::endl;
readVFS(new VFS::BsaArchive(name));
}
else if(bfs::is_directory(bfs::path(name)))
{
// std::cout << "Reading All Files in: " << name << std::endl;
readVFS(new VFS::FileSystemArchive(name),name);
}
else
{
std::cerr << "ERROR: \"" << name << "\" is not a nif file, bsa file, or directory!" << std::endl;
}
}
catch (std::exception& e)
{
std::cerr << "ERROR, an exception has occurred: " << e.what() << std::endl;
}
}
return 0;
}

@ -18,16 +18,16 @@ opencs_hdrs_noqt (model/doc
opencs_units (model/world
idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable
nestedtableproxymodel idtree pathgridcommands
idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel
pathgridcommands
)
opencs_units_noqt (model/world
universalid record commands columnbase scriptcontext cell refidcollection
universalid record commands columnbase columnimp scriptcontext cell refidcollection
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope
pathgrid landtexture land nestedtablewrapper nestedcollection nestedcoladapterimp nestedinfocollection
idcompletionmanager
idcompletionmanager npcstats metadata
)
opencs_hdrs_noqt (model/world
@ -36,13 +36,18 @@ opencs_hdrs_noqt (model/world
opencs_units (model/tools
tools reportmodel
tools reportmodel mergeoperation
)
opencs_units_noqt (model/tools
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck
startscriptcheck search searchoperation searchstage pathgridcheck
startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck
mergestages
)
opencs_hdrs_noqt (model/tools
mergestate
)
@ -63,19 +68,20 @@ opencs_hdrs_noqt (view/doc
opencs_units (view/world
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
cellcreator referenceablecreator referencecreator scenesubview
cellcreator referenceablecreator startscriptcreator referencecreator scenesubview
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
dialoguespinbox
dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator
)
opencs_units_noqt (view/world
subviews enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate
scripthighlighter idvalidator dialoguecreator physicssystem idcompletiondelegate
colordelegate dragdroputils
)
opencs_units (view/widget
scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle scenetoolrun modebutton
scenetooltoggle2 completerpopup
scenetooltoggle2 completerpopup coloreditor colorpickerpopup droplineedit
)
opencs_units (view/render
@ -95,7 +101,7 @@ opencs_hdrs_noqt (view/render
opencs_units (view/tools
reportsubview reporttable searchsubview searchbox
reportsubview reporttable searchsubview searchbox merge
)
opencs_units_noqt (view/tools
@ -155,19 +161,16 @@ if(WIN32)
set(QT_USE_QTMAIN TRUE)
endif(WIN32)
set(BOOST_COMPONENTS system filesystem program_options thread wave)
if(WIN32)
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale)
endif(WIN32)
find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork REQUIRED)
include(${QT_USE_FILE})
qt4_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI})
qt4_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT})
qt4_add_resources(OPENCS_RES_SRC ${OPENCS_RES})
if (DESIRED_QT_VERSION MATCHES 4)
include(${QT_USE_FILE})
qt4_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI})
qt4_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT})
qt4_add_resources(OPENCS_RES_SRC ${OPENCS_RES})
else()
qt5_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI})
qt5_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT})
qt5_add_resources(OPENCS_RES_SRC ${OPENCS_RES})
endif()
# for compiled .ui files
include_directories(${CMAKE_CURRENT_BINARY_DIR})
@ -208,12 +211,36 @@ target_link_libraries(openmw-cs
${OGRE_Overlay_LIBRARIES}
${OGRE_STATIC_PLUGINS}
${SHINY_LIBRARIES}
${Boost_LIBRARIES}
${Boost_SYSTEM_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_WAVE_LIBRARY}
${BULLET_LIBRARIES}
${QT_LIBRARIES}
components
)
if (DESIRED_QT_VERSION MATCHES 4)
target_link_libraries(openmw-cs
${QT_QTGUI_LIBRARY}
${QT_QTCORE_LIBRARY}
${QT_QTNETWORK_LIBRARY})
if (WIN32)
target_link_libraries(openmw-cs ${QT_QTMAIN_LIBRARY})
endif()
else()
qt5_use_modules(openmw-cs Widgets Core Network)
if (WIN32)
target_link_libraries(Qt5::WinMain)
endif()
endif()
if (WIN32)
target_link_libraries(openmw-cs ${Boost_LOCALE_LIBRARY})
endif()
if(APPLE)
INSTALL(TARGETS openmw-cs BUNDLE DESTINATION OpenMW COMPONENT BUNDLE)
endif()

@ -1,4 +1,3 @@
#include "editor.hpp"
#include <openengine/bullet/BulletShapeLoader.h>
@ -7,6 +6,9 @@
#include <QLocalServer>
#include <QLocalSocket>
#include <QMessageBox>
#include <QSplashScreen>
#include <QFont>
#include <QTimer>
#include <OgreRoot.h>
#include <OgreRenderWindow.h>
@ -24,7 +26,8 @@
CS::Editor::Editor (OgreInit::OgreInit& ogreInit)
: mUserSettings (mCfgMgr), mOverlaySystem (0), mDocumentManager (mCfgMgr),
mViewManager (mDocumentManager), mPid(""),
mLock(), mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL)
mLock(), mMerge (mDocumentManager),
mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL)
{
std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig();
@ -46,9 +49,12 @@ CS::Editor::Editor (OgreInit::OgreInit& ogreInit)
mNewGame.setLocalData (mLocal);
mFileDialog.setLocalData (mLocal);
mMerge.setLocalData (mLocal);
connect (&mDocumentManager, SIGNAL (documentAdded (CSMDoc::Document *)),
this, SLOT (documentAdded (CSMDoc::Document *)));
connect (&mDocumentManager, SIGNAL (documentAboutToBeRemoved (CSMDoc::Document *)),
this, SLOT (documentAboutToBeRemoved (CSMDoc::Document *)));
connect (&mDocumentManager, SIGNAL (lastDocumentDeleted()),
this, SLOT (lastDocumentDeleted()));
@ -56,6 +62,7 @@ CS::Editor::Editor (OgreInit::OgreInit& ogreInit)
connect (&mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ()));
connect (&mViewManager, SIGNAL (loadDocumentRequest ()), this, SLOT (loadDocument ()));
connect (&mViewManager, SIGNAL (editSettingsRequest()), this, SLOT (showSettings ()));
connect (&mViewManager, SIGNAL (mergeDocument (CSMDoc::Document *)), this, SLOT (mergeDocument (CSMDoc::Document *)));
connect (&mStartup, SIGNAL (createGame()), this, SLOT (createGame ()));
connect (&mStartup, SIGNAL (createAddon()), this, SLOT (createAddon ()));
@ -95,7 +102,7 @@ void CS::Editor::setupDataFiles (const Files::PathContainer& dataDirs)
}
}
std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfig()
std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfig(bool quiet)
{
boost::program_options::variables_map variables;
boost::program_options::options_description desc("Syntax: openmw-cs <options>\nAllowed options");
@ -115,7 +122,7 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
boost::program_options::notify(variables);
mCfgMgr.readConfiguration(variables, desc);
mCfgMgr.readConfiguration(variables, desc, quiet);
mDocumentManager.setEncoding (
ToUTF8::calculateEncoding (variables["encoding"].as<std::string>()));
@ -156,15 +163,23 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
}
dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end());
Files::PathContainer canonicalPaths;
//iterate the data directories and add them to the file dialog for loading
for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)
{
boost::filesystem::path p = boost::filesystem::canonical(*iter);
Files::PathContainer::iterator it = std::find(canonicalPaths.begin(), canonicalPaths.end(), p);
if (it == canonicalPaths.end())
canonicalPaths.push_back(p);
else
continue;
QString path = QString::fromUtf8 (iter->string().c_str());
mFileDialog.addFiles(path);
}
return std::make_pair (dataDirs, variables["fallback-archive"].as<std::vector<std::string> >());
return std::make_pair (canonicalPaths, variables["fallback-archive"].as<std::vector<std::string> >());
}
void CS::Editor::createGame()
@ -195,6 +210,11 @@ void CS::Editor::cancelCreateGame()
void CS::Editor::createAddon()
{
mStartup.hide();
mFileDialog.clearFiles();
std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig(/*quiet*/true);
setupDataFiles (config.first);
mFileDialog.showDialog (CSVDoc::ContentAction_New);
}
@ -215,6 +235,11 @@ void CS::Editor::cancelFileDialog()
void CS::Editor::loadDocument()
{
mStartup.hide();
mFileDialog.clearFiles();
std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig(/*quiet*/true);
setupDataFiles (config.first);
mFileDialog.showDialog (CSVDoc::ContentAction_Edit);
}
@ -243,6 +268,7 @@ void CS::Editor::createNewFile (const boost::filesystem::path &savePath)
mDocumentManager.addDocument (files, savePath, true);
mFileDialog.hide();
showSplashMessage();
}
void CS::Editor::createNewGame (const boost::filesystem::path& file)
@ -254,6 +280,7 @@ void CS::Editor::createNewGame (const boost::filesystem::path& file)
mDocumentManager.addDocument (files, file, true);
mNewGame.hide();
showSplashMessage();
}
void CS::Editor::showStartup()
@ -307,12 +334,12 @@ bool CS::Editor::makeIPCServer()
mServer->close();
fullPath.remove(QRegExp("dummy$"));
fullPath += mIpcServerName;
if(boost::filesystem::exists(fullPath.toStdString().c_str()))
if(boost::filesystem::exists(fullPath.toUtf8().constData()))
{
// TODO: compare pid of the current process with that in the file
std::cout << "Detected unclean shutdown." << std::endl;
// delete the stale file
if(remove(fullPath.toStdString().c_str()))
if(remove(fullPath.toUtf8().constData()))
std::cerr << "ERROR removing stale connection file" << std::endl;
}
}
@ -468,9 +495,65 @@ std::auto_ptr<sh::Factory> CS::Editor::setupGraphics()
void CS::Editor::documentAdded (CSMDoc::Document *document)
{
mViewManager.addView (document);
showSplashMessage();
}
void CS::Editor::documentAboutToBeRemoved (CSMDoc::Document *document)
{
if (mMerge.getDocument()==document)
mMerge.cancel();
}
void CS::Editor::lastDocumentDeleted()
{
QApplication::quit();
}
void CS::Editor::showSplashMessage()
{
CSMSettings::UserSettings &settings = CSMSettings::UserSettings::instance();
if(settings.settingValue ("filter/project-added") == "true" ||
settings.settingValue ("filter/project-modified") == "true")
{
QPixmap img(QPixmap(":./openmw-cs.png"));
QString msgTop("You have active global filters.");
QString msgBottom("Some rows may be hidden!");
QFont splashFont(QFont("Arial", 16, QFont::Bold)); // TODO: use system font?
//splashFont.setStretch(125);
QFontMetrics fm(splashFont);
int msgWidth = std::max(fm.width(msgTop), fm.width(msgBottom));
img.scaledToWidth(msgWidth);
QSplashScreen *splash = new QSplashScreen(img, Qt::WindowStaysOnTopHint);
splash->setFont(splashFont);
splash->resize(msgWidth+20, splash->height()+fm.lineSpacing()*2+20); // add some margin
// try to center the message near the center of the saved window
QWidget dummy;
bool xWorkaround = settings.settingValue ("window/x-save-state-workaround").toStdString() == "true";
if (settings.settingValue ("window/save-state").toStdString() == "true" &&
!(xWorkaround && settings.settingValue ("window/maximized").toStdString() == "true"))
{
dummy.restoreGeometry(settings.value("window/geometry").toByteArray());
splash->move(dummy.x()+std::max(0, (dummy.width()-msgWidth)/2),
dummy.y()+std::max(0, (dummy.height()-splash->height())/2));
}
else
splash->move(std::max(0, splash->x()-msgWidth/2), splash->y());
splash->show();
splash->showMessage(msgTop+"\n"+msgBottom, Qt::AlignHCenter|Qt::AlignBottom, Qt::red);
QTimer::singleShot(4000, splash, SLOT(close())); // 4 seconds should be enough
splash->raise(); // for X windows
}
}
void CS::Editor::mergeDocument (CSMDoc::Document *document)
{
mMerge.configure (document);
mMerge.show();
mMerge.raise();
mMerge.activateWindow();
}

@ -32,11 +32,18 @@
#include "view/settings/dialog.hpp"
#include "view/render/overlaysystem.hpp"
#include "view/tools/merge.hpp"
namespace OgreInit
{
class OgreInit;
}
namespace CSMDoc
{
class Document;
}
namespace CS
{
class Editor : public QObject
@ -59,10 +66,13 @@ namespace CS
boost::interprocess::file_lock mLock;
boost::filesystem::ofstream mPidFile;
bool mFsStrict;
CSVTools::Merge mMerge;
void showSplashMessage();
void setupDataFiles (const Files::PathContainer& dataDirs);
std::pair<Files::PathContainer, std::vector<std::string> > readConfig();
std::pair<Files::PathContainer, std::vector<std::string> > readConfig(bool quiet=false);
///< \return data paths
// not implemented
@ -101,8 +111,12 @@ namespace CS
void documentAdded (CSMDoc::Document *document);
void documentAboutToBeRemoved (CSMDoc::Document *document);
void lastDocumentDeleted();
void mergeDocument (CSMDoc::Document *document);
private:
QString mIpcServerName;

@ -1,4 +1,3 @@
#include "editor.hpp"
#include <exception>
@ -13,6 +12,8 @@
#include <components/ogreinit/ogreinit.hpp>
#include "model/doc/messages.hpp"
#include "model/world/universalid.hpp"
#ifdef Q_OS_MAC
@ -52,6 +53,7 @@ int main(int argc, char *argv[])
qRegisterMetaType<std::string> ("std::string");
qRegisterMetaType<CSMWorld::UniversalId> ("CSMWorld::UniversalId");
qRegisterMetaType<CSMDoc::Message> ("CSMDoc::Message");
OgreInit::OgreInit ogreInit;

@ -1,4 +1,3 @@
#include "blacklist.hpp"
#include <algorithm>

@ -799,9 +799,9 @@ void CSMDoc::Document::addGmsts()
"sBookSkillMessage",
"sBounty",
"sBreath",
"sBribe",
"sBribe",
"sBribe",
"sBribe 10 Gold",
"sBribe 100 Gold",
"sBribe 1000 Gold",
"sBribeFail",
"sBribeSuccess",
"sBuy",
@ -2251,14 +2251,14 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager,
const std::vector<std::string>& blacklistedScripts)
: mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, resourcesManager),
mTools (*this),
mTools (*this, encoding),
mProjectPath ((configuration.getUserDataPath() / "projects") /
(savePath.filename().string() + ".project")),
mSavingOperation (*this, mProjectPath, encoding),
mSaving (&mSavingOperation),
mResDir(resDir),
mRunner (mProjectPath), mPhysics(boost::shared_ptr<CSVWorld::PhysicsSystem>()),
mIdCompletionManager(mData)
mDirty (false), mIdCompletionManager(mData)
{
if (mContentFiles.empty())
throw std::runtime_error ("Empty content file sequence");
@ -2272,7 +2272,7 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
if (boost::filesystem::exists (customFiltersPath))
{
destination << std::ifstream(customFiltersPath.c_str(), std::ios::binary).rdbuf();
destination << std::ifstream(customFiltersPath.string().c_str(), std::ios::binary).rdbuf();
}
else
{
@ -2282,9 +2282,6 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
if (mNew)
{
mData.setDescription ("");
mData.setAuthor ("");
if (mContentFiles.size()==1)
createBase();
}
@ -2299,13 +2296,15 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
connect (&mTools, SIGNAL (done (int, bool)), this, SLOT (operationDone (int, bool)));
connect (&mTools, SIGNAL (mergeDone (CSMDoc::Document*)),
this, SIGNAL (mergeDone (CSMDoc::Document*)));
connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
connect (&mSaving, SIGNAL (done (int, bool)), this, SLOT (operationDone (int, bool)));
connect (
&mSaving, SIGNAL (reportMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int)),
this, SLOT (reportMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int)));
&mSaving, SIGNAL (reportMessage (const CSMDoc::Message&, int)),
this, SLOT (reportMessage (const CSMDoc::Message&, int)));
connect (&mRunner, SIGNAL (runStateChanged()), this, SLOT (runStateChanged()));
}
@ -2323,7 +2322,7 @@ int CSMDoc::Document::getState() const
{
int state = 0;
if (!mUndoStack.isClean())
if (!mUndoStack.isClean() || mDirty)
state |= State_Modified;
if (mSaving.isRunning())
@ -2369,9 +2368,9 @@ void CSMDoc::Document::save()
emit stateChanged (getState(), this);
}
CSMWorld::UniversalId CSMDoc::Document::verify()
CSMWorld::UniversalId CSMDoc::Document::verify (const CSMWorld::UniversalId& reportId)
{
CSMWorld::UniversalId id = mTools.runVerifier();
CSMWorld::UniversalId id = mTools.runVerifier (reportId);
emit stateChanged (getState(), this);
return id;
}
@ -2388,6 +2387,12 @@ void CSMDoc::Document::runSearch (const CSMWorld::UniversalId& searchId, const C
emit stateChanged (getState(), this);
}
void CSMDoc::Document::runMerge (std::auto_ptr<CSMDoc::Document> target)
{
mTools.runMerge (target);
emit stateChanged (getState(), this);
}
void CSMDoc::Document::abortOperation (int type)
{
if (type==State_Saving)
@ -2401,15 +2406,17 @@ void CSMDoc::Document::modificationStateChanged (bool clean)
emit stateChanged (getState(), this);
}
void CSMDoc::Document::reportMessage (const CSMWorld::UniversalId& id, const std::string& message,
const std::string& hint, int type)
void CSMDoc::Document::reportMessage (const CSMDoc::Message& message, int type)
{
/// \todo find a better way to get these messages to the user.
std::cout << message << std::endl;
std::cout << message.mMessage << std::endl;
}
void CSMDoc::Document::operationDone (int type, bool failed)
{
if (type==CSMDoc::State_Saving && !failed)
mDirty = false;
emit stateChanged (getState(), this);
}
@ -2494,3 +2501,8 @@ CSMWorld::IdCompletionManager &CSMDoc::Document::getIdCompletionManager()
{
return mIdCompletionManager;
}
void CSMDoc::Document::flagAsDirty()
{
mDirty = true;
}

@ -67,6 +67,8 @@ namespace CSMDoc
Blacklist mBlacklist;
Runner mRunner;
boost::shared_ptr<CSVWorld::PhysicsSystem> mPhysics;
bool mDirty;
CSMWorld::IdCompletionManager mIdCompletionManager;
// It is important that the undo stack is declared last, because on desctruction it fires a signal, that is connected to a slot, that is
@ -120,12 +122,14 @@ namespace CSMDoc
void save();
CSMWorld::UniversalId verify();
CSMWorld::UniversalId verify (const CSMWorld::UniversalId& reportId = CSMWorld::UniversalId());
CSMWorld::UniversalId newSearch();
void runSearch (const CSMWorld::UniversalId& searchId, const CSMTools::Search& search);
void runMerge (std::auto_ptr<CSMDoc::Document> target);
void abortOperation (int type);
const CSMWorld::Data& getData() const;
@ -148,18 +152,23 @@ namespace CSMDoc
CSMWorld::IdCompletionManager &getIdCompletionManager();
void flagAsDirty();
signals:
void stateChanged (int state, CSMDoc::Document *document);
void progress (int current, int max, int type, int threads, CSMDoc::Document *document);
/// \attention When this signal is emitted, *this hands over the ownership of the
/// document. This signal must be handled to avoid a leak.
void mergeDone (CSMDoc::Document *document);
private slots:
void modificationStateChanged (bool clean);
void reportMessage (const CSMWorld::UniversalId& id, const std::string& message,
const std::string& hint, int type);
void reportMessage (const CSMDoc::Message& message, int type);
void operationDone (int type, bool failed);
@ -172,4 +181,3 @@ namespace CSMDoc
}
#endif

@ -1,4 +1,3 @@
#include "documentmanager.hpp"
#include <algorithm>
@ -57,10 +56,24 @@ bool CSMDoc::DocumentManager::isEmpty()
void CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath,
bool new_)
{
Document *document = new Document (mConfiguration, files, new_, savePath, mResDir, mEncoding, mResourcesManager, mBlacklistedScripts);
Document *document = makeDocument (files, savePath, new_);
insertDocument (document);
}
CSMDoc::Document *CSMDoc::DocumentManager::makeDocument (
const std::vector< boost::filesystem::path >& files,
const boost::filesystem::path& savePath, bool new_)
{
return new Document (mConfiguration, files, new_, savePath, mResDir, mEncoding, mResourcesManager, mBlacklistedScripts);
}
void CSMDoc::DocumentManager::insertDocument (CSMDoc::Document *document)
{
mDocuments.push_back (document);
connect (document, SIGNAL (mergeDone (CSMDoc::Document*)),
this, SLOT (insertDocument (CSMDoc::Document*)));
emit loadRequest (document);
mLoader.hasThingsToDo().wakeAll();
@ -73,6 +86,8 @@ void CSMDoc::DocumentManager::removeDocument (CSMDoc::Document *document)
if (iter==mDocuments.end())
throw std::runtime_error ("removing invalid document");
emit documentAboutToBeRemoved (document);
mDocuments.erase (iter);
document->deleteLater();

@ -50,6 +50,15 @@ namespace CSMDoc
///< \param new_ Do not load the last content file in \a files and instead create in an
/// appropriate way.
/// Create a new document. The ownership of the created document is transferred to
/// the calling function. The DocumentManager does not manage it. Loading has not
/// taken place at the point when the document is returned.
///
/// \param new_ Do not load the last content file in \a files and instead create in an
/// appropriate way.
Document *makeDocument (const std::vector< boost::filesystem::path >& files,
const boost::filesystem::path& savePath, bool new_);
void setResourceDir (const boost::filesystem::path& parResDir);
void setEncoding (ToUTF8::FromType encoding);
@ -79,10 +88,16 @@ namespace CSMDoc
void removeDocument (CSMDoc::Document *document);
///< Emits the lastDocumentDeleted signal, if applicable.
/// Hand over document to *this. The ownership is transferred. The DocumentManager
/// will initiate the load procedure, if necessary
void insertDocument (CSMDoc::Document *document);
signals:
void documentAdded (CSMDoc::Document *document);
void documentAboutToBeRemoved (CSMDoc::Document *document);
void loadRequest (CSMDoc::Document *document);
void lastDocumentDeleted();

@ -1,4 +1,3 @@
#include "loader.hpp"
#include <QTimer>
@ -52,7 +51,7 @@ void CSMDoc::Loader::load()
{
if (iter->second.mRecordsLeft)
{
CSMDoc::Messages messages;
Messages messages (Message::Severity_Error);
for (int i=0; i<batchingSize; ++i) // do not flood the system with update signals
if (document->getData().continueLoading (messages))
{
@ -68,7 +67,7 @@ void CSMDoc::Loader::load()
for (CSMDoc::Messages::Iterator iter (messages.begin());
iter!=messages.end(); ++iter)
{
document->getReport (log)->add (iter->mId, iter->mMessage);
document->getReport (log)->add (*iter);
emit loadMessage (document, iter->mMessage);
}
}

@ -1,15 +1,38 @@
#include "messages.hpp"
CSMDoc::Message::Message() {}
CSMDoc::Message::Message (const CSMWorld::UniversalId& id, const std::string& message,
const std::string& hint, Severity severity)
: mId (id), mMessage (message), mHint (hint), mSeverity (severity)
{}
std::string CSMDoc::Message::toString (Severity severity)
{
switch (severity)
{
case CSMDoc::Message::Severity_Info: return "Information";
case CSMDoc::Message::Severity_Warning: return "Warning";
case CSMDoc::Message::Severity_Error: return "Error";
case CSMDoc::Message::Severity_SeriousError: return "Serious Error";
case CSMDoc::Message::Severity_Default: break;
}
return "";
}
CSMDoc::Messages::Messages (Message::Severity default_)
: mDefault (default_)
{}
void CSMDoc::Messages::add (const CSMWorld::UniversalId& id, const std::string& message,
const std::string& hint)
const std::string& hint, Message::Severity severity)
{
Message data;
data.mId = id;
data.mMessage = message;
data.mHint = hint;
if (severity==Message::Severity_Default)
severity = mDefault;
mMessages.push_back (data);
mMessages.push_back (Message (id, message, hint, severity));
}
void CSMDoc::Messages::push_back (const std::pair<CSMWorld::UniversalId, std::string>& data)

@ -4,20 +4,43 @@
#include <string>
#include <vector>
#include <QMetaType>
#include "../world/universalid.hpp"
namespace CSMDoc
{
struct Message
{
enum Severity
{
Severity_Info = 0, // no problem
Severity_Warning = 1, // a potential problem, but we are probably fine
Severity_Error = 2, // an error; we are not fine
Severity_SeriousError = 3, // an error so bad we can't even be sure if we are
// reporting it correctly
Severity_Default = 4
};
CSMWorld::UniversalId mId;
std::string mMessage;
std::string mHint;
Severity mSeverity;
Message();
Message (const CSMWorld::UniversalId& id, const std::string& message,
const std::string& hint, Severity severity);
static std::string toString (Severity severity);
};
class Messages
{
public:
struct Message
{
CSMWorld::UniversalId mId;
std::string mMessage;
std::string mHint;
};
// \deprecated Use CSMDoc::Message directly instead.
typedef CSMDoc::Message Message;
typedef std::vector<Message> Collection;
@ -26,11 +49,15 @@ namespace CSMDoc
private:
Collection mMessages;
Message::Severity mDefault;
public:
Messages (Message::Severity default_);
void add (const CSMWorld::UniversalId& id, const std::string& message,
const std::string& hint = "");
const std::string& hint = "",
Message::Severity severity = Message::Severity_Default);
/// \deprecated Use add instead.
void push_back (const std::pair<CSMWorld::UniversalId, std::string>& data);
@ -41,4 +68,6 @@ namespace CSMDoc
};
}
Q_DECLARE_METATYPE (CSMDoc::Message)
#endif

@ -1,4 +1,3 @@
#include "operation.hpp"
#include <string>
@ -7,6 +6,7 @@
#include <QTimer>
#include "../world/universalid.hpp"
#include "../settings/usersettings.hpp"
#include "state.hpp"
#include "stage.hpp"
@ -23,13 +23,17 @@ 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);
}
}
CSMDoc::Operation::Operation (int type, bool ordered, bool finalAlways)
: mType (type), mStages(std::vector<std::pair<Stage *, int> >()), mCurrentStage(mStages.begin()),
mCurrentStep(0), mCurrentStepTotal(0), mTotalSteps(0), mOrdered (ordered),
mFinalAlways (finalAlways), mError(false), mConnected (false)
mFinalAlways (finalAlways), mError(false), mConnected (false), mPrepared (false),
mDefaultSeverity (Message::Severity_Error)
{
mTimer = new QTimer (this);
}
@ -49,8 +53,8 @@ void CSMDoc::Operation::run()
connect (mTimer, SIGNAL (timeout()), this, SLOT (executeStage()));
mConnected = true;
}
prepareStages();
mPrepared = false;
mTimer->start (0);
}
@ -60,6 +64,19 @@ 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;
}
bool CSMDoc::Operation::hasError() const
{
return mError;
@ -84,9 +101,23 @@ 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()
{
Messages messages;
if (!mPrepared)
{
prepareStages();
mPrepared = true;
}
Messages messages (mDefaultSeverity);
while (mCurrentStage!=mStages.end())
{
@ -103,7 +134,7 @@ void CSMDoc::Operation::executeStage()
}
catch (const std::exception& e)
{
emit reportMessage (CSMWorld::UniversalId(), e.what(), "", mType);
emit reportMessage (Message (CSMWorld::UniversalId(), e.what(), "", Message::Severity_SeriousError), mType);
abort();
}
@ -115,7 +146,7 @@ void CSMDoc::Operation::executeStage()
emit progress (mCurrentStepTotal, mTotalSteps ? mTotalSteps : 1, mType);
for (Messages::Iterator iter (messages.begin()); iter!=messages.end(); ++iter)
emit reportMessage (iter->mId, iter->mMessage, iter->mHint, mType);
emit reportMessage (*iter, mType);
if (mCurrentStage==mStages.end())
operationDone();

@ -2,9 +2,13 @@
#define CSM_DOC_OPERATION_H
#include <vector>
#include <map>
#include <QObject>
#include <QTimer>
#include <QStringList>
#include "messages.hpp"
namespace CSMWorld
{
@ -30,6 +34,9 @@ namespace CSMDoc
bool mError;
bool mConnected;
QTimer *mTimer;
std::map<QString, QStringList> mSettings;
bool mPrepared;
Message::Severity mDefaultSeverity;
void prepareStages();
@ -46,14 +53,21 @@ 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);
bool hasError() const;
signals:
void progress (int current, int max, int type);
void reportMessage (const CSMWorld::UniversalId& id, const std::string& message,
const std::string& hint, int type);
void reportMessage (const CSMDoc::Message& message, int type);
void done (int type, bool failed);
@ -63,11 +77,15 @@ namespace CSMDoc
void run();
void updateUserSetting (const QString& name, const QStringList& value);
private slots:
void executeStage();
void operationDone();
protected slots:
virtual void operationDone();
};
}

@ -1,6 +1,7 @@
#include "operationholder.hpp"
#include "../settings/usersettings.hpp"
#include "operation.hpp"
CSMDoc::OperationHolder::OperationHolder (Operation *operation) : mRunning (false)
@ -19,8 +20,8 @@ void CSMDoc::OperationHolder::setOperation (Operation *operation)
this, SIGNAL (progress (int, int, int)));
connect (
mOperation, SIGNAL (reportMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int)),
this, SIGNAL (reportMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int)));
mOperation, SIGNAL (reportMessage (const CSMDoc::Message&, int)),
this, SIGNAL (reportMessage (const CSMDoc::Message&, int)));
connect (
mOperation, SIGNAL (done (int, bool)),
@ -29,6 +30,9 @@ 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

@ -4,6 +4,8 @@
#include <QObject>
#include <QThread>
#include "messages.hpp"
namespace CSMWorld
{
class UniversalId;
@ -44,8 +46,7 @@ namespace CSMDoc
void progress (int current, int max, int type);
void reportMessage (const CSMWorld::UniversalId& id, const std::string& message,
const std::string& hint, int type);
void reportMessage (const CSMDoc::Message& message, int type);
void done (int type, bool failed);

@ -1,4 +1,3 @@
#include "runner.hpp"
#include <QApplication>

@ -1,4 +1,3 @@
#include "saving.hpp"
#include "../world/data.hpp"
@ -81,22 +80,25 @@ CSMDoc::Saving::Saving (Document& document, const boost::filesystem::path& proje
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::StartScript> >
(mDocument.getData().getStartScripts(), mState));
appendStage (new WriteDialogueCollectionStage (mDocument, mState, false));
appendStage (new WriteDialogueCollectionStage (mDocument, mState, true));
appendStage (new WriteRefIdCollectionStage (mDocument, mState));
appendStage (new CollectionReferencesStage (mDocument, mState));
appendStage (new WriteCellCollectionStage (mDocument, mState));
appendStage (new WritePathgridCollectionStage (mDocument, mState));
// Dialogue can reference objects and cells so must be written after these records for vanilla-compatible files
appendStage (new WriteLandCollectionStage (mDocument, mState));
appendStage (new WriteDialogueCollectionStage (mDocument, mState, false));
appendStage (new WriteDialogueCollectionStage (mDocument, mState, true));
appendStage (new WritePathgridCollectionStage (mDocument, mState));
appendStage (new WriteLandTextureCollectionStage (mDocument, mState));
// references Land Textures
appendStage (new WriteLandCollectionStage (mDocument, mState));
// close file and clean up
appendStage (new CloseSaveStage (mState));

@ -1,4 +1,3 @@
#include "savingstages.hpp"
#include <fstream>
@ -53,18 +52,16 @@ void CSMDoc::WriteHeaderStage::perform (int stage, Messages& messages)
mState.getWriter().clearMaster();
mState.getWriter().setFormat (0);
if (mSimple)
{
mState.getWriter().setAuthor ("");
mState.getWriter().setDescription ("");
mState.getWriter().setRecordCount (0);
mState.getWriter().setFormat (ESM::Header::CurrentFormat);
}
else
{
mState.getWriter().setAuthor (mDocument.getData().getAuthor());
mState.getWriter().setDescription (mDocument.getData().getDescription());
mDocument.getData().getMetaData().save (mState.getWriter());
mState.getWriter().setRecordCount (
mDocument.getData().count (CSMWorld::RecordBase::State_Modified) +
mDocument.getData().count (CSMWorld::RecordBase::State_ModifiedOnly) +
@ -137,23 +134,34 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message
state==CSMWorld::RecordBase::State_ModifiedOnly ||
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 && state != CSMWorld::RecordBase::State_Modified
&& state != CSMWorld::RecordBase::State_ModifiedOnly)
{
mState.getWriter().startRecord (topic.mBase.sRecordId);
mState.getWriter().writeHNCString ("NAME", topic.mBase.mId);
topic.mBase.save (mState.getWriter());
mState.getWriter().endRecord (topic.mBase.sRecordId);
}
else
{
mState.getWriter().startRecord (topic.mModified.sRecordId);
mState.getWriter().writeHNCString ("NAME", topic.mModified.mId);
topic.mModified.save (mState.getWriter());
mState.getWriter().endRecord (topic.mModified.sRecordId);
}
// write modified selected info records
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second;
++iter)
{
CSMWorld::RecordBase::State state = iter->mState;
CSMWorld::RecordBase::State infoState = iter->mState;
if (state==CSMWorld::RecordBase::State_Deleted)
if (infoState==CSMWorld::RecordBase::State_Deleted)
{
/// \todo wrote record with delete flag
}
else if (state==CSMWorld::RecordBase::State_Modified ||
state==CSMWorld::RecordBase::State_ModifiedOnly)
else if (infoState==CSMWorld::RecordBase::State_Modified ||
infoState==CSMWorld::RecordBase::State_ModifiedOnly)
{
ESM::DialInfo info = iter->get();
info.mId = info.mId.substr (info.mId.find_last_of ('#')+1);
@ -418,15 +426,16 @@ void CSMDoc::WriteLandCollectionStage::perform (int stage, Messages& messages)
if (land.mState==CSMWorld::RecordBase::State_Modified ||
land.mState==CSMWorld::RecordBase::State_ModifiedOnly)
{
CSMWorld::Land record = land.get();
const CSMWorld::Land& record = land.get();
mState.getWriter().startRecord (record.sRecordId);
mState.getWriter().startRecord (record.mLand->sRecordId);
record.save (mState.getWriter());
record.mLand->save (mState.getWriter());
if(record.mLand->mLandData)
record.mLand->mLandData->save (mState.getWriter());
if (const ESM::Land::LandData *data = record.getLandData (record.mDataTypes))
data->save (mState.getWriter());
mState.getWriter().endRecord (record.mLand->sRecordId);
mState.getWriter().endRecord (record.sRecordId);
}
else if (land.mState==CSMWorld::RecordBase::State_Deleted)
{
@ -457,6 +466,8 @@ void CSMDoc::WriteLandTextureCollectionStage::perform (int stage, Messages& mess
mState.getWriter().startRecord (record.sRecordId);
mState.getWriter().writeHNString("NAME", record.mId);
record.save (mState.getWriter());
mState.getWriter().endRecord (record.sRecordId);

@ -1,4 +1,3 @@
#include "savingstate.hpp"
#include "operation.hpp"

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

@ -8,6 +8,8 @@
#include "messages.hpp"
class QString;
namespace CSMDoc
{
class Stage
@ -21,6 +23,9 @@ 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);
};
}

@ -12,7 +12,7 @@ namespace CSMDoc
State_Saving = 16,
State_Verifying = 32,
State_Compiling = 64, // not implemented yet
State_Merging = 64,
State_Searching = 128,
State_Loading = 256 // pseudo-state; can not be encountered in a loaded document
};

@ -1,4 +1,3 @@
#include "andnode.hpp"
#include <sstream>

@ -1,4 +1,3 @@
#include "booleannode.hpp"
CSMFilter::BooleanNode::BooleanNode (bool true_) : mTrue (true_) {}

@ -1,4 +1,3 @@
#include "leafnode.hpp"
std::vector<int> CSMFilter::LeafNode::getReferencedColumns() const

@ -1,4 +1,3 @@
#include "narynode.hpp"
#include <sstream>

@ -1,4 +1,3 @@
#include "node.hpp"
CSMFilter::Node::Node() {}

@ -1,4 +1,3 @@
#include "notnode.hpp"
CSMFilter::NotNode::NotNode (boost::shared_ptr<Node> child) : UnaryNode (child, "not") {}

@ -1,4 +1,3 @@
#include "ornode.hpp"
#include <sstream>

@ -1,4 +1,3 @@
#include "parser.hpp"
#include <cctype>

@ -1,4 +1,3 @@
#include "textnode.hpp"
#include <sstream>

@ -1,4 +1,3 @@
#include "unarynode.hpp"
CSMFilter::UnaryNode::UnaryNode (boost::shared_ptr<Node> child, const std::string& name)

@ -1,4 +1,3 @@
#include "valuenode.hpp"
#include <sstream>

@ -161,6 +161,16 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
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.");
Setting *saveState = createSetting (Type_CheckBox, "save-state", "Save window size and position");
saveState->setDefaultValue ("true");
saveState->setToolTip ("Remember window size and position between editing sessions.");
Setting *saveX = createSetting (Type_CheckBox, "x-save-state-workaround", "X windows workaround");
saveX->setDefaultValue ("false");
saveX->setToolTip ("Some X window managers don't remember the windows state before being"
" maximized. In such environments exiting while maximized will correctly start in a maximized"
" window, but restoring back to the normal size won't work. Try this workaround.");
}
declareSection ("records", "Records");
@ -179,7 +189,7 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
ritd->setDeclaredValues (values);
}
declareSection ("table-input", "Table Input");
declareSection ("table-input", "ID Tables");
{
QString inPlaceEdit ("Edit in Place");
QString editRecord ("Edit Record");
@ -232,6 +242,67 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
"Jump to the added or cloned record.");
jumpToAdded->setDefaultValue (defaultValue);
jumpToAdded->setDeclaredValues (jumpValues);
Setting *jumpToModified = createSetting (Type_CheckBox, "jump-to-modified", "Jump to modified Record");
jumpToModified->setDefaultValue ("true");
jumpToModified->setToolTip ("Whether to jump to the modified record. This setting effects the instances table only."
"\nCan be useful in finding the moved or modified object instance while 3D editing.");
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");
@ -252,12 +323,12 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
autoDelete->setDefaultValue ("true");
}
declareSection ("script-editor", "Script Editor");
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.");
" 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");
@ -271,6 +342,30 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
"\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);
@ -300,6 +395,27 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
formatId->setToolTip ("(Default: Blue) Use one of the following formats:" + tooltip);
}
declareSection ("filter", "Global Filter");
{
Setting *projAdded = createSetting (Type_CheckBox, "project-added", "Project::added initial value");
projAdded->setDefaultValue ("false");
projAdded->setToolTip ("Show records added by the project when opening a table."
" Other records are filterd out.");
Setting *projModified = createSetting (Type_CheckBox, "project-modified", "Project::modified initial value");
projModified->setDefaultValue ("false");
projModified->setToolTip ("Show records modified by the project when opening a table."
" Other records are filterd out.");
}
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");
}
{
/******************************************************************
* There are three types of values:
@ -520,6 +636,21 @@ QString CSMSettings::UserSettings::setting(const QString &viewKey, const QString
return QString();
}
QVariant CSMSettings::UserSettings::value(const QString &viewKey, const QVariant &value)
{
if(value != QVariant())
{
mSettingDefinitions->setValue (viewKey, value);
return value;
}
else if(mSettingDefinitions->contains(viewKey))
{
return mSettingDefinitions->value (viewKey);
}
return QVariant();
}
bool CSMSettings::UserSettings::hasSettingDefinitions (const QString &viewKey) const
{
return (mSettingDefinitions->contains (viewKey));

@ -82,6 +82,8 @@ namespace CSMSettings {
QString setting(const QString &viewKey, const QString &value = QString());
QVariant value(const QString &viewKey, const QVariant &value = QVariant());
private:
void buildSettingModelDefaults();

@ -1,4 +1,3 @@
#include "birthsigncheck.hpp"
#include <sstream>

@ -1,4 +1,3 @@
#include "classcheck.hpp"
#include <sstream>

@ -1,4 +1,3 @@
#include "factioncheck.hpp"
#include <sstream>

@ -0,0 +1,133 @@
#include "magiceffectcheck.hpp"
#include <components/misc/resourcehelpers.hpp>
#include "../world/resources.hpp"
#include "../world/data.hpp"
namespace
{
void addMessageIfNotEmpty(CSMDoc::Messages &messages, const CSMWorld::UniversalId &id, const std::string text)
{
if (!text.empty())
{
messages.push_back(std::make_pair(id, text));
}
}
}
bool CSMTools::MagicEffectCheckStage::isTextureExists(const std::string &texture, bool isIcon) const
{
const CSMWorld::Resources &textures = isIcon ? mIcons : mTextures;
bool exists = false;
if (textures.searchId(texture) != -1)
{
exists = true;
}
else
{
std::string ddsTexture = texture;
if (Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && textures.searchId(ddsTexture) != -1)
{
exists = true;
}
}
return exists;
}
std::string CSMTools::MagicEffectCheckStage::checkReferenceable(const std::string &id,
const CSMWorld::UniversalId &type,
const std::string &column) const
{
std::string error;
if (!id.empty())
{
CSMWorld::RefIdData::LocalIndex index = mReferenceables.getDataSet().searchId(id);
if (index.first == -1)
{
error = "No such " + column + " '" + id + "'";
}
else if (index.second != type.getType())
{
error = column + " is not of type " + type.getTypeName();
}
}
return error;
}
std::string CSMTools::MagicEffectCheckStage::checkSound(const std::string &id, const std::string &column) const
{
std::string error;
if (!id.empty() && mSounds.searchId(id) == -1)
{
error = "No such " + column + " '" + id + "'";
}
return error;
}
CSMTools::MagicEffectCheckStage::MagicEffectCheckStage(const CSMWorld::IdCollection<ESM::MagicEffect> &effects,
const CSMWorld::IdCollection<ESM::Sound> &sounds,
const CSMWorld::RefIdCollection &referenceables,
const CSMWorld::Resources &icons,
const CSMWorld::Resources &textures)
: mMagicEffects(effects),
mSounds(sounds),
mReferenceables(referenceables),
mIcons(icons),
mTextures(textures)
{}
int CSMTools::MagicEffectCheckStage::setup()
{
return mMagicEffects.getSize();
}
void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messages)
{
ESM::MagicEffect effect = mMagicEffects.getRecord(stage).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_MagicEffect, effect.mId);
if (effect.mData.mBaseCost < 0.0f)
{
messages.push_back(std::make_pair(id, "Base Cost is negative"));
}
if (effect.mIcon.empty())
{
messages.push_back(std::make_pair(id, "Icon is not specified"));
}
else if (!isTextureExists(effect.mIcon, true))
{
messages.push_back(std::make_pair(id, "No such Icon '" + effect.mIcon + "'"));
}
if (!effect.mParticle.empty() && !isTextureExists(effect.mParticle, false))
{
messages.push_back(std::make_pair(id, "No such Particle '" + effect.mParticle + "'"));
}
addMessageIfNotEmpty(messages,
id,
checkReferenceable(effect.mCasting, CSMWorld::UniversalId::Type_Static, "Casting Object"));
addMessageIfNotEmpty(messages,
id,
checkReferenceable(effect.mHit, CSMWorld::UniversalId::Type_Static, "Hit Object"));
addMessageIfNotEmpty(messages,
id,
checkReferenceable(effect.mArea, CSMWorld::UniversalId::Type_Static, "Area Object"));
addMessageIfNotEmpty(messages,
id,
checkReferenceable(effect.mBolt, CSMWorld::UniversalId::Type_Weapon, "Bolt Object"));
addMessageIfNotEmpty(messages, id, checkSound(effect.mCastSound, "Casting Sound"));
addMessageIfNotEmpty(messages, id, checkSound(effect.mHitSound, "Hit Sound"));
addMessageIfNotEmpty(messages, id, checkSound(effect.mAreaSound, "Area Sound"));
addMessageIfNotEmpty(messages, id, checkSound(effect.mBoltSound, "Bolt Sound"));
if (effect.mDescription.empty())
{
messages.push_back(std::make_pair(id, "Description is empty"));
}
}

@ -0,0 +1,50 @@
#ifndef CSM_TOOLS_MAGICEFFECTCHECK_HPP
#define CSM_TOOLS_MAGICEFFECTCHECK_HPP
#include <components/esm/loadmgef.hpp>
#include <components/esm/loadsoun.hpp>
#include "../world/idcollection.hpp"
#include "../world/refidcollection.hpp"
#include "../doc/stage.hpp"
namespace CSMWorld
{
class Resources;
}
namespace CSMTools
{
/// \brief VerifyStage: make sure that magic effect records are internally consistent
class MagicEffectCheckStage : public CSMDoc::Stage
{
const CSMWorld::IdCollection<ESM::MagicEffect> &mMagicEffects;
const CSMWorld::IdCollection<ESM::Sound> &mSounds;
const CSMWorld::RefIdCollection &mReferenceables;
const CSMWorld::Resources &mIcons;
const CSMWorld::Resources &mTextures;
private:
bool isTextureExists(const std::string &texture, bool isIcon) const;
std::string checkReferenceable(const std::string &id,
const CSMWorld::UniversalId &type,
const std::string &column) const;
std::string checkSound(const std::string &id, const std::string &column) const;
public:
MagicEffectCheckStage(const CSMWorld::IdCollection<ESM::MagicEffect> &effects,
const CSMWorld::IdCollection<ESM::Sound> &sounds,
const CSMWorld::RefIdCollection &referenceables,
const CSMWorld::Resources &icons,
const CSMWorld::Resources &textures);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages &messages);
///< Messages resulting from this tage will be appended to \a messages.
};
}
#endif

@ -1,4 +1,3 @@
#include "mandatoryid.hpp"
#include "../world/collectionbase.hpp"

@ -0,0 +1,59 @@
#include "mergeoperation.hpp"
#include "../doc/state.hpp"
#include "../doc/document.hpp"
#include "mergestages.hpp"
CSMTools::MergeOperation::MergeOperation (CSMDoc::Document& document, ToUTF8::FromType encoding)
: CSMDoc::Operation (CSMDoc::State_Merging, true), mState (document)
{
appendStage (new StartMergeStage (mState));
appendStage (new MergeIdCollectionStage<ESM::Global> (mState, &CSMWorld::Data::getGlobals));
appendStage (new MergeIdCollectionStage<ESM::GameSetting> (mState, &CSMWorld::Data::getGmsts));
appendStage (new MergeIdCollectionStage<ESM::Skill> (mState, &CSMWorld::Data::getSkills));
appendStage (new MergeIdCollectionStage<ESM::Class> (mState, &CSMWorld::Data::getClasses));
appendStage (new MergeIdCollectionStage<ESM::Faction> (mState, &CSMWorld::Data::getFactions));
appendStage (new MergeIdCollectionStage<ESM::Race> (mState, &CSMWorld::Data::getRaces));
appendStage (new MergeIdCollectionStage<ESM::Sound> (mState, &CSMWorld::Data::getSounds));
appendStage (new MergeIdCollectionStage<ESM::Script> (mState, &CSMWorld::Data::getScripts));
appendStage (new MergeIdCollectionStage<ESM::Region> (mState, &CSMWorld::Data::getRegions));
appendStage (new MergeIdCollectionStage<ESM::BirthSign> (mState, &CSMWorld::Data::getBirthsigns));
appendStage (new MergeIdCollectionStage<ESM::Spell> (mState, &CSMWorld::Data::getSpells));
appendStage (new MergeIdCollectionStage<ESM::Dialogue> (mState, &CSMWorld::Data::getTopics));
appendStage (new MergeIdCollectionStage<ESM::Dialogue> (mState, &CSMWorld::Data::getJournals));
appendStage (new MergeIdCollectionStage<CSMWorld::Cell> (mState, &CSMWorld::Data::getCells));
appendStage (new MergeIdCollectionStage<ESM::Filter> (mState, &CSMWorld::Data::getFilters));
appendStage (new MergeIdCollectionStage<ESM::Enchantment> (mState, &CSMWorld::Data::getEnchantments));
appendStage (new MergeIdCollectionStage<ESM::BodyPart> (mState, &CSMWorld::Data::getBodyParts));
appendStage (new MergeIdCollectionStage<ESM::DebugProfile> (mState, &CSMWorld::Data::getDebugProfiles));
appendStage (new MergeIdCollectionStage<ESM::SoundGenerator> (mState, &CSMWorld::Data::getSoundGens));
appendStage (new MergeIdCollectionStage<ESM::MagicEffect> (mState, &CSMWorld::Data::getMagicEffects));
appendStage (new MergeIdCollectionStage<ESM::StartScript> (mState, &CSMWorld::Data::getStartScripts));
appendStage (new MergeIdCollectionStage<CSMWorld::Pathgrid, CSMWorld::SubCellCollection<CSMWorld::Pathgrid> > (mState, &CSMWorld::Data::getPathgrids));
appendStage (new MergeIdCollectionStage<CSMWorld::Info, CSMWorld::InfoCollection> (mState, &CSMWorld::Data::getTopicInfos));
appendStage (new MergeIdCollectionStage<CSMWorld::Info, CSMWorld::InfoCollection> (mState, &CSMWorld::Data::getJournalInfos));
appendStage (new MergeRefIdsStage (mState));
appendStage (new MergeReferencesStage (mState));
appendStage (new MergeReferencesStage (mState));
appendStage (new ListLandTexturesMergeStage (mState));
appendStage (new MergeLandTexturesStage (mState));
appendStage (new MergeLandStage (mState));
appendStage (new FinishMergedDocumentStage (mState, encoding));
}
void CSMTools::MergeOperation::setTarget (std::auto_ptr<CSMDoc::Document> document)
{
mState.mTarget = document;
}
void CSMTools::MergeOperation::operationDone()
{
CSMDoc::Operation::operationDone();
if (mState.mCompleted)
emit mergeDone (mState.mTarget.release());
}

@ -0,0 +1,45 @@
#ifndef CSM_TOOLS_MERGEOPERATION_H
#define CSM_TOOLS_MERGEOPERATION_H
#include <memory>
#include <components/to_utf8/to_utf8.hpp>
#include "../doc/operation.hpp"
#include "mergestate.hpp"
namespace CSMDoc
{
class Document;
}
namespace CSMTools
{
class MergeOperation : public CSMDoc::Operation
{
Q_OBJECT
MergeState mState;
public:
MergeOperation (CSMDoc::Document& document, ToUTF8::FromType encoding);
/// \attention Do not call this function while a merge is running.
void setTarget (std::auto_ptr<CSMDoc::Document> document);
protected slots:
virtual void operationDone();
signals:
/// \attention When this signal is emitted, *this hands over the ownership of the
/// document. This signal must be handled to avoid a leak.
void mergeDone (CSMDoc::Document *document);
};
}
#endif

@ -0,0 +1,258 @@
#include "mergestages.hpp"
#include <sstream>
#include <components/misc/stringops.hpp>
#include "mergestate.hpp"
#include "../doc/document.hpp"
#include "../world/data.hpp"
CSMTools::StartMergeStage::StartMergeStage (MergeState& state)
: mState (state)
{}
int CSMTools::StartMergeStage::setup()
{
return 1;
}
void CSMTools::StartMergeStage::perform (int stage, CSMDoc::Messages& messages)
{
mState.mCompleted = false;
mState.mTextureIndices.clear();
}
CSMTools::FinishMergedDocumentStage::FinishMergedDocumentStage (MergeState& state, ToUTF8::FromType encoding)
: mState (state), mEncoder (encoding)
{}
int CSMTools::FinishMergedDocumentStage::setup()
{
return 1;
}
void CSMTools::FinishMergedDocumentStage::perform (int stage, CSMDoc::Messages& messages)
{
// We know that the content file list contains at least two entries and that the first one
// does exist on disc (otherwise it would have been impossible to initiate a merge on that
// document).
boost::filesystem::path path = mState.mSource.getContentFiles()[0];
ESM::ESMReader reader;
reader.setEncoder (&mEncoder);
reader.open (path.string());
CSMWorld::MetaData source;
source.mId = "sys::meta";
source.load (reader);
CSMWorld::MetaData target = mState.mTarget->getData().getMetaData();
target.mAuthor = source.mAuthor;
target.mDescription = source.mDescription;
mState.mTarget->getData().setMetaData (target);
mState.mCompleted = true;
}
CSMTools::MergeRefIdsStage::MergeRefIdsStage (MergeState& state) : mState (state) {}
int CSMTools::MergeRefIdsStage::setup()
{
return mState.mSource.getData().getReferenceables().getSize();
}
void CSMTools::MergeRefIdsStage::perform (int stage, CSMDoc::Messages& messages)
{
mState.mSource.getData().getReferenceables().copyTo (
stage, mState.mTarget->getData().getReferenceables());
}
CSMTools::MergeReferencesStage::MergeReferencesStage (MergeState& state)
: mState (state)
{}
int CSMTools::MergeReferencesStage::setup()
{
mIndex.clear();
return mState.mSource.getData().getReferences().getSize();
}
void CSMTools::MergeReferencesStage::perform (int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<CSMWorld::CellRef>& record =
mState.mSource.getData().getReferences().getRecord (stage);
if (!record.isDeleted())
{
CSMWorld::CellRef ref = record.get();
ref.mOriginalCell = ref.mCell;
ref.mRefNum.mIndex = mIndex[Misc::StringUtils::lowerCase (ref.mCell)]++;
ref.mRefNum.mContentFile = 0;
CSMWorld::Record<CSMWorld::CellRef> newRecord (
CSMWorld::RecordBase::State_ModifiedOnly, 0, &ref);
mState.mTarget->getData().getReferences().appendRecord (newRecord);
}
}
CSMTools::ListLandTexturesMergeStage::ListLandTexturesMergeStage (MergeState& state)
: mState (state)
{}
int CSMTools::ListLandTexturesMergeStage::setup()
{
return mState.mSource.getData().getLand().getSize();
}
void CSMTools::ListLandTexturesMergeStage::perform (int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<CSMWorld::Land>& record =
mState.mSource.getData().getLand().getRecord (stage);
if (!record.isDeleted())
{
const CSMWorld::Land& land = record.get();
// make sure record is loaded
land.loadData (ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML |
ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX | ESM::Land::DATA_WNAM);
if (const ESM::Land::LandData *data = land.getLandData (ESM::Land::DATA_VTEX))
{
// list texture indices
std::pair<uint16_t, int> key;
key.second = land.mPlugin;
for (int i=0; i<ESM::Land::LAND_NUM_TEXTURES; ++i)
{
key.first = data->mTextures[i];
mState.mTextureIndices[key] = -1;
}
}
}
}
CSMTools::MergeLandTexturesStage::MergeLandTexturesStage (MergeState& state)
: mState (state), mNext (mState.mTextureIndices.end())
{}
int CSMTools::MergeLandTexturesStage::setup()
{
// Should use the size of mState.mTextureIndices instead, but that is not available at this
// point. Unless there are any errors in the land and land texture records this will not
// make a difference.
return mState.mSource.getData().getLandTextures().getSize();
}
void CSMTools::MergeLandTexturesStage::perform (int stage, CSMDoc::Messages& messages)
{
if (stage==0)
mNext = mState.mTextureIndices.begin();
bool found = false;
do
{
if (mNext==mState.mTextureIndices.end())
return;
mNext->second = stage+1;
std::ostringstream stream;
stream << mNext->first.first-1 << "_" << mNext->first.second;
int index = mState.mSource.getData().getLandTextures().searchId (stream.str());
if (index!=-1)
{
CSMWorld::LandTexture texture =
mState.mSource.getData().getLandTextures().getRecord (index).get();
std::ostringstream stream;
stream << mNext->second-1 << "_0";
texture.mIndex = mNext->second-1;
texture.mId = stream.str();
CSMWorld::Record<CSMWorld::LandTexture> newRecord (
CSMWorld::RecordBase::State_ModifiedOnly, 0, &texture);
mState.mTarget->getData().getLandTextures().appendRecord (newRecord);
found = true;
}
++mNext;
}
while (!found);
}
CSMTools::MergeLandStage::MergeLandStage (MergeState& state) : mState (state) {}
int CSMTools::MergeLandStage::setup()
{
return mState.mSource.getData().getLand().getSize();
}
void CSMTools::MergeLandStage::perform (int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<CSMWorld::Land>& record =
mState.mSource.getData().getLand().getRecord (stage);
if (!record.isDeleted())
{
const CSMWorld::Land& land = record.get();
land.loadData (ESM::Land::DATA_VCLR | ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML |
ESM::Land::DATA_VTEX | ESM::Land::DATA_WNAM);
CSMWorld::Land newLand (land);
newLand.mEsm = 0; // avoid potential dangling pointer (ESMReader isn't needed anyway,
// because record is already fully loaded)
newLand.mPlugin = 0;
if (land.mDataTypes & ESM::Land::DATA_VTEX)
{
// adjust land texture references
if (ESM::Land::LandData *data = newLand.getLandData())
{
std::pair<uint16_t, int> key;
key.second = land.mPlugin;
for (int i=0; i<ESM::Land::LAND_NUM_TEXTURES; ++i)
{
key.first = data->mTextures[i];
std::map<std::pair<uint16_t, int>, int>::const_iterator iter =
mState.mTextureIndices.find (key);
if (iter!=mState.mTextureIndices.end())
data->mTextures[i] = iter->second;
else
data->mTextures[i] = 0;
}
}
}
CSMWorld::Record<CSMWorld::Land> newRecord (
CSMWorld::RecordBase::State_ModifiedOnly, 0, &newLand);
mState.mTarget->getData().getLand().appendRecord (newRecord);
}
}

@ -0,0 +1,166 @@
#ifndef CSM_TOOLS_MERGESTAGES_H
#define CSM_TOOLS_MERGESTAGES_H
#include <algorithm>
#include <map>
#include <components/to_utf8/to_utf8.hpp>
#include "../doc/stage.hpp"
#include "../world/data.hpp"
#include "mergestate.hpp"
namespace CSMTools
{
class StartMergeStage : public CSMDoc::Stage
{
MergeState& mState;
public:
StartMergeStage (MergeState& state);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
class FinishMergedDocumentStage : public CSMDoc::Stage
{
MergeState& mState;
ToUTF8::Utf8Encoder mEncoder;
public:
FinishMergedDocumentStage (MergeState& state, ToUTF8::FromType encoding);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
template<typename RecordType, typename Collection = CSMWorld::IdCollection<RecordType> >
class MergeIdCollectionStage : public CSMDoc::Stage
{
MergeState& mState;
Collection& (CSMWorld::Data::*mAccessor)();
public:
MergeIdCollectionStage (MergeState& state, Collection& (CSMWorld::Data::*accessor)());
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
template<typename RecordType, typename Collection>
MergeIdCollectionStage<RecordType, Collection>::MergeIdCollectionStage (MergeState& state, Collection& (CSMWorld::Data::*accessor)())
: mState (state), mAccessor (accessor)
{}
template<typename RecordType, typename Collection>
int MergeIdCollectionStage<RecordType, Collection>::setup()
{
return (mState.mSource.getData().*mAccessor)().getSize();
}
template<typename RecordType, typename Collection>
void MergeIdCollectionStage<RecordType, Collection>::perform (int stage, CSMDoc::Messages& messages)
{
const Collection& source = (mState.mSource.getData().*mAccessor)();
Collection& target = (mState.mTarget->getData().*mAccessor)();
const CSMWorld::Record<RecordType>& record = source.getRecord (stage);
if (!record.isDeleted())
target.appendRecord (CSMWorld::Record<RecordType> (CSMWorld::RecordBase::State_ModifiedOnly, 0, &record.get()));
}
class MergeRefIdsStage : public CSMDoc::Stage
{
MergeState& mState;
public:
MergeRefIdsStage (MergeState& state);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
class MergeReferencesStage : public CSMDoc::Stage
{
MergeState& mState;
std::map<std::string, int> mIndex;
public:
MergeReferencesStage (MergeState& state);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
class ListLandTexturesMergeStage : public CSMDoc::Stage
{
MergeState& mState;
public:
ListLandTexturesMergeStage (MergeState& state);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
class MergeLandTexturesStage : public CSMDoc::Stage
{
MergeState& mState;
std::map<std::pair<uint16_t, int>, int>::iterator mNext;
public:
MergeLandTexturesStage (MergeState& state);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
class MergeLandStage : public CSMDoc::Stage
{
MergeState& mState;
public:
MergeLandStage (MergeState& state);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
}
#endif

@ -0,0 +1,24 @@
#ifndef CSM_TOOLS_MERGESTATE_H
#define CSM_TOOLS_MERGESTATE_H
#include <stdint.h>
#include <memory>
#include <map>
#include "../doc/document.hpp"
namespace CSMTools
{
struct MergeState
{
std::auto_ptr<CSMDoc::Document> mTarget;
CSMDoc::Document& mSource;
bool mCompleted;
std::map<std::pair<uint16_t, int>, int> mTextureIndices; // (texture, content file) -> new texture
MergeState (CSMDoc::Document& source) : mSource (source), mCompleted (false) {}
};
}
#endif

@ -30,9 +30,9 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
// check the number of pathgrid points
if (pathgrid.mData.mS2 > static_cast<int>(pathgrid.mPoints.size()))
messages.push_back (std::make_pair (id, pathgrid.mId + " has less points than expected"));
messages.add (id, pathgrid.mId + " has less points than expected", "", CSMDoc::Message::Severity_Error);
else if (pathgrid.mData.mS2 > static_cast<int>(pathgrid.mPoints.size()))
messages.push_back (std::make_pair (id, pathgrid.mId + " has more points than expected"));
messages.add (id, pathgrid.mId + " has more points than expected", "", CSMDoc::Message::Severity_Error);
std::vector<CSMTools::Point> pointList(pathgrid.mPoints.size());
std::vector<int> duplList;
@ -51,7 +51,7 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
std::ostringstream ss;
ss << "has a duplicate edge between points" << pathgrid.mEdges[i].mV0
<< " and " << pathgrid.mEdges[i].mV1;
messages.push_back (std::make_pair (id, pathgrid.mId + ss.str()));
messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error);
break;
}
}
@ -64,7 +64,7 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
{
std::ostringstream ss;
ss << " has an edge connecting a non-existent point " << pathgrid.mEdges[i].mV0;
messages.push_back (std::make_pair (id, pathgrid.mId + ss.str()));
messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error);
}
}
@ -75,13 +75,13 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
{
std::ostringstream ss;
ss << " has has less edges than expected for point " << i;
messages.push_back (std::make_pair (id, pathgrid.mId + ss.str()));
messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error);
}
else if (pathgrid.mPoints[i].mConnectionNum < pointList[i].mConnectionNum)
{
std::ostringstream ss;
ss << " has has more edges than expected for point " << i;
messages.push_back (std::make_pair (id, pathgrid.mId + ss.str()));
messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error);
}
// check that edges are bidirectional
@ -101,7 +101,7 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
{
std::ostringstream ss;
ss << " has a missing edge between points " << i << " and " << pointList[i].mOtherIndex[j];
messages.push_back (std::make_pair (id, pathgrid.mId + ss.str()));
messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error);
}
}
@ -124,7 +124,7 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
<< ") x=" << pathgrid.mPoints[i].mX
<< ", y=" << pathgrid.mPoints[i].mY
<< ", z=" << pathgrid.mPoints[i].mZ;
messages.push_back (std::make_pair (id, pathgrid.mId + ss.str()));
messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Warning);
duplList.push_back(i);
break;
@ -143,7 +143,7 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
<< ") x=" << pathgrid.mPoints[i].mX
<< ", y=" << pathgrid.mPoints[i].mY
<< ", z=" << pathgrid.mPoints[i].mZ;
messages.push_back (std::make_pair (id, pathgrid.mId + ss.str()));
messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Warning);
}
}

@ -1,4 +1,3 @@
#include "racecheck.hpp"
#include <sstream>

@ -468,6 +468,9 @@ 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 that mentioned scripts exist
scriptCheck<ESM::Creature>(creature, messages, id.toString());
}
@ -648,7 +651,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck (
if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated
{
if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0008 = autocalculated flag
if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0010 = autocalculated flag
{
messages.push_back (std::make_pair (id, npc.mId + " mNpdtType or flags mismatch!")); //should not happend?
return;

@ -1,4 +1,3 @@
#include "regioncheck.hpp"
#include <sstream>

@ -1,4 +1,3 @@
#include "reportmodel.hpp"
#include <stdexcept>
@ -6,24 +5,18 @@
#include "../world/columns.hpp"
CSMTools::ReportModel::Line::Line (const CSMWorld::UniversalId& id, const std::string& message,
const std::string& hint)
: mId (id), mMessage (message), mHint (hint)
{}
CSMTools::ReportModel::ReportModel (bool fieldColumn)
CSMTools::ReportModel::ReportModel (bool fieldColumn, bool severityColumn)
: mColumnField (-1), mColumnSeverity (-1)
{
int index = 3;
if (severityColumn)
mColumnSeverity = index++;
if (fieldColumn)
{
mColumnField = 3;
mColumnDescription = 4;
}
else
{
mColumnDescription = 3;
mColumnField = index++;
mColumnField = -1;
}
mColumnDescription = index;
}
int CSMTools::ReportModel::rowCount (const QModelIndex & parent) const
@ -52,7 +45,7 @@ QVariant CSMTools::ReportModel::data (const QModelIndex & index, int role) const
case Column_Type:
return static_cast<int> (mRows.at (index.row()).mId.getType());
case Column_Id:
{
CSMWorld::UniversalId id = mRows.at (index.row()).mId;
@ -62,7 +55,7 @@ QVariant CSMTools::ReportModel::data (const QModelIndex & index, int role) const
return QString ("-");
}
case Column_Hint:
return QString::fromUtf8 (mRows.at (index.row()).mHint.c_str());
@ -88,7 +81,13 @@ QVariant CSMTools::ReportModel::data (const QModelIndex & index, int role) const
return QString::fromUtf8 (field.c_str());
}
if (index.column()==mColumnSeverity)
{
return QString::fromUtf8 (
CSMDoc::Message::toString (mRows.at (index.row()).mSeverity).c_str());
}
return QVariant();
}
@ -112,6 +111,9 @@ QVariant CSMTools::ReportModel::headerData (int section, Qt::Orientation orienta
if (section==mColumnField)
return "Field";
if (section==mColumnSeverity)
return "Severity";
return "-";
}
@ -132,19 +134,18 @@ bool CSMTools::ReportModel::removeRows (int row, int count, const QModelIndex& p
return true;
}
void CSMTools::ReportModel::add (const CSMWorld::UniversalId& id, const std::string& message,
const std::string& hint)
void CSMTools::ReportModel::add (const CSMDoc::Message& message)
{
beginInsertRows (QModelIndex(), mRows.size(), mRows.size());
mRows.push_back (Line (id, message, hint));
mRows.push_back (message);
endInsertRows();
}
void CSMTools::ReportModel::flagAsReplaced (int index)
{
Line& line = mRows.at (index);
CSMDoc::Message& line = mRows.at (index);
std::string hint = line.mHint;
if (hint.empty() || hint[0]!='R')
@ -176,3 +177,16 @@ void CSMTools::ReportModel::clear()
endRemoveRows();
}
}
int CSMTools::ReportModel::countErrors() const
{
int count = 0;
for (std::vector<CSMDoc::Messages::Message>::const_iterator iter (mRows.begin());
iter!=mRows.end(); ++iter)
if (iter->mSeverity==CSMDoc::Message::Severity_Error ||
iter->mSeverity==CSMDoc::Message::Severity_SeriousError)
++count;
return count;
}

@ -6,6 +6,8 @@
#include <QAbstractTableModel>
#include "../doc/messages.hpp"
#include "../world/universalid.hpp"
namespace CSMTools
@ -14,17 +16,7 @@ namespace CSMTools
{
Q_OBJECT
struct Line
{
Line (const CSMWorld::UniversalId& id, const std::string& message,
const std::string& hint);
CSMWorld::UniversalId mId;
std::string mMessage;
std::string mHint;
};
std::vector<Line> mRows;
std::vector<CSMDoc::Messages::Message> mRows;
// Fixed columns
enum Columns
@ -35,10 +27,11 @@ namespace CSMTools
// Configurable columns
int mColumnDescription;
int mColumnField;
int mColumnSeverity;
public:
ReportModel (bool fieldColumn = false);
ReportModel (bool fieldColumn = false, bool severityColumn = true);
virtual int rowCount (const QModelIndex & parent = QModelIndex()) const;
@ -50,8 +43,7 @@ namespace CSMTools
virtual bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex());
void add (const CSMWorld::UniversalId& id, const std::string& message,
const std::string& hint = "");
void add (const CSMDoc::Message& message);
void flagAsReplaced (int index);
@ -60,6 +52,9 @@ namespace CSMTools
std::string getHint (int row) const;
void clear();
// Return number of messages with Error or SeriousError severity.
int countErrors() const;
};
}

@ -1,4 +1,3 @@
#include "scriptcheck.hpp"
#include <components/compiler/tokenloc.hpp>
@ -11,6 +10,17 @@
#include "../world/data.hpp"
CSMDoc::Message::Severity CSMTools::ScriptCheckStage::getSeverity (Type type)
{
switch (type)
{
case WarningMessage: return CSMDoc::Message::Severity_Warning;
case ErrorMessage: return CSMDoc::Message::Severity_Error;
}
return CSMDoc::Message::Severity_SeriousError;
}
void CSMTools::ScriptCheckStage::report (const std::string& message, const Compiler::TokenLoc& loc,
Type type)
{
@ -18,11 +28,6 @@ void CSMTools::ScriptCheckStage::report (const std::string& message, const Compi
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId);
if (type==ErrorMessage)
stream << "error ";
else
stream << "warning ";
stream
<< "script " << mFile
<< ", line " << loc.mLine << ", column " << loc.mColumn
@ -32,19 +37,21 @@ void CSMTools::ScriptCheckStage::report (const std::string& message, const Compi
hintStream << "l:" << loc.mLine << " " << loc.mColumn;
mMessages->add (id, stream.str(), hintStream.str());
mMessages->add (id, stream.str(), hintStream.str(), getSeverity (type));
}
void CSMTools::ScriptCheckStage::report (const std::string& message, Type type)
{
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId);
mMessages->push_back (std::make_pair (id,
(type==ErrorMessage ? "error: " : "warning: ") + message));
std::ostringstream stream;
stream << "script " << mFile << ": " << message;
mMessages->add (id, stream.str(), "", getSeverity (type));
}
CSMTools::ScriptCheckStage::ScriptCheckStage (const CSMDoc::Document& document)
: mDocument (document), mContext (document.getData()), mMessages (0)
: mDocument (document), mContext (document.getData()), mMessages (0), mWarningMode (Mode_Ignore)
{
/// \todo add an option to configure warning mode
setWarningsMode (0);
@ -58,6 +65,7 @@ int CSMTools::ScriptCheckStage::setup()
mContext.clear();
mMessages = 0;
mId.clear();
Compiler::ErrorHandler::reset();
return mDocument.getData().getScripts().getSize();
}
@ -72,6 +80,13 @@ void CSMTools::ScriptCheckStage::perform (int stage, CSMDoc::Messages& messages)
mMessages = &messages;
switch (mWarningMode)
{
case Mode_Ignore: setWarningsMode (0); break;
case Mode_Normal: setWarningsMode (1); break;
case Mode_Strict: setWarningsMode (2); break;
}
try
{
const CSMWorld::Data& data = mDocument.getData();
@ -93,9 +108,24 @@ void CSMTools::ScriptCheckStage::perform (int stage, CSMDoc::Messages& messages)
{
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId);
messages.push_back (std::make_pair (id,
std::string ("Critical compile error: ") + error.what()));
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;
}
}

@ -18,13 +18,23 @@ namespace CSMTools
/// \brief VerifyStage: make sure that scripts compile
class ScriptCheckStage : public CSMDoc::Stage, private Compiler::ErrorHandler
{
enum WarningMode
{
Mode_Ignore,
Mode_Normal,
Mode_Strict
};
const CSMDoc::Document& mDocument;
Compiler::Extensions mExtensions;
CSMWorld::ScriptContext mContext;
std::string mId;
std::string mFile;
CSMDoc::Messages *mMessages;
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.
@ -40,6 +50,8 @@ 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);
};
}

@ -1,4 +1,3 @@
#include "search.hpp"
#include <stdexcept>
@ -280,7 +279,7 @@ void CSMTools::Search::replace (CSMDoc::Document& document, CSMWorld::IdTableBas
bool CSMTools::Search::verify (CSMDoc::Document& document, CSMWorld::IdTableBase *model,
const CSMWorld::UniversalId& id, const std::string& messageHint) const
{
CSMDoc::Messages messages;
CSMDoc::Messages messages (CSMDoc::Message::Severity_Info);
int row = model->getModelIndex (id.getId(),
model->findColumnIndex (CSMWorld::Columns::ColumnId_Id)).row();

@ -1,4 +1,3 @@
#include "searchoperation.hpp"
#include "../doc/state.hpp"
@ -21,6 +20,8 @@ CSMTools::SearchOperation::SearchOperation (CSMDoc::Document& document)
iter!=types.end(); ++iter)
appendStage (new SearchStage (&dynamic_cast<CSMWorld::IdTableBase&> (
*document.getData().getTableModel (*iter))));
setDefaultSeverity (CSMDoc::Message::Severity_Info);
}
void CSMTools::SearchOperation::configure (const Search& search)

@ -1,4 +1,3 @@
#include "searchstage.hpp"
#include "../world/idtablebase.hpp"

@ -1,4 +1,3 @@
#include "skillcheck.hpp"
#include <sstream>

@ -1,4 +1,3 @@
#include "soundcheck.hpp"
#include <sstream>

@ -0,0 +1,53 @@
#include "soundgencheck.hpp"
#include <sstream>
#include "../world/refiddata.hpp"
#include "../world/universalid.hpp"
CSMTools::SoundGenCheckStage::SoundGenCheckStage(const CSMWorld::IdCollection<ESM::SoundGenerator> &soundGens,
const CSMWorld::IdCollection<ESM::Sound> &sounds,
const CSMWorld::RefIdCollection &referenceables)
: mSoundGens(soundGens),
mSounds(sounds),
mReferenceables(referenceables)
{}
int CSMTools::SoundGenCheckStage::setup()
{
return mSoundGens.getSize();
}
void CSMTools::SoundGenCheckStage::perform(int stage, CSMDoc::Messages &messages)
{
const CSMWorld::Record<ESM::SoundGenerator> &record = mSoundGens.getRecord(stage);
if (record.isDeleted())
{
return;
}
const ESM::SoundGenerator soundGen = record.get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_SoundGen, soundGen.mId);
if (!soundGen.mCreature.empty())
{
CSMWorld::RefIdData::LocalIndex creatureIndex = mReferenceables.getDataSet().searchId(soundGen.mCreature);
if (creatureIndex.first == -1)
{
messages.push_back(std::make_pair(id, "No such creature '" + soundGen.mCreature + "'"));
}
else if (creatureIndex.second != CSMWorld::UniversalId::Type_Creature)
{
messages.push_back(std::make_pair(id, "'" + soundGen.mCreature + "' is not a creature"));
}
}
if (soundGen.mSound.empty())
{
messages.push_back(std::make_pair(id, "Sound is not specified"));
}
else if (mSounds.searchId(soundGen.mSound) == -1)
{
messages.push_back(std::make_pair(id, "No such sound '" + soundGen.mSound + "'"));
}
}

@ -0,0 +1,30 @@
#ifndef CSM_TOOLS_SOUNDGENCHECK_HPP
#define CSM_TOOLS_SOUNDGENCHECK_HPP
#include "../world/data.hpp"
#include "../doc/stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that sound gen records are internally consistent
class SoundGenCheckStage : public CSMDoc::Stage
{
const CSMWorld::IdCollection<ESM::SoundGenerator> &mSoundGens;
const CSMWorld::IdCollection<ESM::Sound> &mSounds;
const CSMWorld::RefIdCollection &mReferenceables;
public:
SoundGenCheckStage(const CSMWorld::IdCollection<ESM::SoundGenerator> &soundGens,
const CSMWorld::IdCollection<ESM::Sound> &sounds,
const CSMWorld::RefIdCollection &referenceables);
virtual int setup();
///< \return number of steps
virtual void perform(int stage, CSMDoc::Messages &messages);
///< Messages resulting from this stage will be appended to \a messages.
};
}
#endif

@ -1,4 +1,3 @@
#include "spellcheck.hpp"
#include <sstream>

@ -1,4 +1,3 @@
#include "startscriptcheck.hpp"
#include <components/misc/stringops.hpp>

@ -1,4 +1,3 @@
#include "tools.hpp"
#include <QThreadPool>
@ -27,6 +26,9 @@
#include "startscriptcheck.hpp"
#include "searchoperation.hpp"
#include "pathgridcheck.hpp"
#include "soundgencheck.hpp"
#include "magiceffectcheck.hpp"
#include "mergeoperation.hpp"
CSMDoc::OperationHolder *CSMTools::Tools::get (int type)
{
@ -34,6 +36,7 @@ CSMDoc::OperationHolder *CSMTools::Tools::get (int type)
{
case CSMDoc::State_Verifying: return &mVerifier;
case CSMDoc::State_Searching: return &mSearch;
case CSMDoc::State_Merging: return &mMerge;
}
return 0;
@ -50,11 +53,15 @@ 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 CSMWorld::UniversalId&, const std::string&, const std::string&, int)),
this, SLOT (verifierMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int)));
connect (&mVerifier, SIGNAL (reportMessage (const CSMDoc::Message&, int)),
this, SLOT (verifierMessage (const CSMDoc::Message&, int)));
std::vector<std::string> mandatoryIds; // I want C++11, damn it!
mandatoryIds.push_back ("Day");
@ -99,15 +106,25 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier()
mVerifierOperation->appendStage (new PathgridCheckStage (mData.getPathgrids()));
mVerifierOperation->appendStage (new SoundGenCheckStage (mData.getSoundGens(),
mData.getSounds(),
mData.getReferenceables()));
mVerifierOperation->appendStage (new MagicEffectCheckStage (mData.getMagicEffects(),
mData.getSounds(),
mData.getReferenceables(),
mData.getResources (CSMWorld::UniversalId::Type_Icons),
mData.getResources (CSMWorld::UniversalId::Type_Textures)));
mVerifier.setOperation (mVerifierOperation);
}
return &mVerifier;
}
CSMTools::Tools::Tools (CSMDoc::Document& document)
CSMTools::Tools::Tools (CSMDoc::Document& document, ToUTF8::FromType encoding)
: mDocument (document), mData (document.getData()), mVerifierOperation (0),
mSearchOperation (0), mNextReportNumber (0)
mSearchOperation (0), mMergeOperation (0), mNextReportNumber (0), mEncoding (encoding)
{
// index 0: load error log
mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel));
@ -115,9 +132,12 @@ CSMTools::Tools::Tools (CSMDoc::Document& document)
connect (&mSearch, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int)));
connect (&mSearch, SIGNAL (done (int, bool)), this, SIGNAL (done (int, bool)));
connect (&mSearch,
SIGNAL (reportMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int)),
this, SLOT (verifierMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int)));
connect (&mSearch, SIGNAL (reportMessage (const CSMDoc::Message&, int)),
this, SLOT (verifierMessage (const CSMDoc::Message&, int)));
connect (&mMerge, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int)));
connect (&mMerge, SIGNAL (done (int, bool)), this, SIGNAL (done (int, bool)));
// don't need to connect report message, since there are no messages for merge
}
CSMTools::Tools::~Tools()
@ -134,23 +154,34 @@ CSMTools::Tools::~Tools()
delete mSearchOperation;
}
if (mMergeOperation)
{
mMerge.abortAndWait();
delete mMergeOperation;
}
for (std::map<int, ReportModel *>::iterator iter (mReports.begin()); iter!=mReports.end(); ++iter)
delete iter->second;
}
CSMWorld::UniversalId CSMTools::Tools::runVerifier()
CSMWorld::UniversalId CSMTools::Tools::runVerifier (const CSMWorld::UniversalId& reportId)
{
mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel));
mActiveReports[CSMDoc::State_Verifying] = mNextReportNumber-1;
int reportNumber = reportId.getType()==CSMWorld::UniversalId::Type_VerificationResults ?
reportId.getIndex() : mNextReportNumber++;
if (mReports.find (reportNumber)==mReports.end())
mReports.insert (std::make_pair (reportNumber, new ReportModel));
mActiveReports[CSMDoc::State_Verifying] = reportNumber;
getVerifier()->start();
return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_VerificationResults, mNextReportNumber-1);
return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_VerificationResults, reportNumber);
}
CSMWorld::UniversalId CSMTools::Tools::newSearch()
{
mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel (true)));
mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel (true, false)));
return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Search, mNextReportNumber-1);
}
@ -170,6 +201,25 @@ void CSMTools::Tools::runSearch (const CSMWorld::UniversalId& searchId, const Se
mSearch.start();
}
void CSMTools::Tools::runMerge (std::auto_ptr<CSMDoc::Document> target)
{
// not setting an active report, because merge does not produce messages
if (!mMergeOperation)
{
mMergeOperation = new MergeOperation (mDocument, mEncoding);
mMerge.setOperation (mMergeOperation);
connect (mMergeOperation, SIGNAL (mergeDone (CSMDoc::Document*)),
this, SIGNAL (mergeDone (CSMDoc::Document*)));
}
target->flagAsDirty();
mMergeOperation->setTarget (target);
mMerge.start();
}
void CSMTools::Tools::abortOperation (int type)
{
if (CSMDoc::OperationHolder *operation = get (type))
@ -182,6 +232,7 @@ int CSMTools::Tools::getRunningOperations() const
{
CSMDoc::State_Verifying,
CSMDoc::State_Searching,
CSMDoc::State_Merging,
-1
};
@ -205,12 +256,10 @@ CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId&
return mReports.at (id.getIndex());
}
void CSMTools::Tools::verifierMessage (const CSMWorld::UniversalId& id, const std::string& message,
const std::string& hint, int type)
void CSMTools::Tools::verifierMessage (const CSMDoc::Message& message, int type)
{
std::map<int, int>::iterator iter = mActiveReports.find (type);
if (iter!=mActiveReports.end())
mReports[iter->second]->add (id, message, hint);
mReports[iter->second]->add (message);
}

@ -1,9 +1,14 @@
#ifndef CSM_TOOLS_TOOLS_H
#define CSM_TOOLS_TOOLS_H
#include <memory>
#include <map>
#include <components/to_utf8/to_utf8.hpp>
#include <QObject>
#include <map>
#include <boost/filesystem/path.hpp>
#include "../doc/operationholder.hpp"
@ -24,6 +29,7 @@ namespace CSMTools
class ReportModel;
class Search;
class SearchOperation;
class MergeOperation;
class Tools : public QObject
{
@ -35,9 +41,12 @@ namespace CSMTools
CSMDoc::OperationHolder mVerifier;
SearchOperation *mSearchOperation;
CSMDoc::OperationHolder mSearch;
MergeOperation *mMergeOperation;
CSMDoc::OperationHolder mMerge;
std::map<int, ReportModel *> mReports;
int mNextReportNumber;
std::map<int, int> mActiveReports; // type, report number
ToUTF8::FromType mEncoding;
// not implemented
Tools (const Tools&);
@ -53,18 +62,23 @@ namespace CSMTools
public:
Tools (CSMDoc::Document& document);
Tools (CSMDoc::Document& document, ToUTF8::FromType encoding);
virtual ~Tools();
CSMWorld::UniversalId runVerifier();
///< \return ID of the report for this verification run
/// \param reportId If a valid VerificationResults ID, run verifier for the
/// specified report instead of creating a new one.
///
/// \return ID of the report for this verification run
CSMWorld::UniversalId runVerifier (const CSMWorld::UniversalId& reportId = CSMWorld::UniversalId());
/// Return ID of the report for this search.
CSMWorld::UniversalId newSearch();
void runSearch (const CSMWorld::UniversalId& searchId, const Search& search);
void runMerge (std::auto_ptr<CSMDoc::Document> target);
void abortOperation (int type);
///< \attention The operation is not aborted immediately.
@ -75,14 +89,17 @@ namespace CSMTools
private slots:
void verifierMessage (const CSMWorld::UniversalId& id, const std::string& message,
const std::string& hint, int type);
void verifierMessage (const CSMDoc::Message& message, int type);
signals:
void progress (int current, int max, int type);
void done (int type, bool failed);
/// \attention When this signal is emitted, *this hands over the ownership of the
/// document. This signal must be handled to avoid a leak.
void mergeDone (CSMDoc::Document *document);
};
}

@ -1,4 +1,3 @@
#include "cell.hpp"
#include <sstream>

@ -1,4 +1,3 @@
#include "cellcoordinates.hpp"
#include <ostream>

@ -1,4 +1,3 @@
#include "cellselection.hpp"
#include <cmath>

@ -1,4 +1,3 @@
#include "collectionbase.hpp"
#include <stdexcept>

@ -77,16 +77,18 @@ bool CSMWorld::ColumnBase::isId (Display display)
Display_Video,
Display_Id,
Display_SkillImpact,
Display_SkillId,
Display_EffectRange,
Display_EffectId,
Display_PartRefType,
Display_AiPackageType,
Display_YesNo,
Display_InfoCondFunc,
Display_InfoCondVar,
Display_InfoCondComp,
Display_RaceSkill,
Display_EffectSkill,
Display_EffectAttribute,
Display_IngredEffectId,
Display_None
};
@ -100,7 +102,8 @@ bool CSMWorld::ColumnBase::isId (Display display)
bool CSMWorld::ColumnBase::isText (Display display)
{
return display==Display_String || display==Display_LongString;
return display==Display_String || display==Display_LongString ||
display==Display_String32 || display==Display_LongString256;
}
bool CSMWorld::ColumnBase::isScript (Display display)

@ -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,
@ -113,16 +120,20 @@ namespace CSMWorld
Display_SoundGeneratorType,
Display_School,
Display_Id,
Display_SkillImpact,
Display_SkillId,
Display_EffectRange,
Display_EffectId,
Display_PartRefType,
Display_AiPackageType,
Display_YesNo,
Display_InfoCondFunc,
Display_InfoCondVar,
Display_InfoCondComp,
Display_RaceSkill,
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,19 +197,32 @@ 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)
{
// There is nothing to do here.
// This prevents exceptions from parent's implementation
}
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
@ -213,4 +237,6 @@ namespace CSMWorld
};
}
Q_DECLARE_METATYPE(CSMWorld::ColumnBase::TableEditModes)
#endif

@ -0,0 +1,28 @@
#include "columnimp.hpp"
CSMWorld::BodyPartRaceColumn::BodyPartRaceColumn(const MeshTypeColumn<ESM::BodyPart> *meshType)
: mMeshType(meshType)
{}
QVariant CSMWorld::BodyPartRaceColumn::get(const Record<ESM::BodyPart> &record) const
{
if (mMeshType != NULL && mMeshType->get(record) == ESM::BodyPart::MT_Skin)
{
return QString::fromUtf8(record.get().mRace.c_str());
}
return QVariant(QVariant::UserType);
}
void CSMWorld::BodyPartRaceColumn::set(Record<ESM::BodyPart> &record, const QVariant &data)
{
ESM::BodyPart record2 = record.get();
record2.mRace = data.toString().toUtf8().constData();
record.setModified(record2);
}
bool CSMWorld::BodyPartRaceColumn::isEditable() const
{
return true;
}

@ -9,6 +9,10 @@
#include <QColor>
#include <components/esm/loadbody.hpp>
#include <components/esm/loadskil.hpp>
#include <components/esm/loadrace.hpp>
#include "columnbase.hpp"
#include "columns.hpp"
#include "info.hpp"
@ -694,7 +698,7 @@ namespace CSMWorld
QColor colour = data.value<QColor>();
record2.mMapColor = colour.rgb() & 0xffffff;
record2.mMapColor = (colour.blue() << 16) | (colour.green() << 8) | colour.red();
record.setModified (record2);
}
@ -1911,8 +1915,8 @@ namespace CSMWorld
template<typename ESXRecordT>
struct MeshTypeColumn : public Column<ESXRecordT>
{
MeshTypeColumn()
: Column<ESXRecordT> (Columns::ColumnId_MeshType, ColumnBase::Display_MeshType)
MeshTypeColumn(int flags = ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue)
: Column<ESXRecordT> (Columns::ColumnId_MeshType, ColumnBase::Display_MeshType, flags)
{}
virtual QVariant get (const Record<ESXRecordT>& record) const
@ -2308,6 +2312,89 @@ namespace CSMWorld
return true;
}
};
template<typename ESXRecordT>
struct FormatColumn : public Column<ESXRecordT>
{
FormatColumn()
: Column<ESXRecordT> (Columns::ColumnId_FileFormat, ColumnBase::Display_Integer)
{}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return record.get().mFormat;
}
virtual bool isEditable() const
{
return false;
}
};
template<typename ESXRecordT>
struct AuthorColumn : public Column<ESXRecordT>
{
AuthorColumn()
: Column<ESXRecordT> (Columns::ColumnId_Author, ColumnBase::Display_String32)
{}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return QString::fromUtf8 (record.get().mAuthor.c_str());
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mAuthor = data.toString().toUtf8().constData();
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
};
template<typename ESXRecordT>
struct FileDescriptionColumn : public Column<ESXRecordT>
{
FileDescriptionColumn()
: Column<ESXRecordT> (Columns::ColumnId_FileDescription, ColumnBase::Display_LongString256)
{}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return QString::fromUtf8 (record.get().mDescription.c_str());
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mDescription = data.toString().toUtf8().constData();
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
};
struct BodyPartRaceColumn : public RaceColumn<ESM::BodyPart>
{
const MeshTypeColumn<ESM::BodyPart> *mMeshType;
BodyPartRaceColumn(const MeshTypeColumn<ESM::BodyPart> *meshType);
virtual QVariant get(const Record<ESM::BodyPart> &record) const;
virtual void set(Record<ESM::BodyPart> &record, const QVariant &data);
virtual bool isEditable() const;
};
}
#endif

@ -1,4 +1,3 @@
#include "columns.hpp"
#include <components/misc/stringops.hpp>
@ -35,6 +34,8 @@ namespace CSMWorld
{ ColumnId_Volume, "Volume" },
{ ColumnId_MinRange, "Min Range" },
{ ColumnId_MaxRange, "Max Range" },
{ ColumnId_MinMagnitude, "Min Magnitude" },
{ ColumnId_MaxMagnitude, "Max Magnitude" },
{ ColumnId_SoundFile, "Sound File" },
{ ColumnId_MapColour, "Map Colour" },
{ ColumnId_SleepEncounter, "Sleep Encounter" },
@ -70,7 +71,6 @@ namespace CSMWorld
{ ColumnId_Weight, "Weight" },
{ ColumnId_EnchantmentPoints, "Enchantment Points" },
{ ColumnId_Quality, "Quality" },
{ ColumnId_Ai, "AI" },
{ ColumnId_AiHello, "AI Hello" },
{ ColumnId_AiFlee, "AI Flee" },
{ ColumnId_AiFight, "AI Fight" },
@ -92,7 +92,7 @@ namespace CSMWorld
{ ColumnId_Trainer, "Trainer" },
{ ColumnId_Spellmaking, "Spellmaking" },
{ ColumnId_EnchantingService, "Enchanting Service" },
{ ColumnId_RepairService, "Repair Serivce" },
{ ColumnId_RepairService, "Repair Service" },
{ ColumnId_ApparatusType, "Apparatus Type" },
{ ColumnId_ArmorType, "Armor Type" },
{ ColumnId_Health, "Health" },
@ -107,7 +107,6 @@ namespace CSMWorld
{ ColumnId_OriginalCreature, "Original Creature" },
{ ColumnId_Biped, "Biped" },
{ ColumnId_HasWeapon, "Has Weapon" },
{ ColumnId_NoMovement, "No Movement" },
{ ColumnId_Swims, "Swims" },
{ ColumnId_Flies, "Flies" },
{ ColumnId_Walks, "Walks" },
@ -199,8 +198,6 @@ namespace CSMWorld
{ ColumnId_RotY, "Rotation Y"},
{ ColumnId_RotZ, "Rotation Z"},
{ ColumnId_Skill, "Skill" },
{ ColumnId_OwnerGlobal, "Owner Global" },
{ ColumnId_DefaultProfile, "Default Profile" },
{ ColumnId_BypassNewGame, "Bypass New Game" },
@ -252,7 +249,7 @@ namespace CSMWorld
{ ColumnId_AiWanderDist, "Wander Dist" },
{ ColumnId_AiDuration, "Ai Duration" },
{ ColumnId_AiWanderToD, "Wander ToD" },
{ ColumnId_AiWanderIdle, "Wander Idle" },
//{ ColumnId_AiWanderIdle, "Wander Idle" },
{ ColumnId_AiWanderRepeat, "Wander Repeat" },
{ ColumnId_AiActivateName, "Activate" },
{ ColumnId_AiTargetId, "Target ID" },
@ -265,13 +262,13 @@ namespace CSMWorld
{ ColumnId_LevelledList,"Levelled List" },
{ ColumnId_LevelledItemId,"Levelled Item" },
{ ColumnId_LevelledItemLevel,"Level" },
{ ColumnId_LevelledItemLevel,"Item Level" },
{ ColumnId_LevelledItemType, "Calculate all levels <= player" },
{ ColumnId_LevelledItemTypeEach, "Select a new item each instance" },
{ ColumnId_LevelledItemChanceNone, "Chance None" },
{ ColumnId_PowerList, "Powers" },
{ ColumnId_SkillImpact, "Skills" },
{ ColumnId_Skill, "Skill" },
{ ColumnId_InfoList, "Info List" },
{ ColumnId_InfoCondition, "Info Conditions" },
@ -281,26 +278,24 @@ namespace CSMWorld
{ ColumnId_InfoCondValue, "Values" },
{ ColumnId_OriginalCell, "Original Cell" },
{ ColumnId_NpcAttributes, "Attributes" },
{ ColumnId_NpcSkills, "Skills" },
{ ColumnId_NpcAttributes, "NPC Attributes" },
{ ColumnId_NpcSkills, "NPC Skill" },
{ ColumnId_UChar, "Value [0..255]" },
{ ColumnId_NpcMisc, "Misc" },
{ ColumnId_NpcLevel, "Level" },
{ ColumnId_NpcMisc, "NPC Misc" },
{ ColumnId_Level, "Level" },
{ ColumnId_NpcFactionID, "Faction ID" },
{ ColumnId_NpcHealth, "Health" },
{ ColumnId_NpcMana, "Mana" },
{ ColumnId_NpcFatigue, "Fatigue" },
{ ColumnId_NpcDisposition, "Disposition" },
{ ColumnId_Mana, "Mana" },
{ ColumnId_Fatigue, "Fatigue" },
{ ColumnId_NpcDisposition, "NPC Disposition" },
{ ColumnId_NpcReputation, "Reputation" },
{ ColumnId_NpcRank, "Rank" },
{ ColumnId_NpcGold, "Gold" },
{ ColumnId_NpcRank, "NPC Rank" },
{ ColumnId_Gold, "Gold" },
{ ColumnId_NpcPersistence, "Persistent" },
{ ColumnId_RaceAttributes, "Attributes" },
{ ColumnId_RaceMaleValue, "Male" },
{ ColumnId_RaceFemaleValue, "Female" },
{ ColumnId_RaceAttributes, "Race Attributes" },
{ ColumnId_Male, "Male" },
{ ColumnId_RaceSkillBonus, "Skill Bonus" },
{ ColumnId_RaceSkill, "Skills" },
{ ColumnId_RaceBonus, "Bonus" },
{ ColumnId_Interior, "Interior" },
@ -311,6 +306,30 @@ namespace CSMWorld
{ ColumnId_WaterLevel, "Water Level" },
{ ColumnId_MapColor, "Map Color" },
{ ColumnId_FileFormat, "File Format" },
{ ColumnId_FileDescription, "File Description" },
{ ColumnId_Author, "Author" },
{ ColumnId_CreatureAttributes, "Creature Attributes" },
{ ColumnId_AttributeValue, "Attrib Value" },
{ ColumnId_CreatureAttack, "Creature Attack" },
{ ColumnId_MinAttack, "Min Attack" },
{ ColumnId_MaxAttack, "Max Attack" },
{ ColumnId_CreatureMisc, "Creature Misc" },
{ ColumnId_Idle1, "Idle 1" },
{ ColumnId_Idle2, "Idle 2" },
{ ColumnId_Idle3, "Idle 3" },
{ ColumnId_Idle4, "Idle 4" },
{ ColumnId_Idle5, "Idle 5" },
{ ColumnId_Idle6, "Idle 6" },
{ ColumnId_Idle7, "Idle 7" },
{ ColumnId_Idle8, "Idle 8" },
{ ColumnId_SpellSrc, "From Race" },
{ ColumnId_SpellCost, "Cast Cost" },
{ ColumnId_SpellChance, "Cast Chance" },
{ ColumnId_UseValue1, "Use value 1" },
{ ColumnId_UseValue2, "Use value 2" },
{ ColumnId_UseValue3, "Use value 3" },
@ -532,11 +551,6 @@ namespace
"AI Wander", "AI Travel", "AI Follow", "AI Escort", "AI Activate", 0
};
static const char *sAiWanderRepeat[] =
{
"No", "Yes", 0
};
static const char *sInfoCondFunc[] =
{
" ", "Function", "Global", "Local", "Journal",
@ -571,17 +585,15 @@ namespace
case CSMWorld::Columns::ColumnId_MeshType: return sMeshTypes;
case CSMWorld::Columns::ColumnId_SoundGeneratorType: return sSoundGeneratorType;
case CSMWorld::Columns::ColumnId_School: return sSchools;
case CSMWorld::Columns::ColumnId_SkillImpact: return sSkills;
case CSMWorld::Columns::ColumnId_Skill: return sSkills;
case CSMWorld::Columns::ColumnId_EffectRange: return sEffectRange;
case CSMWorld::Columns::ColumnId_EffectId: return sEffectId;
case CSMWorld::Columns::ColumnId_PartRefType: return sPartRefType;
case CSMWorld::Columns::ColumnId_AiPackageType: return sAiPackageType;
case CSMWorld::Columns::ColumnId_AiWanderRepeat: return sAiWanderRepeat;
case CSMWorld::Columns::ColumnId_InfoCondFunc: return sInfoCondFunc;
// FIXME: don't have dynamic value enum delegate, use Display_String for now
//case CSMWorld::Columns::ColumnId_InfoCond: return sInfoCond;
case CSMWorld::Columns::ColumnId_InfoCondComp: return sInfoCondComp;
case CSMWorld::Columns::ColumnId_RaceSkill: return sSkills;
default: return 0;
}

@ -65,7 +65,7 @@ namespace CSMWorld
ColumnId_Weight = 50,
ColumnId_EnchantmentPoints = 51,
ColumnId_Quality = 52,
ColumnId_Ai = 53,
// unused
ColumnId_AiHello = 54,
ColumnId_AiFlee = 55,
ColumnId_AiFight = 56,
@ -102,7 +102,7 @@ namespace CSMWorld
ColumnId_OriginalCreature = 87,
ColumnId_Biped = 88,
ColumnId_HasWeapon = 89,
ColumnId_NoMovement = 90,
// used for SpellSrc
ColumnId_Swims = 91,
ColumnId_Flies = 92,
ColumnId_Walks = 93,
@ -189,7 +189,7 @@ namespace CSMWorld
ColumnId_RotX = 174,
ColumnId_RotY = 175,
ColumnId_RotZ = 176,
ColumnId_Skill = 177,
// used for SpellCost
ColumnId_OwnerGlobal = 178,
ColumnId_DefaultProfile = 179,
ColumnId_BypassNewGame = 180,
@ -241,7 +241,7 @@ namespace CSMWorld
ColumnId_AiWanderDist = 221,
ColumnId_AiDuration = 222,
ColumnId_AiWanderToD = 223,
ColumnId_AiWanderIdle = 224,
// unused
ColumnId_AiWanderRepeat = 225,
ColumnId_AiActivateName = 226,
// use ColumnId_PosX, etc for AI destinations
@ -261,7 +261,7 @@ namespace CSMWorld
ColumnId_LevelledItemChanceNone = 238,
ColumnId_PowerList = 239,
ColumnId_SkillImpact = 240, // impact from magic effects
ColumnId_Skill = 240,
ColumnId_InfoList = 241,
ColumnId_InfoCondition = 242,
@ -276,22 +276,22 @@ namespace CSMWorld
ColumnId_NpcSkills = 249,
ColumnId_UChar = 250,
ColumnId_NpcMisc = 251,
ColumnId_NpcLevel = 252,
ColumnId_Level = 252,
ColumnId_NpcFactionID = 253,
ColumnId_NpcHealth = 254,
ColumnId_NpcMana = 255,
ColumnId_NpcFatigue = 256,
// used for SpellChance
ColumnId_Mana = 255,
ColumnId_Fatigue = 256,
ColumnId_NpcDisposition = 257,
ColumnId_NpcReputation = 258,
ColumnId_NpcRank = 259,
ColumnId_NpcGold = 260,
ColumnId_Gold = 260,
ColumnId_NpcPersistence = 261,
ColumnId_RaceAttributes = 262,
ColumnId_RaceMaleValue = 263,
ColumnId_RaceFemaleValue = 264,
ColumnId_Male = 263,
// unused
ColumnId_RaceSkillBonus = 265,
ColumnId_RaceSkill = 266,
// unused
ColumnId_RaceBonus = 267,
ColumnId_Interior = 268,
@ -302,6 +302,33 @@ namespace CSMWorld
ColumnId_WaterLevel = 273,
ColumnId_MapColor = 274,
ColumnId_FileFormat = 275,
ColumnId_FileDescription = 276,
ColumnId_Author = 277,
ColumnId_MinMagnitude = 278,
ColumnId_MaxMagnitude = 279,
ColumnId_CreatureAttributes = 280,
ColumnId_AttributeValue = 281,
ColumnId_CreatureAttack = 282,
ColumnId_MinAttack = 283,
ColumnId_MaxAttack = 284,
ColumnId_CreatureMisc = 285,
ColumnId_Idle1 = 286,
ColumnId_Idle2 = 287,
ColumnId_Idle3 = 288,
ColumnId_Idle4 = 289,
ColumnId_Idle5 = 290,
ColumnId_Idle6 = 291,
ColumnId_Idle7 = 292,
ColumnId_Idle8 = 293,
ColumnId_SpellSrc = 90,
ColumnId_SpellCost = 177,
ColumnId_SpellChance = 254,
// Allocated to a separate value range, so we don't get a collision should we ever need
// to extend the number of use values.
ColumnId_UseValue1 = 0x10000,

@ -1,4 +1,3 @@
#include "commanddispatcher.hpp"
#include <algorithm>

@ -21,19 +21,31 @@ CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelI
// Replace proxy with actual model
mIndex = proxy->mapToSource (index);
mModel = proxy->sourceModel();
}
if (mIndex.parent().isValid())
{
setText ("Modify " + dynamic_cast<CSMWorld::IdTree*>(mModel)->nestedHeaderData (
mIndex.parent().column(), mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString());
}
else
{
setText ("Modify " + mModel->headerData (mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString());
}
// Remember record state before the modification
if (CSMWorld::IdTable *table = dynamic_cast<IdTable *>(mModel))
{
mHasRecordState = true;
int stateColumnIndex = table->findColumnIndex(Columns::ColumnId_Modification);
mRecordStateIndex = table->index(mIndex.row(), stateColumnIndex);
int rowIndex = mIndex.row();
if (mIndex.parent().isValid())
{
rowIndex = mIndex.parent().row();
}
mRecordStateIndex = table->index(rowIndex, stateColumnIndex);
mOldRecordState = static_cast<CSMWorld::RecordBase::State>(table->data(mRecordStateIndex).toInt());
}
}
@ -58,6 +70,25 @@ void CSMWorld::CreateCommand::applyModifications()
{
for (std::map<int, QVariant>::const_iterator iter (mValues.begin()); iter!=mValues.end(); ++iter)
mModel.setData (mModel.getModelIndex (mId, iter->first), iter->second);
if (!mNestedValues.empty())
{
CSMWorld::IdTree *tree = dynamic_cast<CSMWorld::IdTree *>(&mModel);
if (tree == NULL)
{
throw std::logic_error("CSMWorld::CreateCommand: Attempt to add nested values to the non-nested model");
}
std::map<int, std::pair<int, QVariant> >::const_iterator current = mNestedValues.begin();
std::map<int, std::pair<int, QVariant> >::const_iterator end = mNestedValues.end();
for (; current != end; ++current)
{
QModelIndex index = tree->index(0,
current->second.first,
tree->getNestedModelIndex(mId, current->first));
tree->setData(index, current->second.second);
}
}
}
CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand* parent)
@ -71,6 +102,11 @@ void CSMWorld::CreateCommand::addValue (int column, const QVariant& value)
mValues[column] = value;
}
void CSMWorld::CreateCommand::addNestedValue(int parentColumn, int nestedColumn, const QVariant &value)
{
mNestedValues[parentColumn] = std::make_pair(nestedColumn, value);
}
void CSMWorld::CreateCommand::setType (UniversalId::Type type)
{
mType = type;
@ -258,21 +294,24 @@ CSMWorld::DeleteNestedCommand::DeleteNestedCommand (IdTree& model,
std::string title =
model.headerData(parentColumn, Qt::Horizontal, Qt::DisplayRole).toString().toUtf8().constData();
setText (("Delete row in " + title + " sub-table of " + mId).c_str());
QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn);
mModifyParentCommand = new ModifyCommand(mModel, parentIndex, parentIndex.data(Qt::EditRole), this);
}
void CSMWorld::DeleteNestedCommand::redo()
{
const QModelIndex& parentIndex = mModel.getModelIndex(mId, mParentColumn);
QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn);
mModel.removeRows (mNestedRow, 1, parentIndex);
mModifyParentCommand->redo();
}
void CSMWorld::DeleteNestedCommand::undo()
{
const QModelIndex& parentIndex = mModel.getModelIndex(mId, mParentColumn);
QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn);
mModel.setNestedTable(parentIndex, getOld());
mModifyParentCommand->undo();
}
CSMWorld::AddNestedCommand::AddNestedCommand(IdTree& model, const std::string& id, int nestedRow, int parentColumn, QUndoCommand* parent)
@ -286,20 +325,23 @@ CSMWorld::AddNestedCommand::AddNestedCommand(IdTree& model, const std::string& i
std::string title =
model.headerData(parentColumn, Qt::Horizontal, Qt::DisplayRole).toString().toUtf8().constData();
setText (("Add row in " + title + " sub-table of " + mId).c_str());
QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn);
mModifyParentCommand = new ModifyCommand(mModel, parentIndex, parentIndex.data(Qt::EditRole), this);
}
void CSMWorld::AddNestedCommand::redo()
{
const QModelIndex& parentIndex = mModel.getModelIndex(mId, mParentColumn);
QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn);
mModel.addNestedRow (parentIndex, mNewRow);
mModifyParentCommand->redo();
}
void CSMWorld::AddNestedCommand::undo()
{
const QModelIndex& parentIndex = mModel.getModelIndex(mId, mParentColumn);
QModelIndex parentIndex = mModel.getModelIndex(mId, mParentColumn);
mModel.setNestedTable(parentIndex, getOld());
mModifyParentCommand->undo();
}
CSMWorld::NestedTableStoring::NestedTableStoring(const IdTree& model, const std::string& id, int parentColumn)

@ -47,6 +47,9 @@ namespace CSMWorld
class CreateCommand : public QUndoCommand
{
std::map<int, QVariant> mValues;
std::map<int, std::pair<int, QVariant> > mNestedValues;
///< Parameter order: a parent column, a nested column, a data.
///< A nested row has index of 0.
protected:
@ -67,6 +70,8 @@ namespace CSMWorld
void addValue (int column, const QVariant& value);
void addNestedValue(int parentColumn, int nestedColumn, const QVariant &value);
virtual void redo();
virtual void undo();
@ -194,6 +199,9 @@ namespace CSMWorld
int mNestedRow;
// The command to redo/undo the Modified status of a record
ModifyCommand *mModifyParentCommand;
public:
DeleteNestedCommand (IdTree& model,
@ -214,6 +222,9 @@ namespace CSMWorld
int mParentColumn;
// The command to redo/undo the Modified status of a record
ModifyCommand *mModifyParentCommand;
public:
AddNestedCommand(IdTree& model,

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

Loading…
Cancel
Save