diff --git a/.travis.yml b/.travis.yml index 1fc85dca3..6b03be114 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,7 +38,7 @@ before_script: - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/before_script.osx.sh; fi script: - cd ./build - - if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then ${ANALYZE}make -j4; fi + - if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then ${ANALYZE}make -j2; fi - if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi after_script: - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi diff --git a/CMakeLists.txt b/CMakeLists.txt index ec69c5560..5f3a0fcc1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,25 +98,29 @@ endif() cmake_minimum_required(VERSION 2.6) # Sound setup -set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE SWRESAMPLE AVRESAMPLE) unset(FFMPEG_LIBRARIES CACHE) -find_package(FFmpeg) + +find_package(FFmpeg REQUIRED) + +set (FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${SWSCALE_LIBRARY}) + if ( NOT AVCODEC_FOUND OR NOT AVFORMAT_FOUND OR NOT AVUTIL_FOUND OR NOT SWSCALE_FOUND ) message(FATAL_ERROR "FFmpeg component required, but not found!") endif() set(SOUND_INPUT_INCLUDES ${FFMPEG_INCLUDE_DIRS}) -set(SOUND_INPUT_LIBRARY ${FFMPEG_LIBRARIES} ${SWSCALE_LIBRARIES}) if( SWRESAMPLE_FOUND ) add_definitions(-DHAVE_LIBSWRESAMPLE) - set(SOUND_INPUT_LIBRARY ${FFMPEG_LIBRARIES} ${SWRESAMPLE_LIBRARIES}) + set (FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${SWRESAMPLE_LIBRARIES}) else() if( AVRESAMPLE_FOUND ) - set(SOUND_INPUT_LIBRARY ${FFMPEG_LIBRARIES} ${AVRESAMPLE_LIBRARIES}) + set (FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${AVRESAMPLE_LIBRARIES}) else() message(FATAL_ERROR "Install either libswresample (FFmpeg) or libavresample (Libav).") endif() endif() +set(SOUND_INPUT_LIBRARY ${FFMPEG_LIBRARIES}) + # TinyXML option(USE_SYSTEM_TINYXML "Use system TinyXML library instead of internal." OFF) if(USE_SYSTEM_TINYXML) @@ -151,6 +155,19 @@ endif() # Dependencies +set(DESIRED_QT_VERSION 4 CACHE STRING "The QT version OpenMW should use (4 or 5)") +message(STATUS "Using Qt${DESIRED_QT_VERSION}") + +if (DESIRED_QT_VERSION MATCHES 4) + find_package(Qt4 REQUIRED COMPONENTS QtCore QtGui QtNetwork) +else() + find_package(Qt5Widgets REQUIRED) + find_package(Qt5Core REQUIRED) + find_package(Qt5Network REQUIRED) + # Instruct CMake to run moc automatically when needed. + #set(CMAKE_AUTOMOC ON) +endif() + # Fix for not visible pthreads functions for linker with glibc 2.15 if (UNIX AND NOT APPLE) find_package (Threads) @@ -171,7 +188,7 @@ if (HAVE_UNORDERED_MAP) endif () -set(BOOST_COMPONENTS system filesystem program_options) +set(BOOST_COMPONENTS system filesystem program_options thread wave) if(WIN32) set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale) endif(WIN32) diff --git a/apps/bsatool/CMakeLists.txt b/apps/bsatool/CMakeLists.txt index 3f1988a70..27baff815 100644 --- a/apps/bsatool/CMakeLists.txt +++ b/apps/bsatool/CMakeLists.txt @@ -9,7 +9,8 @@ add_executable(bsatool ) target_link_libraries(bsatool - ${Boost_LIBRARIES} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} components ) diff --git a/apps/esmtool/CMakeLists.txt b/apps/esmtool/CMakeLists.txt index 1d0026215..1d5e662d8 100644 --- a/apps/esmtool/CMakeLists.txt +++ b/apps/esmtool/CMakeLists.txt @@ -13,7 +13,7 @@ add_executable(esmtool ) target_link_libraries(esmtool - ${Boost_LIBRARIES} + ${Boost_PROGRAM_OPTIONS_LIBRARY} components ) diff --git a/apps/esmtool/labels.cpp b/apps/esmtool/labels.cpp index 88e188df0..883a9e728 100644 --- a/apps/esmtool/labels.cpp +++ b/apps/esmtool/labels.cpp @@ -119,7 +119,7 @@ std::string clothingTypeLabel(int idx) } std::string armorTypeLabel(int idx) -{ +{ if (idx >= 0 && idx <= 10) { static const char *armorTypeLabels[] = { @@ -645,7 +645,7 @@ std::string ruleFunction(int idx) else return "Invalid"; } - + // The "unused flag bits" should probably be defined alongside the // defined bits in the ESM component. The names of the flag bits are // very inconsistent. @@ -653,7 +653,7 @@ std::string ruleFunction(int idx) std::string bodyPartFlags(int flags) { std::string properties = ""; - if (flags == 0) properties += "[None] "; + if (flags == 0) properties += "[None] "; if (flags & ESM::BodyPart::BPF_Female) properties += "Female "; if (flags & ESM::BodyPart::BPF_NotPlayable) properties += "NotPlayable "; int unused = (0xFFFFFFFF ^ @@ -667,7 +667,7 @@ std::string bodyPartFlags(int flags) std::string cellFlags(int flags) { std::string properties = ""; - if (flags == 0) properties += "[None] "; + if (flags == 0) properties += "[None] "; if (flags & ESM::Cell::HasWater) properties += "HasWater "; if (flags & ESM::Cell::Interior) properties += "Interior "; if (flags & ESM::Cell::NoSleep) properties += "NoSleep "; @@ -830,12 +830,12 @@ std::string npcFlags(int flags) std::string properties = ""; if (flags == 0) properties += "[None] "; // Mythicmods and the ESM component differ. Mythicmods says - // 0x8=None and 0x10=AutoCalc, while our code defines 0x8 as - // AutoCalc. The former seems to be correct. All Bethesda - // records have bit 0x8 set. A suspiciously large portion of - // females have autocalc turned off. - if (flags & ESM::NPC::Autocalc) properties += "Unknown "; - if (flags & 0x00000010) properties += "Autocalc "; + // 0x8=None and 0x10=AutoCalc, while our code previously defined + // 0x8 as AutoCalc. The former seems to be correct. All Bethesda + // records have bit 0x8 set. Previously, suspiciously large portion + // of females had autocalc turned off. + if (flags & 0x00000008) properties += "Unknown "; + if (flags & ESM::NPC::Autocalc) properties += "Autocalc "; if (flags & ESM::NPC::Female) properties += "Female "; if (flags & ESM::NPC::Respawn) properties += "Respawn "; if (flags & ESM::NPC::Essential) properties += "Essential "; @@ -847,8 +847,8 @@ std::string npcFlags(int flags) // however the only unknown bit occurs on ALL records, and // relatively few NPCs have this bit set. int unused = (0xFFFFFFFF ^ - (ESM::NPC::Autocalc| - 0x00000010| + (0x00000008| + ESM::NPC::Autocalc| ESM::NPC::Female| ESM::NPC::Respawn| ESM::NPC::Essential| diff --git a/apps/essimporter/CMakeLists.txt b/apps/essimporter/CMakeLists.txt index 72ef364ee..84e31dad9 100644 --- a/apps/essimporter/CMakeLists.txt +++ b/apps/essimporter/CMakeLists.txt @@ -33,7 +33,8 @@ add_executable(openmw-essimporter ) target_link_libraries(openmw-essimporter - ${Boost_LIBRARIES} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} components ) diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index 0de79f8f6..caf96054e 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -57,7 +57,6 @@ set(LAUNCHER_UI source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER}) -find_package(Qt4 REQUIRED) set(QT_USE_QTGUI 1) # Set some platform specific settings @@ -66,12 +65,17 @@ if(WIN32) set(QT_USE_QTMAIN TRUE) endif(WIN32) -QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc) -QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC}) -QT4_WRAP_UI(UI_HDRS ${LAUNCHER_UI}) +if (DESIRED_QT_VERSION MATCHES 4) + include(${QT_USE_FILE}) + QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc) + QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC}) + QT4_WRAP_UI(UI_HDRS ${LAUNCHER_UI}) +else() + QT5_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc) + QT5_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC}) + QT5_WRAP_UI(UI_HDRS ${LAUNCHER_UI}) +endif() - -include(${QT_USE_FILE}) include_directories(${CMAKE_CURRENT_BINARY_DIR}) if(NOT WIN32) include_directories(${LIBUNSHIELD_INCLUDE_DIR}) @@ -88,17 +92,27 @@ add_executable(openmw-launcher ) target_link_libraries(openmw-launcher - ${Boost_LIBRARIES} ${OGRE_LIBRARIES} ${OGRE_STATIC_PLUGINS} ${SDL2_LIBRARY_ONLY} - ${QT_LIBRARIES} components ) +if (DESIRED_QT_VERSION MATCHES 4) + target_link_libraries(openmw-launcher ${QT_QTGUI_LIBRARY} ${QT_QTCORE_LIBRARY}) + if(WIN32) + target_link_libraries(openmw-launcher ${QT_QTMAIN_LIBRARY}) + endif(WIN32) +else() + qt5_use_modules(openmw-launcher Widgets Core) + if (WIN32) + target_link_libraries(Qt5::WinMain) + endif() +endif() if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) target_link_libraries(openmw-launcher gcov) endif() + diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp index ba0686110..32fe8c93a 100644 --- a/apps/launcher/main.cpp +++ b/apps/launcher/main.cpp @@ -54,9 +54,6 @@ int main(int argc, char *argv[]) QDir::setCurrent(dir.absolutePath()); - // Support non-latin characters - QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); - Launcher::MainDialog mainWin; Launcher::FirstRunDialogResult result = mainWin.showFirstRunDialog(); diff --git a/apps/mwiniimporter/CMakeLists.txt b/apps/mwiniimporter/CMakeLists.txt index 753c86fef..4024c0b42 100644 --- a/apps/mwiniimporter/CMakeLists.txt +++ b/apps/mwiniimporter/CMakeLists.txt @@ -14,10 +14,16 @@ add_executable(openmw-iniimporter ) target_link_libraries(openmw-iniimporter - ${Boost_LIBRARIES} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} components ) +if (WIN32) + target_link_libraries(openmw-iniimporter + ${Boost_LOCALE_LIBRARY}) +endif() + if (MINGW) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -municode") endif() diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 37d13223a..d86798af9 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -18,7 +18,7 @@ opencs_hdrs_noqt (model/doc opencs_units (model/world - idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree + idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel ) @@ -41,7 +41,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 pathgridcheck + startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck ) @@ -70,11 +70,12 @@ opencs_units (view/world opencs_units_noqt (view/world subviews enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate scripthighlighter idvalidator dialoguecreator physicssystem idcompletiondelegate + colordelegate ) opencs_units (view/widget scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle scenetoolrun modebutton - scenetooltoggle2 completerpopup + scenetooltoggle2 completerpopup coloreditor colorpickerpopup ) opencs_units (view/render @@ -153,19 +154,16 @@ if(WIN32) set(QT_USE_QTMAIN TRUE) endif(WIN32) -set(BOOST_COMPONENTS system filesystem program_options thread wave) -if(WIN32) - set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale) -endif(WIN32) - -find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) - -find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork REQUIRED) -include(${QT_USE_FILE}) - -qt4_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI}) -qt4_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT}) -qt4_add_resources(OPENCS_RES_SRC ${OPENCS_RES}) +if (DESIRED_QT_VERSION MATCHES 4) + include(${QT_USE_FILE}) + qt4_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI}) + qt4_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT}) + qt4_add_resources(OPENCS_RES_SRC ${OPENCS_RES}) +else() + qt5_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI}) + qt5_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT}) + qt5_add_resources(OPENCS_RES_SRC ${OPENCS_RES}) +endif() # for compiled .ui files include_directories(${CMAKE_CURRENT_BINARY_DIR}) @@ -206,12 +204,36 @@ target_link_libraries(openmw-cs ${OGRE_Overlay_LIBRARIES} ${OGRE_STATIC_PLUGINS} ${SHINY_LIBRARIES} - ${Boost_LIBRARIES} + ${Boost_SYSTEM_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_WAVE_LIBRARY} ${BULLET_LIBRARIES} - ${QT_LIBRARIES} components ) +if (DESIRED_QT_VERSION MATCHES 4) + target_link_libraries(openmw-cs + ${QT_QTGUI_LIBRARY} + ${QT_QTCORE_LIBRARY} + ${QT_QTNETWORK_LIBRARY}) + + if (WIN32) + target_link_libraries(openmw-cs ${QT_QTMAIN_LIBRARY}) + endif() + +else() + qt5_use_modules(openmw-cs Widgets Core Network) + if (WIN32) + target_link_libraries(Qt5::WinMain) + endif() +endif() + +if (WIN32) + target_link_libraries(openmw-cs ${Boost_LOCALE_LIBRARY}) +endif() + + if(APPLE) INSTALL(TARGETS openmw-cs BUNDLE DESTINATION OpenMW COMPONENT BUNDLE) endif() diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 15082c023..0bcaff6a5 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -98,7 +98,7 @@ void CS::Editor::setupDataFiles (const Files::PathContainer& dataDirs) } } -std::pair > CS::Editor::readConfig() +std::pair > CS::Editor::readConfig(bool quiet) { boost::program_options::variables_map variables; boost::program_options::options_description desc("Syntax: openmw-cs \nAllowed options"); @@ -118,7 +118,7 @@ std::pair > CS::Editor::readConfi boost::program_options::notify(variables); - mCfgMgr.readConfiguration(variables, desc); + mCfgMgr.readConfiguration(variables, desc, quiet); mDocumentManager.setEncoding ( ToUTF8::calculateEncoding (variables["encoding"].as())); @@ -198,6 +198,11 @@ void CS::Editor::cancelCreateGame() void CS::Editor::createAddon() { mStartup.hide(); + + mFileDialog.clearFiles(); + std::pair > config = readConfig(/*quiet*/true); + setupDataFiles (config.first); + mFileDialog.showDialog (CSVDoc::ContentAction_New); } @@ -218,6 +223,11 @@ void CS::Editor::cancelFileDialog() void CS::Editor::loadDocument() { mStartup.hide(); + + mFileDialog.clearFiles(); + std::pair > config = readConfig(/*quiet*/true); + setupDataFiles (config.first); + mFileDialog.showDialog (CSVDoc::ContentAction_Edit); } @@ -312,12 +322,12 @@ bool CS::Editor::makeIPCServer() mServer->close(); fullPath.remove(QRegExp("dummy$")); fullPath += mIpcServerName; - if(boost::filesystem::exists(fullPath.toStdString().c_str())) + if(boost::filesystem::exists(fullPath.toUtf8().constData())) { // TODO: compare pid of the current process with that in the file std::cout << "Detected unclean shutdown." << std::endl; // delete the stale file - if(remove(fullPath.toStdString().c_str())) + if(remove(fullPath.toUtf8().constData())) std::cerr << "ERROR removing stale connection file" << std::endl; } } diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index 2ff674934..040ca963f 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -64,7 +64,7 @@ namespace CS void setupDataFiles (const Files::PathContainer& dataDirs); - std::pair > readConfig(); + std::pair > readConfig(bool quiet=false); ///< \return data paths // not implemented diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 18a4f4878..b833b527f 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2272,7 +2272,7 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, if (boost::filesystem::exists (customFiltersPath)) { - destination << std::ifstream(customFiltersPath.c_str(), std::ios::binary).rdbuf(); + destination << std::ifstream(customFiltersPath.string().c_str(), std::ios::binary).rdbuf(); } else { diff --git a/apps/opencs/model/settings/usersettings.cpp b/apps/opencs/model/settings/usersettings.cpp index 868c919f7..a29077df2 100644 --- a/apps/opencs/model/settings/usersettings.cpp +++ b/apps/opencs/model/settings/usersettings.cpp @@ -244,6 +244,47 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() jumpToAdded->setDeclaredValues (jumpValues); } + declareSection ("report-input", "Report Input"); + { + QString none ("None"); + QString edit ("Edit"); + QString remove ("Remove"); + QString editAndRemove ("Edit And Remove"); + + QStringList values; + values << none << edit << remove << editAndRemove; + + QString toolTip = "
    " + "
  • None
  • " + "
  • Edit: Open a table or dialogue suitable for addressing the listed report
  • " + "
  • Remove: Remove the report from the report table
  • " + "
  • Edit and Remove: Open a table or dialogue suitable for addressing the listed report, then remove the report from the report table
  • " + "
