1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-29 06:15:32 +00:00

Merge branch 'master' into cc9cii

Conflicts:
	apps/opencs/view/doc/view.hpp
This commit is contained in:
cc9cii 2015-06-19 07:49:18 +10:00
commit 3b27e643b8
122 changed files with 1581 additions and 500 deletions

View file

@ -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

View file

@ -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)

View file

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

View file

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

View file

@ -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|

View file

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

View file

@ -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()

View file

@ -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();

View file

@ -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()

View file

@ -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()

View file

@ -98,7 +98,7 @@ void CS::Editor::setupDataFiles (const Files::PathContainer& dataDirs)
}
}
std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfig()
std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfig(bool quiet)
{
boost::program_options::variables_map variables;
boost::program_options::options_description desc("Syntax: openmw-cs <options>\nAllowed options");
@ -118,7 +118,7 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
boost::program_options::notify(variables);
mCfgMgr.readConfiguration(variables, desc);
mCfgMgr.readConfiguration(variables, desc, quiet);
mDocumentManager.setEncoding (
ToUTF8::calculateEncoding (variables["encoding"].as<std::string>()));
@ -198,6 +198,11 @@ void CS::Editor::cancelCreateGame()
void CS::Editor::createAddon()
{
mStartup.hide();
mFileDialog.clearFiles();
std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig(/*quiet*/true);
setupDataFiles (config.first);
mFileDialog.showDialog (CSVDoc::ContentAction_New);
}
@ -218,6 +223,11 @@ void CS::Editor::cancelFileDialog()
void CS::Editor::loadDocument()
{
mStartup.hide();
mFileDialog.clearFiles();
std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig(/*quiet*/true);
setupDataFiles (config.first);
mFileDialog.showDialog (CSVDoc::ContentAction_Edit);
}
@ -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;
}
}

View file

@ -64,7 +64,7 @@ namespace CS
void setupDataFiles (const Files::PathContainer& dataDirs);
std::pair<Files::PathContainer, std::vector<std::string> > readConfig();
std::pair<Files::PathContainer, std::vector<std::string> > readConfig(bool quiet=false);
///< \return data paths
// not implemented

View file

@ -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
{

View file

@ -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 = "<ul>"
"<li>None</li>"
"<li>Edit: Open a table or dialogue suitable for addressing the listed report</li>"
"<li>Remove: Remove the report from the report table</li>"
"<li>Edit and Remove: Open a table or dialogue suitable for addressing the listed report, then remove the report from the report table</li>"
"</ul>";
Setting *doubleClick = createSetting (Type_ComboBox, "double", "Double Click");
doubleClick->setDeclaredValues (values);
doubleClick->setDefaultValue (edit);
doubleClick->setToolTip ("Action on double click in report table:<p>" + toolTip);
Setting *shiftDoubleClick = createSetting (Type_ComboBox, "double-s",
"Shift Double Click");
shiftDoubleClick->setDeclaredValues (values);
shiftDoubleClick->setDefaultValue (remove);
shiftDoubleClick->setToolTip ("Action on shift double click in report table:<p>" + toolTip);
Setting *ctrlDoubleClick = createSetting (Type_ComboBox, "double-c",
"Control Double Click");
ctrlDoubleClick->setDeclaredValues (values);
ctrlDoubleClick->setDefaultValue (editAndRemove);
ctrlDoubleClick->setToolTip ("Action on control double click in report table:<p>" + toolTip);
Setting *shiftCtrlDoubleClick = createSetting (Type_ComboBox, "double-sc",
"Shift Control Double Click");
shiftCtrlDoubleClick->setDeclaredValues (values);
shiftCtrlDoubleClick->setDefaultValue (none);
shiftCtrlDoubleClick->setToolTip ("Action on shift control double click in report table:<p>" + toolTip);
}
declareSection ("search", "Search & Replace");
{
Setting *before = createSetting (Type_SpinBox, "char-before",

View file

@ -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;

View file

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

View file

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

View file

@ -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);
}

View file

@ -694,7 +694,7 @@ namespace CSMWorld
QColor colour = data.value<QColor>();
record2.mMapColor = colour.rgb() & 0xffffff;
record2.mMapColor = (colour.blue() << 16) | (colour.green() << 8) | colour.red();
record.setModified (record2);
}

View file

