From 31db37afd460201d83767b7a4f0cd4933a7b96c1 Mon Sep 17 00:00:00 2001 From: k1ll Date: Wed, 19 Jun 2013 20:22:07 +0200 Subject: [PATCH 01/54] FindMygui mingw --- cmake/FindMyGUI.cmake | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/cmake/FindMyGUI.cmake b/cmake/FindMyGUI.cmake index 40fa2373f..2829a74d3 100644 --- a/cmake/FindMyGUI.cmake +++ b/cmake/FindMyGUI.cmake @@ -19,7 +19,46 @@ include(PreprocessorUtils) # ENDIF (MYGUI_LIBRARIES AND MYGUI_INCLUDE_DIRS) IF (WIN32) #Windows + MESSAGE(STATUS "Looking for MyGUI") + + IF(MINGW) + + FIND_PATH ( MYGUI_INCLUDE_DIRS MyGUI.h PATH_SUFFIXES MYGUI) + FIND_PATH ( MYGUI_PLATFORM_INCLUDE_DIRS MyGUI_OgrePlatform.h PATH_SUFFIXES MYGUI) + FIND_LIBRARY ( MYGUI_LIBRARIES_REL NAMES + libMyGUIEngine${CMAKE_SHARED_LIBRARY_SUFFIX} + libMyGUI.OgrePlatform${CMAKE_STATIC_LIBRARY_SUFFIX} + HINTS + ${MYGUI_LIB_DIR} + PATH_SUFFIXES "" release relwithdebinfo minsizerel ) + + FIND_LIBRARY ( MYGUI_LIBRARIES_DBG NAMES + libMyGUIEngine_d${CMAKE_SHARED_LIBRARY_SUFFIX} + libMyGUI.OgrePlatform_d${CMAKE_STATIC_LIBRARY_SUFFIX} + HINTS + ${MYGUI_LIB_DIR} + PATH_SUFFIXES "" debug ) + + FIND_LIBRARY ( MYGUI_PLATFORM_LIBRARIES_REL NAMES + libMyGUI.OgrePlatform${CMAKE_STATIC_LIBRARY_SUFFIX} + HINTS + ${MYGUI_LIB_DIR} + PATH_SUFFIXES "" release relwithdebinfo minsizerel ) + + FIND_LIBRARY ( MYGUI_PLATFORM_LIBRARIES_DBG NAMES + MyGUI.OgrePlatform_d${CMAKE_STATIC_LIBRARY_SUFFIX} + HINTS + ${MYGUI_LIB_DIR} + PATH_SUFFIXES "" debug ) + + make_library_set ( MYGUI_LIBRARIES ) + make_library_set ( MYGUI_PLATFORM_LIBRARIES ) + + MESSAGE ("${MYGUI_LIBRARIES}") + MESSAGE ("${MYGUI_PLATFORM_LIBRARIES}") + ENDIF(MINGW) + SET(MYGUISDK $ENV{MYGUI_HOME}) IF (MYGUISDK) findpkg_begin ( "MYGUI" ) From 00b7712a5910932f5bb8e333e6a28883f506092a Mon Sep 17 00:00:00 2001 From: k1ll Date: Sat, 22 Jun 2013 11:33:22 +0200 Subject: [PATCH 02/54] Fix shlwapi include case and add the lib for linking when building for windows --- components/CMakeLists.txt | 4 ++++ components/files/windowspath.cpp | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 38fcd88e3..5bd18d119 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -170,6 +170,10 @@ if (GIT_CHECKOUT) add_dependencies (components git-version) endif (GIT_CHECKOUT) +if (WIN32) +target_link_libraries(components shlwapi) +endif() + # Fix for not visible pthreads functions for linker with glibc 2.15 if (UNIX AND NOT APPLE) target_link_libraries(components ${CMAKE_THREAD_LIBS_INIT}) diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index 0df782702..ece4049a8 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -6,9 +6,7 @@ #include #include -#include - -#pragma comment(lib, "Shlwapi.lib") +#include #include namespace bconv = boost::locale::conv; From a230050ec8d4d094a5905b0280173fc3e780a09a Mon Sep 17 00:00:00 2001 From: k1ll Date: Sat, 22 Jun 2013 11:37:17 +0200 Subject: [PATCH 03/54] Boost tr1 unordered map does not work with mingw --- apps/openmw/mwworld/worldimp.cpp | 2 +- components/files/configurationmanager.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 013386f8f..744057e43 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1,6 +1,6 @@ #include "worldimp.hpp" -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include #elif defined HAVE_UNORDERED_MAP #include diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index b0b7fea9a..24b08c523 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -1,7 +1,7 @@ #ifndef COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP #define COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__MINGW32__) #include #elif defined HAVE_UNORDERED_MAP #include From de247ec94b26f08b15fff61a3fed070a7e06109c Mon Sep 17 00:00:00 2001 From: k1ll Date: Sat, 22 Jun 2013 11:48:52 +0200 Subject: [PATCH 04/54] Fix ifdef --- extern/sdl4ogre/sdlwindowhelper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/sdl4ogre/sdlwindowhelper.cpp b/extern/sdl4ogre/sdlwindowhelper.cpp index 637fae0ef..6690e3cab 100644 --- a/extern/sdl4ogre/sdlwindowhelper.cpp +++ b/extern/sdl4ogre/sdlwindowhelper.cpp @@ -30,7 +30,7 @@ SDLWindowHelper::SDLWindowHelper (SDL_Window* window, int w, int h, switch (wmInfo.subsystem) { -#ifdef WIN32 +#ifdef _WIN32 case SDL_SYSWM_WINDOWS: // Windows code winHandle = Ogre::StringConverter::toString((uintptr_t)wmInfo.info.win.window); From c61919501dfb4416797c398a957bcf5259748ed5 Mon Sep 17 00:00:00 2001 From: k1ll Date: Sat, 22 Jun 2013 11:56:18 +0200 Subject: [PATCH 05/54] Fix unnecessary include, ssize_t define and use pretty function instead of funcsig for mingw --- extern/ogre-ffmpeg-videoplayer/audiodecoder.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extern/ogre-ffmpeg-videoplayer/audiodecoder.hpp b/extern/ogre-ffmpeg-videoplayer/audiodecoder.hpp index b05b16d42..fe36ec39f 100644 --- a/extern/ogre-ffmpeg-videoplayer/audiodecoder.hpp +++ b/extern/ogre-ffmpeg-videoplayer/audiodecoder.hpp @@ -21,8 +21,8 @@ extern "C" #endif } -#ifdef _WIN32 -#include +#if defined(_WIN32) && !defined(__MINGW32__) +#include typedef SSIZE_T ssize_t; #endif From b194af4ab27d35a167c69c9b8f6a6f481017b237 Mon Sep 17 00:00:00 2001 From: k1ll Date: Sat, 6 Jul 2013 13:32:59 +0200 Subject: [PATCH 06/54] Add static qt to components --- components/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 5bd18d119..e6158c0a2 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -170,6 +170,10 @@ if (GIT_CHECKOUT) add_dependencies (components git-version) endif (GIT_CHECKOUT) +if(MINGW) +target_link_libraries(components ${QT_LIBRARIES}) +endif() + if (WIN32) target_link_libraries(components shlwapi) endif() From 8e094d6fa5ed03434c1fd7e86a1018dc8a895137 Mon Sep 17 00:00:00 2001 From: k1ll Date: Sat, 6 Jul 2013 17:18:11 +0200 Subject: [PATCH 07/54] Add static bullet to components --- components/CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index e6158c0a2..01de1c28e 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -127,6 +127,9 @@ set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui ) find_package(Qt4 COMPONENTS QtCore QtGui) +if(MINGW) +find_package(Bullet REQUIRED COMPONENTS Collision) +endif() if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) add_component_qt_dir (contentselector @@ -171,7 +174,7 @@ if (GIT_CHECKOUT) endif (GIT_CHECKOUT) if(MINGW) -target_link_libraries(components ${QT_LIBRARIES}) +target_link_libraries(components ${QT_LIBRARIES} ${BULLET_LIBRARIES}) endif() if (WIN32) From fe03727ae5fa6e598b151122ad5f3878e52b2e3c Mon Sep 17 00:00:00 2001 From: k1ll Date: Tue, 6 Aug 2013 18:55:17 +0200 Subject: [PATCH 08/54] Don't force linking static boost libraries when building with mingw --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 07fffd577..2c83e2662 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -134,9 +134,11 @@ endif() # Platform specific if (WIN32) + if(NOT MINGW) set(Boost_USE_STATIC_LIBS ON) set(PLATFORM_INCLUDE_DIR "platform") add_definitions(-DBOOST_ALL_NO_LIB) + endif(NOT MINGW) # Suppress WinMain(), provided by SDL add_definitions(-DSDL_MAIN_HANDLED) From 96d9afec384d16de4876c3e3b74a190afee28732 Mon Sep 17 00:00:00 2001 From: k1ll Date: Fri, 23 May 2014 23:26:05 +0200 Subject: [PATCH 09/54] More header case fixes --- apps/openmw/main.cpp | 2 +- extern/oics/tinyxml.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 070136dfd..2d2c9af0c 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -15,7 +15,7 @@ #if defined(_WIN32) // For OutputDebugString #define WIN32_LEAN_AND_MEAN -#include +#include // makes __argc and __argv available on windows #include #endif diff --git a/extern/oics/tinyxml.cpp b/extern/oics/tinyxml.cpp index 21b2d9c9a..5d8eb475a 100644 --- a/extern/oics/tinyxml.cpp +++ b/extern/oics/tinyxml.cpp @@ -32,7 +32,7 @@ distribution. #include "tinyxml.h" #ifdef _WIN32 -#include // import MultiByteToWideChar +#include // import MultiByteToWideChar #endif From 901ee5b756ab1d156e055ccf1f5d0624d240c53b Mon Sep 17 00:00:00 2001 From: k1ll Date: Fri, 23 May 2014 23:35:28 +0200 Subject: [PATCH 10/54] Add flag for mingw-w64 unicode support --- apps/mwiniimporter/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/mwiniimporter/CMakeLists.txt b/apps/mwiniimporter/CMakeLists.txt index 790d47dc4..753c86fef 100644 --- a/apps/mwiniimporter/CMakeLists.txt +++ b/apps/mwiniimporter/CMakeLists.txt @@ -18,6 +18,10 @@ target_link_libraries(openmw-iniimporter components ) +if (MINGW) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -municode") +endif() + if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) target_link_libraries(openmw-iniimporter gcov) From 7494340b66500c08aa6f6f7fe41c957d293cdcab Mon Sep 17 00:00:00 2001 From: cc9cii Date: Mon, 18 May 2015 22:08:36 +1000 Subject: [PATCH 11/54] Add the remaining NPC data for editing with dialogue subview. Should resolve Bug #2547. --- apps/opencs/model/world/columns.cpp | 15 + apps/opencs/model/world/columns.hpp | 15 + apps/opencs/model/world/refidadapterimp.cpp | 350 +++++++++++++++++++- apps/opencs/model/world/refidadapterimp.hpp | 95 ++++++ apps/opencs/model/world/refidcollection.cpp | 56 ++++ apps/opencs/view/world/dialoguesubview.cpp | 6 +- 6 files changed, 535 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index 89ee6258b..2bfe79464 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -281,6 +281,21 @@ namespace CSMWorld { ColumnId_InfoCondValue, "Value" }, { ColumnId_OriginalCell, "Original Cell" }, + { ColumnId_NpcAttributes, "Attributes" }, + { ColumnId_NpcSkills, "Skills" }, + { ColumnId_UChar, "Value [0..255]" }, + { ColumnId_NpcMisc, "Misc" }, + { ColumnId_NpcLevel, "Level" }, + { ColumnId_NpcFactionID, "Faction ID" }, + { ColumnId_NpcHealth, "Health" }, + { ColumnId_NpcMana, "Mana" }, + { ColumnId_NpcFatigue, "Fatigue" }, + { ColumnId_NpcDisposition, "Disposition" }, + { ColumnId_NpcReputation, "Reputation" }, + { ColumnId_NpcRank, "Rank" }, + { ColumnId_NpcGold, "Gold" }, + { ColumnId_NpcPersistence, "Persistent" }, + { ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue2, "Use value 2" }, { ColumnId_UseValue3, "Use value 3" }, diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index f971f3fd8..b2234ad49 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -272,6 +272,21 @@ namespace CSMWorld ColumnId_OriginalCell = 247, + ColumnId_NpcAttributes = 248, + ColumnId_NpcSkills = 249, + ColumnId_UChar = 250, + ColumnId_NpcMisc = 251, + ColumnId_NpcLevel = 252, + ColumnId_NpcFactionID = 253, + ColumnId_NpcHealth = 254, + ColumnId_NpcMana = 255, + ColumnId_NpcFatigue = 256, + ColumnId_NpcDisposition = 257, + ColumnId_NpcReputation = 258, + ColumnId_NpcRank = 259, + ColumnId_NpcGold = 260, + ColumnId_NpcPersistence = 261, + // 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, diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp index 98c1b6f0f..a5f0b2244 100644 --- a/apps/opencs/model/world/refidadapterimp.cpp +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -468,7 +468,10 @@ CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns) mClass(NULL), mFaction(NULL), mHair(NULL), - mHead(NULL) + mHead(NULL), + mAttributes(NULL), + mSkills(NULL), + mMisc(NULL) {} CSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns) @@ -496,6 +499,17 @@ QVariant CSMWorld::NpcRefIdAdapter::getData (const RefIdColumn *column, const Re if (column==mColumns.mHead) return QString::fromUtf8 (record.get().mHead.c_str()); + if (column==mColumns.mAttributes || column==mColumns.mSkills) + { + if ((record.get().mFlags & ESM::NPC::Autocalc) != 0) + return QVariant(); + else + return true; + } + + if (column==mColumns.mMisc) + return true; + std::map::const_iterator iter = mColumns.mFlags.find (column); @@ -538,6 +552,340 @@ void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& d } } +CSMWorld::NpcAttributesRefIdAdapter::NpcAttributesRefIdAdapter () +{} + +void CSMWorld::NpcAttributesRefIdAdapter::addNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int position) const +{ + // Do nothing, this table cannot be changed by the user +} + +void CSMWorld::NpcAttributesRefIdAdapter::removeNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int rowToRemove) const +{ + // Do nothing, this table cannot be changed by the user +} + +void CSMWorld::NpcAttributesRefIdAdapter::setNestedTable (const RefIdColumn* column, + RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const +{ + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); + ESM::NPC npc = record.get(); + + // store the whole struct + npc.mNpdt52 = + static_cast > &>(nestedTable).mNestedTable.at(0); + + record.setModified (npc); +} + +CSMWorld::NestedTableWrapperBase* CSMWorld::NpcAttributesRefIdAdapter::nestedTable (const RefIdColumn* column, + const RefIdData& data, int index) const +{ + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); + + // return the whole struct + std::vector wrap; + wrap.push_back(record.get().mNpdt52); + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper >(wrap); +} + +QVariant CSMWorld::NpcAttributesRefIdAdapter::getNestedData (const RefIdColumn *column, + const RefIdData& data, int index, int subRowIndex, int subColIndex) const +{ + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); + + const ESM::NPC::NPDTstruct52& npcStruct = record.get().mNpdt52; + + if (subColIndex == 0) + switch (subRowIndex) + { + case 0: return QString("Strength"); + case 1: return QString("Intelligence"); + case 2: return QString("Willpower"); + case 3: return QString("Agility"); + case 4: return QString("Speed"); + case 5: return QString("Endurance"); + case 6: return QString("Personality"); + case 7: return QString("Luck"); + default: return QVariant(); // throw an exception here? + } + else if (subColIndex == 1) + switch (subRowIndex) + { + case 0: return static_cast(npcStruct.mStrength); + case 1: return static_cast(npcStruct.mIntelligence); + case 2: return static_cast(npcStruct.mWillpower); + case 3: return static_cast(npcStruct.mAgility); + case 4: return static_cast(npcStruct.mSpeed); + case 5: return static_cast(npcStruct.mEndurance); + case 6: return static_cast(npcStruct.mPersonality); + case 7: return static_cast(npcStruct.mLuck); + default: return QVariant(); // throw an exception here? + } + else + return QVariant(); // throw an exception here? +} + +void CSMWorld::NpcAttributesRefIdAdapter::setNestedData (const RefIdColumn *column, + RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const +{ + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Npc))); + ESM::NPC npc = record.get(); + ESM::NPC::NPDTstruct52& npcStruct = npc.mNpdt52; + + if (subColIndex == 1) + switch(subRowIndex) + { + case 0: npcStruct.mStrength = static_cast(value.toInt()); break; + case 1: npcStruct.mIntelligence = static_cast(value.toInt()); break; + case 2: npcStruct.mWillpower = static_cast(value.toInt()); break; + case 3: npcStruct.mAgility = static_cast(value.toInt()); break; + case 4: npcStruct.mSpeed = static_cast(value.toInt()); break; + case 5: npcStruct.mEndurance = static_cast(value.toInt()); break; + case 6: npcStruct.mPersonality = static_cast(value.toInt()); break; + case 7: npcStruct.mLuck = static_cast(value.toInt()); break; + default: return; // throw an exception here? + } + else + return; // throw an exception here? + + record.setModified (npc); +} + +int CSMWorld::NpcAttributesRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const +{ + return 2; +} + +int CSMWorld::NpcAttributesRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const +{ + // There are 8 attributes + return 8; +} + +CSMWorld::NpcSkillsRefIdAdapter::NpcSkillsRefIdAdapter () +{} + +void CSMWorld::NpcSkillsRefIdAdapter::addNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int position) const +{ + // Do nothing, this table cannot be changed by the user +} + +void CSMWorld::NpcSkillsRefIdAdapter::removeNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int rowToRemove) const +{ + // Do nothing, this table cannot be changed by the user +} + +void CSMWorld::NpcSkillsRefIdAdapter::setNestedTable (const RefIdColumn* column, + RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const +{ + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); + ESM::NPC npc = record.get(); + + // store the whole struct + npc.mNpdt52 = + static_cast > &>(nestedTable).mNestedTable.at(0); + + record.setModified (npc); +} + +CSMWorld::NestedTableWrapperBase* CSMWorld::NpcSkillsRefIdAdapter::nestedTable (const RefIdColumn* column, + const RefIdData& data, int index) const +{ + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); + + // return the whole struct + std::vector wrap; + wrap.push_back(record.get().mNpdt52); + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper >(wrap); +} + +QVariant CSMWorld::NpcSkillsRefIdAdapter::getNestedData (const RefIdColumn *column, + const RefIdData& data, int index, int subRowIndex, int subColIndex) const +{ + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); + + const ESM::NPC::NPDTstruct52& npcStruct = record.get().mNpdt52; + + if (subRowIndex < 0 || subRowIndex >= ESM::Skill::Length) + std::cout << "getNestedDatat index" << std::endl; + //throw std::runtime_error ("index out of range"); + + if (subColIndex == 0) + return QString(ESM::Skill::sSkillNames[subRowIndex].c_str()); + else if (subColIndex == 1) + return static_cast(npcStruct.mSkills[subRowIndex]); + else + return QVariant(); // throw an exception here? +} + +void CSMWorld::NpcSkillsRefIdAdapter::setNestedData (const RefIdColumn *column, + RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const +{ + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Npc))); + ESM::NPC npc = record.get(); + ESM::NPC::NPDTstruct52& npcStruct = npc.mNpdt52; + + if (subRowIndex < 0 || subRowIndex >= ESM::Skill::Length) + std::cout << "setNestedDatat index" << std::endl; + //throw std::runtime_error ("index out of range"); + + if (subColIndex == 1) + npcStruct.mSkills[subRowIndex] = static_cast(value.toInt()); + else + return; // throw an exception here? + + record.setModified (npc); +} + +int CSMWorld::NpcSkillsRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const +{ + return 2; +} + +int CSMWorld::NpcSkillsRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const +{ + // There are 27 skills + return ESM::Skill::Length; +} + +CSMWorld::NpcMiscRefIdAdapter::NpcMiscRefIdAdapter () +{} + +CSMWorld::NpcMiscRefIdAdapter::~NpcMiscRefIdAdapter() +{} + +void CSMWorld::NpcMiscRefIdAdapter::addNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int position) const +{ + throw std::logic_error ("cannot add a row to a fixed table"); +} + +void CSMWorld::NpcMiscRefIdAdapter::removeNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int rowToRemove) const +{ + throw std::logic_error ("cannot remove a row to a fixed table"); +} + +void CSMWorld::NpcMiscRefIdAdapter::setNestedTable (const RefIdColumn* column, + RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const +{ + throw std::logic_error ("table operation not supported"); +} + +CSMWorld::NestedTableWrapperBase* CSMWorld::NpcMiscRefIdAdapter::nestedTable (const RefIdColumn* column, + const RefIdData& data, int index) const +{ + throw std::logic_error ("table operation not supported"); +} + +QVariant CSMWorld::NpcMiscRefIdAdapter::getNestedData (const RefIdColumn *column, + const RefIdData& data, int index, int subRowIndex, int subColIndex) const +{ + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); + + bool autoCalc = (record.get().mFlags & ESM::NPC::Autocalc) != 0; + + if (autoCalc) + switch (subColIndex) + { + case 0: return static_cast(record.get().mNpdt12.mLevel); + case 1: return QVariant(); + case 2: return QVariant(); + case 3: return QVariant(); + case 4: return QVariant(); + case 5: return static_cast(record.get().mNpdt12.mDisposition); + case 6: return static_cast(record.get().mNpdt12.mReputation); + case 7: return static_cast(record.get().mNpdt12.mRank); + case 8: return record.get().mNpdt12.mGold; + case 9: return record.get().mPersistent == true; + default: return QVariant(); // throw an exception here? + } + else + switch (subColIndex) + { + case 0: return static_cast(record.get().mNpdt52.mLevel); + case 1: return static_cast(record.get().mNpdt52.mFactionID); + case 2: return static_cast(record.get().mNpdt52.mHealth); + case 3: return static_cast(record.get().mNpdt52.mMana); + case 4: return static_cast(record.get().mNpdt52.mFatigue); + case 5: return static_cast(record.get().mNpdt52.mDisposition); + case 6: return static_cast(record.get().mNpdt52.mReputation); + case 7: return static_cast(record.get().mNpdt52.mRank); + case 8: return record.get().mNpdt52.mGold; + case 9: return record.get().mPersistent == true; + default: return QVariant(); // throw an exception here? + } +} + +void CSMWorld::NpcMiscRefIdAdapter::setNestedData (const RefIdColumn *column, + RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const +{ + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Npc))); + ESM::NPC npc = record.get(); + + bool autoCalc = (record.get().mFlags & ESM::NPC::Autocalc) != 0; + + if (autoCalc) + switch(subColIndex) + { + case 0: npc.mNpdt12.mLevel = static_cast(value.toInt()); break; + case 1: return; + case 2: return; + case 3: return; + case 4: return; + case 5: npc.mNpdt12.mDisposition = static_cast(value.toInt()); break; + case 6: npc.mNpdt12.mReputation = static_cast(value.toInt()); break; + case 7: npc.mNpdt12.mRank = static_cast(value.toInt()); break; + case 8: npc.mNpdt12.mGold = value.toInt(); break; + case 9: npc.mPersistent = value.toBool(); break; + default: return; // throw an exception here? + } + else + switch(subColIndex) + { + case 0: npc.mNpdt52.mLevel = static_cast(value.toInt()); break; + case 1: npc.mNpdt52.mFactionID = static_cast(value.toInt()); break; + case 2: npc.mNpdt52.mHealth = static_cast(value.toInt()); break; + case 3: npc.mNpdt52.mMana = static_cast(value.toInt()); break; + case 4: npc.mNpdt52.mFatigue = static_cast(value.toInt()); break; + case 5: npc.mNpdt52.mDisposition = static_cast(value.toInt()); break; + case 6: npc.mNpdt52.mReputation = static_cast(value.toInt()); break; + case 7: npc.mNpdt52.mRank = static_cast(value.toInt()); break; + case 8: npc.mNpdt52.mGold = value.toInt(); break; + case 9: npc.mPersistent = value.toBool(); break; + default: return; // throw an exception here? + } + + record.setModified (npc); +} + +int CSMWorld::NpcMiscRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const +{ + return 10; // Level, FactionID, Health, Mana, Fatigue, Disposition, Reputation, Rank, Gold, Persist +} + +int CSMWorld::NpcMiscRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const +{ + return 1; // fixed at size 1 +} + CSMWorld::WeaponColumns::WeaponColumns (const EnchantableColumns& columns) : EnchantableColumns (columns) {} diff --git a/apps/opencs/model/world/refidadapterimp.hpp b/apps/opencs/model/world/refidadapterimp.hpp index 61e8115c0..143b46c44 100644 --- a/apps/opencs/model/world/refidadapterimp.hpp +++ b/apps/opencs/model/world/refidadapterimp.hpp @@ -792,6 +792,9 @@ namespace CSMWorld const RefIdColumn *mFaction; const RefIdColumn *mHair; const RefIdColumn *mHead; + const RefIdColumn *mAttributes; // depends on npc type + const RefIdColumn *mSkills; // depends on npc type + const RefIdColumn *mMisc; // may depend on npc type, e.g. FactionID NpcColumns (const ActorColumns& actorColumns); }; @@ -842,8 +845,100 @@ namespace CSMWorld ///< If the data type does not match an exception is thrown. }; + class NestedRefIdAdapterBase; + class NpcAttributesRefIdAdapter : public NestedRefIdAdapterBase + { + public: + + NpcAttributesRefIdAdapter (); + + virtual void addNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int position) const; + + virtual void removeNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int rowToRemove) const; + + virtual void setNestedTable (const RefIdColumn* column, + RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const; + + virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, + const RefIdData& data, int index) const; + + virtual QVariant getNestedData (const RefIdColumn *column, + const RefIdData& data, int index, int subRowIndex, int subColIndex) const; + + virtual void setNestedData (const RefIdColumn *column, + RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const; + + virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const; + + virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const; + }; + + class NpcSkillsRefIdAdapter : public NestedRefIdAdapterBase + { + public: + + NpcSkillsRefIdAdapter (); + + virtual void addNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int position) const; + + virtual void removeNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int rowToRemove) const; + + virtual void setNestedTable (const RefIdColumn* column, + RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const; + + virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, + const RefIdData& data, int index) const; + + virtual QVariant getNestedData (const RefIdColumn *column, + const RefIdData& data, int index, int subRowIndex, int subColIndex) const; + + virtual void setNestedData (const RefIdColumn *column, + RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const; + + virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const; + + virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const; + }; + + class NpcMiscRefIdAdapter : public NestedRefIdAdapterBase + { + NpcMiscRefIdAdapter (const NpcMiscRefIdAdapter&); + NpcMiscRefIdAdapter& operator= (const NpcMiscRefIdAdapter&); + + public: + + NpcMiscRefIdAdapter (); + virtual ~NpcMiscRefIdAdapter(); + + virtual void addNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int position) const; + + virtual void removeNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int rowToRemove) const; + + virtual void setNestedTable (const RefIdColumn* column, + RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const; + + virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, + const RefIdData& data, int index) const; + + virtual QVariant getNestedData (const RefIdColumn *column, + const RefIdData& data, int index, int subRowIndex, int subColIndex) const; + + virtual void setNestedData (const RefIdColumn *column, + RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const; + + virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const; + + virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const; + }; + template class EffectsListAdapter; diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 1941c505a..9d9e9826a 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -427,6 +427,62 @@ CSMWorld::RefIdCollection::RefIdCollection() npcColumns.mFlags.insert (std::make_pair (metalBlood, ESM::NPC::Metal)); + // Need a way to add a table of stats and values (rather than adding a long list of + // entries in the dialogue subview) E.g. attributes+stats(health, mana, fatigue), skills + // These needs to be driven from the autocalculated setting. + + // Nested table + mColumns.push_back(RefIdColumn (Columns::ColumnId_NpcAttributes, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + npcColumns.mAttributes = &mColumns.back(); + std::map attrMap; + attrMap.insert(std::make_pair(UniversalId::Type_Npc, new NpcAttributesRefIdAdapter())); + mNestedAdapters.push_back (std::make_pair(&mColumns.back(), attrMap)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcAttributes, CSMWorld::ColumnBase::Display_String, false, false)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_UChar, CSMWorld::ColumnBase::Display_Integer)); + + // Nested table + mColumns.push_back(RefIdColumn (Columns::ColumnId_NpcSkills, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + npcColumns.mSkills = &mColumns.back(); + std::map skillsMap; + skillsMap.insert(std::make_pair(UniversalId::Type_Npc, new NpcSkillsRefIdAdapter())); + mNestedAdapters.push_back (std::make_pair(&mColumns.back(), skillsMap)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcSkills, CSMWorld::ColumnBase::Display_String, false, false)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_UChar, CSMWorld::ColumnBase::Display_Integer)); + + // Nested list + mColumns.push_back(RefIdColumn (Columns::ColumnId_NpcMisc, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List)); + npcColumns.mMisc = &mColumns.back(); + std::map miscMap; + miscMap.insert(std::make_pair(UniversalId::Type_Npc, new NpcMiscRefIdAdapter())); + mNestedAdapters.push_back (std::make_pair(&mColumns.back(), miscMap)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcLevel, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcFactionID, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcHealth, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcMana, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcFatigue, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcDisposition, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcReputation, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcRank, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcGold, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_NpcPersistence, CSMWorld::ColumnBase::Display_Boolean)); + WeaponColumns weaponColumns (enchantableColumns); mColumns.push_back (RefIdColumn (Columns::ColumnId_WeaponType, ColumnBase::Display_WeaponType)); diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 0d0e82dbf..4b4afc666 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "../../model/world/nestedtableproxymodel.hpp" #include "../../model/world/columnbase.hpp" @@ -511,7 +512,7 @@ void CSVWorld::EditWidget::remake(int row) mNestedTableMapper->setModel(mNestedModels.back()); // FIXME: lack MIME support? mNestedTableDispatcher = - new DialogueDelegateDispatcher (this, mTable, mCommandDispatcher, mDocument, mNestedModels.back()); + new DialogueDelegateDispatcher (0/*this*/, mTable, mCommandDispatcher, mDocument, mNestedModels.back()); mNestedTableMapper->setItemDelegate(mNestedTableDispatcher); int columnCount = @@ -763,6 +764,9 @@ void CSVWorld::DialogueSubView::dataChanged (const QModelIndex & index) CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (currentIndex.row(), 1)).toInt()); mEditWidget->setDisabled (state==CSMWorld::RecordBase::State_Deleted || mLocked); + int y = mEditWidget->verticalScrollBar()->value(); + mEditWidget->remake (index.row()); + mEditWidget->verticalScrollBar()->setValue(y); } } From 0ecfd9119f1c7ec5c2326c7d15a1b1a1813fa354 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Tue, 19 May 2015 06:56:38 +1000 Subject: [PATCH 12/54] Add the remaining Race data for editing with dialogue subview. Should resolve Bug #2546. --- apps/opencs/model/world/columnbase.cpp | 1 + apps/opencs/model/world/columnbase.hpp | 1 + apps/opencs/model/world/columns.cpp | 8 + apps/opencs/model/world/columns.hpp | 7 + apps/opencs/model/world/data.cpp | 18 +++ .../model/world/nestedcoladapterimp.cpp | 153 ++++++++++++++++++ .../model/world/nestedcoladapterimp.hpp | 51 ++++++ apps/opencs/view/doc/viewmanager.cpp | 3 +- 8 files changed, 241 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/world/columnbase.cpp b/apps/opencs/model/world/columnbase.cpp index cf125aa63..087a1df7b 100644 --- a/apps/opencs/model/world/columnbase.cpp +++ b/apps/opencs/model/world/columnbase.cpp @@ -84,6 +84,7 @@ bool CSMWorld::ColumnBase::isId (Display display) Display_InfoCondFunc, Display_InfoCondVar, Display_InfoCondComp, + Display_RaceSkill, Display_None }; diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index 2d2513774..d6dd4b291 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -119,6 +119,7 @@ namespace CSMWorld Display_InfoCondFunc, Display_InfoCondVar, Display_InfoCondComp, + Display_RaceSkill, //top level columns that nest other columns Display_NestedHeader diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index 2bfe79464..daf537c0c 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -296,6 +296,13 @@ namespace CSMWorld { ColumnId_NpcGold, "Gold" }, { ColumnId_NpcPersistence, "Persistent" }, + { ColumnId_RaceAttributes, "Attributes" }, + { ColumnId_RaceMaleValue, "Male" }, + { ColumnId_RaceFemaleValue, "Female" }, + { ColumnId_RaceSkillBonus, "Skill Bonus" }, + { ColumnId_RaceSkill, "Skills" }, + { ColumnId_RaceBonus, "Bonus" }, + { ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue2, "Use value 2" }, { ColumnId_UseValue3, "Use value 3" }, @@ -566,6 +573,7 @@ namespace // 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; } diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index b2234ad49..85276a695 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -287,6 +287,13 @@ namespace CSMWorld ColumnId_NpcGold = 260, ColumnId_NpcPersistence = 261, + ColumnId_RaceAttributes = 262, + ColumnId_RaceMaleValue = 263, + ColumnId_RaceFemaleValue = 264, + ColumnId_RaceSkillBonus = 265, + ColumnId_RaceSkill = 266, + ColumnId_RaceBonus = 267, + // 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, diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index e2fab0a25..fa7e48a0e 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -136,6 +136,24 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new SpellListAdapter ())); mRaces.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_SpellId, ColumnBase::Display_String)); + // Race attributes + mRaces.addColumn (new NestedParentColumn (Columns::ColumnId_RaceAttributes)); + index = mRaces.getColumns()-1; + mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new RaceAttributeAdapter())); + mRaces.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_RaceAttributes, ColumnBase::Display_String, false)); + mRaces.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_RaceMaleValue, ColumnBase::Display_Integer)); + mRaces.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_RaceFemaleValue, ColumnBase::Display_Integer)); + // Race skill bonus + mRaces.addColumn (new NestedParentColumn (Columns::ColumnId_RaceSkillBonus)); + index = mRaces.getColumns()-1; + mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new RaceSkillsBonusAdapter())); + mRaces.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_RaceSkill, ColumnBase::Display_RaceSkill)); + mRaces.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_RaceBonus, ColumnBase::Display_Integer)); mSounds.addColumn (new StringIdColumn); mSounds.addColumn (new RecordStateColumn); diff --git a/apps/opencs/model/world/nestedcoladapterimp.cpp b/apps/opencs/model/world/nestedcoladapterimp.cpp index a0d764576..7efe14dee 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.cpp +++ b/apps/opencs/model/world/nestedcoladapterimp.cpp @@ -880,4 +880,157 @@ namespace CSMWorld { return static_cast(record.get().mSelects.size()); } + + RaceAttributeAdapter::RaceAttributeAdapter () {} + + void RaceAttributeAdapter::addRow(Record& record, int position) const + { + // Do nothing, this table cannot be changed by the user + } + + void RaceAttributeAdapter::removeRow(Record& record, int rowToRemove) const + { + // Do nothing, this table cannot be changed by the user + } + + void RaceAttributeAdapter::setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const + { + ESM::Race race = record.get(); + + race.mData = + static_cast >&>(nestedTable).mNestedTable.at(0); + + record.setModified (race); + } + + NestedTableWrapperBase* RaceAttributeAdapter::table(const Record& record) const + { + std::vector wrap; + wrap.push_back(record.get().mData); + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper >(wrap); + } + + QVariant RaceAttributeAdapter::getData(const Record& record, + int subRowIndex, int subColIndex) const + { + ESM::Race race = record.get(); + + if (subRowIndex < 0 || subRowIndex >= ESM::Attribute::Length) + throw std::runtime_error ("index out of range"); + + switch (subColIndex) + { + case 0: return QString(ESM::Attribute::sAttributeNames[subRowIndex].c_str()); + case 1: return race.mData.mAttributeValues[subRowIndex].mMale; + case 2: return race.mData.mAttributeValues[subRowIndex].mFemale; + default: throw std::runtime_error("Race Attribute subcolumn index out of range"); + } + } + + void RaceAttributeAdapter::setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const + { + ESM::Race race = record.get(); + + if (subRowIndex < 0 || subRowIndex >= ESM::Attribute::Length) + throw std::runtime_error ("index out of range"); + + switch (subColIndex) + { + case 0: return; // throw an exception here? + case 1: race.mData.mAttributeValues[subRowIndex].mMale = value.toInt(); break; + case 2: race.mData.mAttributeValues[subRowIndex].mFemale = value.toInt(); break; + default: throw std::runtime_error("Race Attribute subcolumn index out of range"); + } + + record.setModified (race); + } + + int RaceAttributeAdapter::getColumnsCount(const Record& record) const + { + return 3; // attrib, male, female + } + + int RaceAttributeAdapter::getRowsCount(const Record& record) const + { + return ESM::Attribute::Length; // there are 8 attributes + } + + RaceSkillsBonusAdapter::RaceSkillsBonusAdapter () {} + + void RaceSkillsBonusAdapter::addRow(Record& record, int position) const + { + // Do nothing, this table cannot be changed by the user + } + + void RaceSkillsBonusAdapter::removeRow(Record& record, int rowToRemove) const + { + // Do nothing, this table cannot be changed by the user + } + + void RaceSkillsBonusAdapter::setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const + { + ESM::Race race = record.get(); + + race.mData = + static_cast >&>(nestedTable).mNestedTable.at(0); + + record.setModified (race); + } + + NestedTableWrapperBase* RaceSkillsBonusAdapter::table(const Record& record) const + { + std::vector wrap; + wrap.push_back(record.get().mData); + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper >(wrap); + } + + QVariant RaceSkillsBonusAdapter::getData(const Record& record, + int subRowIndex, int subColIndex) const + { + ESM::Race race = record.get(); + + if (subRowIndex < 0 || subRowIndex >= static_cast(sizeof(race.mData.mBonus)/sizeof(race.mData.mBonus[0]))) + throw std::runtime_error ("index out of range"); + + switch (subColIndex) + { + case 0: return race.mData.mBonus[subRowIndex].mSkill; // can be -1 + case 1: return race.mData.mBonus[subRowIndex].mBonus; + default: throw std::runtime_error("Race skill bonus subcolumn index out of range"); + } + } + + void RaceSkillsBonusAdapter::setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const + { + ESM::Race race = record.get(); + + if (subRowIndex < 0 || subRowIndex >= static_cast(sizeof(race.mData.mBonus)/sizeof(race.mData.mBonus[0]))) + throw std::runtime_error ("index out of range"); + + switch (subColIndex) + { + case 0: race.mData.mBonus[subRowIndex].mSkill = value.toInt(); break; // can be -1 + case 1: race.mData.mBonus[subRowIndex].mBonus = value.toInt(); break; + default: throw std::runtime_error("Race skill bonus subcolumn index out of range"); + } + + record.setModified (race); + } + + int RaceSkillsBonusAdapter::getColumnsCount(const Record& record) const + { + return 2; // skill, bonus + } + + int RaceSkillsBonusAdapter::getRowsCount(const Record& record) const + { + // there are 7 skill bonuses + return static_cast(sizeof(record.get().mData.mBonus)/sizeof(record.get().mData.mBonus[0])); + } } diff --git a/apps/opencs/model/world/nestedcoladapterimp.hpp b/apps/opencs/model/world/nestedcoladapterimp.hpp index 776a908ba..4b1e2cd09 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.hpp +++ b/apps/opencs/model/world/nestedcoladapterimp.hpp @@ -8,6 +8,7 @@ #include // for converting magic effect id to string & back #include // for converting skill names #include // for converting attributes +#include #include "nestedcolumnadapter.hpp" #include "nestedtablewrapper.hpp" @@ -437,6 +438,56 @@ namespace CSMWorld virtual int getRowsCount(const Record& record) const; }; + + class RaceAttributeAdapter : public NestedColumnAdapter + { + public: + RaceAttributeAdapter (); + + virtual void addRow(Record& record, int position) const; + + virtual void removeRow(Record& record, int rowToRemove) const; + + virtual void setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const; + + virtual NestedTableWrapperBase* table(const Record& record) const; + + virtual QVariant getData(const Record& record, + int subRowIndex, int subColIndex) const; + + virtual void setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const; + + virtual int getColumnsCount(const Record& record) const; + + virtual int getRowsCount(const Record& record) const; + }; + + class RaceSkillsBonusAdapter : public NestedColumnAdapter + { + public: + RaceSkillsBonusAdapter (); + + virtual void addRow(Record& record, int position) const; + + virtual void removeRow(Record& record, int rowToRemove) const; + + virtual void setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const; + + virtual NestedTableWrapperBase* table(const Record& record) const; + + virtual QVariant getData(const Record& record, + int subRowIndex, int subColIndex) const; + + virtual void setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const; + + virtual int getColumnsCount(const Record& record) const; + + virtual int getRowsCount(const Record& record) const; + }; } #endif // CSM_WOLRD_NESTEDCOLADAPTERIMP_H diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 6362f9659..97b7aac19 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -92,7 +92,8 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) { CSMWorld::ColumnBase::Display_AiPackageType, CSMWorld::Columns::ColumnId_AiPackageType, false }, { CSMWorld::ColumnBase::Display_YesNo, CSMWorld::Columns::ColumnId_AiWanderRepeat, false }, { CSMWorld::ColumnBase::Display_InfoCondFunc, CSMWorld::Columns::ColumnId_InfoCondFunc, false }, - { CSMWorld::ColumnBase::Display_InfoCondComp, CSMWorld::Columns::ColumnId_InfoCondComp, false } + { CSMWorld::ColumnBase::Display_InfoCondComp, CSMWorld::Columns::ColumnId_InfoCondComp, false }, + { CSMWorld::ColumnBase::Display_RaceSkill, CSMWorld::Columns::ColumnId_RaceSkill, true }, }; for (std::size_t i=0; i Date: Tue, 19 May 2015 19:19:52 +1000 Subject: [PATCH 13/54] Fix occasional crash with DialogueSubView, e.g. when deleting a row of a subtable. --- apps/opencs/view/world/dialoguesubview.cpp | 53 ++++++++++++---------- apps/opencs/view/world/dialoguesubview.hpp | 2 +- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 4b4afc666..fee302e2c 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -348,16 +348,19 @@ CSVWorld::DialogueDelegateDispatcher::~DialogueDelegateDispatcher() CSVWorld::EditWidget::~EditWidget() { for (unsigned i = 0; i < mNestedModels.size(); ++i) - { delete mNestedModels[i]; - } - delete mNestedTableDispatcher; + + if (mDispatcher) + delete mDispatcher; + + if (mNestedTableDispatcher) + delete mNestedTableDispatcher; } CSVWorld::EditWidget::EditWidget(QWidget *parent, int row, CSMWorld::IdTable* table, CSMWorld::CommandDispatcher& commandDispatcher, CSMDoc::Document& document, bool createAndDelete) : -mDispatcher(this, table, commandDispatcher, document), +mDispatcher(0), mNestedTableDispatcher(NULL), QScrollArea(parent), mWidgetMapper(NULL), @@ -369,41 +372,41 @@ mTable(table) { remake (row); - connect(&mDispatcher, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)), + connect(mDispatcher, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)), this, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*))); } void CSVWorld::EditWidget::remake(int row) { for (unsigned i = 0; i < mNestedModels.size(); ++i) - { delete mNestedModels[i]; - } + mNestedModels.clear(); - delete mNestedTableDispatcher; - if (mMainWidget) - { - delete mMainWidget; - mMainWidget = 0; - } - mMainWidget = new QWidget (this); + if (mDispatcher) + delete mDispatcher; + mDispatcher = new DialogueDelegateDispatcher(0/*this*/, mTable, mCommandDispatcher, mDocument); + + if (mNestedTableDispatcher) + delete mNestedTableDispatcher; //not sure if widget mapper can handle deleting the widgets that were mapped if (mWidgetMapper) - { delete mWidgetMapper; - mWidgetMapper = 0; - } + + mWidgetMapper = new QDataWidgetMapper (this); + mWidgetMapper->setModel(mTable); + mWidgetMapper->setItemDelegate(mDispatcher); + if (mNestedTableMapper) - { delete mNestedTableMapper; - mNestedTableMapper = 0; - } - mWidgetMapper = new QDataWidgetMapper (this); - mWidgetMapper->setModel(mTable); - mWidgetMapper->setItemDelegate(&mDispatcher); + if (mMainWidget) + { + QWidget *del = this->takeWidget(); + del->deleteLater(); + } + mMainWidget = new QWidget (this); QFrame* line = new QFrame(mMainWidget); line->setObjectName(QString::fromUtf8("line")); @@ -476,8 +479,8 @@ void CSVWorld::EditWidget::remake(int row) } else if (!(flags & CSMWorld::ColumnBase::Flag_Dialogue_List)) { - mDispatcher.makeDelegate (display); - QWidget* editor = mDispatcher.makeEditor (display, (mTable->index (row, i))); + mDispatcher->makeDelegate (display); + QWidget* editor = mDispatcher->makeEditor (display, (mTable->index (row, i))); if (editor) { diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index b5a44d266..8783d935f 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -165,7 +165,7 @@ namespace CSVWorld Q_OBJECT QDataWidgetMapper *mWidgetMapper; QDataWidgetMapper *mNestedTableMapper; - DialogueDelegateDispatcher mDispatcher; + DialogueDelegateDispatcher *mDispatcher; DialogueDelegateDispatcher *mNestedTableDispatcher; QWidget* mMainWidget; CSMWorld::IdTable* mTable; From b27a879352b155205a9809f567007e97bf7dd1a6 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Tue, 19 May 2015 22:01:40 +1000 Subject: [PATCH 14/54] Add the remaining Cell data for editing with dialogue subview. Should resolve Bug #2551. - NOTE: the interior water flag and water level logic needs reviewing - does not automatically disable region names for interiors without mQuasiEx flag - Colour values can't be entered as RGB - Region names are not drop down menus --- apps/opencs/model/world/columns.cpp | 11 +- apps/opencs/model/world/columns.hpp | 11 +- apps/opencs/model/world/data.cpp | 25 ++- apps/opencs/model/world/data.hpp | 2 +- .../model/world/nestedcoladapterimp.cpp | 161 +++++++++++++++++- .../model/world/nestedcoladapterimp.hpp | 26 +++ 6 files changed, 230 insertions(+), 6 deletions(-) diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index daf537c0c..ee7a00ca0 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -62,7 +62,7 @@ namespace CSMWorld { ColumnId_StarterSpell, "Starter Spell" }, { ColumnId_AlwaysSucceeds, "Always Succeeds" }, { ColumnId_SleepForbidden, "Sleep Forbidden" }, - { ColumnId_InteriorWater, "Interior Water" }, + { ColumnId_Water, "Has Water" }, { ColumnId_InteriorSky, "Interior Sky" }, { ColumnId_Model, "Model" }, { ColumnId_Script, "Script" }, @@ -303,6 +303,15 @@ namespace CSMWorld { ColumnId_RaceSkill, "Skills" }, { ColumnId_RaceBonus, "Bonus" }, + { ColumnId_Interior, "Interior" }, + { ColumnId_Ambient, "Ambient" }, + { ColumnId_Sunlight, "Sunlight" }, + { ColumnId_Fog, "Fog" }, + { ColumnId_FogDensity, "Fog Density" }, + { ColumnId_WaterLevel, "Water Level" }, + { ColumnId_InteriorWater, "Interior Water" }, + { ColumnId_MapColor, "Map Color" }, + { ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue2, "Use value 2" }, { ColumnId_UseValue3, "Use value 3" }, diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 85276a695..3a345b3ec 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -57,7 +57,7 @@ namespace CSMWorld ColumnId_StarterSpell = 42, ColumnId_AlwaysSucceeds = 43, ColumnId_SleepForbidden = 44, - ColumnId_InteriorWater = 45, + ColumnId_Water = 45, ColumnId_InteriorSky = 46, ColumnId_Model = 47, ColumnId_Script = 48, @@ -294,6 +294,15 @@ namespace CSMWorld ColumnId_RaceSkill = 266, ColumnId_RaceBonus = 267, + ColumnId_Interior = 268, + ColumnId_Ambient = 269, + ColumnId_Sunlight = 270, + ColumnId_Fog = 271, + ColumnId_FogDensity = 272, + ColumnId_WaterLevel = 273, + ColumnId_InteriorWater = 274, + ColumnId_MapColor = 275, + // 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, diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index fa7e48a0e..00349d476 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -287,10 +287,31 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc mCells.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Cell)); mCells.addColumn (new NameColumn); mCells.addColumn (new FlagColumn (Columns::ColumnId_SleepForbidden, ESM::Cell::NoSleep)); - mCells.addColumn (new FlagColumn (Columns::ColumnId_InteriorWater, ESM::Cell::HasWater)); + mCells.addColumn (new FlagColumn (Columns::ColumnId_Water, ESM::Cell::HasWater)); mCells.addColumn (new FlagColumn (Columns::ColumnId_InteriorSky, ESM::Cell::QuasiEx)); mCells.addColumn (new RegionColumn); mCells.addColumn (new RefNumCounterColumn); + // Misc Cell data + mCells.addColumn (new NestedParentColumn (Columns::ColumnId_Cell, + ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List)); + index = mCells.getColumns()-1; + mCells.addAdapter (std::make_pair(&mCells.getColumn(index), new CellListAdapter ())); + mCells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_Interior, ColumnBase::Display_Boolean)); + mCells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_Ambient, ColumnBase::Display_Integer)); + mCells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_Sunlight, ColumnBase::Display_Integer)); + mCells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_Fog, ColumnBase::Display_Integer)); + mCells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_FogDensity, ColumnBase::Display_Float)); + mCells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_InteriorWater, ColumnBase::Display_Boolean)); + mCells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_WaterLevel, ColumnBase::Display_Float)); + mCells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_MapColor, ColumnBase::Display_Integer)); mEnchantments.addColumn (new StringIdColumn); mEnchantments.addColumn (new RecordStateColumn); @@ -465,7 +486,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc addModel (new IdTree (&mTopicInfos, &mTopicInfos, IdTable::Feature_ReorderWithinTopic), UniversalId::Type_TopicInfo); addModel (new IdTable (&mJournalInfos, IdTable::Feature_ReorderWithinTopic), UniversalId::Type_JournalInfo); - addModel (new IdTable (&mCells, IdTable::Feature_ViewId), UniversalId::Type_Cell); + addModel (new IdTree (&mCells, &mCells, IdTable::Feature_ViewId), UniversalId::Type_Cell); addModel (new IdTree (&mEnchantments, &mEnchantments), UniversalId::Type_Enchantment); addModel (new IdTable (&mBodyParts), UniversalId::Type_BodyPart); addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen); diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 8689b98c0..060e47bd9 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -88,7 +88,7 @@ namespace CSMWorld IdCollection mStartScripts; NestedInfoCollection mTopicInfos; InfoCollection mJournalInfos; - IdCollection mCells; + NestedIdCollection mCells; IdCollection mLandTextures; IdCollection mLand; RefIdCollection mReferenceables; diff --git a/apps/opencs/model/world/nestedcoladapterimp.cpp b/apps/opencs/model/world/nestedcoladapterimp.cpp index 7efe14dee..a63ebe672 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.cpp +++ b/apps/opencs/model/world/nestedcoladapterimp.cpp @@ -481,7 +481,7 @@ namespace CSMWorld void InfoListAdapter::removeRow(Record& record, int rowToRemove) const { - throw std::logic_error ("cannot add a row to a fixed table"); + throw std::logic_error ("cannot remove a row to a fixed table"); } void InfoListAdapter::setTable(Record& record, @@ -1033,4 +1033,163 @@ namespace CSMWorld // there are 7 skill bonuses return static_cast(sizeof(record.get().mData.mBonus)/sizeof(record.get().mData.mBonus[0])); } + + CellListAdapter::CellListAdapter () {} + + void CellListAdapter::addRow(Record& record, int position) const + { + throw std::logic_error ("cannot add a row to a fixed table"); + } + + void CellListAdapter::removeRow(Record& record, int rowToRemove) const + { + throw std::logic_error ("cannot remove a row to a fixed table"); + } + + void CellListAdapter::setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const + { + throw std::logic_error ("table operation not supported"); + } + + NestedTableWrapperBase* CellListAdapter::table(const Record& record) const + { + throw std::logic_error ("table operation not supported"); + } + + QVariant CellListAdapter::getData(const Record& record, + int subRowIndex, int subColIndex) const + { + CSMWorld::Cell cell = record.get(); + + bool isInterior = (cell.mData.mFlags & ESM::Cell::Interior) != 0; + bool behaveLikeExterior = (cell.mData.mFlags & ESM::Cell::QuasiEx) != 0; + + switch (subColIndex) + { + case 0: return isInterior; + case 1: return (isInterior && !behaveLikeExterior) ? cell.mAmbi.mAmbient : QVariant(); + case 2: return (isInterior && !behaveLikeExterior) ? cell.mAmbi.mSunlight : QVariant(); + case 3: return (isInterior && !behaveLikeExterior) ? cell.mAmbi.mFog : QVariant(); + case 4: return (isInterior && !behaveLikeExterior) ? cell.mAmbi.mFogDensity : QVariant(); + case 5: return (isInterior && !behaveLikeExterior) ? cell.mWaterInt==true : QVariant(); + case 6: + { + if (isInterior && !behaveLikeExterior && cell.mWaterInt) + return cell.mWater; + else + return QVariant(); + } + case 7: return isInterior ? QVariant() : cell.mMapColor; // TODO: how to select? + //case 8: return isInterior ? behaveLikeExterior : QVariant(); + default: throw std::runtime_error("Cell subcolumn index out of range"); + } + } + + void CellListAdapter::setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const + { + CSMWorld::Cell cell = record.get(); + + bool isInterior = (cell.mData.mFlags & ESM::Cell::Interior) != 0; + bool behaveLikeExterior = (cell.mData.mFlags & ESM::Cell::QuasiEx) != 0; + + switch (subColIndex) + { + case 0: + { + if (value.toBool()) + cell.mData.mFlags |= ESM::Cell::Interior; + else + cell.mData.mFlags &= ~ESM::Cell::Interior; + break; + } + case 1: + { + if (isInterior && !behaveLikeExterior) + cell.mAmbi.mAmbient = static_cast(value.toInt()); + else + return; // return without saving + break; + } + case 2: + { + if (isInterior && !behaveLikeExterior) + cell.mAmbi.mSunlight = static_cast(value.toInt()); + else + return; // return without saving + break; + } + case 3: + { + if (isInterior && !behaveLikeExterior) + cell.mAmbi.mFog = static_cast(value.toInt()); + else + return; // return without saving + break; + } + case 4: + { + if (isInterior && !behaveLikeExterior) + cell.mAmbi.mFogDensity = value.toFloat(); + else + return; // return without saving + break; + } + case 5: + { + if (isInterior && !behaveLikeExterior) + cell.mWaterInt = value.toBool(); + else + return; // return without saving + break; + } + case 6: + { + if (isInterior && !behaveLikeExterior && cell.mWaterInt) + cell.mWater = value.toFloat(); + else + return; // return without saving + break; + } + case 7: + { + if (!isInterior) + cell.mMapColor = value.toInt(); + else + return; // return without saving + break; + } +#if 0 + // redundant since this flag is shown in the main table as "Interior Sky" + // keep here for documenting the logic based on vanilla + case 8: + { + if (isInterior) + { + if (value.toBool()) + cell.mData.mFlags |= ESM::Cell::QuasiEx; + else + cell.mData.mFlags &= ~ESM::Cell::QuasiEx; + } + else + return; // return without saving + break; + } +#endif + default: throw std::runtime_error("Cell subcolumn index out of range"); + } + + record.setModified (cell); + } + + int CellListAdapter::getColumnsCount(const Record& record) const + { + return 8; + } + + int CellListAdapter::getRowsCount(const Record& record) const + { + return 1; // fixed at size 1 + } } diff --git a/apps/opencs/model/world/nestedcoladapterimp.hpp b/apps/opencs/model/world/nestedcoladapterimp.hpp index 4b1e2cd09..81c52588b 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.hpp +++ b/apps/opencs/model/world/nestedcoladapterimp.hpp @@ -12,6 +12,7 @@ #include "nestedcolumnadapter.hpp" #include "nestedtablewrapper.hpp" +#include "cell.hpp" namespace ESM { @@ -488,6 +489,31 @@ namespace CSMWorld virtual int getRowsCount(const Record& record) const; }; + + class CellListAdapter : public NestedColumnAdapter + { + public: + CellListAdapter (); + + virtual void addRow(Record& record, int position) const; + + virtual void removeRow(Record& record, int rowToRemove) const; + + virtual void setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const; + + virtual NestedTableWrapperBase* table(const Record& record) const; + + virtual QVariant getData(const Record& record, + int subRowIndex, int subColIndex) const; + + virtual void setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const; + + virtual int getColumnsCount(const Record& record) const; + + virtual int getRowsCount(const Record& record) const; + }; } #endif // CSM_WOLRD_NESTEDCOLADAPTERIMP_H From 1d0b8587a1682dd1116b0a254f4b0ea4416fefd9 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Wed, 20 May 2015 21:14:17 +1000 Subject: [PATCH 15/54] Use HasWater bit flag rather than mWaterInt bool. --- apps/opencs/model/world/columns.cpp | 3 +-- apps/opencs/model/world/columns.hpp | 5 ++-- apps/opencs/model/world/data.cpp | 4 +-- .../model/world/nestedcoladapterimp.cpp | 27 +++++++------------ 4 files changed, 14 insertions(+), 25 deletions(-) diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index ee7a00ca0..c937997a9 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -62,7 +62,7 @@ namespace CSMWorld { ColumnId_StarterSpell, "Starter Spell" }, { ColumnId_AlwaysSucceeds, "Always Succeeds" }, { ColumnId_SleepForbidden, "Sleep Forbidden" }, - { ColumnId_Water, "Has Water" }, + { ColumnId_InteriorWater, "Interior Water" }, { ColumnId_InteriorSky, "Interior Sky" }, { ColumnId_Model, "Model" }, { ColumnId_Script, "Script" }, @@ -309,7 +309,6 @@ namespace CSMWorld { ColumnId_Fog, "Fog" }, { ColumnId_FogDensity, "Fog Density" }, { ColumnId_WaterLevel, "Water Level" }, - { ColumnId_InteriorWater, "Interior Water" }, { ColumnId_MapColor, "Map Color" }, { ColumnId_UseValue1, "Use value 1" }, diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 3a345b3ec..191bbdea8 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -57,7 +57,7 @@ namespace CSMWorld ColumnId_StarterSpell = 42, ColumnId_AlwaysSucceeds = 43, ColumnId_SleepForbidden = 44, - ColumnId_Water = 45, + ColumnId_InteriorWater = 45, ColumnId_InteriorSky = 46, ColumnId_Model = 47, ColumnId_Script = 48, @@ -300,8 +300,7 @@ namespace CSMWorld ColumnId_Fog = 271, ColumnId_FogDensity = 272, ColumnId_WaterLevel = 273, - ColumnId_InteriorWater = 274, - ColumnId_MapColor = 275, + ColumnId_MapColor = 274, // Allocated to a separate value range, so we don't get a collision should we ever need // to extend the number of use values. diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 00349d476..920c7db71 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -287,7 +287,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc mCells.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Cell)); mCells.addColumn (new NameColumn); mCells.addColumn (new FlagColumn (Columns::ColumnId_SleepForbidden, ESM::Cell::NoSleep)); - mCells.addColumn (new FlagColumn (Columns::ColumnId_Water, ESM::Cell::HasWater)); + mCells.addColumn (new FlagColumn (Columns::ColumnId_InteriorWater, ESM::Cell::HasWater)); mCells.addColumn (new FlagColumn (Columns::ColumnId_InteriorSky, ESM::Cell::QuasiEx)); mCells.addColumn (new RegionColumn); mCells.addColumn (new RefNumCounterColumn); @@ -306,8 +306,6 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc new NestedChildColumn (Columns::ColumnId_Fog, ColumnBase::Display_Integer)); mCells.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_FogDensity, ColumnBase::Display_Float)); - mCells.getNestableColumn(index)->addColumn( - new NestedChildColumn (Columns::ColumnId_InteriorWater, ColumnBase::Display_Boolean)); mCells.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_WaterLevel, ColumnBase::Display_Float)); mCells.getNestableColumn(index)->addColumn( diff --git a/apps/opencs/model/world/nestedcoladapterimp.cpp b/apps/opencs/model/world/nestedcoladapterimp.cpp index a63ebe672..8c897f71c 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.cpp +++ b/apps/opencs/model/world/nestedcoladapterimp.cpp @@ -1064,6 +1064,7 @@ namespace CSMWorld bool isInterior = (cell.mData.mFlags & ESM::Cell::Interior) != 0; bool behaveLikeExterior = (cell.mData.mFlags & ESM::Cell::QuasiEx) != 0; + bool interiorWater = (cell.mData.mFlags & ESM::Cell::HasWater) != 0; switch (subColIndex) { @@ -1072,16 +1073,15 @@ namespace CSMWorld case 2: return (isInterior && !behaveLikeExterior) ? cell.mAmbi.mSunlight : QVariant(); case 3: return (isInterior && !behaveLikeExterior) ? cell.mAmbi.mFog : QVariant(); case 4: return (isInterior && !behaveLikeExterior) ? cell.mAmbi.mFogDensity : QVariant(); - case 5: return (isInterior && !behaveLikeExterior) ? cell.mWaterInt==true : QVariant(); - case 6: + case 5: { - if (isInterior && !behaveLikeExterior && cell.mWaterInt) + if (isInterior && !behaveLikeExterior && interiorWater) return cell.mWater; else return QVariant(); } - case 7: return isInterior ? QVariant() : cell.mMapColor; // TODO: how to select? - //case 8: return isInterior ? behaveLikeExterior : QVariant(); + case 6: return isInterior ? QVariant() : cell.mMapColor; // TODO: how to select? + //case 7: return isInterior ? behaveLikeExterior : QVariant(); default: throw std::runtime_error("Cell subcolumn index out of range"); } } @@ -1093,6 +1093,7 @@ namespace CSMWorld bool isInterior = (cell.mData.mFlags & ESM::Cell::Interior) != 0; bool behaveLikeExterior = (cell.mData.mFlags & ESM::Cell::QuasiEx) != 0; + bool interiorWater = (cell.mData.mFlags & ESM::Cell::HasWater) != 0; switch (subColIndex) { @@ -1138,21 +1139,13 @@ namespace CSMWorld } case 5: { - if (isInterior && !behaveLikeExterior) - cell.mWaterInt = value.toBool(); - else - return; // return without saving - break; - } - case 6: - { - if (isInterior && !behaveLikeExterior && cell.mWaterInt) + if (isInterior && !behaveLikeExterior && interiorWater) cell.mWater = value.toFloat(); else return; // return without saving break; } - case 7: + case 6: { if (!isInterior) cell.mMapColor = value.toInt(); @@ -1163,7 +1156,7 @@ namespace CSMWorld #if 0 // redundant since this flag is shown in the main table as "Interior Sky" // keep here for documenting the logic based on vanilla - case 8: + case 7: { if (isInterior) { @@ -1185,7 +1178,7 @@ namespace CSMWorld int CellListAdapter::getColumnsCount(const Record& record) const { - return 8; + return 7; } int CellListAdapter::getRowsCount(const Record& record) const From 2fc964ca7372b7f67055706c661f79202ddbe539 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Thu, 21 May 2015 13:11:07 +1000 Subject: [PATCH 16/54] Grey out disabled entries rather than hiding them. --- .../model/world/nestedcoladapterimp.cpp | 20 ++++++++++------ apps/opencs/model/world/refidadapterimp.cpp | 10 ++++---- apps/opencs/model/world/refidadapterimp.hpp | 2 +- apps/opencs/view/world/dialoguesubview.cpp | 23 ++++++++++++++++++- 4 files changed, 41 insertions(+), 14 deletions(-) diff --git a/apps/opencs/model/world/nestedcoladapterimp.cpp b/apps/opencs/model/world/nestedcoladapterimp.cpp index 8c897f71c..b7d09777d 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.cpp +++ b/apps/opencs/model/world/nestedcoladapterimp.cpp @@ -1069,19 +1069,25 @@ namespace CSMWorld switch (subColIndex) { case 0: return isInterior; - case 1: return (isInterior && !behaveLikeExterior) ? cell.mAmbi.mAmbient : QVariant(); - case 2: return (isInterior && !behaveLikeExterior) ? cell.mAmbi.mSunlight : QVariant(); - case 3: return (isInterior && !behaveLikeExterior) ? cell.mAmbi.mFog : QVariant(); - case 4: return (isInterior && !behaveLikeExterior) ? cell.mAmbi.mFogDensity : QVariant(); + case 1: return (isInterior && !behaveLikeExterior) ? + cell.mAmbi.mAmbient : QVariant(QVariant::UserType); + case 2: return (isInterior && !behaveLikeExterior) ? + cell.mAmbi.mSunlight : QVariant(QVariant::UserType); + case 3: return (isInterior && !behaveLikeExterior) ? + cell.mAmbi.mFog : QVariant(QVariant::UserType); + case 4: return (isInterior && !behaveLikeExterior) ? + cell.mAmbi.mFogDensity : QVariant(QVariant::UserType); case 5: { if (isInterior && !behaveLikeExterior && interiorWater) return cell.mWater; else - return QVariant(); + return QVariant(QVariant::UserType); } - case 6: return isInterior ? QVariant() : cell.mMapColor; // TODO: how to select? - //case 7: return isInterior ? behaveLikeExterior : QVariant(); + case 6: return isInterior ? + QVariant(QVariant::UserType) : cell.mMapColor; // TODO: how to select? + //case 7: return isInterior ? + //behaveLikeExterior : QVariant(QVariant::UserType); default: throw std::runtime_error("Cell subcolumn index out of range"); } } diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp index a5f0b2244..ee045afd0 100644 --- a/apps/opencs/model/world/refidadapterimp.cpp +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -502,7 +502,7 @@ QVariant CSMWorld::NpcRefIdAdapter::getData (const RefIdColumn *column, const Re if (column==mColumns.mAttributes || column==mColumns.mSkills) { if ((record.get().mFlags & ESM::NPC::Autocalc) != 0) - return QVariant(); + return QVariant(QVariant::UserType); else return true; } @@ -805,10 +805,10 @@ QVariant CSMWorld::NpcMiscRefIdAdapter::getNestedData (const RefIdColumn *column switch (subColIndex) { case 0: return static_cast(record.get().mNpdt12.mLevel); - case 1: return QVariant(); - case 2: return QVariant(); - case 3: return QVariant(); - case 4: return QVariant(); + case 1: return QVariant(QVariant::UserType); + case 2: return QVariant(QVariant::UserType); + case 3: return QVariant(QVariant::UserType); + case 4: return QVariant(QVariant::UserType); case 5: return static_cast(record.get().mNpdt12.mDisposition); case 6: return static_cast(record.get().mNpdt12.mReputation); case 7: return static_cast(record.get().mNpdt12.mRank); diff --git a/apps/opencs/model/world/refidadapterimp.hpp b/apps/opencs/model/world/refidadapterimp.hpp index 143b46c44..3411429d0 100644 --- a/apps/opencs/model/world/refidadapterimp.hpp +++ b/apps/opencs/model/world/refidadapterimp.hpp @@ -1976,7 +1976,7 @@ namespace CSMWorld { switch (subColIndex) { - case 0: return QVariant(); // don't allow checkbox editor to be created + case 0: return QVariant(QVariant::UserType); // disable the checkbox editor case 1: return record.get().mFlags & ESM::CreatureLevList::AllLevels; case 2: return static_cast (record.get().mChanceNone); default: diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index fee302e2c..0a5eb5202 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -461,7 +461,14 @@ void CSVWorld::EditWidget::remake(int row) NestedTable* table = new NestedTable(mDocument, id, mNestedModels.back(), this); // FIXME: does not work well when enum delegates are used //table->resizeColumnsToContents(); - table->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::CurrentChanged); + + if(mTable->index(row, i).data().type() == QVariant::UserType) + { + table->setEditTriggers(QAbstractItemView::NoEditTriggers); + table->setEnabled(false); + } + else + table->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::CurrentChanged); int rows = mTable->rowCount(mTable->index(row, i)); int rowHeight = (rows == 0) ? table->horizontalHeader()->height() : table->rowHeight(0); @@ -473,6 +480,8 @@ void CSVWorld::EditWidget::remake(int row) new QLabel (mTable->headerData (i, Qt::Horizontal, Qt::DisplayRole).toString(), mMainWidget); label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed); + if(mTable->index(row, i).data().type() == QVariant::UserType) + label->setEnabled(false); tablesLayout->addWidget(label); tablesLayout->addWidget(table); @@ -503,6 +512,12 @@ void CSVWorld::EditWidget::remake(int row) unlockedLayout->addWidget (editor, unlocked, 1); ++unlocked; } + + if(mTable->index(row, i).data().type() == QVariant::UserType) + { + editor->setEnabled(false); + label->setEnabled(false); + } } } else @@ -550,6 +565,12 @@ void CSVWorld::EditWidget::remake(int row) unlockedLayout->addWidget (label, unlocked, 0); unlockedLayout->addWidget (editor, unlocked, 1); ++unlocked; + + if(mNestedModels.back()->index(0, col).data().type() == QVariant::UserType) + { + editor->setEnabled(false); + label->setEnabled(false); + } } } mNestedTableMapper->setCurrentModelIndex(mNestedModels.back()->index(0, 0)); From aecd9a275e34d5e539e90713ca0e4158220f526d Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 22 May 2015 06:09:55 +1000 Subject: [PATCH 17/54] Remove debugging comments. --- apps/opencs/model/world/refidadapterimp.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp index ee045afd0..0bb565e51 100644 --- a/apps/opencs/model/world/refidadapterimp.cpp +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -721,8 +721,7 @@ QVariant CSMWorld::NpcSkillsRefIdAdapter::getNestedData (const RefIdColumn *colu const ESM::NPC::NPDTstruct52& npcStruct = record.get().mNpdt52; if (subRowIndex < 0 || subRowIndex >= ESM::Skill::Length) - std::cout << "getNestedDatat index" << std::endl; - //throw std::runtime_error ("index out of range"); + throw std::runtime_error ("index out of range"); if (subColIndex == 0) return QString(ESM::Skill::sSkillNames[subRowIndex].c_str()); @@ -741,8 +740,7 @@ void CSMWorld::NpcSkillsRefIdAdapter::setNestedData (const RefIdColumn *column, ESM::NPC::NPDTstruct52& npcStruct = npc.mNpdt52; if (subRowIndex < 0 || subRowIndex >= ESM::Skill::Length) - std::cout << "setNestedDatat index" << std::endl; - //throw std::runtime_error ("index out of range"); + throw std::runtime_error ("index out of range"); if (subColIndex == 1) npcStruct.mSkills[subRowIndex] = static_cast(value.toInt()); From d1b6289cad544ddf71b59c22e0edbc92404f81b1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 22 May 2015 19:56:39 +0200 Subject: [PATCH 18/54] Don't teleport followers when using teleportation spells --- apps/openmw/mwclass/door.cpp | 2 +- apps/openmw/mwgui/travelwindow.cpp | 2 +- apps/openmw/mwmechanics/spellcasting.cpp | 2 +- apps/openmw/mwworld/actionteleport.cpp | 25 +++++++++++++----------- apps/openmw/mwworld/actionteleport.hpp | 4 +++- apps/openmw/mwworld/worldimp.cpp | 2 +- 6 files changed, 21 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 2d39881b1..48fc3b64c 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -147,7 +147,7 @@ namespace MWClass if (ptr.getCellRef().getTeleport()) { - boost::shared_ptr action(new MWWorld::ActionTeleport (ptr.getCellRef().getDestCell(), ptr.getCellRef().getDoorDest())); + boost::shared_ptr action(new MWWorld::ActionTeleport (ptr.getCellRef().getDestCell(), ptr.getCellRef().getDoorDest(), true)); action->setSound(openSound); diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 4da1ab33a..ba6fc2a78 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -183,7 +183,7 @@ namespace MWGui MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); // Teleports any followers, too. - MWWorld::ActionTeleport action(interior ? cellname : "", pos); + MWWorld::ActionTeleport action(interior ? cellname : "", pos, true); action.execute(player); MWBase::Environment::get().getWindowManager()->fadeScreenOut(0); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 8a43cc932..1f3a88827 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -679,7 +679,7 @@ namespace MWMechanics if (markedCell) { MWWorld::ActionTeleport action(markedCell->isExterior() ? "" : markedCell->getCell()->mName, - markedPosition); + markedPosition, false); action.execute(target); } } diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index 8bbb08008..fccd176a8 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -25,23 +25,26 @@ namespace namespace MWWorld { ActionTeleport::ActionTeleport (const std::string& cellName, - const ESM::Position& position) - : Action (true), mCellName (cellName), mPosition (position) + const ESM::Position& position, bool teleportFollowers) + : Action (true), mCellName (cellName), mPosition (position), mTeleportFollowers(teleportFollowers) { } void ActionTeleport::executeImp (const Ptr& actor) { - //find any NPC that is following the actor and teleport him too - std::set followers; - getFollowers(actor, followers); - for(std::set::iterator it = followers.begin();it != followers.end();++it) + if (mTeleportFollowers) { - MWWorld::Ptr follower = *it; - if (Ogre::Vector3(follower.getRefData().getPosition().pos).squaredDistance( - Ogre::Vector3( actor.getRefData().getPosition().pos)) - <= 800*800) - teleport(*it); + //find any NPC that is following the actor and teleport him too + std::set followers; + getFollowers(actor, followers); + for(std::set::iterator it = followers.begin();it != followers.end();++it) + { + MWWorld::Ptr follower = *it; + if (Ogre::Vector3(follower.getRefData().getPosition().pos).squaredDistance( + Ogre::Vector3( actor.getRefData().getPosition().pos)) + <= 800*800) + teleport(*it); + } } teleport(actor); diff --git a/apps/openmw/mwworld/actionteleport.hpp b/apps/openmw/mwworld/actionteleport.hpp index 9ca664de8..6191ee9f6 100644 --- a/apps/openmw/mwworld/actionteleport.hpp +++ b/apps/openmw/mwworld/actionteleport.hpp @@ -13,6 +13,7 @@ namespace MWWorld { std::string mCellName; ESM::Position mPosition; + bool mTeleportFollowers; /// Teleports this actor and also teleports anyone following that actor. virtual void executeImp (const Ptr& actor); @@ -22,8 +23,9 @@ namespace MWWorld public: - ActionTeleport (const std::string& cellName, const ESM::Position& position); + ActionTeleport (const std::string& cellName, const ESM::Position& position, bool teleportFollowers); ///< If cellName is empty, an exterior cell is assumed. + /// @param teleportFollowers Whether to teleport any following actors of the target actor as well. }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index bb0402c4e..96fad106e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2921,7 +2921,7 @@ namespace MWWorld if ( !closestMarker.mCell->isExterior() ) cellName = closestMarker.mCell->getCell()->mName; - MWWorld::ActionTeleport action(cellName, closestMarker.getRefData().getPosition()); + MWWorld::ActionTeleport action(cellName, closestMarker.getRefData().getPosition(), false); action.execute(ptr); } From 21af1913e1d1e30dc3fc91e503ff862dba0fae1d Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 24 May 2015 19:18:54 +1000 Subject: [PATCH 19/54] Ensure ColumnId names are unique. --- apps/opencs/model/world/columns.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index c937997a9..9491c3246 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -176,7 +176,7 @@ namespace CSMWorld { ColumnId_ContainerContent, "Content" }, { ColumnId_ItemCount, "Count" }, - { ColumnId_InventoryItemId, "ID"}, + { ColumnId_InventoryItemId, "Item ID"}, { ColumnId_CombatState, "Combat" }, { ColumnId_MagicState, "Magic" }, @@ -188,10 +188,10 @@ namespace CSMWorld { ColumnId_ActorInventory, "Inventory" }, { ColumnId_SpellList, "Spells" }, - { ColumnId_SpellId, "ID"}, + { ColumnId_SpellId, "Spell ID"}, { ColumnId_NpcDestinations, "Destinations" }, - { ColumnId_DestinationCell, "Cell"}, + { ColumnId_DestinationCell, "Dest Cell"}, { ColumnId_PosX, "Dest X"}, { ColumnId_PosY, "Dest Y"}, { ColumnId_PosZ, "Dest Z"}, @@ -224,17 +224,17 @@ namespace CSMWorld { ColumnId_BoltSound, "Bolt Sound" }, { ColumnId_PathgridPoints, "Points" }, - { ColumnId_PathgridIndex, "Index" }, + { ColumnId_PathgridIndex, "pIndex" }, { ColumnId_PathgridPosX, "X" }, { ColumnId_PathgridPosY, "Y" }, { ColumnId_PathgridPosZ, "Z" }, { ColumnId_PathgridEdges, "Edges" }, - { ColumnId_PathgridEdgeIndex, "Index" }, + { ColumnId_PathgridEdgeIndex, "eIndex" }, { ColumnId_PathgridEdge0, "Point 0" }, { ColumnId_PathgridEdge1, "Point 1" }, { ColumnId_RegionSounds, "Sounds" }, - { ColumnId_SoundName, "Name" }, + { ColumnId_SoundName, "Sound Name" }, { ColumnId_SoundChance, "Chance" }, { ColumnId_FactionReactions, "Reactions" }, @@ -250,7 +250,7 @@ namespace CSMWorld { ColumnId_AiPackageList, "Ai Packages" }, { ColumnId_AiPackageType, "Package" }, { ColumnId_AiWanderDist, "Wander Dist" }, - { ColumnId_AiDuration, "Duration" }, + { ColumnId_AiDuration, "Ai Duration" }, { ColumnId_AiWanderToD, "Wander ToD" }, { ColumnId_AiWanderIdle, "Wander Idle" }, { ColumnId_AiWanderRepeat, "Wander Repeat" }, @@ -260,11 +260,11 @@ namespace CSMWorld { ColumnId_PartRefList, "Part Reference" }, { ColumnId_PartRefType, "Type" }, - { ColumnId_PartRefMale, "Male" }, - { ColumnId_PartRefFemale, "Female" }, + { ColumnId_PartRefMale, "Male Part" }, + { ColumnId_PartRefFemale, "Female Part" }, { ColumnId_LevelledList,"Levelled List" }, - { ColumnId_LevelledItemId,"Item ID" }, + { ColumnId_LevelledItemId,"Levelled Item" }, { ColumnId_LevelledItemLevel,"Level" }, { ColumnId_LevelledItemType, "Calculate all levels <= player" }, { ColumnId_LevelledItemTypeEach, "Select a new item each instance" }, @@ -278,7 +278,7 @@ namespace CSMWorld { ColumnId_InfoCondFunc, "Function" }, { ColumnId_InfoCondVar, "Func/Variable" }, { ColumnId_InfoCondComp, "Comp" }, - { ColumnId_InfoCondValue, "Value" }, + { ColumnId_InfoCondValue, "Values" }, { ColumnId_OriginalCell, "Original Cell" }, { ColumnId_NpcAttributes, "Attributes" }, From 22420c3a83e5294bbf9d2ef73383d7fc151ca87f Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 24 May 2015 23:19:06 +1000 Subject: [PATCH 20/54] Close DialogueSubView if the corresponding record is deleted. Should resolve Bug #2575. - ToDo: Doesn't seem to shrink the widget width properly (when horizontal scrollbar is active) --- apps/opencs/view/world/dialoguesubview.cpp | 19 +++++++++++++++++++ apps/opencs/view/world/dialoguesubview.hpp | 2 ++ 2 files changed, 21 insertions(+) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 647accd4c..0860cf2b2 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -578,6 +578,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM mCommandDispatcher (document, CSMWorld::UniversalId::getParentType (id.getType())) { connect(mTable, SIGNAL(dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT(dataChanged(const QModelIndex&))); + connect(mTable, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), this, SLOT(rowsAboutToBeRemoved(const QModelIndex&, int, int))); changeCurrentId(id.getId()); @@ -740,6 +741,9 @@ void CSVWorld::DialogueSubView::nextId () void CSVWorld::DialogueSubView::setEditLock (bool locked) { + if (!mEditWidget) // hack to indicate that mCurrentId is no longer valid + return; + mLocked = locked; QModelIndex currentIndex(mTable->getModelIndex(mCurrentId, 0)); @@ -766,6 +770,21 @@ void CSVWorld::DialogueSubView::dataChanged (const QModelIndex & index) } } +void CSVWorld::DialogueSubView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +{ + QModelIndex currentIndex(mTable->getModelIndex(mCurrentId, 0)); + + if (currentIndex.isValid() && currentIndex.row() >= start && currentIndex.row() <= end) + { + if(mEditWidget) + { + delete mEditWidget; + mEditWidget = 0; + } + emit closeRequest(this); + } +} + void CSVWorld::DialogueSubView::tableMimeDataDropped (QWidget* editor, const QModelIndex& index, const CSMWorld::UniversalId& id, diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index b5a44d266..6d12d62c1 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -235,6 +235,8 @@ namespace CSVWorld const CSMDoc::Document* document); void requestFocus (const std::string& id); + + void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); }; } From e6d2821b78affea63b588988f1b338dcc4667315 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Mon, 25 May 2015 09:15:07 +1000 Subject: [PATCH 21/54] Refresh table filters when the model is updated. Should resolve Bug #2579. --- apps/opencs/model/world/idtableproxymodel.cpp | 6 ++++++ apps/opencs/model/world/idtableproxymodel.hpp | 2 ++ apps/opencs/view/world/table.cpp | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/apps/opencs/model/world/idtableproxymodel.cpp b/apps/opencs/model/world/idtableproxymodel.cpp index 93c1749c6..84869716e 100644 --- a/apps/opencs/model/world/idtableproxymodel.cpp +++ b/apps/opencs/model/world/idtableproxymodel.cpp @@ -53,3 +53,9 @@ bool CSMWorld::IdTableProxyModel::lessThan(const QModelIndex &left, const QModel { return QSortFilterProxyModel::lessThan(left, right); } + +void CSMWorld::IdTableProxyModel::refreshFilter() +{ + updateColumnMap(); + invalidateFilter(); +} diff --git a/apps/opencs/model/world/idtableproxymodel.hpp b/apps/opencs/model/world/idtableproxymodel.hpp index 23e3193fb..8683c2b9e 100644 --- a/apps/opencs/model/world/idtableproxymodel.hpp +++ b/apps/opencs/model/world/idtableproxymodel.hpp @@ -34,6 +34,8 @@ namespace CSMWorld void setFilter (const boost::shared_ptr& filter); + void refreshFilter(); + protected: bool lessThan(const QModelIndex &left, const QModelIndex &right) const; diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index b8972edbe..bb3dfa4d3 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -649,6 +649,10 @@ void CSVWorld::Table::tableSizeUpdate() } emit tableSizeChanged (size, deleted, modified); + + // not really related to tableSizeUpdate() but placed here for convenience rather than + // creating a bunch of extra connections & slot + mProxyModel->refreshFilter(); } void CSVWorld::Table::selectionSizeUpdate() From 3c82e6d03401795d1e147a8e2f5d32e812eb9c4a Mon Sep 17 00:00:00 2001 From: cc9cii Date: Mon, 25 May 2015 16:36:42 +1000 Subject: [PATCH 22/54] Retrieve the correct index for the type of record being un-deleted. --- apps/opencs/model/world/idtable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index a0dd0b47b..36b3f5c97 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -160,7 +160,7 @@ void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& reco if (index==-1) { - int index = mIdCollection->getAppendIndex (id); + int index = mIdCollection->getAppendIndex (id, type); beginInsertRows (QModelIndex(), index, index); From 18f047f50e1f720bafd486a1185f113f2dc35d60 Mon Sep 17 00:00:00 2001 From: Cramal Date: Mon, 25 May 2015 18:40:13 +0900 Subject: [PATCH 23/54] Moving to Object/Instance terminology + minor spelling corrections --- manual/opencs/creating_file.tex | 2 +- manual/opencs/main.tex | 2 ++ manual/opencs/recordmodification.tex | 16 ++++++++-------- manual/opencs/recordtypes.tex | 8 ++++---- manual/opencs/tables.tex | 18 +++++++++--------- 5 files changed, 24 insertions(+), 22 deletions(-) diff --git a/manual/opencs/creating_file.tex b/manual/opencs/creating_file.tex index 317e453b7..8cab33fb9 100644 --- a/manual/opencs/creating_file.tex +++ b/manual/opencs/creating_file.tex @@ -17,7 +17,7 @@ It is simple. When you click either \textbf{Create new Addon} you will be asked The last thing to do is to name your your addon and click create. \paragraph{Selecting File for Editing} -Clicking \textbf{Edit A Content File} will show somewhat similar window. Here you should select your Game file with drop down menu. If you want to edit this game file, simply click \textbf{OK} button. If you want to alter addon depending on that file, mark it with checkbox and than click \textbf{Ok} button. +Clicking \textbf{Edit A Content File} will show somewhat similar window. Here you should select your Game file with drop down menu. If you want to edit this game file, simply click \textbf{OK} button. If you want to alter addon depending on that file, mark it with check-box and than click \textbf{Ok} button. \subsection{Advanced} If you are paying attention, you noticed any extra icon with wrench. This one will open small settings window. Those are general OpenCS settings. We will cover this is separate section.\\ diff --git a/manual/opencs/main.tex b/manual/opencs/main.tex index f3a46c1b7..fd85baee4 100644 --- a/manual/opencs/main.tex +++ b/manual/opencs/main.tex @@ -25,7 +25,9 @@ \title{OpenCS User Manual} \maketitle +\newpage \tableofcontents{} +\newpage \input{files_and_directories} \input{creating_file} \input{windows} diff --git a/manual/opencs/recordmodification.tex b/manual/opencs/recordmodification.tex index b1cf866d6..570bd4a95 100644 --- a/manual/opencs/recordmodification.tex +++ b/manual/opencs/recordmodification.tex @@ -1,17 +1,17 @@ \section{Records Modification} \subsection{Introduction} -So far you learned how to browse trough records stored inside the content files, but not how to modify them using the \OCS{} editor. Although browsing is certainly a usefull ability on it's own, You probabbly counted on doing actual editing with this editor. There are few ways user can alter records stored in the content files, each suited for certain class of a problem. In this section We will describe how to do change records using tables interface and edit panel. +So far you learned how to browse trough records stored inside the content files, but not how to modify them using the \OCS{} editor. Although browsing is certainly a useful ability on it's own, You probably counted on doing actual editing with this editor. There are few ways user can alter records stored in the content files, each suited for certain class of a problem. In this section We will describe how to do change records using tables interface and edit panel. \subsubsection{Glossary} \begin{description} - \item[Edit Panel] Interface element used inside the \OCS{} to present records data for editing. Unlike table it showes only one record at the time. However it also presents fields that are not visible inside the table. It is also safe to say that Edit Panel presents data in way that is easier to read thanks to it's horizontal layout. + \item[Edit Panel] Interface element used inside the \OCS{} to present records data for editing. Unlike table it shows only one record at the time. However it also presents fields that are not visible inside the table. It is also safe to say that Edit Panel presents data in way that is easier to read thanks to it's horizontal layout. \end{description} \subsection{Edit Panel Interface} -Edit Panel is designed to aid you with record modification tasks. As It has been said, it uses vertical layout and presents some additional fields when compared with the table -- and some fields, even if they are actually displayed in the table, clearly ill-suited for modification isnide of them (this applies to fields that holds long text strings -- like descriptions). It also displays visual difference beetween non-editable field and editable.\\ +Edit Panel is designed to aid you with record modification tasks. As It has been said, it uses vertical layout and presents some additional fields when compared with the table -- and some fields, even if they are actually displayed in the table, clearly ill-suited for modification inside of them (this applies to fields that holds long text strings -- like descriptions). It also displays visual difference between non-editable field and editable.\\ To open edit panel, please open context menu on any record and choose edit action. This will open edit panel in the same window as your table and will present you the record fields. First data fields are actually not user editable and presented in the form of the text labels at the top of the edit panel. Lower data fields are presented in the form of actually user-editable widgets. Those includes spinboxes, text edits and text fields\footnote{Those are actually a valid terms used to describe classes of the user interface elements. If you don't understand those, don't worry -- those are very standard {GUI} elements present in almost every application since the rise of the desktop metaphor.}. Once you will finish editing one of those fields, data will be updated. There is no apply button of any sort -- simply use one of those widgets and be merry.\\ -In addition to that you probabbly noticed some icons in the bar located at the very bottom of the edit panel. Those can be used to perform the following actions: +In addition to that you probably noticed some icons in the bar located at the very bottom of the edit panel. Those can be used to perform the following actions: \begin{description} \item[Preview] This will launch simple preview panel -- which will be described later. @@ -20,7 +20,7 @@ In addition to that you probabbly noticed some icons in the bar located at the v \end{description} \subsection{Verification tool} -As you could notice there is nothing that can stop you from breaking the game by violating record fields logic, and yet -- it is something that you are always trying to avoid. To adress this problem \OCS{} utilizes so called verification tool (or verifer as many prefer to call it) that basicly goes trough all records and checks if it contains any illogical fields. This includes for instance torch duration equal 0\footnote{Interestingly negative values are perfectly fine: they indicate that light source has no duration limit at all. There are records like this in the original game.} or characters without name, race or any other record with a mandatory field missing.\\ -This tool is even more usefull than it seems. If you somehow delete race that is used by some of the characters, all those characters will be suddenly broken. As a rule of thumb it is a good idea to use verifer before saving your content file.\\ -To launch this usefull tool %don't remember, todo... -Resoults are presented as a yet another table with short (and hopefully descriptive enough) description of the identified problem. It is worth noticing that some records located in the \MW{} esm files are listed by the verification tool -- it is not fault of our tool: those records are just broken. For instance, you actually may find the 0 duration torch. However, those records are usually not placed in game world itself -- and that's good since \MW{} game engine will crash if player equip light source like this!\footnote{We would like to thanks \BS{} for such a usefull testing material. It makes us feel special.} +As you could notice there is nothing that can stop you from breaking the game by violating record fields logic, and yet -- it is something that you are always trying to avoid. To address this problem \OCS{} utilizes so called verification tool (or verifer as many prefer to call it) that basically goes trough all records and checks if it contains any illogical fields. This includes for instance torch duration equal 0\footnote{Interestingly negative values are perfectly fine: they indicate that light source has no duration limit at all. There are records like this in the original game.} or characters without name, race or any other record with a mandatory field missing.\\ +This tool is even more useful than it seems. If you somehow delete race that is used by some of the characters, all those characters will be suddenly broken. As a rule of thumb it is a good idea to use verifer before saving your content file.\\ +To launch this useful tool %don't remember, todo... +Results are presented as a yet another table with short (and hopefully descriptive enough) description of the identified problem. It is worth noticing that some records located in the \MW{} esm files are listed by the verification tool -- it is not fault of our tool: those records are just broken. For instance, you actually may find the 0 duration torch. However, those records are usually not placed in game world itself -- and that's good since \MW{} game engine will crash if player equip light source like this!\footnote{We would like to thanks \BS{} for such a useful testing material. It makes us feel special.} diff --git a/manual/opencs/recordtypes.tex b/manual/opencs/recordtypes.tex index 4dbfbe286..6db338e7e 100644 --- a/manual/opencs/recordtypes.tex +++ b/manual/opencs/recordtypes.tex @@ -1,15 +1,15 @@ \section{Record Types} \subsection{Introduction} -A gameworld contains many objects, such as chests, weapons and monsters. All these objects are merely instances of templates that we call Referenceables. The OpenCS Referenceables table contains information about each of these template objects, eg. its value and weight in the case of items and an aggression level in the case of NPCs. +A gameworld contains many items, such as chests, weapons and monsters. All these items are merely instances of templates that we call \textbf{Objects}. The OpenCS \textbf{Objects} table contains information about each of these template objects, eg. its value and weight in the case of items and an aggression level in the case of NPCs. Let's go through all Record Types and discuss what you can tell OpenCS about them. \begin{description} \item[Activator:] When the player enters the same cell as this object, a script is started. Often it also has a \textbf{Script} attached to it, though it not mandatory. These scripts are small bits of code written in a special scripting language that OpenCS can read and interpret. - \item[Potion:] This is a potion that is not self-made. It has an \textbf{Icon} for your inventory, Aside from the self-explanatory \textbf{Weight} and \textbf{Coin Value}, it has an attribute called \textbf{Auto Calc} set to ``False''. This means that the effects of this potion are preconfigured. This does not happen when the player makes their own potion. - \item[Apparatus:] This is a tool to make potions. Again there's an icon for your inventory as well as a weight and a coin value. It also has a \textbf{Quality} value attached to it: higher the number, the better the effect on your potions will be. The \textbf{Apparatus Type} describes if the item is a Calcinator, Retort, Alembir or Mortar & Pestal. Each has a different effect on the potion the player makes. For more information on this subject, please refer to the \href{http://www.uesp.net/wiki/Morrowind:Alchemy#Tools}{UESP page on Alchemy Tools}. - \item[Armor:] This type of item adds \textbf{Enchantment Points} to the mix. Every piece of clothing or armor has a ''pool'' of potential magicka that gets unlocked when you enchant it. Strong enchantments consume more magicka from this pool: the stronger the enchantment, the more Enchantment Points each cast will take up. For more information on this subject, please refer to the \href{http://www.uesp.net/wiki/Morrowind:Enchant}{Enchant page on UESP}. \textbf{Health} means the amount of hit points this piece of armor has. If it sustains enough damage, the armor will be destroyed. Finally, \textbf{Armor Value} tells the game how much points to add to the player character's Armor Rating. + \item[Potion:] This is a potion that is not self-made. It has an \textbf{Icon} for your inventory, Aside from the self-explanatory \textbf{Weight} and \textbf{Coin Value}, it has an attribute called \textbf{Auto Calc} set to ``False''. This means that the effects of this potion are pre-configured. This does not happen when the player makes their own potion. + \item[Apparatus:] This is a tool to make potions. Again there's an icon for your inventory as well as a weight and a coin value. It also has a \textbf{Quality} value attached to it: higher the number, the better the effect on your potions will be. The \textbf{Apparatus Type} describes if the item is a Calcinator, Retort, Alembic or Mortar & Pestle. Each has a different effect on the potion the player makes. For more information on this subject, please refer to the \href{http://www.uesp.net/wiki/Morrowind:Alchemy#Tools}{UESP page on Alchemy Tools}. + \item[Armor:] This type of item adds \textbf{Enchantment Points} to the mix. Every piece of clothing or armor has a ''pool'' of potential Magicka that gets unlocked when you enchant it. Strong enchantments consume more Magicka from this pool: the stronger the enchantment, the more Enchantment Points each cast will take up. For more information on this subject, please refer to the \href{http://www.uesp.net/wiki/Morrowind:Enchant}{Enchant page on UESP}. \textbf{Health} means the amount of hit points this piece of armor has. If it sustains enough damage, the armor will be destroyed. Finally, \textbf{Armor Value} tells the game how much points to add to the player character's Armor Rating. \item[Book:] This includes scrolls and notes. For the game to make the distinction between books and scrolls, an extra property, \textbf{Scroll}, has been added. Under the \textbf{Skill} column a scroll or book can have an in-game skill listed. Reading this item will raise the player's level in that specific skill. For more information on this, please refer to the \href{http://www.uesp.net/wiki/Morrowind:Skill_Books}{Skill Books page on UESP}. \item[Clothing:] These items work just like Armors, but confer no protective properties. Rather than ``Armor Type'', these items have a ``Clothing Type''. \item[Container:] This is all the stuff that stores items, from chests to sacks to plants. Its \textbf{Capacity} shows how much stuff you can put in the container. You can compare it to the maximum allowed load a player character can carry (who will get over-encumbered and unable to move if he crosses this threshold). A container, however, will just refuse to take the item in question when it gets ''over-encumbered''. \textbf{Organic Container}s are containers such as plants. Containers that \textbf{Respawn} are not safe to store stuff in. After a certain amount of time they will reset to their default contents, meaning that everything in it is gone forever. diff --git a/manual/opencs/tables.tex b/manual/opencs/tables.tex index 503489872..51aac50fd 100644 --- a/manual/opencs/tables.tex +++ b/manual/opencs/tables.tex @@ -14,7 +14,7 @@ Let's browse through the various screens and see what all these tables show. \begin{description} \item[Record:] An entry in \OCS{} representing an item, location, sound, NPC or anything else. - \item[Reference, Referenceable:] When an item is placed in the world, it isn't an isolated and unique object. For example, the game world might contain a lot of exquisite belts on different NPCs and in many crates, but they all refer to one specific record in the game's library: the Exquisite Belt record. In this case, all those belts in crates and on NPCs are references. The central Exquisite Belt record is called a referenceable. This allows modders to make changes to all items of the same type. For example, if you want all exquisite belts to have 4000 enchantment points rather than 400, you will only need to change the referenceable Exquisite Belt rather than all exquisite belts references individually. + \item[Instance, Object:] When an item is placed in the world, it isn't an isolated and unique object. For example, the game world might contain a lot of exquisite belts on different NPCs and in many crates, but they all refer to one specific record in the game's library: the Exquisite Belt record. In this case, all those belts in crates and on NPCs are \textbf{instances}. The central Exquisite Belt record is called a \textbf{object}. This allows modders to make changes to all items of the same type. For example, if you want all exquisite belts to have 4000 enchantment points rather than 400, you will only need to change the \textbf{object}} Exquisite Belt rather than all exquisite belts \textbf{instances}} individually. \end{description} \subsubsection{Recurring Terms} @@ -29,7 +29,7 @@ optionally the Bloodmoon and Tribunal expansions. \item[Modified] means that the record is part of the base game, but has been changed in some way. \item[Deleted] means that this record used to be part of the base game, but has been removed as an entry. This does not mean, however, that the occurrences in the game itself have been removed! For example, if you remove the CharGen\_Bed entry from morrowind.esm, it does not mean the bedroll in the basement -of the Census and Excise Office in Seyda Neen is gone. You're going to have to delete that reference yourself or make sure that that object is replaced +of the Census and Excise Office in Seyda Neen is gone. You're going to have to delete that instance yourself or make sure that that object is replaced by something that still exists otherwise you will get crashes in the worst case scenario. \end{description} @@ -37,12 +37,12 @@ by something that still exists otherwise you will get crashes in the worst case The contents of the game world can be changed by choosing one of the options in the appropriate menu at the top of the screen. \subsubsection{Regions} -This describes the general areas of Vvardenfell. Each of these areas has different rules about things such as encounters and weather. +This describes the general areas of the gameworld. Each of these areas has different rules about things such as encounters and weather. \begin{description} \item[Name:] This is how the game will show your location in-game. - \item[Map Colour:] This is a six-digit hexidecimal representation of the colour used to identify the region on the map available in - World > Region Map. If you do not have an application with a colour picker, you can use your favourite search engine to find a colour picker online. + \item[Map Colour:] This is a six-digit hexadecimal representation of the color used to identify the region on the map available in + World > Region Map. If you do not have an application with a color picker, you can use your favorite search engine to find a color picker on-line. \item[Sleep Encounter:] These are the rules for what kind of enemies you might encounter when you sleep outside in the wild. \end{description} @@ -52,7 +52,7 @@ why would the computer need to keep track the exact locations of NPCs walking th be quite useless and bring your system to its knees! So the world has been divided up into squares we call "cells". Once your character enters a cell, the game will load everything that is going on in that cell so you can interact with it. -In the original \MW{} this could be seen when you were travelling and you would see a small loading bar at the bottom of the screen; +In the original \MW{} this could be seen when you were traveling and you would see a small loading bar at the bottom of the screen; you had just entered a new cell and the game would have to load all the items and NPCs. The Cells screen in \OCS{} provides you with a list of cells in the game, both the interior cells (houses, dungeons, mines, etc.) and the exterior cells (the outside world). @@ -71,7 +71,7 @@ in the game, both the interior cells (houses, dungeons, mines, etc.) and the ext \item[Interior Sky:] Should this interior cell have a sky? This is a rather unique case. The \TB{} expansion took place in a city on the mainland. Normally this would require the city to be composed of exterior cells so it has a sky, weather and the like. But if the player is - in an exterior cell and looks at his in-game map, he sees Vvardenfell with an overview of all exterior cells. The player would have to see + in an exterior cell and looks at his in-game map, he sees the map of the gameworld with an overview of all exterior cells. The player would have to see the city's very own map, as if he was walking around in an interior cell. So the developers decided to create a workaround and take a bit of both: The whole city would technically work exactly like an interior cell, @@ -83,7 +83,7 @@ in the game, both the interior cells (houses, dungeons, mines, etc.) and the ext \end{description} -\subsubsection{Referenceables} +\subsubsection{Objects} This is a library of all the items, triggers, containers, NPCs, etc. in the game. There are several kinds of Record Types. Depending on which type a record is, it will need specific information to function. For example, an NPC needs a value attached to its aggression level. A chest, of course, does not. All Record Types contain at least a~model. How else would the player see them? Usually they also have a Name, which is what you see @@ -91,4 +91,4 @@ when you hover your reticle over the object. This is a library of all the items, triggers, containers, NPCs, etc. in the game. There are several kinds of Record Types. Depending on which type a record is, it will need specific information to function. For example, an NPC needs a value attached to its aggression level. A chest, of course, does not. All Record Types contain at least a model. How else would the player see them? Usually they also have a Name, which is what you see when you hover your reticle over the object. -Please refer to the Record Types section for an overview of what each type of Referenceable does and what you can tell OpenCS about these objects. +Please refer to the Record Types section for an overview of what each type of object does and what you can tell OpenCS about these objects. From ad77c662aaffab0db65712187ae80dba556611ce Mon Sep 17 00:00:00 2001 From: Cramal Date: Mon, 25 May 2015 18:44:21 +0900 Subject: [PATCH 24/54] correct latex error --- manual/opencs/tables.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manual/opencs/tables.tex b/manual/opencs/tables.tex index 51aac50fd..984157f88 100644 --- a/manual/opencs/tables.tex +++ b/manual/opencs/tables.tex @@ -14,7 +14,7 @@ Let's browse through the various screens and see what all these tables show. \begin{description} \item[Record:] An entry in \OCS{} representing an item, location, sound, NPC or anything else. - \item[Instance, Object:] When an item is placed in the world, it isn't an isolated and unique object. For example, the game world might contain a lot of exquisite belts on different NPCs and in many crates, but they all refer to one specific record in the game's library: the Exquisite Belt record. In this case, all those belts in crates and on NPCs are \textbf{instances}. The central Exquisite Belt record is called a \textbf{object}. This allows modders to make changes to all items of the same type. For example, if you want all exquisite belts to have 4000 enchantment points rather than 400, you will only need to change the \textbf{object}} Exquisite Belt rather than all exquisite belts \textbf{instances}} individually. + \item[Instance, Object:] When an item is placed in the world, it isn't an isolated and unique object. For example, the game world might contain a lot of exquisite belts on different NPCs and in many crates, but they all refer to one specific record in the game's library: the Exquisite Belt record. In this case, all those belts in crates and on NPCs are \textbf{instances}. The central Exquisite Belt record is called a \textbf{object}. This allows modders to make changes to all items of the same type. For example, if you want all exquisite belts to have 4000 enchantment points rather than 400, you will only need to change the \textbf{object} Exquisite Belt rather than all exquisite belts \textbf{instances} individually. \end{description} \subsubsection{Recurring Terms} From 08484a46c7e8ed17202eb28d1cbf4f43799e5320 Mon Sep 17 00:00:00 2001 From: Cramal Date: Mon, 25 May 2015 18:45:42 +0900 Subject: [PATCH 25/54] remove latex error --- manual/opencs/recordtypes.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manual/opencs/recordtypes.tex b/manual/opencs/recordtypes.tex index 6db338e7e..661f5ac7d 100644 --- a/manual/opencs/recordtypes.tex +++ b/manual/opencs/recordtypes.tex @@ -8,7 +8,7 @@ Let's go through all Record Types and discuss what you can tell OpenCS about the \begin{description} \item[Activator:] When the player enters the same cell as this object, a script is started. Often it also has a \textbf{Script} attached to it, though it not mandatory. These scripts are small bits of code written in a special scripting language that OpenCS can read and interpret. \item[Potion:] This is a potion that is not self-made. It has an \textbf{Icon} for your inventory, Aside from the self-explanatory \textbf{Weight} and \textbf{Coin Value}, it has an attribute called \textbf{Auto Calc} set to ``False''. This means that the effects of this potion are pre-configured. This does not happen when the player makes their own potion. - \item[Apparatus:] This is a tool to make potions. Again there's an icon for your inventory as well as a weight and a coin value. It also has a \textbf{Quality} value attached to it: higher the number, the better the effect on your potions will be. The \textbf{Apparatus Type} describes if the item is a Calcinator, Retort, Alembic or Mortar & Pestle. Each has a different effect on the potion the player makes. For more information on this subject, please refer to the \href{http://www.uesp.net/wiki/Morrowind:Alchemy#Tools}{UESP page on Alchemy Tools}. + \item[Apparatus:] This is a tool to make potions. Again there's an icon for your inventory as well as a weight and a coin value. It also has a \textbf{Quality} value attached to it: higher the number, the better the effect on your potions will be. The \textbf{Apparatus Type} describes if the item is a Calcinator, Retort, Alembic or Mortar \& Pestle. Each has a different effect on the potion the player makes. For more information on this subject, please refer to the \href{http://www.uesp.net/wiki/Morrowind:Alchemy#Tools}{UESP page on Alchemy Tools}. \item[Armor:] This type of item adds \textbf{Enchantment Points} to the mix. Every piece of clothing or armor has a ''pool'' of potential Magicka that gets unlocked when you enchant it. Strong enchantments consume more Magicka from this pool: the stronger the enchantment, the more Enchantment Points each cast will take up. For more information on this subject, please refer to the \href{http://www.uesp.net/wiki/Morrowind:Enchant}{Enchant page on UESP}. \textbf{Health} means the amount of hit points this piece of armor has. If it sustains enough damage, the armor will be destroyed. Finally, \textbf{Armor Value} tells the game how much points to add to the player character's Armor Rating. \item[Book:] This includes scrolls and notes. For the game to make the distinction between books and scrolls, an extra property, \textbf{Scroll}, has been added. Under the \textbf{Skill} column a scroll or book can have an in-game skill listed. Reading this item will raise the player's level in that specific skill. For more information on this, please refer to the \href{http://www.uesp.net/wiki/Morrowind:Skill_Books}{Skill Books page on UESP}. \item[Clothing:] These items work just like Armors, but confer no protective properties. Rather than ``Armor Type'', these items have a ``Clothing Type''. From 95f740ec40b76db5892ef9c93ef35480fb6a00a7 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Mon, 25 May 2015 22:28:41 +1000 Subject: [PATCH 26/54] Give CSVWorld::EditWidget::mMainWidget a chance to clean up its children. Should resolve Bug #2578. --- apps/opencs/view/world/dialoguesubview.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 2cf30dcbe..7a25a7fc1 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -378,6 +378,13 @@ mDocument (document) void CSVWorld::EditWidget::remake(int row) { + if (mMainWidget) + { + QWidget *del = this->takeWidget(); + del->deleteLater(); + } + mMainWidget = new QWidget (this); + for (unsigned i = 0; i < mNestedModels.size(); ++i) delete mNestedModels[i]; @@ -401,12 +408,6 @@ void CSVWorld::EditWidget::remake(int row) if (mNestedTableMapper) delete mNestedTableMapper; - if (mMainWidget) - { - QWidget *del = this->takeWidget(); - del->deleteLater(); - } - mMainWidget = new QWidget (this); QFrame* line = new QFrame(mMainWidget); line->setObjectName(QString::fromUtf8("line")); From ec808843c3614e4615a6c7afb6f0acc5a28a94c7 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Tue, 26 May 2015 13:35:10 +1000 Subject: [PATCH 27/54] Do not rebuild the dialogue subview unless required. Should resolve Bug #2581. The loss of focus was caused by each text change (i.e. character entry) to a QPlainTextEdit resulting in dataChanged() signal which in turn rebuilt the dialogue subview. Changes in this commit include: - Do not send signal to update entire row if only a single item has changed. - Do not rebuild the dialogue subview unless the data item that triggers a conditional display is changed. - Add column flags to indicate whether the data in this column should rebuild the dialogue subview. - Return the correct flags for nested columns - Disable, rather than grey out, checkbox that does not apply to creature levelled list --- apps/opencs/model/world/columnbase.cpp | 4 ++-- apps/opencs/model/world/columnbase.hpp | 8 ++++--- apps/opencs/model/world/columnimp.hpp | 4 ++-- apps/opencs/model/world/data.cpp | 21 +++++++++++------ apps/opencs/model/world/idtable.cpp | 3 +-- apps/opencs/model/world/idtree.cpp | 6 ++--- apps/opencs/model/world/refidadapterimp.hpp | 2 +- apps/opencs/model/world/refidcollection.cpp | 3 ++- apps/opencs/view/world/dialoguesubview.cpp | 26 +++++++++++++++++---- 9 files changed, 52 insertions(+), 25 deletions(-) diff --git a/apps/opencs/model/world/columnbase.cpp b/apps/opencs/model/world/columnbase.cpp index 0e0ff11d4..3d13538c0 100644 --- a/apps/opencs/model/world/columnbase.cpp +++ b/apps/opencs/model/world/columnbase.cpp @@ -138,8 +138,8 @@ bool CSMWorld::NestableColumn::hasChildren() const } CSMWorld::NestedChildColumn::NestedChildColumn (int id, - CSMWorld::ColumnBase::Display display, bool isEditable) - : NestableColumn (id, display, CSMWorld::ColumnBase::Flag_Dialogue) , mIsEditable(isEditable) + CSMWorld::ColumnBase::Display display, int flags, bool isEditable) + : NestableColumn (id, display, flags) , mIsEditable(isEditable) {} bool CSMWorld::NestedChildColumn::isEditable () const diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index d6dd4b291..70b2a35b7 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -25,7 +25,8 @@ namespace CSMWorld { Flag_Table = 1, // column should be displayed in table view Flag_Dialogue = 2, // column should be displayed in dialogue view - Flag_Dialogue_List = 4 // column should be diaplyed in dialogue view + Flag_Dialogue_List = 4, // column should be diaplyed in dialogue view + Flag_Dialogue_Refresh = 8 // refresh dialogue view if this column is modified }; enum Display @@ -183,7 +184,7 @@ namespace CSMWorld template struct NestedParentColumn : public Column { - NestedParentColumn (int id, int flags = ColumnBase::Flag_Dialogue) : Column (id, + NestedParentColumn (int id, int flags = Flag_Dialogue) : Column (id, ColumnBase::Display_NestedHeader, flags) {} @@ -200,7 +201,8 @@ namespace CSMWorld struct NestedChildColumn : public NestableColumn { - NestedChildColumn (int id, Display display, bool isEditable = true); + NestedChildColumn (int id, + Display display, int flags = Flag_Dialogue, bool isEditable = true); virtual bool isEditable() const; diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index cbe0d74c4..64a1cf3d7 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -467,8 +467,8 @@ namespace CSMWorld int mMask; bool mInverted; - FlagColumn (int columnId, int mask, bool inverted = false) - : Column (columnId, ColumnBase::Display_Boolean), mMask (mask), + FlagColumn (int columnId, int mask, int flags = Flag_Table | Flag_Dialogue, bool inverted = false) + : Column (columnId, ColumnBase::Display_Boolean, flags), mMask (mask), mInverted (inverted) {} diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 920c7db71..c27c068f1 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -141,7 +141,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc index = mRaces.getColumns()-1; mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new RaceAttributeAdapter())); mRaces.getNestableColumn(index)->addColumn( - new NestedChildColumn (Columns::ColumnId_RaceAttributes, ColumnBase::Display_String, false)); + new NestedChildColumn (Columns::ColumnId_RaceAttributes, ColumnBase::Display_String, + ColumnBase::Flag_Dialogue, false)); mRaces.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_RaceMaleValue, ColumnBase::Display_Integer)); mRaces.getNestableColumn(index)->addColumn( @@ -287,8 +288,10 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc mCells.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Cell)); mCells.addColumn (new NameColumn); mCells.addColumn (new FlagColumn (Columns::ColumnId_SleepForbidden, ESM::Cell::NoSleep)); - mCells.addColumn (new FlagColumn (Columns::ColumnId_InteriorWater, ESM::Cell::HasWater)); - mCells.addColumn (new FlagColumn (Columns::ColumnId_InteriorSky, ESM::Cell::QuasiEx)); + mCells.addColumn (new FlagColumn (Columns::ColumnId_InteriorWater, ESM::Cell::HasWater, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh)); + mCells.addColumn (new FlagColumn (Columns::ColumnId_InteriorSky, ESM::Cell::QuasiEx, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh)); mCells.addColumn (new RegionColumn); mCells.addColumn (new RefNumCounterColumn); // Misc Cell data @@ -297,7 +300,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc index = mCells.getColumns()-1; mCells.addAdapter (std::make_pair(&mCells.getColumn(index), new CellListAdapter ())); mCells.getNestableColumn(index)->addColumn( - new NestedChildColumn (Columns::ColumnId_Interior, ColumnBase::Display_Boolean)); + new NestedChildColumn (Columns::ColumnId_Interior, ColumnBase::Display_Boolean, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh)); mCells.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_Ambient, ColumnBase::Display_Integer)); mCells.getNestableColumn(index)->addColumn( @@ -346,7 +350,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc mBodyParts.addColumn (new BodyPartTypeColumn); mBodyParts.addColumn (new VampireColumn); mBodyParts.addColumn (new FlagColumn (Columns::ColumnId_Female, ESM::BodyPart::BPF_Female)); - mBodyParts.addColumn (new FlagColumn (Columns::ColumnId_Playable, ESM::BodyPart::BPF_NotPlayable, true)); + mBodyParts.addColumn (new FlagColumn (Columns::ColumnId_Playable, + ESM::BodyPart::BPF_NotPlayable, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, true)); mBodyParts.addColumn (new MeshTypeColumn); mBodyParts.addColumn (new ModelColumn); mBodyParts.addColumn (new RaceColumn); @@ -393,7 +398,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc // new objects deleted in dtor of NestableColumn // WARNING: The order of the columns below are assumed in PathgridPointListAdapter mPathgrids.getNestableColumn(index)->addColumn( - new NestedChildColumn (Columns::ColumnId_PathgridIndex, ColumnBase::Display_Integer, false)); + new NestedChildColumn (Columns::ColumnId_PathgridIndex, ColumnBase::Display_Integer, + ColumnBase::Flag_Dialogue, false)); mPathgrids.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_PathgridPosX, ColumnBase::Display_Integer)); mPathgrids.getNestableColumn(index)->addColumn( @@ -405,7 +411,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc index = mPathgrids.getColumns()-1; mPathgrids.addAdapter (std::make_pair(&mPathgrids.getColumn(index), new PathgridEdgeListAdapter ())); mPathgrids.getNestableColumn(index)->addColumn( - new NestedChildColumn (Columns::ColumnId_PathgridEdgeIndex, ColumnBase::Display_Integer, false)); + new NestedChildColumn (Columns::ColumnId_PathgridEdgeIndex, ColumnBase::Display_Integer, + ColumnBase::Flag_Dialogue, false)); mPathgrids.getNestableColumn(index)->addColumn( new NestedChildColumn (Columns::ColumnId_PathgridEdge0, ColumnBase::Display_Integer)); mPathgrids.getNestableColumn(index)->addColumn( diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 36b3f5c97..04aa271cc 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -74,8 +74,7 @@ bool CSMWorld::IdTable::setData (const QModelIndex &index, const QVariant &value { mIdCollection->setData (index.row(), index.column(), value); - emit dataChanged (CSMWorld::IdTable::index (index.row(), 0), - CSMWorld::IdTable::index (index.row(), mIdCollection->getColumns()-1)); + emit dataChanged (index, index); return true; } diff --git a/apps/opencs/model/world/idtree.cpp b/apps/opencs/model/world/idtree.cpp index 06db09a0f..7351c03a7 100644 --- a/apps/opencs/model/world/idtree.cpp +++ b/apps/opencs/model/world/idtree.cpp @@ -74,7 +74,7 @@ QVariant CSMWorld::IdTree::nestedHeaderData(int section, int subSection, Qt::Ori return tr(parentColumn->nestedColumn(subSection).getTitle().c_str()); if (role==ColumnBase::Role_Flags) - return idCollection()->getColumn (section).mFlags; + return parentColumn->nestedColumn(subSection).mFlags; if (role==ColumnBase::Role_Display) return parentColumn->nestedColumn(subSection).mDisplayType; @@ -92,8 +92,8 @@ bool CSMWorld::IdTree::setData (const QModelIndex &index, const QVariant &value, mNestedCollection->setNestedData(parentAddress.first, parentAddress.second, value, index.row(), index.column()); - emit dataChanged (CSMWorld::IdTree::index (parentAddress.first, 0), - CSMWorld::IdTree::index (parentAddress.first, idCollection()->getColumns()-1)); + emit dataChanged (index, index); + return true; } else diff --git a/apps/opencs/model/world/refidadapterimp.hpp b/apps/opencs/model/world/refidadapterimp.hpp index 3411429d0..869996da5 100644 --- a/apps/opencs/model/world/refidadapterimp.hpp +++ b/apps/opencs/model/world/refidadapterimp.hpp @@ -1976,7 +1976,7 @@ namespace CSMWorld { switch (subColIndex) { - case 0: return QVariant(QVariant::UserType); // disable the checkbox editor + case 0: return QVariant(); // disable the checkbox editor case 1: return record.get().mFlags & ESM::CreatureLevList::AllLevels; case 2: return static_cast (record.get().mChanceNone); default: diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 9d9e9826a..cda19c87b 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -245,7 +245,8 @@ CSMWorld::RefIdCollection::RefIdCollection() actorsColumns.mServices.insert (std::make_pair (&mColumns.back(), sServiceTable[i].mFlag)); } - mColumns.push_back (RefIdColumn (Columns::ColumnId_AutoCalc, ColumnBase::Display_Boolean)); + mColumns.push_back (RefIdColumn (Columns::ColumnId_AutoCalc, ColumnBase::Display_Boolean, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh)); const RefIdColumn *autoCalc = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_ApparatusType, diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 2cf30dcbe..03fa532c5 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -787,14 +787,32 @@ void CSVWorld::DialogueSubView::dataChanged (const QModelIndex & index) { QModelIndex currentIndex(mTable->getModelIndex(mCurrentId, 0)); - if (currentIndex.isValid() && index.row() == currentIndex.row()) + if (currentIndex.isValid() && + (index.parent().isValid() ? index.parent().row() : index.row()) == currentIndex.row()) { CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (currentIndex.row(), 1)).toInt()); mEditWidget->setDisabled (state==CSMWorld::RecordBase::State_Deleted || mLocked); - int y = mEditWidget->verticalScrollBar()->value(); - mEditWidget->remake (index.row()); - mEditWidget->verticalScrollBar()->setValue(y); + + // Check if the changed data should force refresh (rebuild) the dialogue subview + int flags = 0; + if (index.parent().isValid()) // TODO: check that index is topLeft + { + flags = static_cast(mTable)->nestedHeaderData (index.parent().column(), + index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); + } + else + { + flags = mTable->headerData (index.column(), + Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); + } + + if (flags & CSMWorld::ColumnBase::Flag_Dialogue_Refresh) + { + int y = mEditWidget->verticalScrollBar()->value(); + mEditWidget->remake (index.parent().isValid() ? index.parent().row() : index.row()); + mEditWidget->verticalScrollBar()->setValue(y); + } } } From 2f29c2c077491f378e3abad231010e1643c64319 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Tue, 26 May 2015 13:58:58 +1000 Subject: [PATCH 28/54] Qualify the scope of Flag_Dialogue. --- apps/opencs/model/world/columnbase.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index 70b2a35b7..bf8378e37 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -184,7 +184,7 @@ namespace CSMWorld template struct NestedParentColumn : public Column { - NestedParentColumn (int id, int flags = Flag_Dialogue) : Column (id, + NestedParentColumn (int id, int flags = ColumnBase::Flag_Dialogue) : Column (id, ColumnBase::Display_NestedHeader, flags) {} @@ -202,7 +202,7 @@ namespace CSMWorld struct NestedChildColumn : public NestableColumn { NestedChildColumn (int id, - Display display, int flags = Flag_Dialogue, bool isEditable = true); + Display display, int flags = ColumnBase::Flag_Dialogue, bool isEditable = true); virtual bool isEditable() const; From aafcaf32c5ba50bb1b92c44dda31e1d0f4ba1d5b Mon Sep 17 00:00:00 2001 From: cc9cii Date: Tue, 26 May 2015 16:12:54 +1000 Subject: [PATCH 29/54] Qualify the scope of Flag_Dialogue. (another one) --- apps/opencs/model/world/columnimp.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 64a1cf3d7..6b496e0ca 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -467,7 +467,8 @@ namespace CSMWorld int mMask; bool mInverted; - FlagColumn (int columnId, int mask, int flags = Flag_Table | Flag_Dialogue, bool inverted = false) + FlagColumn (int columnId, int mask, + int flags = ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, bool inverted = false) : Column (columnId, ColumnBase::Display_Boolean, flags), mMask (mask), mInverted (inverted) {} From f06ddd47c40f100a9610af3baf8b572d6308bbed Mon Sep 17 00:00:00 2001 From: cc9cii Date: Tue, 26 May 2015 17:38:22 +1000 Subject: [PATCH 30/54] Return the correct range for a given topic. Should resolve Bug #2569. --- apps/opencs/model/world/infocollection.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/apps/opencs/model/world/infocollection.cpp b/apps/opencs/model/world/infocollection.cpp index f2d81823c..a508d28f3 100644 --- a/apps/opencs/model/world/infocollection.cpp +++ b/apps/opencs/model/world/infocollection.cpp @@ -173,6 +173,17 @@ CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const s RecordConstIterator begin = getRecords().begin()+iter->second; + while (begin != getRecords().begin()) + { + if (!Misc::StringUtils::ciEqual(begin->get().mTopicId, topic2)) + { + // we've gone one too far, go back + ++begin; + break; + } + --begin; + } + // Find end RecordConstIterator end = begin; From 2f976495403ddc5ed39038632021ef536697df11 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 27 May 2015 00:30:39 +0200 Subject: [PATCH 31/54] Fix for cleanup issue when exiting after a failed savegame load (Fixes #2580) --- apps/openmw/mwrender/npcanimation.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 6a6d52e26..f4943ba55 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -196,7 +196,11 @@ const NpcAnimation::PartBoneMap NpcAnimation::sPartList = createPartListMap(); NpcAnimation::~NpcAnimation() { - if (!mListenerDisabled) + if (!mListenerDisabled + // No need to getInventoryStore() to reset, if none exists + // This is to avoid triggering the listener via ensureCustomData()->autoEquip()->fireEquipmentChanged() + // all from within this destructor. ouch! + && mPtr.getRefData().getCustomData()) mPtr.getClass().getInventoryStore(mPtr).setListener(NULL, mPtr); } From 5fd107a95c7a763d3fe7a84d433b8bc7de1dac69 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Wed, 27 May 2015 10:19:26 +1000 Subject: [PATCH 32/54] Fix crash with array out of bounds when refNum index is incorrect. --- components/esm/loadcell.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index 0f8897c48..94f4b0b6e 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -18,9 +18,11 @@ namespace ///< Translate 8bit/24bit code (stored in refNum.mIndex) into a proper refNum void adjustRefNum (ESM::RefNum& refNum, ESM::ESMReader& reader) { - int local = (refNum.mIndex & 0xff000000) >> 24; + unsigned int local = (refNum.mIndex & 0xff000000) >> 24; - if (local) + // If we have an index value that does not make sense, assume that it was an addition + // by the present plugin (but a faulty one) + if (local && local <= reader.getGameFiles().size()) { // If the most significant 8 bits are used, then this reference already exists. // In this case, do not spawn a new reference, but overwrite the old one. From b7044ac119b6bbbdd7b74749c5201a462fec0d59 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Wed, 27 May 2015 10:27:57 +1000 Subject: [PATCH 33/54] Fix crash loading some addon files. Should resolve Bug #2583. - This may be a bug within QSortFilterProxyModel - It isn't 100% clear what aspects of these addon files cause the issue, but something about them causes Qt to lose track of its internal row numbers. --- apps/opencs/model/world/idtableproxymodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/world/idtableproxymodel.cpp b/apps/opencs/model/world/idtableproxymodel.cpp index 84869716e..987d27462 100644 --- a/apps/opencs/model/world/idtableproxymodel.cpp +++ b/apps/opencs/model/world/idtableproxymodel.cpp @@ -46,7 +46,7 @@ void CSMWorld::IdTableProxyModel::setFilter (const boost::shared_ptr Date: Wed, 27 May 2015 15:55:00 +1000 Subject: [PATCH 34/54] Object verifier check to see if the script used by that object actually exists. Should resolve Bug #2582. --- .../opencs/model/tools/referenceablecheck.cpp | 83 ++++++++++++++++--- .../opencs/model/tools/referenceablecheck.hpp | 20 +++-- apps/opencs/model/tools/tools.cpp | 14 ++-- 3 files changed, 93 insertions(+), 24 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index c0991a330..548fcd36f 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -8,12 +8,14 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes, - const CSMWorld::IdCollection& faction) + const CSMWorld::IdCollection& faction, + const CSMWorld::IdCollection& scripts) : mReferencables(referenceable), mRaces(races), mClasses(classes), mFactions(faction), + mScripts(scripts), mPlayerPresent(false) { } @@ -245,6 +247,9 @@ void CSMTools::ReferenceableCheckStage::bookCheck( CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Book, book.mId); inventoryItemCheck(book, messages, id.toString(), true); + + // Check that mentioned scripts exist + scriptCheck(book, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::activatorCheck( @@ -265,6 +270,9 @@ void CSMTools::ReferenceableCheckStage::activatorCheck( //Checking for model, IIRC all activators should have a model if (activator.mModel.empty()) messages.push_back (std::make_pair (id, activator.mId + " has no model")); + + // Check that mentioned scripts exist + scriptCheck(activator, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::potionCheck( @@ -284,6 +292,9 @@ void CSMTools::ReferenceableCheckStage::potionCheck( inventoryItemCheck(potion, messages, id.toString()); //IIRC potion can have empty effects list just fine. + + // Check that mentioned scripts exist + scriptCheck(potion, messages, id.toString()); } @@ -305,6 +316,9 @@ void CSMTools::ReferenceableCheckStage::apparatusCheck( inventoryItemCheck(apparatus, messages, id.toString()); toolCheck(apparatus, messages, id.toString()); + + // Check that mentioned scripts exist + scriptCheck(apparatus, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::armorCheck( @@ -331,6 +345,9 @@ void CSMTools::ReferenceableCheckStage::armorCheck( //checking for health. Only positive numbers are allowed, or 0 is illegal if (armor.mData.mHealth <= 0) messages.push_back (std::make_pair (id, armor.mId + " has non positive health")); + + // Check that mentioned scripts exist + scriptCheck(armor, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::clothingCheck( @@ -348,6 +365,9 @@ void CSMTools::ReferenceableCheckStage::clothingCheck( const ESM::Clothing& clothing = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Clothing, clothing.mId); inventoryItemCheck(clothing, messages, id.toString(), true); + + // Check that mentioned scripts exist + scriptCheck(clothing, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::containerCheck( @@ -377,6 +397,9 @@ void CSMTools::ReferenceableCheckStage::containerCheck( //checking for name if (container.mName.empty()) messages.push_back (std::make_pair (id, container.mId + " has an empty name")); + + // Check that mentioned scripts exist + scriptCheck(container, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::creatureCheck ( @@ -444,6 +467,9 @@ void CSMTools::ReferenceableCheckStage::creatureCheck ( //TODO, find meaning of other values 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 ")); + + // Check that mentioned scripts exist + scriptCheck(creature, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::doorCheck( @@ -455,15 +481,18 @@ void CSMTools::ReferenceableCheckStage::doorCheck( if (baseRecord.isDeleted()) return; - const ESM::Door& Door = (dynamic_cast&>(baseRecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Door, Door.mId); + const ESM::Door& door = (dynamic_cast&>(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Door, door.mId); //usual, name or model - if (Door.mName.empty()) - messages.push_back (std::make_pair (id, Door.mId + " has an empty name")); + if (door.mName.empty()) + messages.push_back (std::make_pair (id, door.mId + " has an empty name")); + + if (door.mModel.empty()) + messages.push_back (std::make_pair (id, door.mId + " has no model")); - if (Door.mModel.empty()) - messages.push_back (std::make_pair (id, Door.mId + " has no model")); + // Check that mentioned scripts exist + scriptCheck(door, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::ingredientCheck( @@ -478,10 +507,13 @@ void CSMTools::ReferenceableCheckStage::ingredientCheck( return; } - const ESM::Ingredient& Ingredient = (dynamic_cast& >(baseRecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Ingredient, Ingredient.mId); + const ESM::Ingredient& ingredient = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Ingredient, ingredient.mId); - inventoryItemCheck(Ingredient, messages, id.toString()); + inventoryItemCheck(ingredient, messages, id.toString()); + + // Check that mentioned scripts exist + scriptCheck(ingredient, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::creaturesLevListCheck( @@ -542,6 +574,9 @@ void CSMTools::ReferenceableCheckStage::lightCheck( if (light.mData.mTime == 0) messages.push_back (std::make_pair (id, light.mId + " has zero duration")); } + + // Check that mentioned scripts exist + scriptCheck(light, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::lockpickCheck( @@ -562,6 +597,9 @@ void CSMTools::ReferenceableCheckStage::lockpickCheck( inventoryItemCheck(lockpick, messages, id.toString()); toolCheck(lockpick, messages, id.toString(), true); + + // Check that mentioned scripts exist + scriptCheck(lockpick, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::miscCheck( @@ -580,6 +618,9 @@ void CSMTools::ReferenceableCheckStage::miscCheck( CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Miscellaneous, miscellaneous.mId); inventoryItemCheck(miscellaneous, messages, id.toString()); + + // Check that mentioned scripts exist + scriptCheck(miscellaneous, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::npcCheck ( @@ -697,6 +738,9 @@ void CSMTools::ReferenceableCheckStage::npcCheck ( messages.push_back (std::make_pair (id, npc.mId + " has no hair")); //TODO: reputation, Disposition, rank, everything else + + // Check that mentioned scripts exist + scriptCheck(npc, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::weaponCheck( @@ -773,6 +817,9 @@ void CSMTools::ReferenceableCheckStage::weaponCheck( messages.push_back (std::make_pair (id, weapon.mId + " has negative reach")); } } + + // Check that mentioned scripts exist + scriptCheck(weapon, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::probeCheck( @@ -792,6 +839,9 @@ void CSMTools::ReferenceableCheckStage::probeCheck( inventoryItemCheck(probe, messages, id.toString()); toolCheck(probe, messages, id.toString(), true); + + // Check that mentioned scripts exist + scriptCheck(probe, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::repairCheck ( @@ -808,6 +858,9 @@ void CSMTools::ReferenceableCheckStage::repairCheck ( inventoryItemCheck (repair, messages, id.toString()); toolCheck (repair, messages, id.toString(), true); + + // Check that mentioned scripts exist + scriptCheck(repair, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::staticCheck ( @@ -919,3 +972,13 @@ template void CSMTools::ReferenceableCheckStage::listCheck ( someList.mId + " contains item with non-positive level")); } } + +template void CSMTools::ReferenceableCheckStage::scriptCheck ( + const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID) +{ + if (!someTool.mScript.empty()) + { + if (mScripts.searchId(someTool.mScript) == -1) + messages.push_back (std::make_pair (someID, someTool.mId + " refers to an unknown script \""+someTool.mScript+"\"")); + } +} diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index ac7ed7082..a34f3a789 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -15,7 +15,8 @@ namespace CSMTools ReferenceableCheckStage (const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes, - const CSMWorld::IdCollection& factions); + const CSMWorld::IdCollection& factions, + const CSMWorld::IdCollection& scripts); virtual void perform(int stage, CSMDoc::Messages& messages); virtual int setup(); @@ -46,26 +47,30 @@ namespace CSMTools //FINAL CHECK void finalCheck (CSMDoc::Messages& messages); - //TEMPLATE CHECKS - template void inventoryItemCheck(const ITEM& someItem, + //TEMPLATE CHECKS + template void inventoryItemCheck(const ITEM& someItem, CSMDoc::Messages& messages, const std::string& someID, bool enchantable); //for all enchantable items. - template void inventoryItemCheck(const ITEM& someItem, + template void inventoryItemCheck(const ITEM& someItem, CSMDoc::Messages& messages, const std::string& someID); //for non-enchantable items. - template void toolCheck(const TOOL& someTool, + template void toolCheck(const TOOL& someTool, CSMDoc::Messages& messages, const std::string& someID, bool canbebroken); //for tools with uses. - template void toolCheck(const TOOL& someTool, + template void toolCheck(const TOOL& someTool, CSMDoc::Messages& messages, const std::string& someID); //for tools without uses. - template void listCheck(const LIST& someList, + template void listCheck(const LIST& someList, + CSMDoc::Messages& messages, + const std::string& someID); + + template void scriptCheck(const TOOL& someTool, CSMDoc::Messages& messages, const std::string& someID); @@ -73,6 +78,7 @@ namespace CSMTools const CSMWorld::IdCollection& mRaces; const CSMWorld::IdCollection& mClasses; const CSMWorld::IdCollection& mFactions; + const CSMWorld::IdCollection& mScripts; bool mPlayerPresent; }; } diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 99e462b1d..979f72486 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -81,7 +81,7 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() mVerifierOperation->appendStage (new SpellCheckStage (mData.getSpells())); - mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions())); + mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions(), mData.getScripts())); mVerifierOperation->appendStage (new ReferenceCheckStage(mData.getReferences(), mData.getReferenceables(), mData.getCells(), mData.getFactions())); @@ -114,7 +114,7 @@ CSMTools::Tools::Tools (CSMDoc::Document& document) 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))); + this, SLOT (verifierMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int))); } CSMTools::Tools::~Tools() @@ -130,7 +130,7 @@ CSMTools::Tools::~Tools() mSearch.abortAndWait(); delete mSearchOperation; } - + for (std::map::iterator iter (mReports.begin()); iter!=mReports.end(); ++iter) delete iter->second; } @@ -149,7 +149,7 @@ CSMWorld::UniversalId CSMTools::Tools::newSearch() { mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel (true))); - return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Search, mNextReportNumber-1); + return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Search, mNextReportNumber-1); } void CSMTools::Tools::runSearch (const CSMWorld::UniversalId& searchId, const Search& search) @@ -159,11 +159,11 @@ void CSMTools::Tools::runSearch (const CSMWorld::UniversalId& searchId, const Se if (!mSearchOperation) { mSearchOperation = new SearchOperation (mDocument); - mSearch.setOperation (mSearchOperation); + mSearch.setOperation (mSearchOperation); } mSearchOperation->configure (search); - + mSearch.start(); } @@ -198,7 +198,7 @@ CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId& id.getType()!=CSMWorld::UniversalId::Type_LoadErrorLog && id.getType()!=CSMWorld::UniversalId::Type_Search) throw std::logic_error ("invalid request for report model: " + id.toString()); - + return mReports.at (id.getIndex()); } From 3a31468af07548a17d2a0cd45020bb9aa552c349 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Wed, 27 May 2015 22:12:11 +1000 Subject: [PATCH 35/54] Pathgrid record verifier. Feature #2000. --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/model/tools/pathgridcheck.cpp | 158 ++++++++++++++++++++++ apps/opencs/model/tools/pathgridcheck.hpp | 31 +++++ apps/opencs/model/tools/tools.cpp | 3 + 4 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 apps/opencs/model/tools/pathgridcheck.cpp create mode 100644 apps/opencs/model/tools/pathgridcheck.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 438f3c694..78b2deb7a 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -40,7 +40,7 @@ opencs_units (model/tools opencs_units_noqt (model/tools mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck - startscriptcheck search searchoperation searchstage + startscriptcheck search searchoperation searchstage pathgridcheck ) diff --git a/apps/opencs/model/tools/pathgridcheck.cpp b/apps/opencs/model/tools/pathgridcheck.cpp new file mode 100644 index 000000000..71f596f4f --- /dev/null +++ b/apps/opencs/model/tools/pathgridcheck.cpp @@ -0,0 +1,158 @@ +#include "pathgridcheck.hpp" + +#include +#include + +#include "../world/universalid.hpp" +#include "../world/idcollection.hpp" +#include "../world/subcellcollection.hpp" +#include "../world/pathgrid.hpp" + + +CSMTools::PathgridCheckStage::PathgridCheckStage (const CSMWorld::SubCellCollection& pathgrids) +: mPathgrids (pathgrids) +{} + +int CSMTools::PathgridCheckStage::setup() +{ + return mPathgrids.getSize(); +} + +void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& messages) +{ + const CSMWorld::Record& record = mPathgrids.getRecord (stage); + + if (record.isDeleted()) + return; + + const CSMWorld::Pathgrid& pathgrid = record.get(); + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Pathgrid, pathgrid.mId); + + // check the number of pathgrid points + if (pathgrid.mData.mS2 > static_cast(pathgrid.mPoints.size())) + messages.push_back (std::make_pair (id, pathgrid.mId + " has less points than expected")); + else if (pathgrid.mData.mS2 > static_cast(pathgrid.mPoints.size())) + messages.push_back (std::make_pair (id, pathgrid.mId + " has more points than expected")); + + struct Point + { + unsigned char mConnectionNum; + std::vector mOtherIndex; + Point() : mConnectionNum(0), mOtherIndex(0) {} + }; + std::vector pointList(pathgrid.mPoints.size()); + std::vector duplList; + + for (unsigned int i = 0; i < pathgrid.mEdges.size(); ++i) + { + if (pathgrid.mEdges[i].mV0 < static_cast(pathgrid.mPoints.size()) && pathgrid.mEdges[i].mV0 >= 0) + { + pointList[pathgrid.mEdges[i].mV0].mConnectionNum++; + // first check for duplicate edges + unsigned int j = 0; + for (; j < pointList[pathgrid.mEdges[i].mV0].mOtherIndex.size(); ++j) + { + if (pointList[pathgrid.mEdges[i].mV0].mOtherIndex[j] == pathgrid.mEdges[i].mV1) + { + 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())); + break; + } + } + + // only add if not a duplicate + if (j == pointList[pathgrid.mEdges[i].mV0].mOtherIndex.size()) + pointList[pathgrid.mEdges[i].mV0].mOtherIndex.push_back(pathgrid.mEdges[i].mV1); + } + else + { + 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())); + } + } + + for (unsigned int i = 0; i < pathgrid.mPoints.size(); ++i) + { + // check the connection number for each point matches the edge connections + if (pathgrid.mPoints[i].mConnectionNum > pointList[i].mConnectionNum) + { + std::ostringstream ss; + ss << " has has less edges than expected for point " << i; + messages.push_back (std::make_pair (id, pathgrid.mId + ss.str())); + } + 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())); + } + + // check that edges are bidirectional + bool foundReverse = false; + for (unsigned int j = 0; j < pointList[i].mOtherIndex.size(); ++j) + { + for (unsigned int k = 0; k < pointList[pointList[i].mOtherIndex[j]].mOtherIndex.size(); ++k) + { + if (pointList[pointList[i].mOtherIndex[j]].mOtherIndex[k] == static_cast(i)) + { + foundReverse = true; + break; + } + } + + if (!foundReverse) + { + 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())); + } + } + + // check duplicate points + // FIXME: how to do this efficiently? + for (unsigned int j = 0; j < pathgrid.mPoints.size(); ++j) + { + if (j == i) + continue; + + if (pathgrid.mPoints[i].mX == pathgrid.mPoints[j].mX && + pathgrid.mPoints[i].mY == pathgrid.mPoints[j].mY && + pathgrid.mPoints[i].mZ == pathgrid.mPoints[j].mZ) + { + std::vector::const_iterator it = find(duplList.begin(), duplList.end(), i); + if (it == duplList.end()) + { + std::ostringstream ss; + ss << " has a duplicated point (" << i + << ") 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())); + + duplList.push_back(i); + break; + } + } + } + } + + // check pathgrid points that are not connected to anything + for (unsigned int i = 0; i < pointList.size(); ++i) + { + if (pointList[i].mConnectionNum == 0) + { + std::ostringstream ss; + ss << " has an orphaned point (" << i + << ") 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())); + } + } + + // TODO: check whether there are disconnected graphs +} diff --git a/apps/opencs/model/tools/pathgridcheck.hpp b/apps/opencs/model/tools/pathgridcheck.hpp new file mode 100644 index 000000000..c90dbc8ed --- /dev/null +++ b/apps/opencs/model/tools/pathgridcheck.hpp @@ -0,0 +1,31 @@ +#ifndef CSM_TOOLS_PATHGRIDCHECK_H +#define CSM_TOOLS_PATHGRIDCHECK_H + +#include "../world/collection.hpp" + +#include "../doc/stage.hpp" + +namespace CSMWorld +{ + struct Pathgrid; + template + class SubCellCollection; +} + +namespace CSMTools +{ + class PathgridCheckStage : public CSMDoc::Stage + { + const CSMWorld::SubCellCollection >& mPathgrids; + + public: + + PathgridCheckStage (const CSMWorld::SubCellCollection >& pathgrids); + + virtual int setup(); + + virtual void perform (int stage, CSMDoc::Messages& messages); + }; +} + +#endif // CSM_TOOLS_PATHGRIDCHECK_H diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 99e462b1d..fd1569697 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -26,6 +26,7 @@ #include "referencecheck.hpp" #include "startscriptcheck.hpp" #include "searchoperation.hpp" +#include "pathgridcheck.hpp" CSMDoc::OperationHolder *CSMTools::Tools::get (int type) { @@ -96,6 +97,8 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() CSMWorld::UniversalId( CSMWorld::UniversalId::Type_Meshes )), mData.getRaces() )); + mVerifierOperation->appendStage (new PathgridCheckStage (mData.getPathgrids())); + mVerifier.setOperation (mVerifierOperation); } From 674a124bc6efe5afeba544a5ebb9eb3fd8e855ef Mon Sep 17 00:00:00 2001 From: cc9cii Date: Wed, 27 May 2015 22:27:32 +1000 Subject: [PATCH 36/54] Don't use local type as a template parameter. --- apps/opencs/model/tools/pathgridcheck.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/opencs/model/tools/pathgridcheck.cpp b/apps/opencs/model/tools/pathgridcheck.cpp index 71f596f4f..8f22cc8cd 100644 --- a/apps/opencs/model/tools/pathgridcheck.cpp +++ b/apps/opencs/model/tools/pathgridcheck.cpp @@ -8,6 +8,15 @@ #include "../world/subcellcollection.hpp" #include "../world/pathgrid.hpp" +namespace +{ + struct Point + { + unsigned char mConnectionNum; + std::vector mOtherIndex; + Point() : mConnectionNum(0), mOtherIndex(0) {} + }; +} CSMTools::PathgridCheckStage::PathgridCheckStage (const CSMWorld::SubCellCollection& pathgrids) : mPathgrids (pathgrids) @@ -35,12 +44,6 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message else if (pathgrid.mData.mS2 > static_cast(pathgrid.mPoints.size())) messages.push_back (std::make_pair (id, pathgrid.mId + " has more points than expected")); - struct Point - { - unsigned char mConnectionNum; - std::vector mOtherIndex; - Point() : mConnectionNum(0), mOtherIndex(0) {} - }; std::vector pointList(pathgrid.mPoints.size()); std::vector duplList; From b6878c2e0c02bc6c3c5f8d14b96e991f2cb1f6bd Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 27 May 2015 19:45:26 +0200 Subject: [PATCH 37/54] improved error handling in LocalScripts::add --- apps/openmw/mwworld/localscripts.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/localscripts.cpp b/apps/openmw/mwworld/localscripts.cpp index 5be66ccea..e30246f7c 100644 --- a/apps/openmw/mwworld/localscripts.cpp +++ b/apps/openmw/mwworld/localscripts.cpp @@ -93,7 +93,7 @@ std::pair MWWorld::LocalScripts::getNext() void MWWorld::LocalScripts::add (const std::string& scriptName, const Ptr& ptr) { - if (const ESM::Script *script = mStore.get().find (scriptName)) + if (const ESM::Script *script = mStore.get().search (scriptName)) { try { @@ -108,6 +108,10 @@ void MWWorld::LocalScripts::add (const std::string& scriptName, const Ptr& ptr) << " because an exception has been thrown: " << exception.what() << std::endl; } } + else + std::cerr + << "failed to add local script " << scriptName + << " because the script does not exist." << std::endl; } void MWWorld::LocalScripts::addCell (CellStore *cell) From 00c165d3a5e1ef5b79c95d54e6edb8421e1ae7c9 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Thu, 28 May 2015 08:44:17 +1000 Subject: [PATCH 38/54] Add user preference option to check unused or redundant pathgrid points. Also resolve namespace clash issue in osx. --- apps/opencs/model/settings/usersettings.cpp | 7 ++++++ apps/opencs/model/tools/pathgridcheck.cpp | 24 ++++++++++++--------- apps/opencs/model/tools/pathgridcheck.hpp | 14 ++++++++++-- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/apps/opencs/model/settings/usersettings.cpp b/apps/opencs/model/settings/usersettings.cpp index ea002c5ed..ea75fd6d9 100644 --- a/apps/opencs/model/settings/usersettings.cpp +++ b/apps/opencs/model/settings/usersettings.cpp @@ -264,6 +264,13 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() monoFont->setToolTip ("Whether to use monospaced fonts on script edit subview."); } + declareSection ("verifier", "Verifier"); + { + Setting *extraPathgrid = createSetting (Type_CheckBox, "pathgrid-extra-check", "Pathgrid: Extra Check"); + extraPathgrid->setDefaultValue ("false"); + extraPathgrid->setToolTip ("Additional checks for orphaned or duplicated pathgrid points"); + } + { /****************************************************************** * There are three types of values: diff --git a/apps/opencs/model/tools/pathgridcheck.cpp b/apps/opencs/model/tools/pathgridcheck.cpp index 8f22cc8cd..c436cfe04 100644 --- a/apps/opencs/model/tools/pathgridcheck.cpp +++ b/apps/opencs/model/tools/pathgridcheck.cpp @@ -8,15 +8,7 @@ #include "../world/subcellcollection.hpp" #include "../world/pathgrid.hpp" -namespace -{ - struct Point - { - unsigned char mConnectionNum; - std::vector mOtherIndex; - Point() : mConnectionNum(0), mOtherIndex(0) {} - }; -} +#include "../settings/usersettings.hpp" CSMTools::PathgridCheckStage::PathgridCheckStage (const CSMWorld::SubCellCollection& pathgrids) : mPathgrids (pathgrids) @@ -29,6 +21,12 @@ int CSMTools::PathgridCheckStage::setup() void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& messages) { + // NOTE: This is horribly inefficient but in order to use signals the entire Stage class + // hierarchy needs to be braught under Qt which seems like an overkill for a small + // performance gain during verify operations + CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + bool extraCheck = userSettings.setting ("verifier/pathgrid-extra-check", QString ("false"))=="true"; + const CSMWorld::Record& record = mPathgrids.getRecord (stage); if (record.isDeleted()) @@ -44,7 +42,7 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message else if (pathgrid.mData.mS2 > static_cast(pathgrid.mPoints.size())) messages.push_back (std::make_pair (id, pathgrid.mId + " has more points than expected")); - std::vector pointList(pathgrid.mPoints.size()); + std::vector pointList(pathgrid.mPoints.size()); std::vector duplList; for (unsigned int i = 0; i < pathgrid.mEdges.size(); ++i) @@ -115,6 +113,9 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message } } + if (!extraCheck) + continue; + // check duplicate points // FIXME: how to do this efficiently? for (unsigned int j = 0; j < pathgrid.mPoints.size(); ++j) @@ -143,6 +144,9 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message } } + if (!extraCheck) + return; + // check pathgrid points that are not connected to anything for (unsigned int i = 0; i < pointList.size(); ++i) { diff --git a/apps/opencs/model/tools/pathgridcheck.hpp b/apps/opencs/model/tools/pathgridcheck.hpp index c90dbc8ed..603235552 100644 --- a/apps/opencs/model/tools/pathgridcheck.hpp +++ b/apps/opencs/model/tools/pathgridcheck.hpp @@ -14,13 +14,23 @@ namespace CSMWorld namespace CSMTools { + + struct Point + { + unsigned char mConnectionNum; + std::vector mOtherIndex; + Point() : mConnectionNum(0), mOtherIndex(0) {} + }; + class PathgridCheckStage : public CSMDoc::Stage { - const CSMWorld::SubCellCollection >& mPathgrids; + const CSMWorld::SubCellCollection >& mPathgrids; public: - PathgridCheckStage (const CSMWorld::SubCellCollection >& pathgrids); + PathgridCheckStage (const CSMWorld::SubCellCollection >& pathgrids); virtual int setup(); From 6821cb4133e0456a42ac279a3fd69b312a6b6c5e Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 29 May 2015 05:40:20 +1000 Subject: [PATCH 39/54] Use the tree model rather than a nested proxy for the dialogue only listing (i.e. non table) items. Should resolve Bug #2586. - QDataWidgetMapper requires the rootindex to be set, which was not possible with the nested proxy model. --- apps/opencs/view/world/dialoguesubview.cpp | 39 +++++++++++++--------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 5e2fa29a5..66e8fcb7a 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -211,8 +211,17 @@ void CSVWorld::DialogueDelegateDispatcher::editorDataCommited(QWidget* editor, void CSVWorld::DialogueDelegateDispatcher::setEditorData (QWidget* editor, const QModelIndex& index) const { - CSMWorld::ColumnBase::Display display = static_cast - (mTable->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + CSMWorld::ColumnBase::Display display = CSMWorld::ColumnBase::Display_None; + if (index.parent().isValid()) + { + display = static_cast + (static_cast(mTable)->nestedHeaderData (index.parent().column(), index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + } + else + { + display = static_cast + (mTable->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + } QLabel* label = qobject_cast(editor); if(label) @@ -523,22 +532,20 @@ void CSVWorld::EditWidget::remake(int row) } else { - mNestedModels.push_back(new CSMWorld::NestedTableProxyModel ( - static_cast(mTable)->index(row, i), - display, static_cast(mTable))); + CSMWorld::IdTree *tree = static_cast(mTable); mNestedTableMapper = new QDataWidgetMapper (this); - mNestedTableMapper->setModel(mNestedModels.back()); + mNestedTableMapper->setModel(tree); // FIXME: lack MIME support? mNestedTableDispatcher = - new DialogueDelegateDispatcher (0/*this*/, mTable, mCommandDispatcher, mDocument, mNestedModels.back()); + new DialogueDelegateDispatcher (0/*this*/, mTable, mCommandDispatcher, mDocument, tree); + mNestedTableMapper->setRootIndex (tree->index(row, i)); mNestedTableMapper->setItemDelegate(mNestedTableDispatcher); - int columnCount = - mTable->columnCount(mTable->getModelIndex (mNestedModels.back()->getParentId(), i)); + int columnCount = tree->columnCount(tree->index(row, i)); for (int col = 0; col < columnCount; ++col) { - int displayRole = mNestedModels.back()->headerData (col, + int displayRole = tree->nestedHeaderData (i, col, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt(); CSMWorld::ColumnBase::Display display = @@ -548,16 +555,16 @@ void CSVWorld::EditWidget::remake(int row) // FIXME: assumed all columns are editable QWidget* editor = - mNestedTableDispatcher->makeEditor (display, mNestedModels.back()->index (0, col)); + mNestedTableDispatcher->makeEditor (display, tree->index (0, col, tree->index(row, i))); if (editor) { mNestedTableMapper->addMapping (editor, col); - std::string disString = mNestedModels.back()->headerData (col, + std::string disString = tree->nestedHeaderData (i, col, Qt::Horizontal, Qt::DisplayRole).toString().toStdString(); - // Need ot use Qt::DisplayRole in order to get the correct string + // Need to use Qt::DisplayRole in order to get the correct string // from CSMWorld::Columns - QLabel* label = new QLabel (mNestedModels.back()->headerData (col, + QLabel* label = new QLabel (tree->nestedHeaderData (i, col, Qt::Horizontal, Qt::DisplayRole).toString(), mMainWidget); label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed); @@ -567,14 +574,14 @@ void CSVWorld::EditWidget::remake(int row) unlockedLayout->addWidget (editor, unlocked, 1); ++unlocked; - if(mNestedModels.back()->index(0, col).data().type() == QVariant::UserType) + if(tree->index(0, col, tree->index(row, i)).data().type() == QVariant::UserType) { editor->setEnabled(false); label->setEnabled(false); } } } - mNestedTableMapper->setCurrentModelIndex(mNestedModels.back()->index(0, 0)); + mNestedTableMapper->setCurrentModelIndex(tree->index(0, 0, tree->index(row, i))); } } } From 9ad69d908529edfecdd830fd970af6ffb3451a88 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 29 May 2015 05:44:35 +1000 Subject: [PATCH 40/54] Allow wheel events in dialogue spin box types only when they have focus. Should resolve Feature #2585. --- apps/opencs/CMakeLists.txt | 1 + apps/opencs/view/world/dialoguespinbox.cpp | 53 ++++++++++++++++++++++ apps/opencs/view/world/dialoguespinbox.hpp | 40 ++++++++++++++++ apps/opencs/view/world/util.cpp | 8 ++-- 4 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 apps/opencs/view/world/dialoguespinbox.cpp create mode 100644 apps/opencs/view/world/dialoguespinbox.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 78b2deb7a..7723b15f5 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -63,6 +63,7 @@ opencs_units (view/world table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator cellcreator referenceablecreator referencecreator scenesubview infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable + dialoguespinbox ) opencs_units_noqt (view/world diff --git a/apps/opencs/view/world/dialoguespinbox.cpp b/apps/opencs/view/world/dialoguespinbox.cpp new file mode 100644 index 000000000..1228ca0da --- /dev/null +++ b/apps/opencs/view/world/dialoguespinbox.cpp @@ -0,0 +1,53 @@ +#include "dialoguespinbox.hpp" + +#include + +CSVWorld::DialogueSpinBox::DialogueSpinBox(QWidget *parent) : QSpinBox(parent) +{ + setFocusPolicy(Qt::StrongFocus); +} + +void CSVWorld::DialogueSpinBox::focusInEvent(QFocusEvent *event) +{ + setFocusPolicy(Qt::WheelFocus); + QSpinBox::focusInEvent(event); +} + +void CSVWorld::DialogueSpinBox::focusOutEvent(QFocusEvent *event) +{ + setFocusPolicy(Qt::StrongFocus); + QSpinBox::focusOutEvent(event); +} + +void CSVWorld::DialogueSpinBox::wheelEvent(QWheelEvent *event) +{ + if (!hasFocus()) + event->ignore(); + else + QSpinBox::wheelEvent(event); +} + +CSVWorld::DialogueDoubleSpinBox::DialogueDoubleSpinBox(QWidget *parent) : QDoubleSpinBox(parent) +{ + setFocusPolicy(Qt::StrongFocus); +} + +void CSVWorld::DialogueDoubleSpinBox::focusInEvent(QFocusEvent *event) +{ + setFocusPolicy(Qt::WheelFocus); + QDoubleSpinBox::focusInEvent(event); +} + +void CSVWorld::DialogueDoubleSpinBox::focusOutEvent(QFocusEvent *event) +{ + setFocusPolicy(Qt::StrongFocus); + QDoubleSpinBox::focusOutEvent(event); +} + +void CSVWorld::DialogueDoubleSpinBox::wheelEvent(QWheelEvent *event) +{ + if (!hasFocus()) + event->ignore(); + else + QDoubleSpinBox::wheelEvent(event); +} diff --git a/apps/opencs/view/world/dialoguespinbox.hpp b/apps/opencs/view/world/dialoguespinbox.hpp new file mode 100644 index 000000000..a68e0c314 --- /dev/null +++ b/apps/opencs/view/world/dialoguespinbox.hpp @@ -0,0 +1,40 @@ +#ifndef CSV_WORLD_DIALOGUESPINBOX_H +#define CSV_WORLD_DIALOGUESPINBOX_H + +#include +#include + +namespace CSVWorld +{ + class DialogueSpinBox : public QSpinBox + { + Q_OBJECT + + public: + + DialogueSpinBox (QWidget *parent = 0); + + protected: + + virtual void focusInEvent(QFocusEvent *event); + virtual void focusOutEvent(QFocusEvent *event); + virtual void wheelEvent(QWheelEvent *event); + }; + + class DialogueDoubleSpinBox : public QDoubleSpinBox + { + Q_OBJECT + + public: + + DialogueDoubleSpinBox (QWidget *parent = 0); + + protected: + + virtual void focusInEvent(QFocusEvent *event); + virtual void focusOutEvent(QFocusEvent *event); + virtual void wheelEvent(QWheelEvent *event); + }; +} + +#endif // CSV_WORLD_DIALOGUESPINBOX_H diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index a11b5bdde..5452214ef 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -9,8 +9,6 @@ #include #include #include -#include -#include #include #include #include @@ -19,7 +17,7 @@ #include "../../model/world/commands.hpp" #include "../../model/world/tablemimedata.hpp" #include "../../model/world/commanddispatcher.hpp" - +#include "dialoguespinbox.hpp" #include "scriptedit.hpp" CSVWorld::NastyTableModelHack::NastyTableModelHack (QAbstractItemModel& model) @@ -174,7 +172,7 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO case CSMWorld::ColumnBase::Display_Integer: { - QSpinBox *sb = new QSpinBox(parent); + DialogueSpinBox *sb = new DialogueSpinBox(parent); sb->setRange(INT_MIN, INT_MAX); return sb; } @@ -185,7 +183,7 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO case CSMWorld::ColumnBase::Display_Float: { - QDoubleSpinBox *dsb = new QDoubleSpinBox(parent); + DialogueDoubleSpinBox *dsb = new DialogueDoubleSpinBox(parent); dsb->setRange(-FLT_MAX, FLT_MAX); dsb->setSingleStep(0.01f); dsb->setDecimals(3); From 734e52d1c82e2f2a250c3a11fbaecd53592ee27f Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 29 May 2015 06:40:40 +1000 Subject: [PATCH 41/54] Move the user preference check to the preparation step before the running of the operation. --- apps/opencs/model/tools/pathgridcheck.cpp | 13 +++++-------- apps/opencs/model/tools/pathgridcheck.hpp | 15 ++++++++------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/apps/opencs/model/tools/pathgridcheck.cpp b/apps/opencs/model/tools/pathgridcheck.cpp index c436cfe04..58fdbeb2d 100644 --- a/apps/opencs/model/tools/pathgridcheck.cpp +++ b/apps/opencs/model/tools/pathgridcheck.cpp @@ -16,17 +16,14 @@ CSMTools::PathgridCheckStage::PathgridCheckStage (const CSMWorld::SubCellCollect int CSMTools::PathgridCheckStage::setup() { + CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + mExtraCheck = userSettings.setting ("verifier/pathgrid-extra-check", QString ("false"))=="true"; + return mPathgrids.getSize(); } void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& messages) { - // NOTE: This is horribly inefficient but in order to use signals the entire Stage class - // hierarchy needs to be braught under Qt which seems like an overkill for a small - // performance gain during verify operations - CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); - bool extraCheck = userSettings.setting ("verifier/pathgrid-extra-check", QString ("false"))=="true"; - const CSMWorld::Record& record = mPathgrids.getRecord (stage); if (record.isDeleted()) @@ -113,7 +110,7 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message } } - if (!extraCheck) + if (!mExtraCheck) continue; // check duplicate points @@ -144,7 +141,7 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message } } - if (!extraCheck) + if (!mExtraCheck) return; // check pathgrid points that are not connected to anything diff --git a/apps/opencs/model/tools/pathgridcheck.hpp b/apps/opencs/model/tools/pathgridcheck.hpp index 603235552..86ace6af2 100644 --- a/apps/opencs/model/tools/pathgridcheck.hpp +++ b/apps/opencs/model/tools/pathgridcheck.hpp @@ -24,17 +24,18 @@ namespace CSMTools class PathgridCheckStage : public CSMDoc::Stage { - const CSMWorld::SubCellCollection >& mPathgrids; + bool mExtraCheck; + const CSMWorld::SubCellCollection >& mPathgrids; - public: + public: - PathgridCheckStage (const CSMWorld::SubCellCollection >& pathgrids); + PathgridCheckStage (const CSMWorld::SubCellCollection >& pathgrids); - virtual int setup(); + virtual int setup(); - virtual void perform (int stage, CSMDoc::Messages& messages); + virtual void perform (int stage, CSMDoc::Messages& messages); }; } From 393cee406fce7f0781c55451b53efd06427d2ab7 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 29 May 2015 19:28:25 +1000 Subject: [PATCH 42/54] Use signals for user preference setting updates. --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/model/tools/pathgridcheck.cpp | 15 +++++++++---- apps/opencs/model/tools/pathgridcheck.hpp | 7 +++++- apps/opencs/model/tools/signalhandler.cpp | 23 ++++++++++++++++++++ apps/opencs/model/tools/signalhandler.hpp | 26 +++++++++++++++++++++++ apps/opencs/model/tools/tools.cpp | 9 ++++++-- apps/opencs/model/tools/tools.hpp | 2 +- 7 files changed, 75 insertions(+), 9 deletions(-) create mode 100644 apps/opencs/model/tools/signalhandler.cpp create mode 100644 apps/opencs/model/tools/signalhandler.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 78b2deb7a..9ed0d6a8b 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -34,7 +34,7 @@ opencs_hdrs_noqt (model/world opencs_units (model/tools - tools reportmodel + tools reportmodel signalhandler ) opencs_units_noqt (model/tools diff --git a/apps/opencs/model/tools/pathgridcheck.cpp b/apps/opencs/model/tools/pathgridcheck.cpp index 58fdbeb2d..2b11b7066 100644 --- a/apps/opencs/model/tools/pathgridcheck.cpp +++ b/apps/opencs/model/tools/pathgridcheck.cpp @@ -10,14 +10,21 @@ #include "../settings/usersettings.hpp" -CSMTools::PathgridCheckStage::PathgridCheckStage (const CSMWorld::SubCellCollection& pathgrids) -: mPathgrids (pathgrids) +#include "signalhandler.hpp" + +CSMTools::PathgridCheckStage::PathgridCheckStage (const CSMWorld::SubCellCollection& pathgrids, + CSMTools::SignalHandler *signalHandler) +: mPathgrids (pathgrids), mSigHandler(signalHandler) {} +CSMTools::PathgridCheckStage::~PathgridCheckStage () +{ + delete mSigHandler; +} + int CSMTools::PathgridCheckStage::setup() { - CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); - mExtraCheck = userSettings.setting ("verifier/pathgrid-extra-check", QString ("false"))=="true"; + mExtraCheck = mSigHandler->extraCheck(); return mPathgrids.getSize(); } diff --git a/apps/opencs/model/tools/pathgridcheck.hpp b/apps/opencs/model/tools/pathgridcheck.hpp index 86ace6af2..8b5dc08a7 100644 --- a/apps/opencs/model/tools/pathgridcheck.hpp +++ b/apps/opencs/model/tools/pathgridcheck.hpp @@ -14,6 +14,7 @@ namespace CSMWorld namespace CSMTools { + class SignalHandler; struct Point { @@ -25,13 +26,17 @@ namespace CSMTools class PathgridCheckStage : public CSMDoc::Stage { bool mExtraCheck; + CSMTools::SignalHandler *mSigHandler; + const CSMWorld::SubCellCollection >& mPathgrids; public: PathgridCheckStage (const CSMWorld::SubCellCollection >& pathgrids); + CSMWorld::IdAccessor >& pathgrids, CSMTools::SignalHandler *signallHandler); + + ~PathgridCheckStage (); virtual int setup(); diff --git a/apps/opencs/model/tools/signalhandler.cpp b/apps/opencs/model/tools/signalhandler.cpp new file mode 100644 index 000000000..cfb52ed63 --- /dev/null +++ b/apps/opencs/model/tools/signalhandler.cpp @@ -0,0 +1,23 @@ +#include "signalhandler.hpp" + +#include "../settings/usersettings.hpp" + +CSMTools::SignalHandler::SignalHandler(bool extraCheck) + : mExtraCheck(extraCheck) +{ + connect (&CSMSettings::UserSettings::instance(), + SIGNAL (userSettingUpdated(const QString &, const QStringList &)), + this, + SLOT (updateUserSetting (const QString &, const QStringList &))); +} + +void CSMTools::SignalHandler::updateUserSetting (const QString &name, const QStringList &list) +{ + if (name=="verifier/pathgrid-extra-check") + mExtraCheck = list.at(0) == "true"; +} + +bool CSMTools::SignalHandler::extraCheck () +{ + return mExtraCheck; +} diff --git a/apps/opencs/model/tools/signalhandler.hpp b/apps/opencs/model/tools/signalhandler.hpp new file mode 100644 index 000000000..c3d075fdf --- /dev/null +++ b/apps/opencs/model/tools/signalhandler.hpp @@ -0,0 +1,26 @@ +#ifndef CSM_TOOLS_SIGNALHANDLER_H +#define CSM_TOOLS_SIGNALHANDLER_H + +#include + +namespace CSMTools +{ + class SignalHandler : public QObject + { + Q_OBJECT + + bool mExtraCheck; + + public: + + SignalHandler (bool extraCheck); + + bool extraCheck (); + + public slots: + + void updateUserSetting (const QString &name, const QStringList &list); + }; +} + +#endif diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 8d93a9433..45c7fbae3 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -10,6 +10,8 @@ #include "../world/data.hpp" #include "../world/universalid.hpp" +#include "../settings/usersettings.hpp" + #include "reportmodel.hpp" #include "mandatoryid.hpp" #include "skillcheck.hpp" @@ -27,6 +29,7 @@ #include "startscriptcheck.hpp" #include "searchoperation.hpp" #include "pathgridcheck.hpp" +#include "signalhandler.hpp" CSMDoc::OperationHolder *CSMTools::Tools::get (int type) { @@ -56,6 +59,8 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() 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))); + CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + std::vector mandatoryIds; // I want C++11, damn it! mandatoryIds.push_back ("Day"); mandatoryIds.push_back ("DaysPassed"); @@ -97,7 +102,8 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() CSMWorld::UniversalId( CSMWorld::UniversalId::Type_Meshes )), mData.getRaces() )); - mVerifierOperation->appendStage (new PathgridCheckStage (mData.getPathgrids())); + mVerifierOperation->appendStage (new PathgridCheckStage (mData.getPathgrids(), + new SignalHandler(userSettings.setting ("verifier/pathgrid-extra-check", QString ("false"))=="true"))); mVerifier.setOperation (mVerifierOperation); } @@ -213,4 +219,3 @@ void CSMTools::Tools::verifierMessage (const CSMWorld::UniversalId& id, const st if (iter!=mActiveReports.end()) mReports[iter->second]->add (id, message, hint); } - diff --git a/apps/opencs/model/tools/tools.hpp b/apps/opencs/model/tools/tools.hpp index 0f9e57044..dc19cf456 100644 --- a/apps/opencs/model/tools/tools.hpp +++ b/apps/opencs/model/tools/tools.hpp @@ -64,7 +64,7 @@ namespace CSMTools CSMWorld::UniversalId newSearch(); void runSearch (const CSMWorld::UniversalId& searchId, const Search& search); - + void abortOperation (int type); ///< \attention The operation is not aborted immediately. From 9cbda0ffada698d45e404c02775bc78d09351636 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 29 May 2015 22:20:43 +1000 Subject: [PATCH 43/54] Allow users to select syntax highlighting colours. Should resolve Feature #2507. --- apps/opencs/model/settings/usersettings.cpp | 36 +++++ apps/opencs/view/world/scriptedit.cpp | 22 ++-- apps/opencs/view/world/scriptedit.hpp | 7 +- apps/opencs/view/world/scripthighlighter.cpp | 132 +++++++++++++++++-- apps/opencs/view/world/scripthighlighter.hpp | 2 + 5 files changed, 182 insertions(+), 17 deletions(-) diff --git a/apps/opencs/model/settings/usersettings.cpp b/apps/opencs/model/settings/usersettings.cpp index ea002c5ed..5b6e7ab8b 100644 --- a/apps/opencs/model/settings/usersettings.cpp +++ b/apps/opencs/model/settings/usersettings.cpp @@ -262,6 +262,42 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() Setting *monoFont = createSetting (Type_CheckBox, "mono-font", "Use monospace font"); monoFont->setDefaultValue ("true"); monoFont->setToolTip ("Whether to use monospaced fonts on script edit subview."); + + QString tooltip = + "\n#RGB (each of R, G, and B is a single hex digit)" + "\n#RRGGBB" + "\n#RRRGGGBBB" + "\n#RRRRGGGGBBBB" + "\nA name from the list of colors defined in the list of SVG color keyword names." + "\nX11 color names may also work."; + + Setting *formatInt = createSetting (Type_LineEdit, "colour-int", "Highlight Colour: Int"); + formatInt->setDefaultValues (QStringList() << "Dark magenta"); + formatInt->setToolTip ("(Default: Green) Use one of the following formats:" + tooltip); + + Setting *formatFloat = createSetting (Type_LineEdit, "colour-float", "Highlight Colour: Float"); + formatFloat->setDefaultValues (QStringList() << "Magenta"); + formatFloat->setToolTip ("(Default: Magenta) Use one of the following formats:" + tooltip); + + Setting *formatName = createSetting (Type_LineEdit, "colour-name", "Highlight Colour: Name"); + formatName->setDefaultValues (QStringList() << "Gray"); + formatName->setToolTip ("(Default: Gray) Use one of the following formats:" + tooltip); + + Setting *formatKeyword = createSetting (Type_LineEdit, "colour-keyword", "Highlight Colour: Keyword"); + formatKeyword->setDefaultValues (QStringList() << "Red"); + formatKeyword->setToolTip ("(Default: Red) Use one of the following formats:" + tooltip); + + Setting *formatSpecial = createSetting (Type_LineEdit, "colour-special", "Highlight Colour: Special"); + formatSpecial->setDefaultValues (QStringList() << "Dark yellow"); + formatSpecial->setToolTip ("(Default: Dark yellow) Use one of the following formats:" + tooltip); + + Setting *formatComment = createSetting (Type_LineEdit, "colour-comment", "Highlight Colour: Comment"); + formatComment->setDefaultValues (QStringList() << "Green"); + formatComment->setToolTip ("(Default: Green) Use one of the following formats:" + tooltip); + + Setting *formatId = createSetting (Type_LineEdit, "colour-id", "Highlight Colour: Id"); + formatId->setDefaultValues (QStringList() << "Blue"); + formatId->setToolTip ("(Default: Blue) Use one of the following formats:" + tooltip); } { diff --git a/apps/opencs/view/world/scriptedit.cpp b/apps/opencs/view/world/scriptedit.cpp index 2f0d82ae1..ad2cddbf8 100644 --- a/apps/opencs/view/world/scriptedit.cpp +++ b/apps/opencs/view/world/scriptedit.cpp @@ -31,11 +31,11 @@ bool CSVWorld::ScriptEdit::event (QEvent *event) if (event->type()==QEvent::ShortcutOverride) { QKeyEvent *keyEvent = static_cast (event); - + if (keyEvent->matches (QKeySequence::Undo) || keyEvent->matches (QKeySequence::Redo)) return true; } - + return QPlainTextEdit::event (event); } @@ -92,13 +92,16 @@ CSVWorld::ScriptEdit::ScriptEdit (const CSMDoc::Document& document, ScriptHighli connect (&mUpdateTimer, SIGNAL (timeout()), this, SLOT (updateHighlighting())); + CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + connect (&userSettings, SIGNAL (userSettingUpdated(const QString &, const QStringList &)), + this, SLOT (updateUserSetting (const QString &, const QStringList &))); + mUpdateTimer.setSingleShot (true); // TODO: provide a font selector dialogue mMonoFont.setStyleHint(QFont::TypeWriter); - std::string useMonoFont = - CSMSettings::UserSettings::instance().setting("script-editor/mono-font", "true").toStdString(); - if (useMonoFont == "true") + + if (userSettings.setting("script-editor/mono-font", "true") == "true") setFont(mMonoFont); mLineNumberArea = new LineNumberArea(this); @@ -107,10 +110,13 @@ CSVWorld::ScriptEdit::ScriptEdit (const CSMDoc::Document& document, ScriptHighli connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int))); connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int))); - std::string showStatusBar = - CSMSettings::UserSettings::instance().settingValue("script-editor/show-linenum").toStdString(); + showLineNum(userSettings.settingValue("script-editor/show-linenum") == "true"); +} - showLineNum(showStatusBar == "true"); +void CSVWorld::ScriptEdit::updateUserSetting (const QString &name, const QStringList &list) +{ + if (mHighlighter->updateUserSetting (name, list)) + updateHighlighting(); } void CSVWorld::ScriptEdit::showLineNum(bool show) diff --git a/apps/opencs/view/world/scriptedit.hpp b/apps/opencs/view/world/scriptedit.hpp index fb577e60e..d17abf24e 100644 --- a/apps/opencs/view/world/scriptedit.hpp +++ b/apps/opencs/view/world/scriptedit.hpp @@ -56,7 +56,7 @@ namespace CSVWorld protected: bool event (QEvent *event); - + public: ScriptEdit (const CSMDoc::Document& document, ScriptHighlighter::Mode mode, @@ -96,7 +96,12 @@ namespace CSVWorld void updateHighlighting(); void updateLineNumberAreaWidth(int newBlockCount); + void updateLineNumberArea(const QRect &, int); + + public slots: + + void updateUserSetting (const QString &name, const QStringList &list); }; class LineNumberArea : public QWidget diff --git a/apps/opencs/view/world/scripthighlighter.cpp b/apps/opencs/view/world/scripthighlighter.cpp index 6dda8d4fa..4923a44d8 100644 --- a/apps/opencs/view/world/scripthighlighter.cpp +++ b/apps/opencs/view/world/scripthighlighter.cpp @@ -6,6 +6,8 @@ #include #include +#include "../../model/settings/usersettings.hpp" + bool CSVWorld::ScriptHighlighter::parseInt (int value, const Compiler::TokenLoc& loc, Compiler::Scanner& scanner) { @@ -78,46 +80,77 @@ CSVWorld::ScriptHighlighter::ScriptHighlighter (const CSMWorld::Data& data, Mode : QSyntaxHighlighter (parent), Compiler::Parser (mErrorHandler, mContext), mContext (data), mMode (mode) { - /// \todo replace this with user settings + CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + + QColor color = QColor(); + { + color.setNamedColor(userSettings.setting("script-editor/colour-int", "Dark magenta")); + if (!color.isValid()) + color = QColor(Qt::darkMagenta); + QTextCharFormat format; - format.setForeground (Qt::darkMagenta); + format.setForeground (color); mScheme.insert (std::make_pair (Type_Int, format)); } { + color.setNamedColor(userSettings.setting ("script-editor/colour-float", "Magenta")); + if (!color.isValid()) + color = QColor(Qt::magenta); + QTextCharFormat format; - format.setForeground (Qt::magenta); + format.setForeground (color); mScheme.insert (std::make_pair (Type_Float, format)); } { + color.setNamedColor(userSettings.setting ("script-editor/colour-name", "Gray")); + if (!color.isValid()) + color = QColor(Qt::gray); + QTextCharFormat format; - format.setForeground (Qt::gray); + format.setForeground (color); mScheme.insert (std::make_pair (Type_Name, format)); } { + color.setNamedColor(userSettings.setting ("script-editor/colour-keyword", "Red")); + if (!color.isValid()) + color = QColor(Qt::red); + QTextCharFormat format; - format.setForeground (Qt::red); + format.setForeground (color); mScheme.insert (std::make_pair (Type_Keyword, format)); } { + color.setNamedColor(userSettings.setting ("script-editor/colour-special", "Dark yellow")); + if (!color.isValid()) + color = QColor(Qt::darkYellow); + QTextCharFormat format; - format.setForeground (Qt::darkYellow); + format.setForeground (color); mScheme.insert (std::make_pair (Type_Special, format)); } { + color.setNamedColor(userSettings.setting ("script-editor/colour-comment", "Green")); + if (!color.isValid()) + color = QColor(Qt::green); + QTextCharFormat format; - format.setForeground (Qt::green); + format.setForeground (color); mScheme.insert (std::make_pair (Type_Comment, format)); } { + color.setNamedColor(userSettings.setting ("script-editor/colour-id", "Blue")); + if (!color.isValid()) + color = QColor(Qt::blue); + QTextCharFormat format; - format.setForeground (Qt::blue); + format.setForeground (color); mScheme.insert (std::make_pair (Type_Id, format)); } @@ -143,3 +176,86 @@ void CSVWorld::ScriptHighlighter::invalidateIds() { mContext.invalidateIds(); } + +bool CSVWorld::ScriptHighlighter::updateUserSetting (const QString &name, const QStringList &list) +{ + if (list.empty()) + return false; + + QColor color = QColor(); + + if (name == "script-editor/colour-int") + { + color.setNamedColor(list.at(0)); + if (!color.isValid()) + return false; + + QTextCharFormat format; + format.setForeground (color); + mScheme[Type_Int] = format; + } + else if (name == "script-editor/colour-float") + { + color.setNamedColor(list.at(0)); + if (!color.isValid()) + return false; + + QTextCharFormat format; + format.setForeground (color); + mScheme[Type_Float] = format; + } + else if (name == "script-editor/colour-name") + { + color.setNamedColor(list.at(0)); + if (!color.isValid()) + return false; + + QTextCharFormat format; + format.setForeground (color); + mScheme[Type_Name] = format; + } + else if (name == "script-editor/colour-keyword") + { + color.setNamedColor(list.at(0)); + if (!color.isValid()) + return false; + + QTextCharFormat format; + format.setForeground (color); + mScheme[Type_Keyword] = format; + } + else if (name == "script-editor/colour-special") + { + color.setNamedColor(list.at(0)); + if (!color.isValid()) + return false; + + QTextCharFormat format; + format.setForeground (color); + mScheme[Type_Special] = format; + } + else if (name == "script-editor/colour-comment") + { + color.setNamedColor(list.at(0)); + if (!color.isValid()) + return false; + + QTextCharFormat format; + format.setForeground (color); + mScheme[Type_Comment] = format; + } + else if (name == "script-editor/colour-id") + { + color.setNamedColor(list.at(0)); + if (!color.isValid()) + return false; + + QTextCharFormat format; + format.setForeground (color); + mScheme[Type_Id] = format; + } + else + return false; + + return true; +} diff --git a/apps/opencs/view/world/scripthighlighter.hpp b/apps/opencs/view/world/scripthighlighter.hpp index 953f2f953..6f1f58e82 100644 --- a/apps/opencs/view/world/scripthighlighter.hpp +++ b/apps/opencs/view/world/scripthighlighter.hpp @@ -87,6 +87,8 @@ namespace CSVWorld virtual void highlightBlock (const QString& text); void invalidateIds(); + + bool updateUserSetting (const QString &name, const QStringList &list); }; } From 0aaac59cc3cfdd834ed36d3585ffc96b38237f67 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 30 May 2015 08:37:58 +1000 Subject: [PATCH 44/54] Return to startup dialog when the open/create action is cancelled. Should resolve Bug #2588. --- apps/opencs/editor.cpp | 30 +++++++++++++++++++++++ apps/opencs/editor.hpp | 2 ++ apps/opencs/model/doc/documentmanager.cpp | 5 ++++ apps/opencs/model/doc/documentmanager.hpp | 2 ++ apps/opencs/view/doc/newgame.cpp | 6 +++++ apps/opencs/view/doc/newgame.hpp | 4 +++ 6 files changed, 49 insertions(+) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 53a9e9e83..84849cbbb 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -67,9 +67,11 @@ CS::Editor::Editor (OgreInit::OgreInit& ogreInit) connect (&mFileDialog, SIGNAL(signalCreateNewFile (const boost::filesystem::path&)), this, SLOT(createNewFile (const boost::filesystem::path&))); + connect (&mFileDialog, SIGNAL (rejected()), this, SLOT (cancelFileDialog ())); connect (&mNewGame, SIGNAL (createRequest (const boost::filesystem::path&)), this, SLOT (createNewGame (const boost::filesystem::path&))); + connect (&mNewGame, SIGNAL (cancelCreateGame()), this, SLOT (cancelCreateGame ())); } CS::Editor::~Editor () @@ -176,12 +178,40 @@ void CS::Editor::createGame() mNewGame.activateWindow(); } +void CS::Editor::cancelCreateGame() +{ + if (!mDocumentManager.isEmpty()) + return; + + mNewGame.hide(); + + if (mStartup.isHidden()) + mStartup.show(); + + mStartup.raise(); + mStartup.activateWindow(); +} + void CS::Editor::createAddon() { mStartup.hide(); mFileDialog.showDialog (CSVDoc::ContentAction_New); } +void CS::Editor::cancelFileDialog() +{ + if (!mDocumentManager.isEmpty()) + return; + + mFileDialog.hide(); + + if (mStartup.isHidden()) + mStartup.show(); + + mStartup.raise(); + mStartup.activateWindow(); +} + void CS::Editor::loadDocument() { mStartup.hide(); diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index 273f0825b..eb85743a3 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -87,6 +87,8 @@ namespace CS void createGame(); void createAddon(); + void cancelCreateGame(); + void cancelFileDialog(); void loadDocument(); void openFiles (const boost::filesystem::path &path); diff --git a/apps/opencs/model/doc/documentmanager.cpp b/apps/opencs/model/doc/documentmanager.cpp index 2d444f245..29d7a8d3a 100644 --- a/apps/opencs/model/doc/documentmanager.cpp +++ b/apps/opencs/model/doc/documentmanager.cpp @@ -49,6 +49,11 @@ CSMDoc::DocumentManager::~DocumentManager() delete *iter; } +bool CSMDoc::DocumentManager::isEmpty() +{ + return mDocuments.empty(); +} + void CSMDoc::DocumentManager::addDocument (const std::vector& files, const boost::filesystem::path& savePath, bool new_) { diff --git a/apps/opencs/model/doc/documentmanager.hpp b/apps/opencs/model/doc/documentmanager.hpp index 0ae73e70c..f3fcbf8ec 100644 --- a/apps/opencs/model/doc/documentmanager.hpp +++ b/apps/opencs/model/doc/documentmanager.hpp @@ -59,6 +59,8 @@ namespace CSMDoc /// Ask OGRE for a list of available resources. void listResources(); + bool isEmpty(); + private: boost::filesystem::path mResDir; diff --git a/apps/opencs/view/doc/newgame.cpp b/apps/opencs/view/doc/newgame.cpp index 98681c499..32b483728 100644 --- a/apps/opencs/view/doc/newgame.cpp +++ b/apps/opencs/view/doc/newgame.cpp @@ -66,3 +66,9 @@ void CSVDoc::NewGameDialogue::create() { emit createRequest (mAdjusterWidget->getPath()); } + +void CSVDoc::NewGameDialogue::reject() +{ + emit cancelCreateGame (); + QDialog::reject(); +} diff --git a/apps/opencs/view/doc/newgame.hpp b/apps/opencs/view/doc/newgame.hpp index 9ad7ea169..70e9d684b 100644 --- a/apps/opencs/view/doc/newgame.hpp +++ b/apps/opencs/view/doc/newgame.hpp @@ -36,11 +36,15 @@ namespace CSVDoc void createRequest (const boost::filesystem::path& file); + void cancelCreateGame (); + private slots: void stateChanged (bool valid); void create(); + + void reject(); }; } From 76196d815dcd4172b16a751352a52b056b5ab58b Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 30 May 2015 18:58:33 +1000 Subject: [PATCH 45/54] Update user preference setting value via an event message queue. --- apps/opencs/model/tools/signalhandler.cpp | 14 ++++++++++++-- apps/opencs/model/tools/signalhandler.hpp | 4 ++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/tools/signalhandler.cpp b/apps/opencs/model/tools/signalhandler.cpp index cfb52ed63..64e5ead55 100644 --- a/apps/opencs/model/tools/signalhandler.cpp +++ b/apps/opencs/model/tools/signalhandler.cpp @@ -1,5 +1,7 @@ #include "signalhandler.hpp" +#include + #include "../settings/usersettings.hpp" CSMTools::SignalHandler::SignalHandler(bool extraCheck) @@ -11,12 +13,20 @@ CSMTools::SignalHandler::SignalHandler(bool extraCheck) SLOT (updateUserSetting (const QString &, const QStringList &))); } +// called from the main thread void CSMTools::SignalHandler::updateUserSetting (const QString &name, const QStringList &list) { - if (name=="verifier/pathgrid-extra-check") - mExtraCheck = list.at(0) == "true"; + if (name=="verifier/pathgrid-extra-check" && !list.empty()) + QMetaObject::invokeMethod(this, "updateExtraCheck", Qt::AutoConnection, Q_ARG(bool, list.at(0) == "true")); +} + +// should be in the operations thread via an event message queue +void CSMTools::SignalHandler::updateExtraCheck (bool extraCheck) +{ + mExtraCheck = extraCheck; } +// called from the operations thread bool CSMTools::SignalHandler::extraCheck () { return mExtraCheck; diff --git a/apps/opencs/model/tools/signalhandler.hpp b/apps/opencs/model/tools/signalhandler.hpp index c3d075fdf..bc063ebd6 100644 --- a/apps/opencs/model/tools/signalhandler.hpp +++ b/apps/opencs/model/tools/signalhandler.hpp @@ -20,6 +20,10 @@ namespace CSMTools public slots: void updateUserSetting (const QString &name, const QStringList &list); + + private slots: + + void updateExtraCheck (bool extraCheck); }; } From 01eba7b721be21a71f54431721b0da039d7c1074 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 30 May 2015 19:17:42 +1000 Subject: [PATCH 46/54] Extra check for thread affinity. --- apps/opencs/model/tools/signalhandler.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/opencs/model/tools/signalhandler.cpp b/apps/opencs/model/tools/signalhandler.cpp index 64e5ead55..2e94e1f87 100644 --- a/apps/opencs/model/tools/signalhandler.cpp +++ b/apps/opencs/model/tools/signalhandler.cpp @@ -1,6 +1,7 @@ #include "signalhandler.hpp" #include +#include #include "../settings/usersettings.hpp" @@ -23,6 +24,13 @@ void CSMTools::SignalHandler::updateUserSetting (const QString &name, const QStr // should be in the operations thread via an event message queue void CSMTools::SignalHandler::updateExtraCheck (bool extraCheck) { + if (thread()!=QThread::currentThread()) + { + QMetaObject::invokeMethod(this,"updateExtraCheck", Qt::QueuedConnection, Q_ARG(bool, extraCheck)); + return; + } + + // extra safety mExtraCheck = extraCheck; } From 88d5aed62d65e2798bf6f21f8e2b053f9af77fd8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 30 May 2015 12:05:35 +0200 Subject: [PATCH 47/54] removed code that interfered with the default window size on Linux (Fixes #2568) --- apps/opencs/view/doc/view.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 5e3df2739..fca9b2715 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -404,11 +404,7 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to width = std::max(width, 300); height = std::max(height, 300); - // trick to get the window decorations and their sizes - show(); - hide(); - resize (width - (frameGeometry().width() - geometry().width()), - height - (frameGeometry().height() - geometry().height())); + resize (width, height); mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks); From c4aa3d3ee319a884e77f05313f3a4441cd6f98dc Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 30 May 2015 21:12:47 +1000 Subject: [PATCH 48/54] Revert user preference setting checks until a thread safe method is worked out. The OSX namespace issue is retained. --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/model/settings/usersettings.cpp | 7 ---- apps/opencs/model/tools/pathgridcheck.cpp | 22 +---------- apps/opencs/model/tools/pathgridcheck.hpp | 9 +---- apps/opencs/model/tools/signalhandler.cpp | 41 --------------------- apps/opencs/model/tools/signalhandler.hpp | 30 --------------- apps/opencs/model/tools/tools.cpp | 9 +---- apps/opencs/model/tools/tools.hpp | 2 +- 8 files changed, 7 insertions(+), 115 deletions(-) delete mode 100644 apps/opencs/model/tools/signalhandler.cpp delete mode 100644 apps/opencs/model/tools/signalhandler.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 9ed0d6a8b..78b2deb7a 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -34,7 +34,7 @@ opencs_hdrs_noqt (model/world opencs_units (model/tools - tools reportmodel signalhandler + tools reportmodel ) opencs_units_noqt (model/tools diff --git a/apps/opencs/model/settings/usersettings.cpp b/apps/opencs/model/settings/usersettings.cpp index ea75fd6d9..ea002c5ed 100644 --- a/apps/opencs/model/settings/usersettings.cpp +++ b/apps/opencs/model/settings/usersettings.cpp @@ -264,13 +264,6 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() monoFont->setToolTip ("Whether to use monospaced fonts on script edit subview."); } - declareSection ("verifier", "Verifier"); - { - Setting *extraPathgrid = createSetting (Type_CheckBox, "pathgrid-extra-check", "Pathgrid: Extra Check"); - extraPathgrid->setDefaultValue ("false"); - extraPathgrid->setToolTip ("Additional checks for orphaned or duplicated pathgrid points"); - } - { /****************************************************************** * There are three types of values: diff --git a/apps/opencs/model/tools/pathgridcheck.cpp b/apps/opencs/model/tools/pathgridcheck.cpp index 2b11b7066..76edeb573 100644 --- a/apps/opencs/model/tools/pathgridcheck.cpp +++ b/apps/opencs/model/tools/pathgridcheck.cpp @@ -8,24 +8,12 @@ #include "../world/subcellcollection.hpp" #include "../world/pathgrid.hpp" -#include "../settings/usersettings.hpp" - -#include "signalhandler.hpp" - -CSMTools::PathgridCheckStage::PathgridCheckStage (const CSMWorld::SubCellCollection& pathgrids, - CSMTools::SignalHandler *signalHandler) -: mPathgrids (pathgrids), mSigHandler(signalHandler) +CSMTools::PathgridCheckStage::PathgridCheckStage (const CSMWorld::SubCellCollection& pathgrids) +: mPathgrids (pathgrids) {} -CSMTools::PathgridCheckStage::~PathgridCheckStage () -{ - delete mSigHandler; -} - int CSMTools::PathgridCheckStage::setup() { - mExtraCheck = mSigHandler->extraCheck(); - return mPathgrids.getSize(); } @@ -117,9 +105,6 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message } } - if (!mExtraCheck) - continue; - // check duplicate points // FIXME: how to do this efficiently? for (unsigned int j = 0; j < pathgrid.mPoints.size(); ++j) @@ -148,9 +133,6 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message } } - if (!mExtraCheck) - return; - // check pathgrid points that are not connected to anything for (unsigned int i = 0; i < pointList.size(); ++i) { diff --git a/apps/opencs/model/tools/pathgridcheck.hpp b/apps/opencs/model/tools/pathgridcheck.hpp index 8b5dc08a7..f45b5bc93 100644 --- a/apps/opencs/model/tools/pathgridcheck.hpp +++ b/apps/opencs/model/tools/pathgridcheck.hpp @@ -14,8 +14,6 @@ namespace CSMWorld namespace CSMTools { - class SignalHandler; - struct Point { unsigned char mConnectionNum; @@ -25,18 +23,13 @@ namespace CSMTools class PathgridCheckStage : public CSMDoc::Stage { - bool mExtraCheck; - CSMTools::SignalHandler *mSigHandler; - const CSMWorld::SubCellCollection >& mPathgrids; public: PathgridCheckStage (const CSMWorld::SubCellCollection >& pathgrids, CSMTools::SignalHandler *signallHandler); - - ~PathgridCheckStage (); + CSMWorld::IdAccessor >& pathgrids); virtual int setup(); diff --git a/apps/opencs/model/tools/signalhandler.cpp b/apps/opencs/model/tools/signalhandler.cpp deleted file mode 100644 index 2e94e1f87..000000000 --- a/apps/opencs/model/tools/signalhandler.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "signalhandler.hpp" - -#include -#include - -#include "../settings/usersettings.hpp" - -CSMTools::SignalHandler::SignalHandler(bool extraCheck) - : mExtraCheck(extraCheck) -{ - connect (&CSMSettings::UserSettings::instance(), - SIGNAL (userSettingUpdated(const QString &, const QStringList &)), - this, - SLOT (updateUserSetting (const QString &, const QStringList &))); -} - -// called from the main thread -void CSMTools::SignalHandler::updateUserSetting (const QString &name, const QStringList &list) -{ - if (name=="verifier/pathgrid-extra-check" && !list.empty()) - QMetaObject::invokeMethod(this, "updateExtraCheck", Qt::AutoConnection, Q_ARG(bool, list.at(0) == "true")); -} - -// should be in the operations thread via an event message queue -void CSMTools::SignalHandler::updateExtraCheck (bool extraCheck) -{ - if (thread()!=QThread::currentThread()) - { - QMetaObject::invokeMethod(this,"updateExtraCheck", Qt::QueuedConnection, Q_ARG(bool, extraCheck)); - return; - } - - // extra safety - mExtraCheck = extraCheck; -} - -// called from the operations thread -bool CSMTools::SignalHandler::extraCheck () -{ - return mExtraCheck; -} diff --git a/apps/opencs/model/tools/signalhandler.hpp b/apps/opencs/model/tools/signalhandler.hpp deleted file mode 100644 index bc063ebd6..000000000 --- a/apps/opencs/model/tools/signalhandler.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef CSM_TOOLS_SIGNALHANDLER_H -#define CSM_TOOLS_SIGNALHANDLER_H - -#include - -namespace CSMTools -{ - class SignalHandler : public QObject - { - Q_OBJECT - - bool mExtraCheck; - - public: - - SignalHandler (bool extraCheck); - - bool extraCheck (); - - public slots: - - void updateUserSetting (const QString &name, const QStringList &list); - - private slots: - - void updateExtraCheck (bool extraCheck); - }; -} - -#endif diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 45c7fbae3..8d93a9433 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -10,8 +10,6 @@ #include "../world/data.hpp" #include "../world/universalid.hpp" -#include "../settings/usersettings.hpp" - #include "reportmodel.hpp" #include "mandatoryid.hpp" #include "skillcheck.hpp" @@ -29,7 +27,6 @@ #include "startscriptcheck.hpp" #include "searchoperation.hpp" #include "pathgridcheck.hpp" -#include "signalhandler.hpp" CSMDoc::OperationHolder *CSMTools::Tools::get (int type) { @@ -59,8 +56,6 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() 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))); - CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); - std::vector mandatoryIds; // I want C++11, damn it! mandatoryIds.push_back ("Day"); mandatoryIds.push_back ("DaysPassed"); @@ -102,8 +97,7 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() CSMWorld::UniversalId( CSMWorld::UniversalId::Type_Meshes )), mData.getRaces() )); - mVerifierOperation->appendStage (new PathgridCheckStage (mData.getPathgrids(), - new SignalHandler(userSettings.setting ("verifier/pathgrid-extra-check", QString ("false"))=="true"))); + mVerifierOperation->appendStage (new PathgridCheckStage (mData.getPathgrids())); mVerifier.setOperation (mVerifierOperation); } @@ -219,3 +213,4 @@ void CSMTools::Tools::verifierMessage (const CSMWorld::UniversalId& id, const st if (iter!=mActiveReports.end()) mReports[iter->second]->add (id, message, hint); } + diff --git a/apps/opencs/model/tools/tools.hpp b/apps/opencs/model/tools/tools.hpp index dc19cf456..0f9e57044 100644 --- a/apps/opencs/model/tools/tools.hpp +++ b/apps/opencs/model/tools/tools.hpp @@ -64,7 +64,7 @@ namespace CSMTools CSMWorld::UniversalId newSearch(); void runSearch (const CSMWorld::UniversalId& searchId, const Search& search); - + void abortOperation (int type); ///< \attention The operation is not aborted immediately. From 7f2dd21c6642147a0cf7f27df29ab6559cab8938 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 30 May 2015 17:20:49 +0200 Subject: [PATCH 49/54] Fix for StartScript regression (Fixes #2590) --- apps/openmw/mwworld/store.hpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index d6aeeb51e..ba8be733a 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -366,6 +366,19 @@ namespace MWWorld inserted.first->second = scpt; } + template <> + inline void Store::load(ESM::ESMReader &esm, const std::string &id) + { + ESM::StartScript s; + s.load(esm); + s.mId = Misc::StringUtils::toLower(s.mId); + std::pair inserted = mStatic.insert(std::make_pair(s.mId, s)); + if (inserted.second) + mShared.push_back(&inserted.first->second); + else + inserted.first->second = s; + } + template <> class Store : public StoreBase { From 5dc6cdeb99907385c7ca9cf513aa46f251595e8b Mon Sep 17 00:00:00 2001 From: Stanislav Bas Date: Mon, 1 Jun 2015 01:14:11 +0300 Subject: [PATCH 50/54] Scroll to the top of the topic list when dialog is started --- apps/openmw/mwgui/dialogue.cpp | 2 ++ components/widgets/list.cpp | 4 ++++ components/widgets/list.hpp | 2 ++ 3 files changed, 8 insertions(+) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 692cea952..48b9be17d 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -364,6 +364,7 @@ namespace MWGui bool sameActor = (mPtr == actor); mPtr = actor; mTopicsList->setEnabled(true); + mTopicsList->scrollToFirstItem(); setTitle(npcName); clearChoices(); @@ -455,6 +456,7 @@ namespace MWGui mKeywordSearch.seed(Misc::StringUtils::lowerCase(*it), intptr_t(t)); } mTopicsList->adjustSize(); + mTopicsList->scrollToFirstItem(); updateHistory(); } diff --git a/components/widgets/list.cpp b/components/widgets/list.cpp index 5a79de3d1..e1d80f022 100644 --- a/components/widgets/list.cpp +++ b/components/widgets/list.cpp @@ -157,4 +157,8 @@ namespace Gui return mScrollView->findWidget (getName() + "_item_" + name)->castType(); } + void MWList::scrollToFirstItem() + { + mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); + } } diff --git a/components/widgets/list.hpp b/components/widgets/list.hpp index 72c8a733c..1c24af6a4 100644 --- a/components/widgets/list.hpp +++ b/components/widgets/list.hpp @@ -46,6 +46,8 @@ namespace Gui MyGUI::Button* getItemWidget(const std::string& name); ///< get widget for an item name, useful to set up tooltip + void scrollToFirstItem(); + virtual void setPropertyOverride(const std::string& _key, const std::string& _value); protected: From 5350ce59efec0beee86f0637e5f6412631e31b79 Mon Sep 17 00:00:00 2001 From: Stanislav Bas Date: Mon, 1 Jun 2015 22:42:41 +0300 Subject: [PATCH 51/54] MWList doesn't scroll to the very bottom when it's shown for first time --- components/widgets/list.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/widgets/list.cpp b/components/widgets/list.cpp index e1d80f022..535a3cad3 100644 --- a/components/widgets/list.cpp +++ b/components/widgets/list.cpp @@ -48,7 +48,7 @@ namespace Gui const int _scrollBarWidth = 20; // fetch this from skin? const int scrollBarWidth = scrollbarShown ? _scrollBarWidth : 0; const int spacing = 3; - size_t viewPosition = -mScrollView->getViewOffset().top; + int viewPosition = mScrollView->getViewOffset().top; while (mScrollView->getChildCount()) { @@ -99,10 +99,10 @@ namespace Gui if (!scrollbarShown && mItemHeight > mClient->getSize().height) redraw(true); - size_t viewRange = mScrollView->getCanvasSize().height; + int viewRange = mScrollView->getCanvasSize().height; if(viewPosition > viewRange) viewPosition = viewRange; - mScrollView->setViewOffset(MyGUI::IntPoint(0, viewPosition * -1)); + mScrollView->setViewOffset(MyGUI::IntPoint(0, viewPosition)); } void MWList::setPropertyOverride(const std::string &_key, const std::string &_value) From 959fe3eb8773be66219575ae161e625f03536f69 Mon Sep 17 00:00:00 2001 From: Stanislav Bas Date: Mon, 1 Jun 2015 23:49:40 +0300 Subject: [PATCH 52/54] Reset the scrollbar position in the topic list when DialogueWindow is closed --- apps/openmw/mwgui/dialogue.cpp | 5 +++-- components/widgets/list.cpp | 2 +- components/widgets/list.hpp | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 48b9be17d..0cb0475c9 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -291,7 +291,10 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); } else + { MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + mTopicsList->scrollToTop(); + } } void DialogueWindow::onWindowResize(MyGUI::Window* _sender) @@ -364,7 +367,6 @@ namespace MWGui bool sameActor = (mPtr == actor); mPtr = actor; mTopicsList->setEnabled(true); - mTopicsList->scrollToFirstItem(); setTitle(npcName); clearChoices(); @@ -456,7 +458,6 @@ namespace MWGui mKeywordSearch.seed(Misc::StringUtils::lowerCase(*it), intptr_t(t)); } mTopicsList->adjustSize(); - mTopicsList->scrollToFirstItem(); updateHistory(); } diff --git a/components/widgets/list.cpp b/components/widgets/list.cpp index 535a3cad3..db4092b3f 100644 --- a/components/widgets/list.cpp +++ b/components/widgets/list.cpp @@ -157,7 +157,7 @@ namespace Gui return mScrollView->findWidget (getName() + "_item_" + name)->castType(); } - void MWList::scrollToFirstItem() + void MWList::scrollToTop() { mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); } diff --git a/components/widgets/list.hpp b/components/widgets/list.hpp index 1c24af6a4..3efe1ff75 100644 --- a/components/widgets/list.hpp +++ b/components/widgets/list.hpp @@ -46,7 +46,7 @@ namespace Gui MyGUI::Button* getItemWidget(const std::string& name); ///< get widget for an item name, useful to set up tooltip - void scrollToFirstItem(); + void scrollToTop(); virtual void setPropertyOverride(const std::string& _key, const std::string& _value); From 6cb221f8d09cce21ccdba5cae6e3fac0f0e49b1b Mon Sep 17 00:00:00 2001 From: Stanislav Bas Date: Tue, 2 Jun 2015 11:55:09 +0300 Subject: [PATCH 53/54] Some minor changes to MWList --- components/widgets/list.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/widgets/list.cpp b/components/widgets/list.cpp index db4092b3f..df7e7d61d 100644 --- a/components/widgets/list.cpp +++ b/components/widgets/list.cpp @@ -48,7 +48,7 @@ namespace Gui const int _scrollBarWidth = 20; // fetch this from skin? const int scrollBarWidth = scrollbarShown ? _scrollBarWidth : 0; const int spacing = 3; - int viewPosition = mScrollView->getViewOffset().top; + int viewPosition = -mScrollView->getViewOffset().top; while (mScrollView->getChildCount()) { @@ -102,7 +102,7 @@ namespace Gui int viewRange = mScrollView->getCanvasSize().height; if(viewPosition > viewRange) viewPosition = viewRange; - mScrollView->setViewOffset(MyGUI::IntPoint(0, viewPosition)); + mScrollView->setViewOffset(MyGUI::IntPoint(0, -viewPosition)); } void MWList::setPropertyOverride(const std::string &_key, const std::string &_value) From ee2763f2d4286bd63e15a72aa96424763146a5f4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 2 Jun 2015 14:43:38 +0200 Subject: [PATCH 54/54] Allow 5th parameter for PlaceAtMe (numeric value, ignored) (Fixes #2591) --- components/compiler/extensions0.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index c56ee2ffb..a16e653c3 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -531,7 +531,7 @@ namespace Compiler extensions.registerInstruction("placeitemcell","ccffff",opcodePlaceItemCell); extensions.registerInstruction("placeitem","cffff",opcodePlaceItem); extensions.registerInstruction("placeatpc","clfl",opcodePlaceAtPc); - extensions.registerInstruction("placeatme","clfl",opcodePlaceAtMe,opcodePlaceAtMeExplicit); + extensions.registerInstruction("placeatme","clflX",opcodePlaceAtMe,opcodePlaceAtMeExplicit); extensions.registerInstruction("modscale","f",opcodeModScale,opcodeModScaleExplicit); extensions.registerInstruction("rotate","cf",opcodeRotate,opcodeRotateExplicit); extensions.registerInstruction("rotateworld","cf",opcodeRotateWorld,opcodeRotateWorldExplicit);