"; + + Setting *doubleClick = createSetting (Type_ComboBox, "double", "Double Click"); + doubleClick->setDeclaredValues (values); + doubleClick->setDefaultValue (edit); + doubleClick->setToolTip ("Action on double click in report table:

" + toolTip); + + Setting *shiftDoubleClick = createSetting (Type_ComboBox, "double-s", + "Shift Double Click"); + shiftDoubleClick->setDeclaredValues (values); + shiftDoubleClick->setDefaultValue (remove); + shiftDoubleClick->setToolTip ("Action on shift double click in report table:

" + toolTip); + + Setting *ctrlDoubleClick = createSetting (Type_ComboBox, "double-c", + "Control Double Click"); + ctrlDoubleClick->setDeclaredValues (values); + ctrlDoubleClick->setDefaultValue (editAndRemove); + ctrlDoubleClick->setToolTip ("Action on control double click in report table:

" + toolTip); + + Setting *shiftCtrlDoubleClick = createSetting (Type_ComboBox, "double-sc", + "Shift Control Double Click"); + shiftCtrlDoubleClick->setDeclaredValues (values); + shiftCtrlDoubleClick->setDefaultValue (none); + shiftCtrlDoubleClick->setToolTip ("Action on shift control double click in report table:

" + toolTip); + } + declareSection ("search", "Search & Replace"); { Setting *before = createSetting (Type_SpinBox, "char-before", diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 548fcd36f..6b323547f 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -648,7 +648,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck ( if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated { - if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0008 = autocalculated flag + if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0010 = autocalculated flag { messages.push_back (std::make_pair (id, npc.mId + " mNpdtType or flags mismatch!")); //should not happend? return; diff --git a/apps/opencs/model/tools/soundgencheck.cpp b/apps/opencs/model/tools/soundgencheck.cpp new file mode 100644 index 000000000..bdf89f19d --- /dev/null +++ b/apps/opencs/model/tools/soundgencheck.cpp @@ -0,0 +1,53 @@ +#include "soundgencheck.hpp" + +#include + +#include "../world/refiddata.hpp" +#include "../world/universalid.hpp" + +CSMTools::SoundGenCheckStage::SoundGenCheckStage(const CSMWorld::IdCollection &soundGens, + const CSMWorld::IdCollection &sounds, + const CSMWorld::RefIdCollection &referenceables) + : mSoundGens(soundGens), + mSounds(sounds), + mReferenceables(referenceables) +{} + +int CSMTools::SoundGenCheckStage::setup() +{ + return mSoundGens.getSize(); +} + +void CSMTools::SoundGenCheckStage::perform(int stage, CSMDoc::Messages &messages) +{ + const CSMWorld::Record &record = mSoundGens.getRecord(stage); + if (record.isDeleted()) + { + return; + } + + const ESM::SoundGenerator soundGen = record.get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_SoundGen, soundGen.mId); + + if (!soundGen.mCreature.empty()) + { + CSMWorld::RefIdData::LocalIndex creatureIndex = mReferenceables.getDataSet().searchId(soundGen.mCreature); + if (creatureIndex.first == -1) + { + messages.push_back(std::make_pair(id, "No such creature '" + soundGen.mCreature + "'")); + } + else if (creatureIndex.second != CSMWorld::UniversalId::Type_Creature) + { + messages.push_back(std::make_pair(id, "'" + soundGen.mCreature + "' is not a creature")); + } + } + + if (soundGen.mSound.empty()) + { + messages.push_back(std::make_pair(id, "Sound is not specified")); + } + else if (mSounds.searchId(soundGen.mSound) == -1) + { + messages.push_back(std::make_pair(id, "No such sound '" + soundGen.mSound + "'")); + } +} diff --git a/apps/opencs/model/tools/soundgencheck.hpp b/apps/opencs/model/tools/soundgencheck.hpp new file mode 100644 index 000000000..91b08f979 --- /dev/null +++ b/apps/opencs/model/tools/soundgencheck.hpp @@ -0,0 +1,30 @@ +#ifndef CSM_TOOLS_SOUNDGENCHECK_HPP +#define CSM_TOOLS_SOUNDGENCHECK_HPP + +#include "../world/data.hpp" + +#include "../doc/stage.hpp" + +namespace CSMTools +{ + /// \brief VerifyStage: make sure that sound gen records are internally consistent + class SoundGenCheckStage : public CSMDoc::Stage + { + const CSMWorld::IdCollection &mSoundGens; + const CSMWorld::IdCollection &mSounds; + const CSMWorld::RefIdCollection &mReferenceables; + + public: + SoundGenCheckStage(const CSMWorld::IdCollection &soundGens, + const CSMWorld::IdCollection &sounds, + const CSMWorld::RefIdCollection &referenceables); + + virtual int setup(); + ///< \return number of steps + + virtual void perform(int stage, CSMDoc::Messages &messages); + ///< Messages resulting from this stage will be appended to \a messages. + }; +} + +#endif diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 8d93a9433..7d70abae5 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -27,6 +27,7 @@ #include "startscriptcheck.hpp" #include "searchoperation.hpp" #include "pathgridcheck.hpp" +#include "soundgencheck.hpp" CSMDoc::OperationHolder *CSMTools::Tools::get (int type) { @@ -99,6 +100,10 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() mVerifierOperation->appendStage (new PathgridCheckStage (mData.getPathgrids())); + mVerifierOperation->appendStage (new SoundGenCheckStage (mData.getSoundGens(), + mData.getSounds(), + mData.getReferenceables())); + mVerifier.setOperation (mVerifierOperation); } diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 8758d924b..a8ae5dfa1 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -694,7 +694,7 @@ namespace CSMWorld QColor colour = data.value(); - record2.mMapColor = colour.rgb() & 0xffffff; + record2.mMapColor = (colour.blue() << 16) | (colour.green() << 8) | colour.red(); record.setModified (record2); } diff --git a/apps/opencs/model/world/idtableproxymodel.cpp b/apps/opencs/model/world/idtableproxymodel.cpp index d8932d205..516644713 100644 --- a/apps/opencs/model/world/idtableproxymodel.cpp +++ b/apps/opencs/model/world/idtableproxymodel.cpp @@ -52,9 +52,10 @@ QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, i void CSMWorld::IdTableProxyModel::setFilter (const boost::shared_ptr& filter) { + beginResetModel(); mFilter = filter; updateColumnMap(); - reset(); + endResetModel(); } bool CSMWorld::IdTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const diff --git a/apps/opencs/model/world/infotableproxymodel.cpp b/apps/opencs/model/world/infotableproxymodel.cpp new file mode 100644 index 000000000..7522e492b --- /dev/null +++ b/apps/opencs/model/world/infotableproxymodel.cpp @@ -0,0 +1,58 @@ +#include "infotableproxymodel.hpp" + +#include "idtablebase.hpp" +#include "columns.hpp" + +CSMWorld::InfoTableProxyModel::InfoTableProxyModel(CSMWorld::UniversalId::Type type, QObject *parent) + : IdTableProxyModel(parent), + mType(type), + mSourceModel(NULL) +{ + Q_ASSERT(type == UniversalId::Type_TopicInfos || type == UniversalId::Type_JournalInfos); +} + +void CSMWorld::InfoTableProxyModel::setSourceModel(QAbstractItemModel *sourceModel) +{ + IdTableProxyModel::setSourceModel(sourceModel); + mSourceModel = dynamic_cast(sourceModel); + connect(mSourceModel, + SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + this, + SLOT(modelDataChanged(const QModelIndex &, const QModelIndex &))); + mFirstRowCache.clear(); +} + +bool CSMWorld::InfoTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + QModelIndex first = mSourceModel->index(getFirstInfoRow(left.row()), left.column()); + QModelIndex second = mSourceModel->index(getFirstInfoRow(right.row()), right.column()); + return IdTableProxyModel::lessThan(first, second); +} + +int CSMWorld::InfoTableProxyModel::getFirstInfoRow(int currentRow) const +{ + Columns::ColumnId columnId = Columns::ColumnId_Topic; + if (mType == UniversalId::Type_JournalInfos) + { + columnId = Columns::ColumnId_Journal; + } + + int column = mSourceModel->findColumnIndex(columnId); + QString info = mSourceModel->data(mSourceModel->index(currentRow, column)).toString(); + + if (mFirstRowCache.contains(info)) + { + return mFirstRowCache[info]; + } + + while (--currentRow >= 0 && + mSourceModel->data(mSourceModel->index(currentRow, column)) == info); + + mFirstRowCache[info] = currentRow + 1; + return currentRow + 1; +} + +void CSMWorld::InfoTableProxyModel::modelDataChanged(const QModelIndex &/*topLeft*/, const QModelIndex &/*bottomRight*/) +{ + mFirstRowCache.clear(); +} diff --git a/apps/opencs/model/world/infotableproxymodel.hpp b/apps/opencs/model/world/infotableproxymodel.hpp new file mode 100644 index 000000000..322831626 --- /dev/null +++ b/apps/opencs/model/world/infotableproxymodel.hpp @@ -0,0 +1,38 @@ +#ifndef CSM_WORLD_INFOTABLEPROXYMODEL_HPP +#define CSM_WORLD_INFOTABLEPROXYMODEL_HPP + +#include + +#include "idtableproxymodel.hpp" +#include "universalid.hpp" + +namespace CSMWorld +{ + class IdTableBase; + + class InfoTableProxyModel : public IdTableProxyModel + { + Q_OBJECT + + UniversalId::Type mType; + IdTableBase *mSourceModel; + + mutable QHash mFirstRowCache; + + int getFirstInfoRow(int currentRow) const; + ///< Finds the first row with the same topic (journal entry) as in \a currentRow + + public: + InfoTableProxyModel(UniversalId::Type type, QObject *parent = 0); + + void setSourceModel(QAbstractItemModel *sourceModel); + + protected: + virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const; + + private slots: + void modelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + }; +} + +#endif diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp index d31a9ceaa..4c369ef24 100644 --- a/apps/opencs/model/world/refidadapterimp.cpp +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -37,10 +37,18 @@ void CSMWorld::PotionRefIdAdapter::setData (const RefIdColumn *column, RefIdData Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Potion))); + ESM::Potion potion = record.get(); + if (column==mAutoCalc) - record.get().mData.mAutoCalc = value.toInt(); + potion.mData.mAutoCalc = value.toInt(); else + { InventoryRefIdAdapter::setData (column, data, index, value); + + return; + } + + record.setModified(potion); } @@ -71,12 +79,19 @@ void CSMWorld::ApparatusRefIdAdapter::setData (const RefIdColumn *column, RefIdD Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Apparatus))); + ESM::Apparatus apparatus = record.get(); + if (column==mType) - record.get().mData.mType = value.toInt(); + apparatus.mData.mType = value.toInt(); else if (column==mQuality) - record.get().mData.mQuality = value.toFloat(); + apparatus.mData.mQuality = value.toFloat(); else + { InventoryRefIdAdapter::setData (column, data, index, value); + + return; + } + record.setModified(apparatus); } @@ -114,14 +129,22 @@ void CSMWorld::ArmorRefIdAdapter::setData (const RefIdColumn *column, RefIdData& Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Armor))); + ESM::Armor armor = record.get(); + if (column==mType) - record.get().mData.mType = value.toInt(); + armor.mData.mType = value.toInt(); else if (column==mHealth) - record.get().mData.mHealth = value.toInt(); + armor.mData.mHealth = value.toInt(); else if (column==mArmor) - record.get().mData.mArmor = value.toInt(); + armor.mData.mArmor = value.toInt(); else + { EnchantableRefIdAdapter::setData (column, data, index, value); + + return; + } + + record.setModified(armor); } CSMWorld::BookRefIdAdapter::BookRefIdAdapter (const EnchantableColumns& columns, @@ -151,12 +174,20 @@ void CSMWorld::BookRefIdAdapter::setData (const RefIdColumn *column, RefIdData& Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Book))); + ESM::Book book = record.get(); + if (column==mScroll) - record.get().mData.mIsScroll = value.toInt(); + book.mData.mIsScroll = value.toInt(); else if (column==mSkill) - record.get().mData.mSkillID = value.toInt(); + book.mData.mSkillID = value.toInt(); else + { EnchantableRefIdAdapter::setData (column, data, index, value); + + return; + } + + record.setModified(book); } CSMWorld::ClothingRefIdAdapter::ClothingRefIdAdapter (const EnchantableColumns& columns, @@ -186,10 +217,18 @@ void CSMWorld::ClothingRefIdAdapter::setData (const RefIdColumn *column, RefIdDa Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Clothing))); + ESM::Clothing clothing = record.get(); + if (column==mType) - record.get().mData.mType = value.toInt(); + clothing.mData.mType = value.toInt(); else + { EnchantableRefIdAdapter::setData (column, data, index, value); + + return; + } + + record.setModified(clothing); } CSMWorld::ContainerRefIdAdapter::ContainerRefIdAdapter (const NameColumns& columns, @@ -226,24 +265,32 @@ void CSMWorld::ContainerRefIdAdapter::setData (const RefIdColumn *column, RefIdD Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Container))); + ESM::Container container = record.get(); + if (column==mWeight) - record.get().mWeight = value.toFloat(); + container.mWeight = value.toFloat(); else if (column==mOrganic) { if (value.toInt()) - record.get().mFlags |= ESM::Container::Organic; + container.mFlags |= ESM::Container::Organic; else - record.get().mFlags &= ~ESM::Container::Organic; + container.mFlags &= ~ESM::Container::Organic; } else if (column==mRespawn) { if (value.toInt()) - record.get().mFlags |= ESM::Container::Respawn; + container.mFlags |= ESM::Container::Respawn; else - record.get().mFlags &= ~ESM::Container::Respawn; + container.mFlags &= ~ESM::Container::Respawn; } else + { NameRefIdAdapter::setData (column, data, index, value); + + return; + } + + record.setModified(container); } CSMWorld::CreatureColumns::CreatureColumns (const ActorColumns& actorColumns) @@ -303,20 +350,22 @@ void CSMWorld::CreatureRefIdAdapter::setData (const RefIdColumn *column, RefIdDa Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); + ESM::Creature creature = record.get(); + if (column==mColumns.mType) - record.get().mData.mType = value.toInt(); + creature.mData.mType = value.toInt(); else if (column==mColumns.mSoul) - record.get().mData.mSoul = value.toInt(); + creature.mData.mSoul = value.toInt(); else if (column==mColumns.mScale) - record.get().mScale = value.toFloat(); + creature.mScale = value.toFloat(); else if (column==mColumns.mOriginal) - record.get().mOriginal = value.toString().toUtf8().constData(); + creature.mOriginal = value.toString().toUtf8().constData(); else if (column==mColumns.mCombat) - record.get().mData.mCombat = value.toInt(); + creature.mData.mCombat = value.toInt(); else if (column==mColumns.mMagic) - record.get().mData.mMagic = value.toInt(); + creature.mData.mMagic = value.toInt(); else if (column==mColumns.mStealth) - record.get().mData.mStealth = value.toInt(); + creature.mData.mStealth = value.toInt(); else { std::map::const_iterator iter = @@ -325,13 +374,19 @@ void CSMWorld::CreatureRefIdAdapter::setData (const RefIdColumn *column, RefIdDa if (iter!=mColumns.mFlags.end()) { if (value.toInt()!=0) - record.get().mFlags |= iter->second; + creature.mFlags |= iter->second; else - record.get().mFlags &= ~iter->second; + creature.mFlags &= ~iter->second; } else + { ActorRefIdAdapter::setData (column, data, index, value); + + return; + } } + + record.setModified(creature); } CSMWorld::DoorRefIdAdapter::DoorRefIdAdapter (const NameColumns& columns, @@ -361,12 +416,20 @@ void CSMWorld::DoorRefIdAdapter::setData (const RefIdColumn *column, RefIdData& Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Door))); + ESM::Door door = record.get(); + if (column==mOpenSound) - record.get().mOpenSound = value.toString().toUtf8().constData(); + door.mOpenSound = value.toString().toUtf8().constData(); else if (column==mCloseSound) - record.get().mCloseSound = value.toString().toUtf8().constData(); + door.mCloseSound = value.toString().toUtf8().constData(); else + { NameRefIdAdapter::setData (column, data, index, value); + + return; + } + + record.setModified(door); } CSMWorld::LightColumns::LightColumns (const InventoryColumns& columns) @@ -409,14 +472,16 @@ void CSMWorld::LightRefIdAdapter::setData (const RefIdColumn *column, RefIdData& Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Light))); + ESM::Light light = record.get(); + if (column==mColumns.mTime) - record.get().mData.mTime = value.toInt(); + light.mData.mTime = value.toInt(); else if (column==mColumns.mRadius) - record.get().mData.mRadius = value.toInt(); + light.mData.mRadius = value.toInt(); else if (column==mColumns.mColor) - record.get().mData.mColor = value.toInt(); + light.mData.mColor = value.toInt(); else if (column==mColumns.mSound) - record.get().mSound = value.toString().toUtf8().constData(); + light.mSound = value.toString().toUtf8().constData(); else { std::map::const_iterator iter = @@ -425,13 +490,19 @@ void CSMWorld::LightRefIdAdapter::setData (const RefIdColumn *column, RefIdData& if (iter!=mColumns.mFlags.end()) { if (value.toInt()!=0) - record.get().mData.mFlags |= iter->second; + light.mData.mFlags |= iter->second; else - record.get().mData.mFlags &= ~iter->second; + light.mData.mFlags &= ~iter->second; } else + { InventoryRefIdAdapter::setData (column, data, index, value); + + return; + } } + + record.setModified (light); } CSMWorld::MiscRefIdAdapter::MiscRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *key) @@ -456,10 +527,18 @@ void CSMWorld::MiscRefIdAdapter::setData (const RefIdColumn *column, RefIdData& Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Miscellaneous))); + ESM::Miscellaneous misc = record.get(); + if (column==mKey) - record.get().mData.mIsKey = value.toInt(); + misc.mData.mIsKey = value.toInt(); else + { InventoryRefIdAdapter::setData (column, data, index, value); + + return; + } + + record.setModified(misc); } CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns) @@ -525,16 +604,18 @@ void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& d Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); + ESM::NPC npc = record.get(); + if (column==mColumns.mRace) - record.get().mRace = value.toString().toUtf8().constData(); + npc.mRace = value.toString().toUtf8().constData(); else if (column==mColumns.mClass) - record.get().mClass = value.toString().toUtf8().constData(); + npc.mClass = value.toString().toUtf8().constData(); else if (column==mColumns.mFaction) - record.get().mFaction = value.toString().toUtf8().constData(); + npc.mFaction = value.toString().toUtf8().constData(); else if (column==mColumns.mHair) - record.get().mHair = value.toString().toUtf8().constData(); + npc.mHair = value.toString().toUtf8().constData(); else if (column==mColumns.mHead) - record.get().mHead = value.toString().toUtf8().constData(); + npc.mHead = value.toString().toUtf8().constData(); else { std::map::const_iterator iter = @@ -543,13 +624,23 @@ void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& d if (iter!=mColumns.mFlags.end()) { if (value.toInt()!=0) - record.get().mFlags |= iter->second; + npc.mFlags |= iter->second; else - record.get().mFlags &= ~iter->second; + npc.mFlags &= ~iter->second; + + if (iter->second == ESM::NPC::Autocalc) + npc.mNpdtType = (value.toInt() != 0) ? ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS + : ESM::NPC::NPC_DEFAULT; } else + { ActorRefIdAdapter::setData (column, data, index, value); + + return; + } } + + record.setModified (npc); } CSMWorld::NpcAttributesRefIdAdapter::NpcAttributesRefIdAdapter () diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp index f63426c04..42bde9c81 100644 --- a/apps/opencs/model/world/regionmap.cpp +++ b/apps/opencs/model/world/regionmap.cpp @@ -334,9 +334,9 @@ QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const mColours.find (Misc::StringUtils::lowerCase (cell->second.mRegion)); if (iter!=mColours.end()) - return QBrush ( - QColor (iter->second>>24, (iter->second>>16) & 255, (iter->second>>8) & 255, - iter->second & 255)); + return QBrush (QColor (iter->second & 0xff, + (iter->second >> 8) & 0xff, + (iter->second >> 16) & 0xff)); if (cell->second.mRegion.empty()) return QBrush (Qt::Dense6Pattern); // no region diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index eeec81109..b6f4aaec3 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -33,6 +33,11 @@ void CSVDoc::FileDialog::addFiles(const QString &path) mSelector->addFiles(path); } +void CSVDoc::FileDialog::clearFiles() +{ + mSelector->clearFiles(); +} + QStringList CSVDoc::FileDialog::selectedFilePaths() { QStringList filePaths; @@ -105,7 +110,6 @@ void CSVDoc::FileDialog::buildNewFileView() connect (mFileWidget, SIGNAL (nameChanged(const QString &, bool)), this, SLOT (slotUpdateAcceptButton(const QString &, bool))); - } ui.projectGroupBoxLayout->insertWidget (0, mFileWidget); @@ -139,7 +143,7 @@ void CSVDoc::FileDialog::slotUpdateAcceptButton(int) { QString name = ""; - if (mAction == ContentAction_New) + if (mFileWidget && mAction == ContentAction_New) name = mFileWidget->getName(); slotUpdateAcceptButton (name, true); diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index 3c23a5cb5..648836565 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -45,6 +45,7 @@ namespace CSVDoc void showDialog (ContentAction action); void addFiles (const QString &path); + void clearFiles (); QString filename() const; QStringList selectedFilePaths(); diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 86e70d538..9c1fd8f97 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include @@ -546,6 +546,10 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin } } + if (mScroll) + QObject::connect(mScroll->horizontalScrollBar(), + SIGNAL(rangeChanged(int,int)), this, SLOT(moveScrollBarToEnd(int,int))); + // User setting for limiting the number of sub views per top level view. // Automatically open a new top level view if this number is exceeded // @@ -617,12 +621,6 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin mSubViewWindow.setMinimumWidth(mSubViewWindow.width()+minWidth); move(0, y()); } - - // Make the new subview visible, setFocus() or raise() don't seem to work - // On Ubuntu the scrollbar does not go right to the end, even if using - // mScroll->horizontalScrollBar()->setValue(mScroll->horizontalScrollBar()->maximum()); - if (mSubViewWindow.width() > rect.width()) - mScroll->horizontalScrollBar()->setValue(mSubViewWindow.width()); } mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view); @@ -645,6 +643,17 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin view->useHint (hint); } +void CSVDoc::View::moveScrollBarToEnd(int min, int max) +{ + if (mScroll) + { + mScroll->horizontalScrollBar()->setValue(max); + + QObject::disconnect(mScroll->horizontalScrollBar(), + SIGNAL(rangeChanged(int,int)), this, SLOT(moveScrollBarToEnd(int,int))); + } +} + void CSVDoc::View::newView() { mViewManager.addView (mDocument); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 17b8f8ec9..c785e5176 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -243,6 +243,8 @@ namespace CSVDoc void closeRequest (SubView *subView); void saveWindowState(); + + void moveScrollBarToEnd(int min, int max); }; } diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index e90a93fb0..294efdc1b 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -6,6 +6,8 @@ #include #include +#include +#include #include "../../model/doc/documentmanager.hpp" #include "../../model/doc/document.hpp" @@ -19,15 +21,12 @@ #include "../world/recordstatusdelegate.hpp" #include "../world/idtypedelegate.hpp" #include "../world/idcompletiondelegate.hpp" +#include "../world/colordelegate.hpp" #include "../../model/settings/usersettings.hpp" #include "view.hpp" -#include -#include -#include - void CSVDoc::ViewManager::updateIndices() { std::map > documents; @@ -63,6 +62,9 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) mDelegateFactories->add (CSMWorld::ColumnBase::Display_RefRecordType, new CSVWorld::IdTypeDelegateFactory()); + mDelegateFactories->add (CSMWorld::ColumnBase::Display_Colour, + new CSVWorld::ColorDelegateFactory()); + std::vector idCompletionColumns = CSMWorld::IdCompletionManager::getDisplayTypes(); for (std::vector::const_iterator current = idCompletionColumns.begin(); current != idCompletionColumns.end(); diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index 462b62b7a..383382938 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include "../../model/doc/document.hpp" diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index e88814818..ba0ec8446 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -7,7 +7,12 @@ #include #include -#include +#include +#include +#include +#include +#include +#include #include "../../model/world/universalid.hpp" #include "../../model/world/idtable.hpp" diff --git a/apps/opencs/view/settings/dialog.cpp b/apps/opencs/view/settings/dialog.cpp index e8832e2bc..38eb7bbc7 100644 --- a/apps/opencs/view/settings/dialog.cpp +++ b/apps/opencs/view/settings/dialog.cpp @@ -8,19 +8,12 @@ #include #include #include +#include #include "../../model/settings/usersettings.hpp" #include "page.hpp" -#include - -#include -#include -#include - -#include -#include CSVSettings::Dialog::Dialog(QMainWindow *parent) : SettingWindow (parent), mStackedWidget (0), mDebugMode (false) diff --git a/apps/opencs/view/settings/dialog.hpp b/apps/opencs/view/settings/dialog.hpp index cb85bddb9..e3a3f575a 100644 --- a/apps/opencs/view/settings/dialog.hpp +++ b/apps/opencs/view/settings/dialog.hpp @@ -3,7 +3,6 @@ #include "settingwindow.hpp" #include "resizeablestackedwidget.hpp" -#include class QStackedWidget; class QListWidget; @@ -26,10 +25,6 @@ namespace CSVSettings { explicit Dialog (QMainWindow *parent = 0); - ///Enables setting debug mode. When the dialog opens, a page is created - ///which displays the SettingModel's contents in a Tree view. - void enableDebugMode (bool state, QStandardItemModel *model = 0); - protected: /// Settings are written on close diff --git a/apps/opencs/view/tools/reporttable.cpp b/apps/opencs/view/tools/reporttable.cpp index 7cfe8e4f0..e530e1159 100644 --- a/apps/opencs/view/tools/reporttable.cpp +++ b/apps/opencs/view/tools/reporttable.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include "../../model/tools/reportmodel.hpp" @@ -94,21 +96,35 @@ void CSVTools::ReportTable::mouseDoubleClickEvent (QMouseEvent *event) selectionModel()->select (index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Rows); - switch (modifiers) + std::map::iterator iter = + mDoubleClickActions.find (modifiers); + + if (iter==mDoubleClickActions.end()) { - case 0: + event->accept(); + return; + } + + switch (iter->second) + { + case Action_None: + + event->accept(); + break; + + case Action_Edit: event->accept(); showSelection(); break; - case Qt::ShiftModifier: + case Action_Remove: event->accept(); removeSelection(); break; - case Qt::ControlModifier: + case Action_EditAndRemove: event->accept(); showSelection(); @@ -121,7 +137,11 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document, const CSMWorld::UniversalId& id, bool richTextDescription, QWidget *parent) : CSVWorld::DragRecordTable (document, parent), mModel (document.getReport (id)) { +#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) + horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive); +#else horizontalHeader()->setResizeMode (QHeaderView::Interactive); +#endif horizontalHeader()->setStretchLastSection (true); verticalHeader()->hide(); setSortingEnabled (true); @@ -149,7 +169,11 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document, mReplaceAction = new QAction (tr ("Replace"), this); connect (mReplaceAction, SIGNAL (triggered()), this, SIGNAL (replaceRequest())); - addAction (mReplaceAction); + addAction (mReplaceAction); + + mDoubleClickActions.insert (std::make_pair (Qt::NoModifier, Action_Edit)); + mDoubleClickActions.insert (std::make_pair (Qt::ShiftModifier, Action_Remove)); + mDoubleClickActions.insert (std::make_pair (Qt::ControlModifier, Action_EditAndRemove)); } std::vector CSVTools::ReportTable::getDraggedRecords() const @@ -170,6 +194,35 @@ std::vector CSVTools::ReportTable::getDraggedRecords() co void CSVTools::ReportTable::updateUserSetting (const QString& name, const QStringList& list) { mIdTypeDelegate->updateUserSetting (name, list); + + QString base ("report-input/double"); + if (name.startsWith (base)) + { + QString modifierString = name.mid (base.size()); + Qt::KeyboardModifiers modifiers = 0; + + if (modifierString=="-s") + modifiers = Qt::ShiftModifier; + else if (modifierString=="-c") + modifiers = Qt::ControlModifier; + else if (modifierString=="-sc") + modifiers = Qt::ShiftModifier | Qt::ControlModifier; + + DoubleClickAction action = Action_None; + + QString value = list.at (0); + + if (value=="Edit") + action = Action_Edit; + else if (value=="Remove") + action = Action_Remove; + else if (value=="Edit And Remove") + action = Action_EditAndRemove; + + mDoubleClickActions[modifiers] = action; + + return; + } } std::vector CSVTools::ReportTable::getReplaceIndices (bool selection) const diff --git a/apps/opencs/view/tools/reporttable.hpp b/apps/opencs/view/tools/reporttable.hpp index c4d5b414e..95ab07cbb 100644 --- a/apps/opencs/view/tools/reporttable.hpp +++ b/apps/opencs/view/tools/reporttable.hpp @@ -1,6 +1,8 @@ #ifndef CSV_TOOLS_REPORTTABLE_H #define CSV_TOOLS_REPORTTABLE_H +#include + #include "../world/dragrecordtable.hpp" class QAction; @@ -21,11 +23,20 @@ namespace CSVTools { Q_OBJECT + enum DoubleClickAction + { + Action_None, + Action_Edit, + Action_Remove, + Action_EditAndRemove + }; + CSMTools::ReportModel *mModel; CSVWorld::CommandDelegate *mIdTypeDelegate; QAction *mShowAction; QAction *mRemoveAction; QAction *mReplaceAction; + std::map mDoubleClickActions; private: diff --git a/apps/opencs/view/widget/coloreditor.cpp b/apps/opencs/view/widget/coloreditor.cpp new file mode 100644 index 000000000..7ef1ec7b1 --- /dev/null +++ b/apps/opencs/view/widget/coloreditor.cpp @@ -0,0 +1,113 @@ +#include "coloreditor.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "colorpickerpopup.hpp" + +CSVWidget::ColorEditor::ColorEditor(const QColor &color, QWidget *parent, bool popupOnStart) + : QPushButton(parent), + mColor(color), + mColorPicker(new ColorPickerPopup(this)), + mPopupOnStart(popupOnStart) +{ + setCheckable(true); + connect(this, SIGNAL(clicked()), this, SLOT(showPicker())); + connect(mColorPicker, SIGNAL(hid()), this, SLOT(pickerHid())); + connect(mColorPicker, SIGNAL(colorChanged(const QColor &)), this, SLOT(pickerColorChanged(const QColor &))); +} + +void CSVWidget::ColorEditor::paintEvent(QPaintEvent *event) +{ + QPushButton::paintEvent(event); + + QRect buttonRect = rect(); + QRect coloredRect(buttonRect.x() + qRound(buttonRect.width() / 4.0), + buttonRect.y() + qRound(buttonRect.height() / 4.0), + buttonRect.width() / 2, + buttonRect.height() / 2); + QPainter painter(this); + painter.fillRect(coloredRect, mColor); + painter.setPen(Qt::black); + painter.drawRect(coloredRect); +} + +void CSVWidget::ColorEditor::showEvent(QShowEvent *event) +{ + QPushButton::showEvent(event); + if (isVisible() && mPopupOnStart) + { + setChecked(true); + showPicker(); + mPopupOnStart = false; + } +} + +QColor CSVWidget::ColorEditor::color() const +{ + return mColor; +} + +void CSVWidget::ColorEditor::setColor(const QColor &color) +{ + mColor = color; + update(); +} + +void CSVWidget::ColorEditor::showPicker() +{ + if (isChecked()) + { + mColorPicker->showPicker(calculatePopupPosition(), mColor); + } + else + { + mColorPicker->hide(); + } +} + +void CSVWidget::ColorEditor::pickerHid() +{ + setChecked(false); + emit pickingFinished(); +} + +void CSVWidget::ColorEditor::pickerColorChanged(const QColor &color) +{ + mColor = color; + update(); +} + +QPoint CSVWidget::ColorEditor::calculatePopupPosition() +{ + QRect editorGeometry = geometry(); + QRect popupGeometry = mColorPicker->geometry(); + QRect screenGeometry = QApplication::desktop()->screenGeometry(); + + // Center the popup horizontally relative to the editor + int localPopupX = (editorGeometry.width() - popupGeometry.width()) / 2; + // Popup position need to be specified in global coords + QPoint popupPosition = mapToGlobal(QPoint(localPopupX, editorGeometry.height())); + + // Make sure that the popup isn't out of the screen + if (popupPosition.x() < screenGeometry.left()) + { + popupPosition.setX(screenGeometry.left() + 1); + } + else if (popupPosition.x() + popupGeometry.width() > screenGeometry.right()) + { + popupPosition.setX(screenGeometry.right() - popupGeometry.width() - 1); + } + if (popupPosition.y() + popupGeometry.height() > screenGeometry.bottom()) + { + // Place the popup above the editor + popupPosition.setY(popupPosition.y() - popupGeometry.height() - editorGeometry.height() - 1); + } + + return popupPosition; +} diff --git a/apps/opencs/view/widget/coloreditor.hpp b/apps/opencs/view/widget/coloreditor.hpp new file mode 100644 index 000000000..61232cb13 --- /dev/null +++ b/apps/opencs/view/widget/coloreditor.hpp @@ -0,0 +1,44 @@ +#ifndef CSV_WIDGET_COLOREDITOR_HPP +#define CSV_WIDGET_COLOREDITOR_HPP + +#include + +class QColor; +class QPoint; +class QSize; + +namespace CSVWidget +{ + class ColorPickerPopup; + + class ColorEditor : public QPushButton + { + Q_OBJECT + + QColor mColor; + ColorPickerPopup *mColorPicker; + bool mPopupOnStart; + + QPoint calculatePopupPosition(); + + public: + ColorEditor(const QColor &color, QWidget *parent = 0, bool popupOnStart = false); + + QColor color() const; + void setColor(const QColor &color); + + protected: + virtual void paintEvent(QPaintEvent *event); + virtual void showEvent(QShowEvent *event); + + private slots: + void showPicker(); + void pickerHid(); + void pickerColorChanged(const QColor &color); + + signals: + void pickingFinished(); + }; +} + +#endif diff --git a/apps/opencs/view/widget/colorpickerpopup.cpp b/apps/opencs/view/widget/colorpickerpopup.cpp new file mode 100644 index 000000000..8e71ce39e --- /dev/null +++ b/apps/opencs/view/widget/colorpickerpopup.cpp @@ -0,0 +1,86 @@ +#include "colorpickerpopup.hpp" + +#include +#include +#include +#include +#include +#include +#include + +CSVWidget::ColorPickerPopup::ColorPickerPopup(QWidget *parent) + : QFrame(parent) +{ + setWindowFlags(Qt::Popup); + setFrameStyle(QFrame::Box | QFrame::Plain); + hide(); + + mColorPicker = new QColorDialog(this); + mColorPicker->setWindowFlags(Qt::Widget); + mColorPicker->setOptions(QColorDialog::NoButtons | QColorDialog::DontUseNativeDialog); + mColorPicker->installEventFilter(this); + mColorPicker->open(); + connect(mColorPicker, + SIGNAL(currentColorChanged(const QColor &)), + this, + SIGNAL(colorChanged(const QColor &))); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(mColorPicker); + layout->setAlignment(Qt::AlignTop | Qt::AlignLeft); + layout->setContentsMargins(0, 0, 0, 0); + setLayout(layout); + setFixedSize(mColorPicker->size()); +} + +void CSVWidget::ColorPickerPopup::showPicker(const QPoint &position, const QColor &initialColor) +{ + QRect geometry = this->geometry(); + geometry.moveTo(position); + setGeometry(geometry); + + mColorPicker->setCurrentColor(initialColor); + show(); +} + +void CSVWidget::ColorPickerPopup::mousePressEvent(QMouseEvent *event) +{ + QPushButton *button = qobject_cast(parentWidget()); + if (button != NULL) + { + QStyleOptionButton option; + option.init(button); + QRect buttonRect = option.rect; + buttonRect.moveTo(button->mapToGlobal(buttonRect.topLeft())); + + // If the mouse is pressed above the pop-up parent, + // the pop-up will be hidden and the pressed signal won't be repeated for the parent + if (buttonRect.contains(event->globalPos()) || buttonRect.contains(event->pos())) + { + setAttribute(Qt::WA_NoMouseReplay); + } + } + QFrame::mousePressEvent(event); +} + +void CSVWidget::ColorPickerPopup::hideEvent(QHideEvent *event) +{ + QFrame::hideEvent(event); + emit hid(); +} + +bool CSVWidget::ColorPickerPopup::eventFilter(QObject *object, QEvent *event) +{ + if (object == mColorPicker && event->type() == QEvent::KeyPress) + { + QKeyEvent *keyEvent = static_cast(event); + // Prevent QColorDialog from closing when Escape is pressed. + // Instead, hide the popup. + if (keyEvent->key() == Qt::Key_Escape) + { + hide(); + return true; + } + } + return QFrame::eventFilter(object, event); +} diff --git a/apps/opencs/view/widget/colorpickerpopup.hpp b/apps/opencs/view/widget/colorpickerpopup.hpp new file mode 100644 index 000000000..602bbdb6d --- /dev/null +++ b/apps/opencs/view/widget/colorpickerpopup.hpp @@ -0,0 +1,32 @@ +#ifndef CSVWIDGET_COLORPICKERPOPUP_HPP +#define CSVWIDGET_COLORPICKERPOPUP_HPP + +#include + +class QColorDialog; + +namespace CSVWidget +{ + class ColorPickerPopup : public QFrame + { + Q_OBJECT + + QColorDialog *mColorPicker; + + public: + explicit ColorPickerPopup(QWidget *parent); + + void showPicker(const QPoint &position, const QColor &initialColor); + + protected: + virtual void mousePressEvent(QMouseEvent *event); + virtual void hideEvent(QHideEvent *event); + virtual bool eventFilter(QObject *object, QEvent *event); + + signals: + void hid(); + void colorChanged(const QColor &color); + }; +} + +#endif diff --git a/apps/opencs/view/widget/scenetoolrun.cpp b/apps/opencs/view/widget/scenetoolrun.cpp index 8de334efe..4c9eb676e 100644 --- a/apps/opencs/view/widget/scenetoolrun.cpp +++ b/apps/opencs/view/widget/scenetoolrun.cpp @@ -65,8 +65,13 @@ CSVWidget::SceneToolRun::SceneToolRun (SceneToolbar *parent, const QString& tool mTable->setShowGrid (false); mTable->verticalHeader()->hide(); mTable->horizontalHeader()->hide(); +#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) + mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch); + mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::ResizeToContents); +#else mTable->horizontalHeader()->setResizeMode (0, QHeaderView::Stretch); mTable->horizontalHeader()->setResizeMode (1, QHeaderView::ResizeToContents); +#endif mTable->setSelectionMode (QAbstractItemView::NoSelection); layout->addWidget (mTable); diff --git a/apps/opencs/view/world/colordelegate.cpp b/apps/opencs/view/world/colordelegate.cpp new file mode 100644 index 000000000..1a89fc675 --- /dev/null +++ b/apps/opencs/view/world/colordelegate.cpp @@ -0,0 +1,36 @@ +#include "colordelegate.hpp" + +#include +#include + +#include "../widget/coloreditor.hpp" + +CSVWorld::ColorDelegate::ColorDelegate(CSMWorld::CommandDispatcher *dispatcher, + CSMDoc::Document& document, + QObject *parent) + : CommandDelegate(dispatcher, document, parent) +{} + +void CSVWorld::ColorDelegate::paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QRect coloredRect(option.rect.x() + qRound(option.rect.width() / 4.0), + option.rect.y() + qRound(option.rect.height() / 4.0), + option.rect.width() / 2, + option.rect.height() / 2); + painter->save(); + painter->fillRect(coloredRect, index.data().value()); + painter->setPen(Qt::black); + painter->drawRect(coloredRect); + painter->restore(); +} + +CSVWorld::CommandDelegate *CSVWorld::ColorDelegateFactory::makeDelegate(CSMWorld::CommandDispatcher *dispatcher, + CSMDoc::Document &document, + QObject *parent) const +{ + return new ColorDelegate(dispatcher, document, parent); +} + + diff --git a/apps/opencs/view/world/colordelegate.hpp b/apps/opencs/view/world/colordelegate.hpp new file mode 100644 index 000000000..87051e86d --- /dev/null +++ b/apps/opencs/view/world/colordelegate.hpp @@ -0,0 +1,37 @@ +#ifndef CSV_WORLD_COLORDELEGATE_HPP +#define CSV_WORLD_COLORDELEGATE_HPP + +#include "util.hpp" + +class QRect; + +namespace CSVWidget +{ + class ColorEditButton; +} + +namespace CSVWorld +{ + class ColorDelegate : public CommandDelegate + { + public: + ColorDelegate(CSMWorld::CommandDispatcher *dispatcher, + CSMDoc::Document& document, + QObject *parent); + + virtual void paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const; + }; + + class ColorDelegateFactory : public CommandDelegateFactory + { + public: + virtual CommandDelegate *makeDelegate(CSMWorld::CommandDispatcher *dispatcher, + CSMDoc::Document &document, + QObject *parent) const; + ///< The ownership of the returned CommandDelegate is transferred to the caller. + }; +} + +#endif diff --git a/apps/opencs/view/world/datadisplaydelegate.cpp b/apps/opencs/view/world/datadisplaydelegate.cpp index b9df52bf7..72f45a18c 100644 --- a/apps/opencs/view/world/datadisplaydelegate.cpp +++ b/apps/opencs/view/world/datadisplaydelegate.cpp @@ -12,11 +12,10 @@ CSVWorld::DataDisplayDelegate::DataDisplayDelegate(const ValueList &values, const QString &settingName, QObject *parent) : EnumDelegate (values, dispatcher, document, parent), mDisplayMode (Mode_TextOnly), - mIcons (icons), mIconSize (QSize(16, 16)), mIconLeftOffset(3), + mIcons (icons), mIconSize (QSize(16, 16)), + mHorizontalMargin(QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1), mTextLeftOffset(8), mSettingKey (pageName + '/' + settingName) { - mTextAlignment.setAlignment (Qt::AlignLeft | Qt::AlignVCenter ); - buildPixmaps(); QString value = @@ -45,16 +44,35 @@ void CSVWorld::DataDisplayDelegate::setIconSize(const QSize& size) buildPixmaps(); } -void CSVWorld::DataDisplayDelegate::setIconLeftOffset(int offset) -{ - mIconLeftOffset = offset; -} - void CSVWorld::DataDisplayDelegate::setTextLeftOffset(int offset) { mTextLeftOffset = offset; } +QSize CSVWorld::DataDisplayDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QSize size = EnumDelegate::sizeHint(option, index); + + int valueIndex = getValueIndex(index); + if (valueIndex != -1) + { + if (mDisplayMode == Mode_IconOnly) + { + size.setWidth(mIconSize.width() + 2 * mHorizontalMargin); + } + else if (mDisplayMode == Mode_IconAndText) + { + size.setWidth(size.width() + mIconSize.width() + mTextLeftOffset); + } + + if (mDisplayMode != Mode_TextOnly) + { + size.setHeight(qMax(size.height(), mIconSize.height())); + } + } + return size; +} + void CSVWorld::DataDisplayDelegate::paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { painter->save(); @@ -64,16 +82,11 @@ void CSVWorld::DataDisplayDelegate::paint (QPainter *painter, const QStyleOption EnumDelegate::paint(painter, option, index); else { - unsigned int i = 0; - - for (; i < mValues.size(); ++i) + int valueIndex = getValueIndex(index); + if (valueIndex != -1) { - if (mValues.at(i).first == index.data().toInt()) - break; + paintIcon(painter, option, valueIndex); } - - if (i < mValues.size() ) - paintIcon (painter, option, i); } painter->restore(); @@ -81,24 +94,28 @@ void CSVWorld::DataDisplayDelegate::paint (QPainter *painter, const QStyleOption void CSVWorld::DataDisplayDelegate::paintIcon (QPainter *painter, const QStyleOptionViewItem &option, int index) const { - //function-level statics QRect iconRect = option.rect; QRect textRect = iconRect; - const QString &text = mValues.at(index).second; - - iconRect.setSize (mIconSize); - iconRect.translate(mIconLeftOffset, (option.rect.height() - iconRect.height())/2); - - if (mDisplayMode == Mode_IconAndText ) + iconRect.setLeft(iconRect.left() + mHorizontalMargin); + iconRect.setRight(option.rect.right() - mHorizontalMargin); + if (mDisplayMode == Mode_IconAndText) { - textRect.translate (iconRect.width() + mTextLeftOffset, 0 ); - painter->drawText (textRect, text, mTextAlignment); - } - else - iconRect.translate( (option.rect.width() - iconRect.width()) / 2, 0); + iconRect.setWidth(mIconSize.width()); + textRect.setLeft(iconRect.right() + mTextLeftOffset); + textRect.setRight(option.rect.right() - mHorizontalMargin); - painter->drawPixmap (iconRect, mPixmaps.at(index).second); + QString text = option.fontMetrics.elidedText(mValues.at(index).second, + option.textElideMode, + textRect.width()); + QApplication::style()->drawItemText(painter, + textRect, + Qt::AlignLeft | Qt::AlignVCenter, + option.palette, + true, + text); + } + QApplication::style()->drawItemPixmap(painter, iconRect, Qt::AlignCenter, mPixmaps.at(index).second); } void CSVWorld::DataDisplayDelegate::updateUserSetting (const QString &name, diff --git a/apps/opencs/view/world/datadisplaydelegate.hpp b/apps/opencs/view/world/datadisplaydelegate.hpp index f6e4c2688..e565a3469 100755 --- a/apps/opencs/view/world/datadisplaydelegate.hpp +++ b/apps/opencs/view/world/datadisplaydelegate.hpp @@ -30,9 +30,8 @@ namespace CSVWorld private: std::vector > mPixmaps; - QTextOption mTextAlignment; QSize mIconSize; - int mIconLeftOffset; + int mHorizontalMargin; int mTextLeftOffset; QString mSettingKey; @@ -46,12 +45,11 @@ namespace CSVWorld virtual void paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + /// pass a QSize defining height / width of icon. Default is QSize (16,16). void setIconSize (const QSize& icon); - /// offset the horizontal position of the icon from the left edge of the cell. Default is 3 pixels. - void setIconLeftOffset (int offset); - /// offset the horizontal position of the text from the right edge of the icon. Default is 8 pixels. void setTextLeftOffset (int offset); diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 66e8fcb7a..ab466dfcd 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -33,6 +33,8 @@ #include "../../model/world/commands.hpp" #include "../../model/doc/document.hpp" +#include "../widget/coloreditor.hpp" + #include "recordstatusdelegate.hpp" #include "util.hpp" #include "tablebottombox.hpp" @@ -331,6 +333,10 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase:: { connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited())); } + else if (qobject_cast(editor)) + { + connect(editor, SIGNAL(pickingFinished()), proxy, SLOT(editorDataCommited())); + } else // throw an exception because this is a coding error throw std::logic_error ("Dialogue editor type missing"); @@ -469,8 +475,7 @@ void CSVWorld::EditWidget::remake(int row) mTable->data (mTable->index (row, idColumn)).toString().toUtf8().constData()); NestedTable* table = new NestedTable(mDocument, id, mNestedModels.back(), this); - // FIXME: does not work well when enum delegates are used - //table->resizeColumnsToContents(); + table->resizeColumnsToContents(); if(mTable->index(row, i).data().type() == QVariant::UserType) { diff --git a/apps/opencs/view/world/dragrecordtable.cpp b/apps/opencs/view/world/dragrecordtable.cpp index 7032fee6d..2a1ae1f40 100644 --- a/apps/opencs/view/world/dragrecordtable.cpp +++ b/apps/opencs/view/world/dragrecordtable.cpp @@ -1,4 +1,5 @@ #include +#include #include "../../model/world/tablemimedata.hpp" #include "dragrecordtable.hpp" diff --git a/apps/opencs/view/world/dragrecordtable.hpp b/apps/opencs/view/world/dragrecordtable.hpp index 4996c03ac..48f9e2528 100644 --- a/apps/opencs/view/world/dragrecordtable.hpp +++ b/apps/opencs/view/world/dragrecordtable.hpp @@ -2,7 +2,7 @@ #define CSV_WORLD_DRAGRECORDTABLE_H #include -#include +#include class QWidget; class QAction; diff --git a/apps/opencs/view/world/enumdelegate.cpp b/apps/opencs/view/world/enumdelegate.cpp index 4b76bf9d6..2190a62c6 100644 --- a/apps/opencs/view/world/enumdelegate.cpp +++ b/apps/opencs/view/world/enumdelegate.cpp @@ -10,6 +10,24 @@ #include "../../model/world/commands.hpp" +int CSVWorld::EnumDelegate::getValueIndex(const QModelIndex &index, int role) const +{ + if (index.isValid() && index.data(role).isValid()) + { + int value = index.data(role).toInt(); + + int size = static_cast(mValues.size()); + for (int i = 0; i < size; ++i) + { + if (value == mValues.at(i).first) + { + return i; + } + } + } + return -1; +} + void CSVWorld::EnumDelegate::setModelDataImp (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const { @@ -67,54 +85,59 @@ QWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent, const QStyleOptio void CSVWorld::EnumDelegate::setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay) const { - if (QComboBox *comboBox = dynamic_cast (editor)) + if (QComboBox *comboBox = dynamic_cast(editor)) { - QVariant data = index.data (Qt::EditRole); - - if (tryDisplay && !data.isValid()) + int role = Qt::EditRole; + if (tryDisplay && !index.data(role).isValid()) { - data = index.data (Qt::DisplayRole); - if (!data.isValid()) + role = Qt::DisplayRole; + if (!index.data(role).isValid()) { return; } } - int value = data.toInt(); - - std::size_t size = mValues.size(); - - for (std::size_t i=0; isetCurrentIndex (i); - break; - } + int valueIndex = getValueIndex(index, role); + if (valueIndex != -1) + { + comboBox->setCurrentIndex(valueIndex); + } } } void CSVWorld::EnumDelegate::paint (QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { - if (index.data().isValid()) + int valueIndex = getValueIndex(index); + if (valueIndex != -1) { - QStyleOptionViewItemV4 option2 (option); - - int value = index.data().toInt(); - - for (std::vector >::const_iterator iter (mValues.begin()); - iter!=mValues.end(); ++iter) - if (iter->first==value) - { - option2.text = iter->second; - - QApplication::style()->drawControl (QStyle::CE_ItemViewItem, &option2, painter); - - break; - } + QStyleOptionViewItemV4 itemOption(option); + itemOption.text = mValues.at(valueIndex).second; + QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &itemOption, painter); } } +QSize CSVWorld::EnumDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + int valueIndex = getValueIndex(index); + if (valueIndex != -1) + { + // Calculate the size hint as for a combobox. + // So, the whole text is visible (isn't elided) when the editor is created + QStyleOptionComboBox itemOption; + itemOption.fontMetrics = option.fontMetrics; + itemOption.palette = option.palette; + itemOption.rect = option.rect; + itemOption.state = option.state; + + const QString &valueText = mValues.at(valueIndex).second; + QSize valueSize = QSize(itemOption.fontMetrics.width(valueText), itemOption.fontMetrics.height()); + + itemOption.currentText = valueText; + return QApplication::style()->sizeFromContents(QStyle::CT_ComboBox, &itemOption, valueSize); + } + return option.rect.size(); +} CSVWorld::EnumDelegateFactory::EnumDelegateFactory() {} diff --git a/apps/opencs/view/world/enumdelegate.hpp b/apps/opencs/view/world/enumdelegate.hpp index 82890c791..a31945427 100644 --- a/apps/opencs/view/world/enumdelegate.hpp +++ b/apps/opencs/view/world/enumdelegate.hpp @@ -19,6 +19,8 @@ namespace CSVWorld std::vector > mValues; + int getValueIndex(const QModelIndex &index, int role = Qt::DisplayRole) const; + private: virtual void setModelDataImp (QWidget *editor, QAbstractItemModel *model, @@ -46,6 +48,8 @@ namespace CSVWorld virtual void paint (QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + }; class EnumDelegateFactory : public CommandDelegateFactory diff --git a/apps/opencs/view/world/nestedtable.cpp b/apps/opencs/view/world/nestedtable.cpp index 5c8762020..112873cb9 100644 --- a/apps/opencs/view/world/nestedtable.cpp +++ b/apps/opencs/view/world/nestedtable.cpp @@ -23,7 +23,11 @@ CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document, setSelectionBehavior (QAbstractItemView::SelectRows); setSelectionMode (QAbstractItemView::ExtendedSelection); +#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) + horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive); +#else horizontalHeader()->setResizeMode (QHeaderView::Interactive); +#endif verticalHeader()->hide(); int columns = model->columnCount(QModelIndex()); diff --git a/apps/opencs/view/world/nestedtable.hpp b/apps/opencs/view/world/nestedtable.hpp index b8e91844c..5db977942 100644 --- a/apps/opencs/view/world/nestedtable.hpp +++ b/apps/opencs/view/world/nestedtable.hpp @@ -2,7 +2,7 @@ #define CSV_WORLD_NESTEDTABLE_H #include -#include +#include class QUndoStack; class QAction; diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index cd9b37a64..e81da23e1 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -68,10 +68,10 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) new CSVDoc::SubViewFactoryWithCreator); manager.add (CSMWorld::UniversalId::Type_TopicInfos, - new CSVDoc::SubViewFactoryWithCreator > (false)); + new CSVDoc::SubViewFactoryWithCreator >); manager.add (CSMWorld::UniversalId::Type_JournalInfos, - new CSVDoc::SubViewFactoryWithCreator > (false)); + new CSVDoc::SubViewFactoryWithCreator >); // Subviews for resources tables manager.add (CSMWorld::UniversalId::Type_Meshes, diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 48e42f41a..c879e81a4 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -13,6 +13,7 @@ #include "../../model/world/data.hpp" #include "../../model/world/commands.hpp" +#include "../../model/world/infotableproxymodel.hpp" #include "../../model/world/idtableproxymodel.hpp" #include "../../model/world/idtablebase.hpp" #include "../../model/world/idtable.hpp" @@ -279,13 +280,26 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, mModel = &dynamic_cast (*mDocument.getData().getTableModel (id)); - mProxyModel = new CSMWorld::IdTableProxyModel (this); + bool isInfoTable = id.getType() == CSMWorld::UniversalId::Type_TopicInfos || + id.getType() == CSMWorld::UniversalId::Type_JournalInfos; + if (isInfoTable) + { + mProxyModel = new CSMWorld::InfoTableProxyModel(id.getType(), this); + } + else + { + mProxyModel = new CSMWorld::IdTableProxyModel (this); + } mProxyModel->setSourceModel (mModel); mDispatcher = new CSMWorld::CommandDispatcher (document, id, this); setModel (mProxyModel); +#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) + horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive); +#else horizontalHeader()->setResizeMode (QHeaderView::Interactive); +#endif verticalHeader()->hide(); setSortingEnabled (sorting); setSelectionBehavior (QAbstractItemView::SelectRows); diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 446cb9699..89e304c37 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include "../../model/filter/node.hpp" #include "../../model/world/columnbase.hpp" diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 003f6c8b1..2eac56843 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "../../model/doc/document.hpp" #include "../../model/world/tablemimedata.hpp" diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index e1d165a24..5974987c0 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -17,6 +17,7 @@ #include "../../model/world/commands.hpp" #include "../../model/world/tablemimedata.hpp" #include "../../model/world/commanddispatcher.hpp" +#include "../widget/coloreditor.hpp" #include "dialoguespinbox.hpp" #include "scriptedit.hpp" @@ -123,10 +124,19 @@ void CSVWorld::CommandDelegate::setModelDataImp (QWidget *editor, QAbstractItemM if (!mCommandDispatcher) return; - NastyTableModelHack hack (*model); - QStyledItemDelegate::setModelData (editor, &hack, index); - - QVariant new_ = hack.getData(); + QVariant new_; + // Color columns use a custom editor, so we need explicitly extract a data from it + CSVWidget::ColorEditor *colorEditor = qobject_cast(editor); + if (colorEditor != NULL) + { + new_ = colorEditor->color(); + } + else + { + NastyTableModelHack hack (*model); + QStyledItemDelegate::setModelData (editor, &hack, index); + new_ = hack.getData(); + } if ((model->data (index)!=new_) && (model->flags(index) & Qt::ItemIsEditable)) mCommandDispatcher->executeModify (model, index, new_); @@ -162,6 +172,12 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO { return QStyledItemDelegate::createEditor(parent, option, index); } + // For tables the pop-up of the color editor should appear immediately after the editor creation + // (the third parameter of ColorEditor's constructor) + else if (display == CSMWorld::ColumnBase::Display_Colour) + { + return new CSVWidget::ColorEditor(index.data().value(), parent, true); + } return createEditor (parent, option, index, display); } @@ -184,7 +200,7 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO { case CSMWorld::ColumnBase::Display_Colour: - return new QLineEdit(parent); + return new CSVWidget::ColorEditor(index.data().value(), parent); case CSMWorld::ColumnBase::Display_Integer: { @@ -284,6 +300,14 @@ void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelInde } } + // Color columns use a custom editor, so we need explicitly set a data for it + CSVWidget::ColorEditor *colorEditor = qobject_cast(editor); + if (colorEditor != NULL) + { + colorEditor->setColor(index.data().value()); + return; + } + QByteArray n = editor->metaObject()->userProperty().name(); if (n == "dateTime") { diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index a183d172d..c7c701d20 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -91,17 +91,6 @@ add_openmw_dir (mwbase ) # Main executable -if (ANDROID) - set(BOOST_COMPONENTS system filesystem program_options thread wave atomic) -else () - set(BOOST_COMPONENTS system filesystem program_options thread wave) -endif () - -if(WIN32) - set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale) -endif(WIN32) - -find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) if (NOT ANDROID) add_executable(openmw @@ -126,10 +115,14 @@ target_link_libraries(openmw ${OGRE_LIBRARIES} ${OGRE_STATIC_PLUGINS} ${SHINY_LIBRARIES} - ${Boost_LIBRARIES} ${OPENAL_LIBRARY} ${SOUND_INPUT_LIBRARY} ${BULLET_LIBRARIES} + ${Boost_SYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_WAVE_LIBRARY} ${MYGUI_LIBRARIES} ${SDL2_LIBRARY} ${MYGUI_PLATFORM_LIBRARIES} diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 649f259d9..8f43c6280 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -677,44 +677,32 @@ namespace MWMechanics return false; } + bool AiCombat::doesPathNeedRecalc(ESM::Pathgrid::Point dest, const ESM::Cell *cell) + { + if (!mPathFinder.getPath().empty()) + { + Ogre::Vector3 currPathTarget(PathFinder::MakeOgreVector3(mPathFinder.getPath().back())); + Ogre::Vector3 newPathTarget = PathFinder::MakeOgreVector3(dest); + float dist = (newPathTarget - currPathTarget).length(); + float targetPosThreshold = (cell->isExterior()) ? 300.0f : 100.0f; + return dist > targetPosThreshold; + } + else + { + // necessarily construct a new path + return true; + } + } + void AiCombat::buildNewPath(const MWWorld::Ptr& actor, const MWWorld::Ptr& target) { - Ogre::Vector3 newPathTarget = Ogre::Vector3(target.getRefData().getPosition().pos); - - float dist; - - if(!mPathFinder.getPath().empty()) - { - ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); - Ogre::Vector3 currPathTarget(PathFinder::MakeOgreVector3(lastPt)); - dist = (newPathTarget - currPathTarget).length(); - } - else dist = 1e+38F; // necessarily construct a new path - - float targetPosThreshold = (actor.getCell()->getCell()->isExterior())? 300.0f : 100.0f; + ESM::Pathgrid::Point newPathTarget = PathFinder::MakePathgridPoint(target.getRefData().getPosition()); //construct new path only if target has moved away more than on [targetPosThreshold] - if(dist > targetPosThreshold) + if (doesPathNeedRecalc(newPathTarget, actor.getCell()->getCell())) { - ESM::Position pos = actor.getRefData().getPosition(); - - ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos)); - - ESM::Pathgrid::Point dest(PathFinder::MakePathgridPoint(newPathTarget)); - - if(!mPathFinder.isPathConstructed()) - mPathFinder.buildPath(start, dest, actor.getCell(), false); - else - { - PathFinder newPathFinder; - newPathFinder.buildPath(start, dest, actor.getCell(), false); - - if(!mPathFinder.getPath().empty()) - { - newPathFinder.syncStart(mPathFinder.getPath()); - mPathFinder = newPathFinder; - } - } + ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(actor.getRefData().getPosition())); + mPathFinder.buildSyncedPath(start, newPathTarget, actor.getCell(), false); } } diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 307df3872..8248975b5 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -53,6 +53,9 @@ namespace MWMechanics virtual void writeState(ESM::AiSequence::AiSequence &sequence) const; + protected: + virtual bool doesPathNeedRecalc(ESM::Pathgrid::Point dest, const ESM::Cell *cell); + private: int mTargetActorId; diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 52a975320..216bf7b09 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -30,9 +30,9 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Po ESM::Position pos = actor.getRefData().getPosition(); //position of the actor /// Stops the actor when it gets too close to a unloaded cell + const ESM::Cell *cell = actor.getCell()->getCell(); { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - const ESM::Cell *cell = actor.getCell()->getCell(); Movement &movement = actor.getClass().getMovementSettings(actor); //Ensure pursuer doesn't leave loaded cells @@ -67,8 +67,8 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Po //*********************** if(mTimer > 0.25) { - if(distance(mPrevDest, dest) > 10) { //Only rebuild path if it's moved - mPathFinder.buildPath(start, dest, actor.getCell(), true); //Rebuild path, in case the target has moved + if (doesPathNeedRecalc(dest, cell)) { //Only rebuild path if it's moved + mPathFinder.buildSyncedPath(start, dest, actor.getCell(), true); //Rebuild path, in case the target has moved mPrevDest = dest; } @@ -123,3 +123,8 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Po return false; } + +bool MWMechanics::AiPackage::doesPathNeedRecalc(ESM::Pathgrid::Point dest, const ESM::Cell *cell) +{ + return distance(mPrevDest, dest) > 10; +} \ No newline at end of file diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index 179ae440b..78a2bfd9f 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -14,6 +14,7 @@ namespace MWWorld namespace ESM { + struct Cell; namespace AiSequence { struct AiSequence; @@ -71,6 +72,8 @@ namespace MWMechanics /** \return If the actor has arrived at his destination **/ bool pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Point dest, float duration); + virtual bool doesPathNeedRecalc(ESM::Pathgrid::Point dest, const ESM::Cell *cell); + // TODO: all this does not belong here, move into temporary storage PathFinder mPathFinder; ObstacleCheck mObstacleCheck; diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 2824e2c6c..4f4d4c79f 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -51,64 +51,31 @@ namespace MWMechanics bool AiTravel::execute (const MWWorld::Ptr& actor, AiState& state, float duration) { - MWBase::World *world = MWBase::Environment::get().getWorld(); ESM::Position pos = actor.getRefData().getPosition(); - Movement &movement = actor.getClass().getMovementSettings(actor); - const ESM::Cell *cell = actor.getCell()->getCell(); actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false); - actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); - MWWorld::Ptr player = world->getPlayerPtr(); - if(cell->mData.mX != player.getCell()->getCell()->mData.mX) - { - int sideX = PathFinder::sgn(cell->mData.mX - player.getCell()->getCell()->mData.mX); - //check if actor is near the border of an inactive cell. If so, stop walking. - if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) > - sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) - { - movement.mPosition[1] = 0; - return false; - } - } - if(cell->mData.mY != player.getCell()->getCell()->mData.mY) - { - int sideY = PathFinder::sgn(cell->mData.mY - player.getCell()->getCell()->mData.mY); - //check if actor is near the border of an inactive cell. If so, stop walking. - if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) > - sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) - { - movement.mPosition[1] = 0; - return false; - } - } - if (!isWithinMaxRange(Ogre::Vector3(mX, mY, mZ), Ogre::Vector3(pos.pos))) return false; + if (pathTo(actor, ESM::Pathgrid::Point(static_cast(mX), static_cast(mY), static_cast(mZ)), duration)) + { + actor.getClass().getMovementSettings(actor).mPosition[1] = 0; + return true; + } + return false; + } + + bool AiTravel::doesPathNeedRecalc(ESM::Pathgrid::Point dest, const ESM::Cell *cell) + { bool cellChange = cell->mData.mX != mCellX || cell->mData.mY != mCellY; - if(!mPathFinder.isPathConstructed() || cellChange) + if (!mPathFinder.isPathConstructed() || cellChange) { mCellX = cell->mData.mX; mCellY = cell->mData.mY; - - ESM::Pathgrid::Point dest(static_cast(mX), static_cast(mY), static_cast(mZ)); - - ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos)); - - mPathFinder.buildPath(start, dest, actor.getCell(), true); - } - - if(mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1])) - { - movement.mPosition[1] = 0; return true; } - - zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); - movement.mPosition[1] = 1; - return false; } diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index c2732e3aa..a5a4577e6 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -34,6 +34,9 @@ namespace MWMechanics virtual int getTypeId() const; + protected: + virtual bool doesPathNeedRecalc(ESM::Pathgrid::Point dest, const ESM::Cell *cell); + private: float mX; float mY; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 076636974..1f4133c0a 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -651,6 +651,9 @@ namespace MWMechanics if (mAllowedNodes.empty()) return; + if (actor.getClass().isPureWaterCreature(actor)) + return; + state.moveIn(new AiWanderStorage()); int index = OEngine::Misc::Rng::rollDice(mAllowedNodes.size()); @@ -669,6 +672,9 @@ namespace MWMechanics MWBase::Environment::get().getWorld()->moveObject(actor, static_cast(dest.mX), static_cast(dest.mY), static_cast(dest.mZ)); actor.getClass().adjustPosition(actor, false); + + // may have changed cell + mStoredAvailableNodes = false; } void AiWander::getAllowedNodes(const MWWorld::Ptr& actor, const ESM::Cell* cell) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 5795f818a..fea993e23 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -114,8 +114,7 @@ namespace MWMechanics } PathFinder::PathFinder() - : mIsPathConstructed(false), - mPathgrid(NULL), + : mPathgrid(NULL), mCell(NULL) { } @@ -124,7 +123,6 @@ namespace MWMechanics { if(!mPath.empty()) mPath.clear(); - mIsPathConstructed = false; } /* @@ -180,7 +178,6 @@ namespace MWMechanics static_cast(endPoint.mX), static_cast(endPoint.mY), static_cast(endPoint.mZ))) { mPath.push_back(endPoint); - mIsPathConstructed = true; return; } } @@ -197,7 +194,6 @@ namespace MWMechanics if(!mPathgrid || mPathgrid->mPoints.empty()) { mPath.push_back(endPoint); - mIsPathConstructed = true; return; } @@ -235,7 +231,6 @@ namespace MWMechanics if(startNode == endNode.first) { mPath.push_back(endPoint); - mIsPathConstructed = true; return; } @@ -243,7 +238,6 @@ namespace MWMechanics if(!mPath.empty()) { - mIsPathConstructed = true; // Add the destination (which may be different to the closest // pathgrid point). However only add if endNode was the closest // point to endPoint. @@ -256,14 +250,8 @@ namespace MWMechanics if(endNode.second) mPath.push_back(endPoint); } - else - mIsPathConstructed = false; } - else - mIsPathConstructed = false; } - else - mIsPathConstructed = false; return; } @@ -271,7 +259,7 @@ namespace MWMechanics float PathFinder::getZAngleToNext(float x, float y) const { // This should never happen (programmers should have an if statement checking - // mIsPathConstructed that prevents this call if otherwise). + // isPathConstructed that prevents this call if otherwise). if(mPath.empty()) return 0.; @@ -293,7 +281,6 @@ namespace MWMechanics mPath.pop_front(); if(mPath.empty()) { - mIsPathConstructed = false; return true; } } @@ -301,23 +288,35 @@ namespace MWMechanics return false; } - // used by AiCombat, see header for the rationale - bool PathFinder::syncStart(const std::list &path) + // see header for the rationale + void PathFinder::buildSyncedPath(const ESM::Pathgrid::Point &startPoint, + const ESM::Pathgrid::Point &endPoint, + const MWWorld::CellStore* cell, + bool allowShortcuts) { if (mPath.size() < 2) - return false; //nothing to pop - - std::list::const_iterator oldStart = path.begin(); - std::list::iterator iter = ++mPath.begin(); - - if( (*iter).mX == oldStart->mX - && (*iter).mY == oldStart->mY - && (*iter).mZ == oldStart->mZ) { - mPath.pop_front(); - return true; + // if path has one point, then it's the destination. + // don't need to worry about bad path for this case + buildPath(startPoint, endPoint, cell, allowShortcuts); + } + else + { + const ESM::Pathgrid::Point oldStart(*getPath().begin()); + buildPath(startPoint, endPoint, cell, allowShortcuts); + if (mPath.size() >= 2) + { + // if 2nd waypoint of new path == 1st waypoint of old, + // delete 1st waypoint of new path. + std::list::iterator iter = ++mPath.begin(); + if (iter->mX == oldStart.mX + && iter->mY == oldStart.mY + && iter->mZ == oldStart.mZ) + { + mPath.pop_front(); + } + } } - return false; } } diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index f48de6624..644d79236 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -48,7 +48,7 @@ namespace MWMechanics bool isPathConstructed() const { - return mIsPathConstructed; + return !mPath.empty(); } int getPathSize() const @@ -63,13 +63,13 @@ namespace MWMechanics /** Synchronize new path with old one to avoid visiting 1 waypoint 2 times @note - If the first point is chosen as the nearest one - the situation can occur when the 1st point of the new path is undesirable - (i.e. the 2nd point of new path == the 1st point of old path). - @param path - old path - @return true if such point was found and deleted + BuildPath() takes closest PathGrid point to NPC as first point of path. + This is undesireable if NPC has just passed a Pathgrid point, as this + makes the 2nd point of the new path == the 1st point of old path. + Which results in NPC "running in a circle" back to the just passed waypoint. */ - bool syncStart(const std::list &path); + void buildSyncedPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, + const MWWorld::CellStore* cell, bool allowShortcuts = true); void addPointToPath(ESM::Pathgrid::Point &point) { @@ -96,8 +96,6 @@ namespace MWMechanics private: - bool mIsPathConstructed; - std::list mPath; const ESM::Pathgrid *mPathgrid; diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index ba8be733a..ab09782b1 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -188,7 +188,7 @@ namespace MWWorld const T *ptr = search(id); if (ptr == 0) { std::ostringstream msg; - msg << "Object '" << id << "' not found (const)"; + msg << T::getRecordType() << " '" << id << "' not found"; throw std::runtime_error(msg.str()); } return ptr; @@ -202,7 +202,7 @@ namespace MWWorld if(ptr == 0) { std::ostringstream msg; - msg << "Object starting with '"< #include #include -#include #include #include #include diff --git a/apps/wizard/unshield/unshieldworker.hpp b/apps/wizard/unshield/unshieldworker.hpp index 5ea7b04ae..c1d3cfff1 100644 --- a/apps/wizard/unshield/unshieldworker.hpp +++ b/apps/wizard/unshield/unshieldworker.hpp @@ -24,7 +24,6 @@ namespace Wizard class UnshieldWorker : public QObject { Q_OBJECT - Q_ENUMS(Wizard::Component) public: UnshieldWorker(QObject *parent = 0); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 01de1c28e..1b33b10f6 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -126,32 +126,30 @@ add_component_dir (version 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 - model/modelitem model/esmfile - model/naturalsort model/contentmodel - model/loadordererror - view/combobox view/contentselector - ) - add_component_qt_dir (config - gamesettings - launchersettings - settingsbase - ) - - add_component_qt_dir (process - processinvoker +add_component_qt_dir (contentselector + model/modelitem model/esmfile + model/naturalsort model/contentmodel + model/loadordererror + view/combobox view/contentselector + ) +add_component_qt_dir (config + gamesettings + launchersettings + settingsbase ) - include(${QT_USE_FILE}) +add_component_qt_dir (process + processinvoker +) + +if (DESIRED_QT_VERSION MATCHES 4) + include(${QT_USE_FILE}) QT4_WRAP_UI(ESM_UI_HDR ${ESM_UI}) - QT4_WRAP_CPP(MOC_SRCS ${COMPONENT_MOC_FILES}) -endif(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) + QT4_WRAP_CPP(MOC_SRCS ${COMPONENT_MOC_FILES}) +else() + QT5_WRAP_UI(ESM_UI_HDR ${ESM_UI}) + QT5_WRAP_CPP(MOC_SRCS ${COMPONENT_MOC_FILES}) +endif() if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64" AND NOT APPLE) @@ -164,19 +162,33 @@ include_directories(${BULLET_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) add_library(components STATIC ${COMPONENT_FILES} ${MOC_SRCS} ${ESM_UI_HDR}) target_link_libraries(components - ${Boost_LIBRARIES} + ${Boost_SYSTEM_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_WAVE_LIBRARY} ${OGRE_LIBRARIES} ${OENGINE_LIBRARY} + ${BULLET_LIBRARIES} ) +if (WIN32) + target_link_libraries(components + ${Boost_LOCALE_LIBRARY}) +endif() + +if (DESIRED_QT_VERSION MATCHES 4) + target_link_libraries(components + ${QT_QTCORE_LIBRARY} + ${QT_QTGUI_LIBRARY}) +else() + qt5_use_modules(components Widgets Core) +endif() + if (GIT_CHECKOUT) add_dependencies (components git-version) endif (GIT_CHECKOUT) -if(MINGW) -target_link_libraries(components ${QT_LIBRARIES} ${BULLET_LIBRARIES}) -endif() - if (WIN32) target_link_libraries(components shlwapi) endif() diff --git a/components/config/gamesettings.cpp b/components/config/gamesettings.cpp index 2b3a8ba4b..ca6bfd80d 100644 --- a/components/config/gamesettings.cpp +++ b/components/config/gamesettings.cpp @@ -174,16 +174,30 @@ bool Config::GameSettings::writeFile(QTextStream &stream) return true; } +bool Config::GameSettings::isOrderedLine(const QString& line) const +{ + return line.contains(QRegExp("^\\s*fallback-archive\\s*=")) + || line.contains(QRegExp("^\\s*fallback\\s*=")) + || line.contains(QRegExp("^\\s*data\\s*=")) + || line.contains(QRegExp("^\\s*data-local\\s*=")) + || line.contains(QRegExp("^\\s*resources\\s*=")) + || line.contains(QRegExp("^\\s*content\\s*=")); +} + // Policy: // -// - Always ignore a line beginning with '#' or empty lines +// - Always ignore a line beginning with '#' or empty lines; added above a config +// entry. // // - If a line in file exists with matching key and first part of value (before ',', // '\n', etc) also matches, then replace the line with that of mUserSettings. -// - else remove line (TODO: maybe replace the line with '#' in front instead?) +// - else remove line // // - If there is no corresponding line in file, add at the end // +// - Removed content items are saved as comments if the item had any comments. +// Content items prepended with '##' are considered previously removed. +// bool Config::GameSettings::writeFileWithComments(QFile &file) { QTextStream stream(&file); @@ -203,60 +217,124 @@ bool Config::GameSettings::writeFileWithComments(QFile &file) if (fileCopy.empty()) return writeFile(stream); - // Temp copy of settings to save, but with the keys appended with the first part of the value + // start + // | + // | +----------------------------------------------------------+ + // | | | + // v v | + // skip non-"ordered" lines (remove "ordered" lines) | + // | ^ | + // | | | + // | non-"ordered" line, write saved comments | + // | ^ | + // v | | + // blank or comment line, save in temp buffer <--------+ | + // | | | | + // | +------- comment line ------+ | + // v (special processing '##') | + // "ordered" line | + // | | + // v | + // save in a separate map of comments keyed by "ordered" line | + // | | + // +----------------------------------------------------------+ // - // ATTENTION! // - // A hack to avoid looping through each line, makes use of the fact that fallbacks values - // are comma separated. - QMap userSettingsCopy; QRegExp settingRegex("^([^=]+)\\s*=\\s*([^,]+)(.*)$"); - QString settingLine; - QMap::const_iterator settingsIter = mUserSettings.begin(); - for (; settingsIter != mUserSettings.end(); ++settingsIter) - { - settingLine = settingsIter.key()+"="+settingsIter.value(); - if (settingRegex.indexIn(settingLine) != -1) - { - userSettingsCopy[settingRegex.cap(1)+"="+settingRegex.cap(2)] = - (settingRegex.captureCount() < 3) ? "" : settingRegex.cap(3); - } - } - - QString keyVal; + std::vector comments; + std::vector::iterator commentStart = fileCopy.end(); + std::map > commentsMap; for (std::vector::iterator iter = fileCopy.begin(); iter != fileCopy.end(); ++iter) { - // skip empty or comment lines - if ((*iter).isEmpty() || (*iter).contains(QRegExp("^\\s*#"))) - continue; - - // look for a key in the line - if (settingRegex.indexIn(*iter) == -1 || settingRegex.captureCount() < 2) + if (isOrderedLine(*iter)) { - // no key or first part of value found in line, replace with a null string which - // will be remved later - *iter = QString(); - continue; + // save in a separate map of comments keyed by "ordered" line + if (!comments.empty()) + { + if (settingRegex.indexIn(*iter) != -1) + { + commentsMap[settingRegex.cap(1)+"="+settingRegex.cap(2)] = comments; + comments.clear(); + commentStart = fileCopy.end(); + } + // else do nothing, malformed line + } + + *iter = QString(); // "ordered" lines to be removed later } - - // look for a matching key in user settings - keyVal = settingRegex.cap(1)+"="+settingRegex.cap(2); - QMap::iterator it = userSettingsCopy.find(keyVal); - if (it == userSettingsCopy.end()) + else if ((*iter).isEmpty() || (*iter).contains(QRegExp("^\\s*#"))) { - // no such key+valStart, replace with a null string which will be remved later - *iter = QString(); + // comment line, save in temp buffer + if (comments.empty()) + commentStart = iter; + + // special removed content processing + if ((*iter).contains(QRegExp("^##content\\s*="))) + { + if (!comments.empty()) + { + commentsMap[*iter] = comments; + comments.clear(); + commentStart = fileCopy.end(); + } + } + else + comments.push_back(*iter); + + *iter = QString(); // assume to be deleted later } else { - *iter = QString(it.key()+it.value()); - userSettingsCopy.erase(it); + int index = settingRegex.indexIn(*iter); + + // blank or non-"ordered" line, write saved comments + if (!comments.empty() && index != -1 && settingRegex.captureCount() >= 2 && + mUserSettings.find(settingRegex.cap(1)) != mUserSettings.end()) + { + for (std::vector::const_iterator it = comments.begin(); it != comments.end(); ++it) + { + *commentStart = *it; + ++commentStart; + } + comments.clear(); + commentStart = fileCopy.end(); + } + + // keep blank lines and non-"ordered" lines other than comments + + // look for a key in the line + if (index == -1 || settingRegex.captureCount() < 2) + { + // no key or first part of value found in line, replace with a null string which + // will be remved later + *iter = QString(); + comments.clear(); + commentStart = fileCopy.end(); + continue; + } + + // look for a matching key in user settings + *iter = QString(); // assume no match + QString key = settingRegex.cap(1); + QString keyVal = settingRegex.cap(1)+"="+settingRegex.cap(2); + QMap::const_iterator i = mUserSettings.find(key); + while (i != mUserSettings.end() && i.key() == key) + { + QString settingLine = i.key() + "=" + i.value(); + if (settingRegex.indexIn(settingLine) != -1) + { + if ((settingRegex.cap(1)+"="+settingRegex.cap(2)) == keyVal) + { + *iter = settingLine; + break; + } + } + ++i; + } } } - // write the new config file - QString key; - QString value; + // comments at top of file for (std::vector::iterator iter = fileCopy.begin(); iter != fileCopy.end(); ++iter) { if ((*iter).isNull()) @@ -264,41 +342,82 @@ bool Config::GameSettings::writeFileWithComments(QFile &file) // Below is based on readFile() code, if that changes corresponding change may be // required (for example duplicates may be inserted if the rules don't match) - if ((*iter).isEmpty() || (*iter).contains(QRegExp("^\\s*#"))) + if (/*(*iter).isEmpty() ||*/ (*iter).contains(QRegExp("^\\s*#"))) { stream << *iter << "\n"; continue; } - - if (settingRegex.indexIn(*iter) == -1 || settingRegex.captureCount() < 2) - continue; - - // Quote paths with spaces - key = settingRegex.cap(1); - value = settingRegex.cap(2)+settingRegex.cap(3); - if (key == QLatin1String("data") - || key == QLatin1String("data-local") - || key == QLatin1String("resources")) - { - if (value.contains(QChar(' '))) - { - value.remove(QChar('\"')); // Remove quotes - - stream << key << "=\"" << value << "\"\n"; - continue; - } - } - stream << key << "=" << value << "\n"; } - if (!userSettingsCopy.empty()) + // Iterate in reverse order to preserve insertion order + QString settingLine; + QMapIterator it(mUserSettings); + it.toBack(); + + while (it.hasPrevious()) { - stream << "# new entries" << "\n"; - QMap::const_iterator it = userSettingsCopy.begin(); - for (; it != userSettingsCopy.end(); ++it) + it.previous(); + + // Quote paths with spaces + if ((it.key() == QLatin1String("data") + || it.key() == QLatin1String("data-local") + || it.key() == QLatin1String("resources")) && it.value().contains(QChar(' '))) { - stream << it.key() << it.value() << "\n"; + QString stripped = it.value(); + stripped.remove(QChar('\"')); // Remove quotes + + settingLine = it.key() + "=\"" + stripped + "\""; } + else + settingLine = it.key() + "=" + it.value(); + + if (settingRegex.indexIn(settingLine) != -1) + { + std::map >::iterator i = + commentsMap.find(settingRegex.cap(1)+"="+settingRegex.cap(2)); + + // check if previous removed content item with comments + if (i == commentsMap.end()) + i = commentsMap.find("##"+settingRegex.cap(1)+"="+settingRegex.cap(2)); + + if (i != commentsMap.end()) + { + std::vector cLines = i->second; + for (std::vector::const_iterator ci = cLines.begin(); ci != cLines.end(); ++ci) + stream << *ci << "\n"; + + commentsMap.erase(i); + } + } + + stream << settingLine << "\n"; + } + + // flush any removed settings + if (!commentsMap.empty()) + { + std::map >::const_iterator i = commentsMap.begin(); + for (; i != commentsMap.end(); ++i) + { + if (i->first.contains(QRegExp("^\\s*content\\s*="))) + { + std::vector cLines = i->second; + for (std::vector::const_iterator ci = cLines.begin(); ci != cLines.end(); ++ci) + stream << *ci << "\n"; + + // mark the content line entry for future preocessing + stream << "##" << i->first << "\n"; + + //commentsMap.erase(i); + } + } + } + + // flush any end comments + if (!comments.empty()) + { + for (std::vector::const_iterator ci = comments.begin(); ci != comments.end(); ++ci) + stream << *ci << "\n"; } file.resize(file.pos()); diff --git a/components/config/gamesettings.hpp b/components/config/gamesettings.hpp index 35614113f..992a3e565 100644 --- a/components/config/gamesettings.hpp +++ b/components/config/gamesettings.hpp @@ -83,6 +83,8 @@ namespace Config QString mDataLocal; static const char sContentKey[]; + + bool isOrderedLine(const QString& line) const; }; } #endif // GAMESETTINGS_HPP diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 62f6d9014..769afee37 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -30,17 +30,6 @@ ContentSelectorModel::ContentModel::~ContentModel() void ContentSelectorModel::ContentModel::setEncoding(const QString &encoding) { mEncoding = encoding; - if (encoding == QLatin1String("win1252")) - mCodec = QTextCodec::codecForName("windows-1252"); - - else if (encoding == QLatin1String("win1251")) - mCodec = QTextCodec::codecForName("windows-1251"); - - else if (encoding == QLatin1String("win1250")) - mCodec = QTextCodec::codecForName("windows-1250"); - - else - return; // This should never happen; } int ContentSelectorModel::ContentModel::columnCount(const QModelIndex &parent) const @@ -484,6 +473,13 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path) sortFiles(); } +void ContentSelectorModel::ContentModel::clearFiles() +{ + beginRemoveRows(QModelIndex(), 0, mFiles.count()-1); + mFiles.clear(); + endRemoveRows(); +} + QStringList ContentSelectorModel::ContentModel::gameFiles() const { QStringList gameFiles; diff --git a/components/contentselector/model/contentmodel.hpp b/components/contentselector/model/contentmodel.hpp index 658555852..bc785a276 100644 --- a/components/contentselector/model/contentmodel.hpp +++ b/components/contentselector/model/contentmodel.hpp @@ -44,6 +44,7 @@ namespace ContentSelectorModel bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); void addFiles(const QString &path); + void clearFiles(); QModelIndex indexFromItem(const EsmFile *item) const; const EsmFile *item(const QString &name) const; @@ -81,7 +82,6 @@ namespace ContentSelectorModel ContentFileList mFiles; QHash mCheckStates; QSet mPluginsWithLoadOrderError; - QTextCodec *mCodec; QString mEncoding; QIcon mWarningIcon; diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index 2fae8e74b..78aa20cd2 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -150,6 +150,11 @@ void ContentSelectorView::ContentSelector::addFiles(const QString &path) mContentModel->uncheckAll(); } +void ContentSelectorView::ContentSelector::clearFiles() +{ + mContentModel->clearFiles(); +} + QString ContentSelectorView::ContentSelector::currentFile() const { QModelIndex currentIdx = ui.addonView->currentIndex(); diff --git a/components/contentselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp index e455807c9..4e9fcfb3c 100644 --- a/components/contentselector/view/contentselector.hpp +++ b/components/contentselector/view/contentselector.hpp @@ -29,6 +29,7 @@ namespace ContentSelectorView QString currentFile() const; void addFiles(const QString &path); + void clearFiles(); void setProfileContent (const QStringList &fileList); void clearCheckStates(); diff --git a/components/esm/loadacti.hpp b/components/esm/loadacti.hpp index 88f27de27..d9a55023b 100644 --- a/components/esm/loadacti.hpp +++ b/components/esm/loadacti.hpp @@ -12,6 +12,8 @@ class ESMWriter; struct Activator { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Activator"; } std::string mId, mName, mScript, mModel; diff --git a/components/esm/loadalch.hpp b/components/esm/loadalch.hpp index 141765aa8..b90a7c448 100644 --- a/components/esm/loadalch.hpp +++ b/components/esm/loadalch.hpp @@ -19,6 +19,9 @@ struct Potion { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Potion"; } + struct ALDTstruct { float mWeight; diff --git a/components/esm/loadappa.hpp b/components/esm/loadappa.hpp index adc8e071f..f18b04648 100644 --- a/components/esm/loadappa.hpp +++ b/components/esm/loadappa.hpp @@ -16,6 +16,8 @@ class ESMWriter; struct Apparatus { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Apparatus"; } enum AppaType { diff --git a/components/esm/loadarmo.hpp b/components/esm/loadarmo.hpp index 356dfc1c5..54416fd31 100644 --- a/components/esm/loadarmo.hpp +++ b/components/esm/loadarmo.hpp @@ -66,6 +66,8 @@ struct PartReferenceList struct Armor { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Armor"; } enum Type { diff --git a/components/esm/loadbody.hpp b/components/esm/loadbody.hpp index 5e9869d24..c48c31305 100644 --- a/components/esm/loadbody.hpp +++ b/components/esm/loadbody.hpp @@ -12,6 +12,8 @@ class ESMWriter; struct BodyPart { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "BodyPart"; } enum MeshPart { diff --git a/components/esm/loadbook.hpp b/components/esm/loadbook.hpp index f96fbd709..6211b3e45 100644 --- a/components/esm/loadbook.hpp +++ b/components/esm/loadbook.hpp @@ -15,6 +15,8 @@ class ESMWriter; struct Book { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Book"; } struct BKDTstruct { diff --git a/components/esm/loadbsgn.hpp b/components/esm/loadbsgn.hpp index 9f9435c8f..f91f91c97 100644 --- a/components/esm/loadbsgn.hpp +++ b/components/esm/loadbsgn.hpp @@ -14,6 +14,8 @@ class ESMWriter; struct BirthSign { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "BirthSign"; } std::string mId, mName, mDescription, mTexture; diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index 1aef97d9f..12fb8c068 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -56,6 +56,8 @@ typedef std::list CellRefTracker; struct Cell { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Cell"; } enum Flags { diff --git a/components/esm/loadclas.hpp b/components/esm/loadclas.hpp index 3e489bb58..972b48e88 100644 --- a/components/esm/loadclas.hpp +++ b/components/esm/loadclas.hpp @@ -18,6 +18,8 @@ class ESMWriter; struct Class { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Class"; } enum AutoCalc { diff --git a/components/esm/loadclot.hpp b/components/esm/loadclot.hpp index 50896622a..6945f224a 100644 --- a/components/esm/loadclot.hpp +++ b/components/esm/loadclot.hpp @@ -18,6 +18,8 @@ class ESMWriter; struct Clothing { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Clothing"; } enum Type { diff --git a/components/esm/loadcont.hpp b/components/esm/loadcont.hpp index 76c522d74..ab587f935 100644 --- a/components/esm/loadcont.hpp +++ b/components/esm/loadcont.hpp @@ -36,6 +36,8 @@ struct InventoryList struct Container { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Container"; } enum Flags { diff --git a/components/esm/loadcrea.hpp b/components/esm/loadcrea.hpp index 1b02aa0ab..47e5954a5 100644 --- a/components/esm/loadcrea.hpp +++ b/components/esm/loadcrea.hpp @@ -22,6 +22,8 @@ class ESMWriter; struct Creature { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Creature"; } // Default is 0x48? enum Flags diff --git a/components/esm/loaddial.hpp b/components/esm/loaddial.hpp index d29948c63..58598d353 100644 --- a/components/esm/loaddial.hpp +++ b/components/esm/loaddial.hpp @@ -21,6 +21,8 @@ class ESMWriter; struct Dialogue { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Dialogue"; } enum Type { diff --git a/components/esm/loaddoor.hpp b/components/esm/loaddoor.hpp index ee2b7f7ac..3073f4e9d 100644 --- a/components/esm/loaddoor.hpp +++ b/components/esm/loaddoor.hpp @@ -12,6 +12,8 @@ class ESMWriter; struct Door { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Door"; } std::string mId, mName, mModel, mScript, mOpenSound, mCloseSound; diff --git a/components/esm/loadench.hpp b/components/esm/loadench.hpp index 3b7746812..cfcdd4edc 100644 --- a/components/esm/loadench.hpp +++ b/components/esm/loadench.hpp @@ -18,6 +18,8 @@ class ESMWriter; struct Enchantment { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Enchantment"; } enum Type { diff --git a/components/esm/loadfact.hpp b/components/esm/loadfact.hpp index d31670fe2..8645e23fd 100644 --- a/components/esm/loadfact.hpp +++ b/components/esm/loadfact.hpp @@ -30,6 +30,8 @@ struct RankData struct Faction { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Faction"; } std::string mId, mName; diff --git a/components/esm/loadglob.hpp b/components/esm/loadglob.hpp index 51b2e2dc9..cc5dbbdcf 100644 --- a/components/esm/loadglob.hpp +++ b/components/esm/loadglob.hpp @@ -18,6 +18,8 @@ class ESMWriter; struct Global { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Global"; } std::string mId; Variant mValue; diff --git a/components/esm/loadgmst.hpp b/components/esm/loadgmst.hpp index 398b8047f..d9d9048b6 100644 --- a/components/esm/loadgmst.hpp +++ b/components/esm/loadgmst.hpp @@ -19,6 +19,8 @@ class ESMWriter; struct GameSetting { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "GameSetting"; } std::string mId; diff --git a/components/esm/loadinfo.hpp b/components/esm/loadinfo.hpp index 59b1af31a..54003b0d9 100644 --- a/components/esm/loadinfo.hpp +++ b/components/esm/loadinfo.hpp @@ -21,6 +21,8 @@ class ESMWriter; struct DialInfo { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "DialInfo"; } enum Gender { diff --git a/components/esm/loadingr.hpp b/components/esm/loadingr.hpp index 85f2d5e7d..5846a9780 100644 --- a/components/esm/loadingr.hpp +++ b/components/esm/loadingr.hpp @@ -16,6 +16,8 @@ class ESMWriter; struct Ingredient { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Ingredient"; } struct IRDTstruct { diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index e510616af..7dc5e8adb 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -18,6 +18,8 @@ class ESMWriter; struct Land { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Land"; } Land(); ~Land(); diff --git a/components/esm/loadlevlist.hpp b/components/esm/loadlevlist.hpp index bcea2b234..dc6fcda5e 100644 --- a/components/esm/loadlevlist.hpp +++ b/components/esm/loadlevlist.hpp @@ -46,6 +46,8 @@ struct LevelledListBase struct CreatureLevList: LevelledListBase { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "CreatureLevList"; } enum Flags { @@ -64,6 +66,8 @@ struct CreatureLevList: LevelledListBase struct ItemLevList: LevelledListBase { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "ItemLevList"; } enum Flags { diff --git a/components/esm/loadligh.hpp b/components/esm/loadligh.hpp index 2c83248f8..ed8c36665 100644 --- a/components/esm/loadligh.hpp +++ b/components/esm/loadligh.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Light { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Light"; } enum Flags { diff --git a/components/esm/loadlock.hpp b/components/esm/loadlock.hpp index c44e2b006..0d678cd64 100644 --- a/components/esm/loadlock.hpp +++ b/components/esm/loadlock.hpp @@ -12,6 +12,8 @@ class ESMWriter; struct Lockpick { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Lockpick"; } struct Data { diff --git a/components/esm/loadltex.hpp b/components/esm/loadltex.hpp index 8b45f8211..50a788105 100644 --- a/components/esm/loadltex.hpp +++ b/components/esm/loadltex.hpp @@ -28,6 +28,8 @@ class ESMWriter; struct LandTexture { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "LandTexture"; } std::string mId, mTexture; int mIndex; diff --git a/components/esm/loadmgef.hpp b/components/esm/loadmgef.hpp index e66322832..32b8a85a6 100644 --- a/components/esm/loadmgef.hpp +++ b/components/esm/loadmgef.hpp @@ -13,6 +13,8 @@ class ESMWriter; struct MagicEffect { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "MagicEffect"; } std::string mId; diff --git a/components/esm/loadmisc.hpp b/components/esm/loadmisc.hpp index 576bd18c0..6e0b4e01b 100644 --- a/components/esm/loadmisc.hpp +++ b/components/esm/loadmisc.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Miscellaneous { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Miscellaneous"; } struct MCDTstruct { diff --git a/components/esm/loadnpc.hpp b/components/esm/loadnpc.hpp index b535b91b0..9bda9560e 100644 --- a/components/esm/loadnpc.hpp +++ b/components/esm/loadnpc.hpp @@ -23,6 +23,8 @@ class ESMWriter; struct NPC { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "NPC"; } // Services enum Services @@ -52,12 +54,12 @@ struct NPC enum Flags { - Female = 0x0001, - Essential = 0x0002, - Respawn = 0x0004, - Autocalc = 0x0008, - Skeleton = 0x0400, // Skeleton blood effect (white) - Metal = 0x0800 // Metal blood effect (golden?) + Female = 0x0001, + Essential = 0x0002, + Respawn = 0x0004, + Autocalc = 0x0010, + Skeleton = 0x0400, // Skeleton blood effect (white) + Metal = 0x0800 // Metal blood effect (golden?) }; enum NpcType diff --git a/components/esm/loadpgrd.hpp b/components/esm/loadpgrd.hpp index 256b86cda..f33ccbedf 100644 --- a/components/esm/loadpgrd.hpp +++ b/components/esm/loadpgrd.hpp @@ -16,6 +16,8 @@ class ESMWriter; struct Pathgrid { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Pathgrid"; } struct DATAstruct { diff --git a/components/esm/loadprob.hpp b/components/esm/loadprob.hpp index b89b2ddeb..c737757aa 100644 --- a/components/esm/loadprob.hpp +++ b/components/esm/loadprob.hpp @@ -12,6 +12,8 @@ class ESMWriter; struct Probe { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Probe"; } struct Data { diff --git a/components/esm/loadrace.hpp b/components/esm/loadrace.hpp index 7d5736d9b..553d2e68b 100644 --- a/components/esm/loadrace.hpp +++ b/components/esm/loadrace.hpp @@ -18,6 +18,8 @@ class ESMWriter; struct Race { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Race"; } struct SkillBonus { diff --git a/components/esm/loadregn.hpp b/components/esm/loadregn.hpp index c231b6aa0..1e241fffb 100644 --- a/components/esm/loadregn.hpp +++ b/components/esm/loadregn.hpp @@ -19,6 +19,8 @@ class ESMWriter; struct Region { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Region"; } #pragma pack(push) #pragma pack(1) diff --git a/components/esm/loadrepa.hpp b/components/esm/loadrepa.hpp index 5b404b0e4..e765bc93a 100644 --- a/components/esm/loadrepa.hpp +++ b/components/esm/loadrepa.hpp @@ -12,6 +12,8 @@ class ESMWriter; struct Repair { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Repair"; } struct Data { diff --git a/components/esm/loadscpt.hpp b/components/esm/loadscpt.hpp index deb71de6a..56390f384 100644 --- a/components/esm/loadscpt.hpp +++ b/components/esm/loadscpt.hpp @@ -20,6 +20,8 @@ class Script { public: static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Script"; } struct SCHDstruct { diff --git a/components/esm/loadskil.hpp b/components/esm/loadskil.hpp index 1b9db5bcf..e00184297 100644 --- a/components/esm/loadskil.hpp +++ b/components/esm/loadskil.hpp @@ -20,6 +20,8 @@ class ESMWriter; struct Skill { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Skill"; } std::string mId; diff --git a/components/esm/loadsndg.hpp b/components/esm/loadsndg.hpp index f89a11208..056958f0a 100644 --- a/components/esm/loadsndg.hpp +++ b/components/esm/loadsndg.hpp @@ -16,6 +16,8 @@ class ESMWriter; struct SoundGenerator { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "SoundGenerator"; } enum Type { diff --git a/components/esm/loadsoun.hpp b/components/esm/loadsoun.hpp index 04a0984fd..ff2202ca7 100644 --- a/components/esm/loadsoun.hpp +++ b/components/esm/loadsoun.hpp @@ -17,6 +17,8 @@ struct SOUNstruct struct Sound { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Sound"; } SOUNstruct mData; std::string mId, mSound; diff --git a/components/esm/loadspel.hpp b/components/esm/loadspel.hpp index 4bd2210ec..491da1d17 100644 --- a/components/esm/loadspel.hpp +++ b/components/esm/loadspel.hpp @@ -14,6 +14,8 @@ class ESMWriter; struct Spell { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Spell"; } enum SpellType { diff --git a/components/esm/loadsscr.hpp b/components/esm/loadsscr.hpp index 1420d16c4..dc7ad6a42 100644 --- a/components/esm/loadsscr.hpp +++ b/components/esm/loadsscr.hpp @@ -20,6 +20,8 @@ class ESMWriter; struct StartScript { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "StartScript"; } std::string mData; std::string mId; diff --git a/components/esm/loadstat.hpp b/components/esm/loadstat.hpp index 45b05136a..21a9e66e8 100644 --- a/components/esm/loadstat.hpp +++ b/components/esm/loadstat.hpp @@ -23,6 +23,8 @@ class ESMWriter; struct Static { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Static"; } std::string mId, mModel; diff --git a/components/esm/loadweap.hpp b/components/esm/loadweap.hpp index 14ddb4708..f66e9f3a6 100644 --- a/components/esm/loadweap.hpp +++ b/components/esm/loadweap.hpp @@ -16,6 +16,8 @@ class ESMWriter; struct Weapon { static unsigned int sRecordId; + /// Return a string descriptor for this record type. Currently used for debugging / error logs only. + static std::string getRecordType() { return "Weapon"; } enum Type { diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index e321b5814..dc6f02b60 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -52,8 +52,11 @@ void ConfigurationManager::setupTokensMapping() } void ConfigurationManager::readConfiguration(boost::program_options::variables_map& variables, - boost::program_options::options_description& description) + boost::program_options::options_description& description, bool quiet) { + bool silent = mSilent; + mSilent = quiet; + loadConfig(mFixedPath.getUserConfigPath(), variables, description); boost::program_options::notify(variables); @@ -62,6 +65,7 @@ void ConfigurationManager::readConfiguration(boost::program_options::variables_m loadConfig(mFixedPath.getGlobalConfigPath(), variables, description); boost::program_options::notify(variables); + mSilent = silent; } void ConfigurationManager::processPaths(Files::PathContainer& dataDirs, bool create) diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index 24b08c523..5f0062c2e 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -29,7 +29,7 @@ struct ConfigurationManager virtual ~ConfigurationManager(); void readConfiguration(boost::program_options::variables_map& variables, - boost::program_options::options_description& description); + boost::program_options::options_description& description, bool quiet=false); void processPaths(Files::PathContainer& dataDirs, bool create = false); ///< \param create Try creating the directory, if it does not exist. diff --git a/extern/ogre-ffmpeg-videoplayer/CMakeLists.txt b/extern/ogre-ffmpeg-videoplayer/CMakeLists.txt index 299a57799..edd5575f4 100644 --- a/extern/ogre-ffmpeg-videoplayer/CMakeLists.txt +++ b/extern/ogre-ffmpeg-videoplayer/CMakeLists.txt @@ -11,32 +11,8 @@ set(OGRE_FFMPEG_VIDEOPLAYER_SOURCE_FILES audiofactory.hpp ) -# Find FFMPEG -set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE SWRESAMPLE AVRESAMPLE) -unset(FFMPEG_LIBRARIES CACHE) -find_package(FFmpeg) -if ( NOT AVCODEC_FOUND OR NOT AVFORMAT_FOUND OR NOT AVUTIL_FOUND OR NOT SWSCALE_FOUND ) - message(FATAL_ERROR "FFmpeg component required, but not found!") -endif() -set(VIDEO_FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${SWSCALE_LIBRARIES}) -if( SWRESAMPLE_FOUND ) - add_definitions(-DHAVE_LIBSWRESAMPLE) - set(VIDEO_FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${SWRESAMPLE_LIBRARIES}) -else() - if( AVRESAMPLE_FOUND ) - set(VIDEO_FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${AVRESAMPLE_LIBRARIES}) - else() - message(FATAL_ERROR "Install either libswresample (FFmpeg) or libavresample (Libav).") - endif() -endif() include_directories(${FFMPEG_INCLUDE_DIRS}) - -# Find Boost -set(BOOST_COMPONENTS thread) -find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) -include_directories(${Boost_INCLUDE_DIRS}) - add_library(${OGRE_FFMPEG_VIDEOPLAYER_LIBRARY} STATIC ${OGRE_FFMPEG_VIDEOPLAYER_SOURCE_FILES}) -target_link_libraries(${OGRE_FFMPEG_VIDEOPLAYER_LIBRARY} ${VIDEO_FFMPEG_LIBRARIES} ${Boost_LIBRARIES}) +target_link_libraries(${OGRE_FFMPEG_VIDEOPLAYER_LIBRARY} ${FFMPEG_LIBRARIES} ${Boost_THREAD_LIBRARY}) link_directories(${CMAKE_CURRENT_BINARY_DIR})