@ -52,9 +52,10 @@ QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, i
void CSMWorld::IdTableProxyModel::setFilter (const boost::shared_ptr<CSMFilter::Node>& filter)
{
beginResetModel();
mFilter = filter;
updateColumnMap();
reset();
endResetModel();
}
bool CSMWorld::IdTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const

View file

@ -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<IdTableBase *>(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();
}

View file

@ -0,0 +1,38 @@
#ifndef CSM_WORLD_INFOTABLEPROXYMODEL_HPP
#define CSM_WORLD_INFOTABLEPROXYMODEL_HPP
#include <QHash>
#include "idtableproxymodel.hpp"
#include "universalid.hpp"
namespace CSMWorld
{
class IdTableBase;
class InfoTableProxyModel : public IdTableProxyModel
{
Q_OBJECT
UniversalId::Type mType;
IdTableBase *mSourceModel;
mutable QHash<QString, int> 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

View file

@ -37,10 +37,18 @@ void CSMWorld::PotionRefIdAdapter::setData (const RefIdColumn *column, RefIdData
Record<ESM::Potion>& record = static_cast<Record<ESM::Potion>&> (
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<ESM::Potion>::setData (column, data, index, value);
return;
}
record.setModified(potion);
}
@ -71,12 +79,19 @@ void CSMWorld::ApparatusRefIdAdapter::setData (const RefIdColumn *column, RefIdD
Record<ESM::Apparatus>& record = static_cast<Record<ESM::Apparatus>&> (
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<ESM::Apparatus>::setData (column, data, index, value);
return;
}
record.setModified(apparatus);
}
@ -114,14 +129,22 @@ void CSMWorld::ArmorRefIdAdapter::setData (const RefIdColumn *column, RefIdData&
Record<ESM::Armor>& record = static_cast<Record<ESM::Armor>&> (
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<ESM::Armor>::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<ESM::Book>& record = static_cast<Record<ESM::Book>&> (
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<ESM::Book>::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<ESM::Clothing>& record = static_cast<Record<ESM::Clothing>&> (
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<ESM::Clothing>::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<ESM::Container>& record = static_cast<Record<ESM::Container>&> (
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<ESM::Container>::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<ESM::Creature>& record = static_cast<Record<ESM::Creature>&> (
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 RefIdColumn *, unsigned int>::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<ESM::Creature>::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<ESM::Door>& record = static_cast<Record<ESM::Door>&> (
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<ESM::Door>::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<ESM::Light>& record = static_cast<Record<ESM::Light>&> (
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 RefIdColumn *, unsigned int>::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<ESM::Light>::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<ESM::Miscellaneous>& record = static_cast<Record<ESM::Miscellaneous>&> (
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<ESM::Miscellaneous>::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<ESM::NPC>& record = static_cast<Record<ESM::NPC>&> (
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 RefIdColumn *, unsigned int>::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<ESM::NPC>::setData (column, data, index, value);
return;
}
}
record.setModified (npc);
}
CSMWorld::NpcAttributesRefIdAdapter::NpcAttributesRefIdAdapter ()

View file

@ -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

View file

@ -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);

View file

@ -45,6 +45,7 @@ namespace CSVDoc
void showDialog (ContentAction action);
void addFiles (const QString &path);
void clearFiles ();
QString filename() const;
QStringList selectedFilePaths();

View file

@ -7,7 +7,7 @@
#include <QMenuBar>
#include <QMdiArea>
#include <QDockWidget>
#include <QtGui/QApplication>
#include <QApplication>
#include <QDesktopWidget>
#include <QScrollArea>
#include <QHBoxLayout>
@ -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);

View file

@ -243,6 +243,8 @@ namespace CSVDoc
void closeRequest (SubView *subView);
void saveWindowState();
void moveScrollBarToEnd(int min, int max);
};
}

View file

@ -6,6 +6,8 @@
#include <QApplication>
#include <QDesktopWidget>
#include <QMessageBox>
#include <QPushButton>
#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 <QMessageBox>
#include <QPushButton>
#include <QtGui/QApplication>
void CSVDoc::ViewManager::updateIndices()
{
std::map<CSMDoc::Document *, std::pair<int, int> > 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<CSMWorld::ColumnBase::Display> idCompletionColumns = CSMWorld::IdCompletionManager::getDisplayTypes();
for (std::vector<CSMWorld::ColumnBase::Display>::const_iterator current = idCompletionColumns.begin();
current != idCompletionColumns.end();

View file

@ -6,7 +6,7 @@
#include <OgreColourValue.h>
#include <OgreCamera.h>
#include <QtGui/qevent.h>
#include <QEvent>
#include "../../model/doc/document.hpp"

View file

@ -7,7 +7,12 @@
#include <OgreSceneManager.h>
#include <OgreEntity.h>
#include <QtGui/qevent.h>
#include <QEvent>
#include <QDragEnterEvent>
#include <QDragMoveEvent>
#include <QDropEvent>
#include <QMouseEvent>
#include <QKeyEvent>
#include "../../model/world/universalid.hpp"
#include "../../model/world/idtable.hpp"

View file

@ -8,19 +8,12 @@
#include <QStackedWidget>
#include <QtGui>
#include <QSplitter>
#include <QDesktopWidget>
#include "../../model/settings/usersettings.hpp"
#include "page.hpp"
#include <QApplication>
#include <QTreeView>
#include <QListView>
#include <QTableView>
#include <QStandardItemModel>
#include <QStandardItem>
CSVSettings::Dialog::Dialog(QMainWindow *parent)
: SettingWindow (parent), mStackedWidget (0), mDebugMode (false)

View file

@ -3,7 +3,6 @@
#include "settingwindow.hpp"
#include "resizeablestackedwidget.hpp"
#include <QStandardItem>
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

View file

@ -9,6 +9,8 @@
#include <QStyledItemDelegate>
#include <QTextDocument>
#include <QPainter>
#include <QContextMenuEvent>
#include <QMouseEvent>
#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<Qt::KeyboardModifiers, DoubleClickAction>::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<CSMWorld::UniversalId> CSVTools::ReportTable::getDraggedRecords() const
@ -170,6 +194,35 @@ std::vector<CSMWorld::UniversalId> 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<int> CSVTools::ReportTable::getReplaceIndices (bool selection) const

View file

@ -1,6 +1,8 @@
#ifndef CSV_TOOLS_REPORTTABLE_H
#define CSV_TOOLS_REPORTTABLE_H
#include <map>
#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<Qt::KeyboardModifiers, DoubleClickAction> mDoubleClickActions;
private:

View file

@ -0,0 +1,113 @@
#include "coloreditor.hpp"
#include <QApplication>
#include <QColor>
#include <QColorDialog>
#include <QDesktopWidget>
#include <QPainter>
#include <QRect>
#include <QShowEvent>
#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;
}

View file

@ -0,0 +1,44 @@
#ifndef CSV_WIDGET_COLOREDITOR_HPP
#define CSV_WIDGET_COLOREDITOR_HPP
#include <QPushButton>
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

View file

@ -0,0 +1,86 @@
#include "colorpickerpopup.hpp"
#include <QColorDialog>
#include <QPushButton>
#include <QEvent>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QLayout>
#include <QStyleOption>
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<QPushButton *>(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<QKeyEvent *>(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);
}

View file

@ -0,0 +1,32 @@
#ifndef CSVWIDGET_COLORPICKERPOPUP_HPP
#define CSVWIDGET_COLORPICKERPOPUP_HPP
#include <QFrame>
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

View file

@ -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);

View file

@ -0,0 +1,36 @@
#include "colordelegate.hpp"
#include <QPainter>
#include <QPushButton>
#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<QColor>());
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);
}

View file

@ -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

View file

@ -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,

View file

@ -30,9 +30,8 @@ namespace CSVWorld
private:
std::vector <std::pair <int, QPixmap> > 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);

View file

@ -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<CSVWidget::ColorEditor *>(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)
{

View file

@ -1,4 +1,5 @@
#include <QDrag>
#include <QDragEnterEvent>
#include "../../model/world/tablemimedata.hpp"
#include "dragrecordtable.hpp"

View file

@ -2,7 +2,7 @@
#define CSV_WORLD_DRAGRECORDTABLE_H
#include <QTableView>
#include <QtGui/qevent.h>
#include <QEvent>
class QWidget;
class QAction;

View file

@ -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<int>(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<QComboBox *> (editor))
if (QComboBox *comboBox = dynamic_cast<QComboBox *>(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; i<size; ++i)
if (mValues[i].first==value)
{
comboBox->setCurrentIndex (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<std::pair<int, QString> >::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() {}

View file

@ -19,6 +19,8 @@ namespace CSVWorld
std::vector<std::pair<int, QString> > 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

View file

@ -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());

View file

@ -2,7 +2,7 @@
#define CSV_WORLD_NESTEDTABLE_H
#include <QTableView>
#include <QtGui/qevent.h>
#include <QEvent>
class QUndoStack;
class QAction;

View file

@ -68,10 +68,10 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
new CSVDoc::SubViewFactoryWithCreator<TableSubView, JournalCreatorFactory>);
manager.add (CSMWorld::UniversalId::Type_TopicInfos,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<InfoCreator> > (false));
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<InfoCreator> >);
manager.add (CSMWorld::UniversalId::Type_JournalInfos,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<InfoCreator> > (false));
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<InfoCreator> >);
// Subviews for resources tables
manager.add (CSMWorld::UniversalId::Type_Meshes,

View file

@ -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<CSMWorld::IdTableBase&> (*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);

View file

@ -4,7 +4,7 @@
#include <vector>
#include <string>
#include <QtGui/qevent.h>
#include <QEvent>
#include "../../model/filter/node.hpp"
#include "../../model/world/columnbase.hpp"

View file

@ -8,6 +8,7 @@
#include <QHeaderView>
#include <QApplication>
#include <QDesktopWidget>
#include <QDropEvent>
#include "../../model/doc/document.hpp"
#include "../../model/world/tablemimedata.hpp"

View file

@ -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<CSVWidget::ColorEditor *>(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<QColor>(), 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<QColor>(), 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<CSVWidget::ColorEditor *>(editor);
if (colorEditor != NULL)
{
colorEditor->setColor(index.data().value<QColor>());
return;
}
QByteArray n = editor->metaObject()->userProperty().name();
if (n == "dateTime") {

View file

@ -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}

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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;

View file

@ -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<int>(mX), static_cast<int>(mY), static_cast<int>(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<int>(mX), static_cast<int>(mY), static_cast<int>(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;
}

View file

@ -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;

View file

@ -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<float>(dest.mX),
static_cast<float>(dest.mY), static_cast<float>(dest.mZ));
actor.getClass().adjustPosition(actor, false);
// may have changed cell
mStoredAvailableNodes = false;
}
void AiWander::getAllowedNodes(const MWWorld::Ptr& actor, const ESM::Cell* cell)

View file

@ -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<float>(endPoint.mX), static_cast<float>(endPoint.mY), static_cast<float>(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<ESM::Pathgrid::Point> &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<ESM::Pathgrid::Point>::const_iterator oldStart = path.begin();
std::list<ESM::Pathgrid::Point>::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<ESM::Pathgrid::Point>::iterator iter = ++mPath.begin();
if (iter->mX == oldStart.mX
&& iter->mY == oldStart.mY
&& iter->mZ == oldStart.mZ)
{
mPath.pop_front();
}
}
}
return false;
}
}

View file

@ -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<ESM::Pathgrid::Point> &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<ESM::Pathgrid::Point> mPath;
const ESM::Pathgrid *mPathgrid;

View file

@ -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 '"<<id<<"' not found (const)";
msg << T::getRecordType() << " starting with '"<<id<<"' not found";
throw std::runtime_error(msg.str());
}
return ptr;
@ -989,7 +989,7 @@ namespace MWWorld
const T *ptr = search(index);
if (ptr == 0) {
std::ostringstream msg;
msg << "Object with index " << index << " not found";
msg << T::getRecordType() << " with index " << index << " not found";
throw std::runtime_error(msg.str());
}
return ptr;

View file

@ -81,7 +81,6 @@ endif (OPENMW_USE_UNSHIELD)
source_group(wizard FILES ${WIZARD} ${WIZARD_HEADER})
find_package(Qt4 REQUIRED)
set(QT_USE_QTGUI 1)
# Set some platform specific settings
@ -90,12 +89,17 @@ if(WIN32)
set(QT_USE_QTMAIN TRUE)
endif(WIN32)
QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/wizard/wizard.qrc)
QT4_WRAP_CPP(MOC_SRCS ${WIZARD_HEADER_MOC})
QT4_WRAP_UI(UI_HDRS ${WIZARD_UI})
if (DESIRED_QT_VERSION MATCHES 4)
include(${QT_USE_FILE})
QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/wizard/wizard.qrc)
QT4_WRAP_CPP(MOC_SRCS ${WIZARD_HEADER_MOC})
QT4_WRAP_UI(UI_HDRS ${WIZARD_UI})
else()
QT5_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/wizard/wizard.qrc)
QT5_WRAP_CPP(MOC_SRCS ${WIZARD_HEADER_MOC})
QT5_WRAP_UI(UI_HDRS ${WIZARD_UI})
endif()
include(${QT_USE_FILE})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
if (OPENMW_USE_UNSHIELD)
@ -112,11 +116,24 @@ add_executable(openmw-wizard
)
target_link_libraries(openmw-wizard
${Boost_LIBRARIES}
${QT_LIBRARIES}
components
)
if (DESIRED_QT_VERSION MATCHES 4)
target_link_libraries(openmw-wizard
${QT_QTGUI_LIBRARY}
${QT_QTCORE_LIBRARY})
if (WIN32)
target_link_libraries(openmw-wizard ${QT_QTMAIN_LIBRARY})
endif()
else()
qt5_use_modules(openmw-wizard Widgets Core)
if (WIN32)
target_link_libraries(Qt5::WinMain)
endif()
endif()
if (OPENMW_USE_UNSHIELD)
target_link_libraries(openmw-wizard ${LIBUNSHIELD_LIBRARY})
endif()

View file

@ -38,9 +38,6 @@ int main(int argc, char *argv[])
QDir::setCurrent(dir.absolutePath());
// Support non-latin characters
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
Wizard::MainWizard wizard;
wizard.show();

View file

@ -6,7 +6,6 @@
#include <QWriteLocker>
#include <QFileDialog>
#include <QFileInfo>
#include <QFileInfoListIterator>
#include <QStringList>
#include <QTextStream>
#include <QTextCodec>

View file

@ -24,7 +24,6 @@ namespace Wizard
class UnshieldWorker : public QObject
{
Q_OBJECT
Q_ENUMS(Wizard::Component)
public:
UnshieldWorker(QObject *parent = 0);

View file

@ -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()

View file

@ -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<QString, QString> userSettingsCopy;
QRegExp settingRegex("^([^=]+)\\s*=\\s*([^,]+)(.*)$");
QString settingLine;
QMap<QString, QString>::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<QString> comments;
std::vector<QString>::iterator commentStart = fileCopy.end();
std::map<QString, std::vector<QString> > commentsMap;
for (std::vector<QString>::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<QString, QString>::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<QString>::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<QString, QString>::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<QString>::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<QString, QString> it(mUserSettings);
it.toBack();
while (it.hasPrevious())
{
stream << "# new entries" << "\n";
QMap<QString, QString>::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<QString, std::vector<QString> >::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<QString> cLines = i->second;
for (std::vector<QString>::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<QString, std::vector<QString> >::const_iterator i = commentsMap.begin();
for (; i != commentsMap.end(); ++i)
{
if (i->first.contains(QRegExp("^\\s*content\\s*=")))
{
std::vector<QString> cLines = i->second;
for (std::vector<QString>::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<QString>::const_iterator ci = comments.begin(); ci != comments.end(); ++ci)
stream << *ci << "\n";
}
file.resize(file.pos());

View file

@ -83,6 +83,8 @@ namespace Config
QString mDataLocal;
static const char sContentKey[];
bool isOrderedLine(const QString& line) const;
};
}
#endif // GAMESETTINGS_HPP

View file

@ -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;

View file

@ -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<QString, Qt::CheckState> mCheckStates;
QSet<QString> mPluginsWithLoadOrderError;
QTextCodec *mCodec;
QString mEncoding;
QIcon mWarningIcon;

View file

@ -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();

View file

@ -29,6 +29,7 @@ namespace ContentSelectorView
QString currentFile() const;
void addFiles(const QString &path);
void clearFiles();
void setProfileContent (const QStringList &fileList);
void clearCheckStates();

View file

@ -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;

View file

@ -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;

View file

@ -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
{

View file

@ -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
{

View file

@ -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
{

View file

@ -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
{

View file

@ -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;

View file

@ -56,6 +56,8 @@ typedef std::list<CellRef> 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
{

View file

@ -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
{

View file

@ -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
{

View file

@ -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
{

View file

@ -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

View file

@ -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
{

View file

@ -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;

View file

@ -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
{

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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
{

View file

@ -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
{

View file

@ -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();

View file

@ -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
{

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