Merge pull request #303 from OpenMW/master while resolving conflicts

# Conflicts:
#	apps/openmw/mwbase/dialoguemanager.hpp
#	apps/openmw/mwdialogue/dialoguemanagerimp.hpp
#	apps/openmw/mwgui/container.cpp
#	apps/openmw/mwgui/windowmanagerimp.cpp
#	apps/openmw/mwscript/dialogueextensions.cpp
new-script-api
David Cernat 7 years ago
commit 38247ff086

@ -6,5 +6,5 @@ brew outdated cmake || brew upgrade cmake
brew outdated pkgconfig || brew upgrade pkgconfig
brew install $macos_qt_formula
curl https://downloads.openmw.org/osx/dependencies/openmw-deps-5e144e2.zip -o ~/openmw-deps.zip
curl https://downloads.openmw.org/osx/dependencies/openmw-deps-c40905f.zip -o ~/openmw-deps.zip
unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null

@ -18,7 +18,7 @@ opencs_hdrs_noqt (model/doc
opencs_units (model/world
idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel
idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel landtexturetableproxymodel
)
@ -70,7 +70,7 @@ opencs_units (view/world
cellcreator pathgridcreator referenceablecreator startscriptcreator referencecreator scenesubview
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator
bodypartcreator
bodypartcreator landtexturecreator landcreator
)
opencs_units_noqt (view/world

@ -240,6 +240,8 @@ void CSMPrefs::State::declare()
declareShortcut ("document-world-cells", "Open Cell List", QKeySequence());
declareShortcut ("document-world-referencables", "Open Object List", QKeySequence());
declareShortcut ("document-world-references", "Open Instance List", QKeySequence());
declareShortcut ("document-world-lands", "Open Lands List", QKeySequence());
declareShortcut ("document-world-landtextures", "Open Land Textures List", QKeySequence());
declareShortcut ("document-world-pathgrid", "Open Pathgrid List", QKeySequence());
declareShortcut ("document-world-regionmap", "Open Region Map", QKeySequence());
declareShortcut ("document-mechanics-globals", "Open Global List", QKeySequence());
@ -276,6 +278,7 @@ void CSMPrefs::State::declare()
declareShortcut ("table-edit", "Edit Record", QKeySequence());
declareShortcut ("table-add", "Add Row/Record", QKeySequence(Qt::ShiftModifier | Qt::Key_A));
declareShortcut ("table-clone", "Clone Record", QKeySequence(Qt::ShiftModifier | Qt::Key_D));
declareShortcut ("touch-record", "Touch Record", QKeySequence());
declareShortcut ("table-revert", "Revert Record", QKeySequence());
declareShortcut ("table-remove", "Remove Row/Record", QKeySequence(Qt::Key_Delete));
declareShortcut ("table-moveup", "Move Record Up", QKeySequence());

@ -6,6 +6,7 @@
#include <algorithm>
#include <cctype>
#include <stdexcept>
#include <string>
#include <functional>
#include <QVariant>
@ -13,8 +14,9 @@
#include <components/misc/stringops.hpp>
#include "columnbase.hpp"
#include "collectionbase.hpp"
#include "land.hpp"
#include "landtexture.hpp"
namespace CSMWorld
{
@ -22,15 +24,14 @@ namespace CSMWorld
template<typename ESXRecordT>
struct IdAccessor
{
std::string& getId (ESXRecordT& record);
void setId(ESXRecordT& record, const std::string& id) const;
const std::string getId (const ESXRecordT& record) const;
};
template<typename ESXRecordT>
std::string& IdAccessor<ESXRecordT>::getId (ESXRecordT& record)
void IdAccessor<ESXRecordT>::setId(ESXRecordT& record, const std::string& id) const
{
return record.mId;
record.mId = id;
}
template<typename ESXRecordT>
@ -39,6 +40,39 @@ namespace CSMWorld
return record.mId;
}
template<>
inline void IdAccessor<Land>::setId (Land& record, const std::string& id) const
{
int x=0, y=0;
Land::parseUniqueRecordId(id, x, y);
record.mX = x;
record.mY = y;
}
template<>
inline void IdAccessor<LandTexture>::setId (LandTexture& record, const std::string& id) const
{
int plugin = 0;
int index = 0;
LandTexture::parseUniqueRecordId(id, plugin, index);
record.mPluginIndex = plugin;
record.mIndex = index;
}
template<>
inline const std::string IdAccessor<Land>::getId (const Land& record) const
{
return Land::createUniqueRecordId(record.mX, record.mY);
}
template<>
inline const std::string IdAccessor<LandTexture>::getId (const LandTexture& record) const
{
return LandTexture::createUniqueRecordId(record.mPluginIndex, record.mIndex);
}
/// \brief Single-type record collection
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
class Collection : public CollectionBase
@ -69,6 +103,13 @@ namespace CSMWorld
///
/// \return Success?
int cloneRecordImp (const std::string& origin, const std::string& dest,
UniversalId::Type type);
///< Returns the index of the clone.
int touchRecordImp (const std::string& id);
///< Returns the index of the record on success, -1 on failure.
public:
Collection();
@ -108,6 +149,10 @@ namespace CSMWorld
const std::string& destination,
const UniversalId::Type type);
virtual bool touchRecord(const std::string& id);
///< Change the state of a record from base to modified, if it is not already.
/// \return True if the record was changed.
virtual int searchId (const std::string& id) const;
////< Search record with \a id.
/// \return index of record (if found) or -1 (not found)
@ -206,16 +251,71 @@ namespace CSMWorld
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::cloneRecord(const std::string& origin,
const std::string& destination,
const UniversalId::Type type)
int Collection<ESXRecordT, IdAccessorT>::cloneRecordImp(const std::string& origin,
const std::string& destination, UniversalId::Type type)
{
Record<ESXRecordT> copy;
copy.mModified = getRecord(origin).get();
copy.mState = RecordBase::State_ModifiedOnly;
copy.get().mId = destination;
IdAccessorT().setId(copy.get(), destination);
int index = getAppendIndex(destination, type);
insertRecord(copy, getAppendIndex(destination, type));
return index;
}
template<typename ESXRecordT, typename IdAccessorT>
int Collection<ESXRecordT, IdAccessorT>::touchRecordImp(const std::string& id)
{
int index = getIndex(id);
Record<ESXRecordT>& record = mRecords.at(index);
if (record.isDeleted())
{
throw std::runtime_error("attempt to touch deleted record");
}
if (!record.isModified())
{
record.setModified(record.get());
return index;
}
return -1;
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::cloneRecord(const std::string& origin,
const std::string& destination, const UniversalId::Type type)
{
cloneRecordImp(origin, destination, type);
}
template<>
inline void Collection<Land, IdAccessor<Land> >::cloneRecord(const std::string& origin,
const std::string& destination, const UniversalId::Type type)
{
int index = cloneRecordImp(origin, destination, type);
mRecords.at(index).get().mPlugin = 0;
}
template<typename ESXRecordT, typename IdAccessorT>
bool Collection<ESXRecordT, IdAccessorT>::touchRecord(const std::string& id)
{
return touchRecordImp(id) != -1;
}
template<>
inline bool Collection<Land, IdAccessor<Land> >::touchRecord(const std::string& id)
{
int index = touchRecordImp(id);
if (index >= 0)
{
mRecords.at(index).get().mPlugin = 0;
return true;
}
return false;
}
template<typename ESXRecordT, typename IdAccessorT>
@ -366,7 +466,7 @@ namespace CSMWorld
UniversalId::Type type)
{
ESXRecordT record;
IdAccessorT().getId (record) = id;
IdAccessorT().setId(record, id);
record.blank();
Record<ESXRecordT> record2;

@ -78,6 +78,8 @@ namespace CSMWorld
const std::string& destination,
const UniversalId::Type type) = 0;
virtual bool touchRecord(const std::string& id) = 0;
virtual const RecordBase& getRecord (const std::string& id) const = 0;
virtual const RecordBase& getRecord (int index) const = 0;

@ -1,10 +1,322 @@
#include "columnimp.hpp"
CSMWorld::BodyPartRaceColumn::BodyPartRaceColumn(const MeshTypeColumn<ESM::BodyPart> *meshType)
#include <stdexcept>
#include <QVector>
namespace CSMWorld
{
/* LandTextureNicknameColumn */
LandTextureNicknameColumn::LandTextureNicknameColumn()
: Column<LandTexture>(Columns::ColumnId_TextureNickname, ColumnBase::Display_String)
{
}
QVariant LandTextureNicknameColumn::get(const Record<LandTexture>& record) const
{
return QString::fromUtf8(record.get().mId.c_str());
}
void LandTextureNicknameColumn::set(Record<LandTexture>& record, const QVariant& data)
{
LandTexture copy = record.get();
copy.mId = data.toString().toUtf8().constData();
record.setModified(copy);
}
bool LandTextureNicknameColumn::isEditable() const
{
return true;
}
/* LandTextureIndexColumn */
LandTextureIndexColumn::LandTextureIndexColumn()
: Column<LandTexture>(Columns::ColumnId_TextureIndex, ColumnBase::Display_Integer)
{
}
QVariant LandTextureIndexColumn::get(const Record<LandTexture>& record) const
{
return record.get().mIndex;
}
bool LandTextureIndexColumn::isEditable() const
{
return false;
}
/* LandPluginIndexColumn */
LandPluginIndexColumn::LandPluginIndexColumn()
: Column<Land>(Columns::ColumnId_PluginIndex, ColumnBase::Display_Integer, 0)
{
}
QVariant LandPluginIndexColumn::get(const Record<Land>& record) const
{
return record.get().mPlugin;
}
bool LandPluginIndexColumn::isEditable() const
{
return false;
}
/* LandTexturePluginIndexColumn */
LandTexturePluginIndexColumn::LandTexturePluginIndexColumn()
: Column<LandTexture>(Columns::ColumnId_PluginIndex, ColumnBase::Display_Integer, 0)
{
}
QVariant LandTexturePluginIndexColumn::get(const Record<LandTexture>& record) const
{
return record.get().mPluginIndex;
}
bool LandTexturePluginIndexColumn::isEditable() const
{
return false;
}
/* LandMapLodColumn */
LandMapLodColumn::LandMapLodColumn()
: Column<Land>(Columns::ColumnId_LandMapLodIndex, ColumnBase::Display_String, 0)
{
}
QVariant LandMapLodColumn::get(const Record<Land>& record) const
{
const int Size = Land::LAND_GLOBAL_MAP_LOD_SIZE;
const Land& land = record.get();
DataType values(Size, 0);
if (land.isDataLoaded(Land::DATA_WNAM))
{
for (int i = 0; i < Size; ++i)
values[i] = land.mWnam[i];
}
QVariant variant;
variant.setValue(values);
return variant;
}
void LandMapLodColumn::set(Record<Land>& record, const QVariant& data)
{
DataType values = data.value<DataType>();
if (values.size() != Land::LAND_GLOBAL_MAP_LOD_SIZE)
throw std::runtime_error("invalid land map LOD data");
Land copy = record.get();
copy.setDataLoaded(Land::DATA_WNAM);
for (int i = 0; i < values.size(); ++i)
{
copy.mWnam[i] = values[i];
}
record.setModified(copy);
}
bool LandMapLodColumn::isEditable() const
{
return true;
}
/* LandNormalsColumn */
LandNormalsColumn::LandNormalsColumn()
: Column<Land>(Columns::ColumnId_LandNormalsIndex, ColumnBase::Display_String, 0)
{
}
QVariant LandNormalsColumn::get(const Record<Land>& record) const
{
const int Size = Land::LAND_NUM_VERTS * 3;
const Land& land = record.get();
DataType values(Size, 0);
if (land.isDataLoaded(Land::DATA_VNML))
{
for (int i = 0; i < Size; ++i)
values[i] = land.getLandData()->mNormals[i];
}
QVariant variant;
variant.setValue(values);
return variant;
}
void LandNormalsColumn::set(Record<Land>& record, const QVariant& data)
{
DataType values = data.value<DataType>();
if (values.size() != Land::LAND_NUM_VERTS * 3)
throw std::runtime_error("invalid land normals data");
Land copy = record.get();
copy.setDataLoaded(Land::DATA_VNML);
for (int i = 0; i < values.size(); ++i)
{
copy.getLandData()->mNormals[i] = values[i];
}
record.setModified(copy);
}
bool LandNormalsColumn::isEditable() const
{
return true;
}
/* LandHeightsColumn */
LandHeightsColumn::LandHeightsColumn()
: Column<Land>(Columns::ColumnId_LandHeightsIndex, ColumnBase::Display_String, 0)
{
}
QVariant LandHeightsColumn::get(const Record<Land>& record) const
{
const int Size = Land::LAND_NUM_VERTS;
const Land& land = record.get();
DataType values(Size, 0);
if (land.isDataLoaded(Land::DATA_VHGT))
{
for (int i = 0; i < Size; ++i)
values[i] = land.getLandData()->mHeights[i];
}
QVariant variant;
variant.setValue(values);
return variant;
}
void LandHeightsColumn::set(Record<Land>& record, const QVariant& data)
{
DataType values = data.value<DataType>();
if (values.size() != Land::LAND_NUM_VERTS)
throw std::runtime_error("invalid land heights data");
Land copy = record.get();
copy.setDataLoaded(Land::DATA_VHGT);
for (int i = 0; i < values.size(); ++i)
{
copy.getLandData()->mHeights[i] = values[i];
}
record.setModified(copy);
}
bool LandHeightsColumn::isEditable() const
{
return true;
}
/* LandColoursColumn */
LandColoursColumn::LandColoursColumn()
: Column<Land>(Columns::ColumnId_LandColoursIndex, ColumnBase::Display_String, 0)
{
}
QVariant LandColoursColumn::get(const Record<Land>& record) const
{
const int Size = Land::LAND_NUM_VERTS * 3;
const Land& land = record.get();
DataType values(Size, 0);
if (land.isDataLoaded(Land::DATA_VCLR))
{
for (int i = 0; i < Size; ++i)
values[i] = land.getLandData()->mColours[i];
}
QVariant variant;
variant.setValue(values);
return variant;
}
void LandColoursColumn::set(Record<Land>& record, const QVariant& data)
{
DataType values = data.value<DataType>();
if (values.size() != Land::LAND_NUM_VERTS * 3)
throw std::runtime_error("invalid land colours data");
Land copy = record.get();
copy.setDataLoaded(Land::DATA_VCLR);
for (int i = 0; i < values.size(); ++i)
{
copy.getLandData()->mColours[i] = values[i];
}
record.setModified(copy);
}
bool LandColoursColumn::isEditable() const
{
return true;
}
/* LandTexturesColumn */
LandTexturesColumn::LandTexturesColumn()
: Column<Land>(Columns::ColumnId_LandTexturesIndex, ColumnBase::Display_String, 0)
{
}
QVariant LandTexturesColumn::get(const Record<Land>& record) const
{
const int Size = Land::LAND_NUM_TEXTURES;
const Land& land = record.get();
DataType values(Size, 0);
if (land.isDataLoaded(Land::DATA_VTEX))
{
for (int i = 0; i < Size; ++i)
values[i] = land.getLandData()->mTextures[i];
}
QVariant variant;
variant.setValue(values);
return variant;
}
void LandTexturesColumn::set(Record<Land>& record, const QVariant& data)
{
DataType values = data.value<DataType>();
if (values.size() != Land::LAND_NUM_TEXTURES)
throw std::runtime_error("invalid land textures data");
Land copy = record.get();
copy.setDataLoaded(Land::DATA_VTEX);
for (int i = 0; i < values.size(); ++i)
{
copy.getLandData()->mTextures[i] = values[i];
}
record.setModified(copy);
}
bool LandTexturesColumn::isEditable() const
{
return true;
}
/* BodyPartRaceColumn */
BodyPartRaceColumn::BodyPartRaceColumn(const MeshTypeColumn<ESM::BodyPart> *meshType)
: mMeshType(meshType)
{}
QVariant CSMWorld::BodyPartRaceColumn::get(const Record<ESM::BodyPart> &record) const
QVariant BodyPartRaceColumn::get(const Record<ESM::BodyPart> &record) const
{
if (mMeshType != NULL && mMeshType->get(record) == ESM::BodyPart::MT_Skin)
{
@ -13,7 +325,7 @@ QVariant CSMWorld::BodyPartRaceColumn::get(const Record<ESM::BodyPart> &record)
return QVariant(QVariant::UserType);
}
void CSMWorld::BodyPartRaceColumn::set(Record<ESM::BodyPart> &record, const QVariant &data)
void BodyPartRaceColumn::set(Record<ESM::BodyPart> &record, const QVariant &data)
{
ESM::BodyPart record2 = record.get();
@ -22,7 +334,8 @@ void CSMWorld::BodyPartRaceColumn::set(Record<ESM::BodyPart> &record, const QVar
record.setModified(record2);
}
bool CSMWorld::BodyPartRaceColumn::isEditable() const
bool BodyPartRaceColumn::isEditable() const
{
return true;
}
}

@ -2,10 +2,12 @@
#define CSM_WOLRD_COLUMNIMP_H
#include <cassert>
#include <cstdint>
#include <sstream>
#include <stdexcept>
#include <QColor>
#include <QVector>
#include <components/esm/loadbody.hpp>
#include <components/esm/loadskil.hpp>
@ -15,6 +17,9 @@
#include "columns.hpp"
#include "info.hpp"
#include "land.hpp"
#include "landtexture.hpp"
namespace CSMWorld
{
/// \note Shares ID with VarValueColumn. A table can not have both.
@ -60,6 +65,20 @@ namespace CSMWorld
}
};
template<>
inline QVariant StringIdColumn<Land>::get(const Record<Land>& record) const
{
const Land& land = record.get();
return QString::fromUtf8(Land::createUniqueRecordId(land.mX, land.mY).c_str());
}
template<>
inline QVariant StringIdColumn<LandTexture>::get(const Record<LandTexture>& record) const
{
const LandTexture& ltex = record.get();
return QString::fromUtf8(LandTexture::createUniqueRecordId(ltex.mPluginIndex, ltex.mIndex).c_str());
}
template<typename ESXRecordT>
struct RecordStateColumn : public Column<ESXRecordT>
{
@ -2418,6 +2437,94 @@ namespace CSMWorld
}
};
struct LandTextureNicknameColumn : public Column<LandTexture>
{
LandTextureNicknameColumn();
QVariant get(const Record<LandTexture>& record) const override;
void set(Record<LandTexture>& record, const QVariant& data) override;
bool isEditable() const override;
};
struct LandTextureIndexColumn : public Column<LandTexture>
{
LandTextureIndexColumn();
QVariant get(const Record<LandTexture>& record) const override;
bool isEditable() const override;
};
struct LandPluginIndexColumn : public Column<Land>
{
LandPluginIndexColumn();
QVariant get(const Record<Land>& record) const override;
bool isEditable() const override;
};
struct LandTexturePluginIndexColumn : public Column<LandTexture>
{
LandTexturePluginIndexColumn();
QVariant get(const Record<LandTexture>& record) const override;
bool isEditable() const override;
};
struct LandMapLodColumn : public Column<Land>
{
using DataType = QVector<signed char>;
LandMapLodColumn();
QVariant get(const Record<Land>& record) const override;
void set(Record<Land>& record, const QVariant& data) override;
bool isEditable() const override;
};
struct LandNormalsColumn : public Column<Land>
{
using DataType = QVector<signed char>;
LandNormalsColumn();
QVariant get(const Record<Land>& record) const override;
void set(Record<Land>& record, const QVariant& data) override;
bool isEditable() const override;
};
struct LandHeightsColumn : public Column<Land>
{
using DataType = QVector<float>;
LandHeightsColumn();
QVariant get(const Record<Land>& record) const override;
void set(Record<Land>& record, const QVariant& data) override;
bool isEditable() const override;
};
struct LandColoursColumn : public Column<Land>
{
using DataType = QVector<unsigned char>;
LandColoursColumn();
QVariant get(const Record<Land>& record) const override;
void set(Record<Land>& record, const QVariant& data) override;
bool isEditable() const override;
};
struct LandTexturesColumn : public Column<Land>
{
using DataType = QVector<uint16_t>;
LandTexturesColumn();
QVariant get(const Record<Land>& record) const override;
void set(Record<Land>& record, const QVariant& data) override;
bool isEditable() const override;
};
struct BodyPartRaceColumn : public RaceColumn<ESM::BodyPart>
{
const MeshTypeColumn<ESM::BodyPart> *mMeshType;
@ -2430,4 +2537,11 @@ namespace CSMWorld
};
}
// This is required to access the type as a QVariant.
Q_DECLARE_METATYPE(CSMWorld::LandMapLodColumn::DataType)
//Q_DECLARE_METATYPE(CSMWorld::LandNormalsColumn::DataType) // Same as LandMapLodColumn::DataType
Q_DECLARE_METATYPE(CSMWorld::LandHeightsColumn::DataType)
Q_DECLARE_METATYPE(CSMWorld::LandColoursColumn::DataType)
Q_DECLARE_METATYPE(CSMWorld::LandTexturesColumn::DataType)
#endif

@ -330,6 +330,14 @@ namespace CSMWorld
{ ColumnId_WeatherChance, "Percent Chance" },
{ ColumnId_Text, "Text" },
{ ColumnId_TextureNickname, "Texture Nickname" },
{ ColumnId_PluginIndex, "Plugin Index" },
{ ColumnId_TextureIndex, "Texture Index" },
{ ColumnId_LandMapLodIndex, "Land map height LOD" },
{ ColumnId_LandNormalsIndex, "Land normals" },
{ ColumnId_LandHeightsIndex, "Land heights" },
{ ColumnId_LandColoursIndex, "Land colors" },
{ ColumnId_LandTexturesIndex, "Land textures" },
{ ColumnId_UseValue1, "Use value 1" },
{ ColumnId_UseValue2, "Use value 2" },

@ -329,6 +329,15 @@ namespace CSMWorld
ColumnId_Text = 297,
ColumnId_TextureNickname = 298,
ColumnId_PluginIndex = 299,
ColumnId_TextureIndex = 300,
ColumnId_LandMapLodIndex = 301,
ColumnId_LandNormalsIndex = 302,
ColumnId_LandHeightsIndex = 303,
ColumnId_LandColoursIndex = 304,
ColumnId_LandTexturesIndex = 305,
// Allocated to a separate value range, so we don't get a collision should we ever need
// to extend the number of use values.
ColumnId_UseValue1 = 0x10000,

@ -2,6 +2,7 @@
#include <cmath>
#include <sstream>
#include <unordered_set>
#include <components/misc/stringops.hpp>
@ -15,6 +16,175 @@
#include "nestedtablewrapper.hpp"
#include "pathgrid.hpp"
CSMWorld::TouchCommand::TouchCommand(IdTable& table, const std::string& id, QUndoCommand* parent)
: QUndoCommand(parent)
, mTable(table)
, mId(id)
, mOld(nullptr)
, mChanged(false)
{
setText(("Touch " + mId).c_str());
mOld.reset(mTable.getRecord(mId).clone());
}
void CSMWorld::TouchCommand::redo()
{
mChanged = mTable.touchRecord(mId);
}
void CSMWorld::TouchCommand::undo()
{
if (mChanged)
{
mTable.setRecord(mId, *mOld);
mChanged = false;
}
}
CSMWorld::ImportLandTexturesCommand::ImportLandTexturesCommand(IdTable& landTable,
IdTable& ltexTable, QUndoCommand* parent)
: QUndoCommand(parent)
, mLands(landTable)
, mLtexs(ltexTable)
, mOldState(0)
{
setText("Copy land textures to current plugin");
}
void CSMWorld::ImportLandTexturesCommand::redo()
{
int pluginColumn = mLands.findColumnIndex(Columns::ColumnId_PluginIndex);
int oldPlugin = mLands.data(mLands.getModelIndex(getOriginId(), pluginColumn)).toInt();
// Original data
int textureColumn = mLands.findColumnIndex(Columns::ColumnId_LandTexturesIndex);
mOld = mLands.data(mLands.getModelIndex(getOriginId(), textureColumn)).value<DataType>();
// Need to make a copy so the old values can be looked up
DataType copy(mOld);
// Perform touch/copy/etc...
onRedo();
// Find all indices used
std::unordered_set<int> texIndices;
for (int i = 0; i < mOld.size(); ++i)
{
// All indices are offset by 1 for a default texture
if (mOld[i] > 0)
texIndices.insert(mOld[i] - 1);
}
std::vector<std::string> oldTextures;
for (int index : texIndices)
{
oldTextures.push_back(LandTexture::createUniqueRecordId(oldPlugin, index));
}
// Import the textures, replace old values
LandTextureIdTable::ImportResults results = dynamic_cast<LandTextureIdTable&>(mLtexs).importTextures(oldTextures);
mCreatedTextures = std::move(results.createdRecords);
for (const auto& it : results.recordMapping)
{
int plugin = 0, newIndex = 0, oldIndex = 0;
LandTexture::parseUniqueRecordId(it.first, plugin, oldIndex);
LandTexture::parseUniqueRecordId(it.second, plugin, newIndex);
if (newIndex != oldIndex)
{
for (int i = 0; i < Land::LAND_NUM_TEXTURES; ++i)
{
// All indices are offset by 1 for a default texture
if (mOld[i] == oldIndex + 1)
copy[i] = newIndex + 1;
}
}
}
// Apply modification
int stateColumn = mLands.findColumnIndex(Columns::ColumnId_Modification);
mOldState = mLands.data(mLands.getModelIndex(getDestinationId(), stateColumn)).toInt();
QVariant variant;
variant.setValue(copy);
mLands.setData(mLands.getModelIndex(getDestinationId(), textureColumn), variant);
}
void CSMWorld::ImportLandTexturesCommand::undo()
{
// Restore to previous
int textureColumn = mLands.findColumnIndex(Columns::ColumnId_LandTexturesIndex);
QVariant variant;
variant.setValue(mOld);
mLands.setData(mLands.getModelIndex(getDestinationId(), textureColumn), variant);
int stateColumn = mLands.findColumnIndex(Columns::ColumnId_Modification);
mLands.setData(mLands.getModelIndex(getDestinationId(), stateColumn), mOldState);
// Undo copy/touch/etc...
onUndo();
for (const std::string& id : mCreatedTextures)
{
int row = mLtexs.getModelIndex(id, 0).row();
mLtexs.removeRows(row, 1);
}
mCreatedTextures.clear();
}
CSMWorld::CopyLandTexturesCommand::CopyLandTexturesCommand(IdTable& landTable, IdTable& ltexTable,
const std::string& origin, const std::string& dest, QUndoCommand* parent)
: ImportLandTexturesCommand(landTable, ltexTable, parent)
, mOriginId(origin)
, mDestId(dest)
{
}
const std::string& CSMWorld::CopyLandTexturesCommand::getOriginId() const
{
return mOriginId;
}
const std::string& CSMWorld::CopyLandTexturesCommand::getDestinationId() const
{
return mDestId;
}
CSMWorld::TouchLandCommand::TouchLandCommand(IdTable& landTable, IdTable& ltexTable,
const std::string& id, QUndoCommand* parent)
: ImportLandTexturesCommand(landTable, ltexTable, parent)
, mId(id)
, mOld(nullptr)
, mChanged(false)
{
setText(("Touch " + mId).c_str());
mOld.reset(mLands.getRecord(mId).clone());
}
const std::string& CSMWorld::TouchLandCommand::getOriginId() const
{
return mId;
}
const std::string& CSMWorld::TouchLandCommand::getDestinationId() const
{
return mId;
}
void CSMWorld::TouchLandCommand::onRedo()
{
mChanged = mLands.touchRecord(mId);
}
void CSMWorld::TouchLandCommand::onUndo()
{
if (mChanged)
{
mLands.setRecord(mId, *mOld);
mChanged = false;
}
}
CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
const QVariant& new_, QUndoCommand* parent)
: QUndoCommand (parent), mModel (&model), mIndex (index), mNew (new_), mHasRecordState(false), mOldRecordState(CSMWorld::RecordBase::State_BaseOnly)

@ -5,12 +5,14 @@
#include <string>
#include <map>
#include <memory>
#include <vector>
#include <QVariant>
#include <QUndoCommand>
#include <QModelIndex>
#include "columnimp.hpp"
#include "universalid.hpp"
#include "nestedtablewrapper.hpp"
@ -24,6 +26,91 @@ namespace CSMWorld
struct RecordBase;
struct NestedTableWrapperBase;
class TouchCommand : public QUndoCommand
{
public:
TouchCommand(IdTable& model, const std::string& id, QUndoCommand* parent=nullptr);
void redo() override;
void undo() override;
private:
IdTable& mTable;
std::string mId;
std::unique_ptr<RecordBase> mOld;
bool mChanged;
};
class ImportLandTexturesCommand : public QUndoCommand
{
public:
ImportLandTexturesCommand(IdTable& landTable, IdTable& ltexTable,
QUndoCommand* parent);
void redo() override;
void undo() override;
protected:
using DataType = LandTexturesColumn::DataType;
virtual const std::string& getOriginId() const = 0;
virtual const std::string& getDestinationId() const = 0;
virtual void onRedo() = 0;
virtual void onUndo() = 0;
IdTable& mLands;
IdTable& mLtexs;
DataType mOld;
int mOldState;
std::vector<std::string> mCreatedTextures;
};
class CopyLandTexturesCommand : public ImportLandTexturesCommand
{
public:
CopyLandTexturesCommand(IdTable& landTable, IdTable& ltexTable, const std::string& origin,
const std::string& dest, QUndoCommand* parent = nullptr);
private:
const std::string& getOriginId() const override;
const std::string& getDestinationId() const override;
void onRedo() override {}
void onUndo() override {}
std::string mOriginId;
std::string mDestId;
};
class TouchLandCommand : public ImportLandTexturesCommand
{
public:
TouchLandCommand(IdTable& landTable, IdTable& ltexTable,
const std::string& id, QUndoCommand* parent = nullptr);
private:
const std::string& getOriginId() const override;
const std::string& getDestinationId() const override;
void onRedo() override;
void onUndo() override;
std::string mId;
std::unique_ptr<RecordBase> mOld;
bool mChanged;
};
class ModifyCommand : public QUndoCommand
{
QAbstractItemModel *mModel;

@ -412,6 +412,24 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat
Columns::ColumnId_NegativeLight, ESM::MagicEffect::NegativeLight));
mMagicEffects.addColumn (new DescriptionColumn<ESM::MagicEffect>);
mLand.addColumn (new StringIdColumn<Land>);
mLand.addColumn (new RecordStateColumn<Land>);
mLand.addColumn (new FixedRecordTypeColumn<Land>(UniversalId::Type_Land));
mLand.addColumn (new LandPluginIndexColumn);
mLand.addColumn (new LandMapLodColumn);
mLand.addColumn (new LandNormalsColumn);
mLand.addColumn (new LandHeightsColumn);
mLand.addColumn (new LandColoursColumn);
mLand.addColumn (new LandTexturesColumn);
mLandTextures.addColumn (new StringIdColumn<LandTexture>(true));
mLandTextures.addColumn (new RecordStateColumn<LandTexture>);
mLandTextures.addColumn (new FixedRecordTypeColumn<LandTexture>(UniversalId::Type_LandTexture));
mLandTextures.addColumn (new LandTextureNicknameColumn);
mLandTextures.addColumn (new LandTexturePluginIndexColumn);
mLandTextures.addColumn (new LandTextureIndexColumn);
mLandTextures.addColumn (new TextureColumn<LandTexture>);
mPathgrids.addColumn (new StringIdColumn<Pathgrid>);
mPathgrids.addColumn (new RecordStateColumn<Pathgrid>);
mPathgrids.addColumn (new FixedRecordTypeColumn<Pathgrid> (UniversalId::Type_Pathgrid));
@ -531,6 +549,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat
addModel (new IdTable (&mBodyParts), UniversalId::Type_BodyPart);
addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen);
addModel (new IdTable (&mMagicEffects), UniversalId::Type_MagicEffect);
addModel (new IdTable (&mLand, IdTable::Feature_AllowTouch), UniversalId::Type_Land);
addModel (new LandTextureIdTable (&mLandTextures), UniversalId::Type_LandTexture);
addModel (new IdTree (&mPathgrids, &mPathgrids), UniversalId::Type_Pathgrid);
addModel (new IdTable (&mStartScripts), UniversalId::Type_StartScript);
addModel (new IdTree (&mReferenceables, &mReferenceables, IdTable::Feature_Preview),
@ -993,19 +1013,7 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
case ESM::REC_LTEX: mLandTextures.load (*mReader, mBase); break;
case ESM::REC_LAND:
{
int index = mLand.load(*mReader, mBase);
// Load all land data for now. A future optimisation may only load non-base data
// if a suitable mechanism for avoiding race conditions can be established.
if (index!=-1/* && !mBase*/)
mLand.getRecord (index).get().loadData (
ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR |
ESM::Land::DATA_VTEX);
break;
}
case ESM::REC_LAND: mLand.load(*mReader, mBase); break;
case ESM::REC_CELL:
{

@ -4,6 +4,7 @@
#include <components/esm/esmreader.hpp>
#include "collection.hpp"
#include "land.hpp"
namespace CSMWorld
{
@ -39,6 +40,22 @@ namespace CSMWorld
record.load (reader, isDeleted);
}
template<>
inline void IdCollection<Land, IdAccessor<Land> >::loadRecord (Land& record,
ESM::ESMReader& reader, bool& isDeleted)
{
record.load (reader, isDeleted);
// Load all land data for now. A future optimisation may only load non-base data
// if a suitable mechanism for avoiding race conditions can be established.
int flags = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML |
ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX;
record.loadData (flags);
// Prevent data from being reloaded.
record.mContext.filename.clear();
}
template<typename ESXRecordT, typename IdAccessorT>
int IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base)
{

@ -1,11 +1,17 @@
#include "idtable.hpp"
#include <algorithm>
#include <cctype>
#include <cstdint>
#include <limits>
#include <map>
#include <stdexcept>
#include <components/esm/cellid.hpp>
#include "collectionbase.hpp"
#include "columnbase.hpp"
#include "landtexture.hpp"
CSMWorld::IdTable::IdTable (CollectionBase *idCollection, unsigned int features)
: IdTableBase (features), mIdCollection (idCollection)
@ -179,6 +185,26 @@ void CSMWorld::IdTable::cloneRecord(const std::string& origin,
endInsertRows();
}
bool CSMWorld::IdTable::touchRecord(const std::string& id)
{
bool changed = mIdCollection->touchRecord(id);
int row = mIdCollection->getIndex(id);
int column = mIdCollection->searchColumnIndex(Columns::ColumnId_RecordType);
if (changed && column != -1)
{
QModelIndex modelIndex = index(row, column);
emit dataChanged(modelIndex, modelIndex);
}
return changed;
}
std::string CSMWorld::IdTable::getId(int row) const
{
return mIdCollection->getId(row);
}
///This method can return only indexes to the top level table cells
QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const
{
@ -281,3 +307,72 @@ CSMWorld::CollectionBase *CSMWorld::IdTable::idCollection() const
{
return mIdCollection;
}
CSMWorld::LandTextureIdTable::LandTextureIdTable(CollectionBase* idCollection, unsigned int features)
: IdTable(idCollection, features)
{
}
CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::importTextures(const std::vector<std::string>& ids)
{
ImportResults results;
// Map existing textures to ids
std::map<std::string, std::string> reverseLookupMap;
for (int i = 0; i < idCollection()->getSize(); ++i)
{
auto& record = static_cast<const Record<LandTexture>&>(idCollection()->getRecord(i));
std::string texture = record.get().mTexture;
std::transform(texture.begin(), texture.end(), texture.begin(), tolower);
if (record.isModified())
reverseLookupMap.emplace(texture, idCollection()->getId(i));
}
for (const std::string& id : ids)
{
int plugin, index;
LandTexture::parseUniqueRecordId(id, plugin, index);
int oldRow = idCollection()->searchId(id);
// If it does not exist or it is in the current plugin, it can be skipped.
if (oldRow <= 0 || plugin == 0)
{
results.recordMapping.push_back(std::make_pair(id, id));
continue;
}
// Look for a pre-existing record
auto& record = static_cast<const Record<LandTexture>&>(idCollection()->getRecord(oldRow));
std::string texture = record.get().mTexture;
std::transform(texture.begin(), texture.end(), texture.begin(), tolower);
auto searchIt = reverseLookupMap.find(texture);
if (searchIt != reverseLookupMap.end())
{
results.recordMapping.push_back(std::make_pair(id, searchIt->second));
continue;
}
// Iterate until an unused index or found, or the index has completely wrapped around.
int startIndex = index;
do {
std::string newId = LandTexture::createUniqueRecordId(0, index);
int newRow = idCollection()->searchId(newId);
if (newRow < 0)
{
// Id not taken, clone it
cloneRecord(id, newId, UniversalId::Type_LandTexture);
results.createdRecords.push_back(newId);
results.recordMapping.push_back(std::make_pair(id, newId));
reverseLookupMap.emplace(texture, newId);
break;
}
const size_t MaxIndex = std::numeric_limits<uint16_t>::max() - 1;
index = (index + 1) % MaxIndex;
} while (index != startIndex);
}
return results;
}

@ -61,6 +61,11 @@ namespace CSMWorld
const std::string& destination,
UniversalId::Type type = UniversalId::Type_None);
bool touchRecord(const std::string& id);
///< Will change the record state to modified, if it is not already.
std::string getId(int row) const;
virtual QModelIndex getModelIndex (const std::string& id, int column) const;
void setRecord (const std::string& id, const RecordBase& record,
@ -93,6 +98,29 @@ namespace CSMWorld
virtual CollectionBase *idCollection() const;
};
/// An IdTable customized to handle the more unique needs of LandTextureId's which behave
/// differently from other records. The major difference is that base records cannot be
/// modified.
class LandTextureIdTable : public IdTable
{
public:
struct ImportResults
{
using StringPair = std::pair<std::string,std::string>;
/// The newly added records
std::vector<std::string> createdRecords;
/// The 1st string is the original id, the 2nd is the mapped id
std::vector<StringPair> recordMapping;
};
LandTextureIdTable(CollectionBase* idCollection, unsigned int features=0);
/// Finds and maps/recreates the specified ids.
ImportResults importTextures(const std::vector<std::string>& ids);
};
}
#endif

@ -32,7 +32,9 @@ namespace CSMWorld
Feature_Preview = 8,
/// Table can not be modified through ordinary means.
Feature_Constant = 16
Feature_Constant = 16,
Feature_AllowTouch = 32
};
private:

@ -1,15 +1,30 @@
#include "land.hpp"
#include <sstream>
#include <stdexcept>
namespace CSMWorld
{
void Land::load(ESM::ESMReader &esm, bool &isDeleted)
{
ESM::Land::load(esm, isDeleted);
}
std::string Land::createUniqueRecordId(int x, int y)
{
std::ostringstream stream;
stream << "#" << mX << " " << mY;
mId = stream.str();
stream << "#" << x << " " << y;
return stream.str();
}
void Land::parseUniqueRecordId(const std::string& id, int& x, int& y)
{
size_t mid = id.find(' ');
if (mid == std::string::npos || id[0] != '#')
throw std::runtime_error("Invalid Land ID");
x = std::stoi(id.substr(1, mid - 1));
y = std::stoi(id.substr(mid + 1));
}
}

@ -12,10 +12,11 @@ namespace CSMWorld
/// \todo Add worldspace support to the Land record.
struct Land : public ESM::Land
{
std::string mId;
/// Loads the metadata and ID
void load (ESM::ESMReader &esm, bool &isDeleted);
static std::string createUniqueRecordId(int x, int y);
static void parseUniqueRecordId(const std::string& id, int& x, int& y);
};
}

@ -1,5 +1,8 @@
#include "landtexture.hpp"
#include <sstream>
#include <stdexcept>
#include <components/esm/esmreader.hpp>
namespace CSMWorld
@ -11,4 +14,21 @@ namespace CSMWorld
mPluginIndex = esm.getIndex();
}
std::string LandTexture::createUniqueRecordId(int plugin, int index)
{
std::stringstream ss;
ss << 'L' << plugin << '#' << index;
return ss.str();
}
void LandTexture::parseUniqueRecordId(const std::string& id, int& plugin, int& index)
{
size_t middle = id.find('#');
if (middle == std::string::npos || id[0] != 'L')
throw std::runtime_error("Invalid LandTexture ID");
plugin = std::stoi(id.substr(1,middle-1));
index = std::stoi(id.substr(middle+1));
}
}

@ -13,6 +13,11 @@ namespace CSMWorld
int mPluginIndex;
void load (ESM::ESMReader &esm, bool &isDeleted);
/// Returns a string identifier that will be unique to any LandTexture.
static std::string createUniqueRecordId(int plugin, int index);
/// Deconstructs a unique string identifier into plugin and index.
static void parseUniqueRecordId(const std::string& id, int& plugin, int& index);
};
}

@ -0,0 +1,21 @@
#include "landtexturetableproxymodel.hpp"
#include "idtable.hpp"
namespace CSMWorld
{
LandTextureTableProxyModel::LandTextureTableProxyModel(QObject* parent)
: IdTableProxyModel(parent)
{
}
bool LandTextureTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const
{
int columnIndex = mSourceModel->findColumnIndex(Columns::ColumnId_Modification);
QModelIndex index = mSourceModel->index(sourceRow, columnIndex);
if (mSourceModel->data(index).toInt() != RecordBase::State_ModifiedOnly)
return false;
return IdTableProxyModel::filterAcceptsRow(sourceRow, sourceParent);
}
}

@ -0,0 +1,22 @@
#ifndef CSM_WORLD_LANDTEXTURETABLEPROXYMODEL_H
#define CSM_WORLD_LANDTEXTURETABLEPROXYMODEL_H
#include "idtableproxymodel.hpp"
namespace CSMWorld
{
/// \brief Removes base records from filtered results.
class LandTextureTableProxyModel : public IdTableProxyModel
{
Q_OBJECT
public:
LandTextureTableProxyModel(QObject* parent = nullptr);
protected:
bool filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const override;
};
}
#endif

@ -813,6 +813,12 @@ void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin,
mData.insertRecord(*newRecord, type, destination);
}
bool CSMWorld::RefIdCollection::touchRecord(const std::string& id)
{
throw std::runtime_error("RefIdCollection::touchRecord is unimplemented");
return false;
}
void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record,
UniversalId::Type type)
{

@ -80,6 +80,8 @@ namespace CSMWorld
const std::string& destination,
const UniversalId::Type type);
virtual bool touchRecord(const std::string& id);
virtual void appendBlankRecord (const std::string& id, UniversalId::Type type);
///< \param type Will be ignored, unless the collection supports multiple record types

@ -53,6 +53,8 @@ namespace
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, "Sound Generators", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Lands, "Lands", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_LandTextures, "LandTextures", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Meta Data Table", 0 },
@ -118,6 +120,8 @@ namespace
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_DebugProfile, "Debug Profile", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_SoundGen, "Sound Generator", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MagicEffect, "Magic Effect", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Land, "Land", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_LandTexture, "LandTexture", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Pathgrid, "Pathgrid", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Meta Data", 0 },

@ -126,6 +126,10 @@ namespace CSMWorld
Type_SoundGen,
Type_MagicEffects,
Type_MagicEffect,
Type_Lands,
Type_Land,
Type_LandTextures,
Type_LandTexture,
Type_Pathgrids,
Type_Pathgrid,
Type_StartScripts,

@ -172,6 +172,16 @@ void CSVDoc::View::setupWorldMenu()
setupShortcut("document-world-references", references);
world->addAction (references);
QAction *lands = new QAction (tr ("Lands"), this);
connect (lands, SIGNAL (triggered()), this, SLOT (addLandsSubView()));
setupShortcut("document-world-lands", lands);
world->addAction (lands);
QAction *landTextures = new QAction (tr ("Land Textures"), this);
connect (landTextures, SIGNAL (triggered()), this, SLOT (addLandTexturesSubView()));
setupShortcut("document-world-landtextures", landTextures);
world->addAction (landTextures);
QAction *grid = new QAction (tr ("Pathgrid"), this);
connect (grid, SIGNAL (triggered()), this, SLOT (addPathgridSubView()));
setupShortcut("document-world-pathgrid", grid);
@ -876,6 +886,16 @@ void CSVDoc::View::addRunLogSubView()
addSubView (CSMWorld::UniversalId::Type_RunLog);
}
void CSVDoc::View::addLandsSubView()
{
addSubView (CSMWorld::UniversalId::Type_Lands);
}
void CSVDoc::View::addLandTexturesSubView()
{
addSubView (CSMWorld::UniversalId::Type_LandTextures);
}
void CSVDoc::View::addPathgridSubView()
{
addSubView (CSMWorld::UniversalId::Type_Pathgrids);

@ -224,6 +224,10 @@ namespace CSVDoc
void addRunLogSubView();
void addLandsSubView();
void addLandTexturesSubView();
void addPathgridSubView();
void addStartScriptsSubView();

@ -26,6 +26,33 @@
#include "terrainstorage.hpp"
#include "object.hpp"
namespace CSVRender
{
class CellNodeContainer : public osg::Referenced
{
public:
CellNodeContainer(Cell* cell) : mCell(cell) {}
Cell* getCell(){ return mCell; }
private:
Cell* mCell;
};
class CellNodeCallback : public osg::NodeCallback
{
public:
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
CellNodeContainer* container = static_cast<CellNodeContainer*>(node->getUserData());
container->getCell()->updateLand();
}
};
}
bool CSVRender::Cell::removeObject (const std::string& id)
{
std::map<std::string, Object *>::iterator iter =
@ -75,10 +102,69 @@ bool CSVRender::Cell::addObjects (int start, int end)
return modified;
}
void CSVRender::Cell::updateLand()
{
if (!mUpdateLand || mLandDeleted)
return;
mUpdateLand = false;
// Cell is deleted
if (mDeleted)
{
unloadLand();
return;
}
// Setup land if available
const CSMWorld::IdCollection<CSMWorld::Land>& land = mData.getLand();
int landIndex = land.searchId(mId);
if (landIndex != -1 && !land.getRecord(mId).isDeleted())
{
const ESM::Land& esmLand = land.getRecord(mId).get();
if (esmLand.getLandData (ESM::Land::DATA_VHGT))
{
if (mTerrain)
{
mTerrain->unloadCell(mCoordinates.getX(), mCoordinates.getY());
mTerrain->clearAssociatedCaches();
}
else
{
mTerrain.reset(new Terrain::TerrainGrid(mCellNode, mCellNode,
mData.getResourceSystem().get(), new TerrainStorage(mData), Mask_Terrain));
}
mTerrain->loadCell(esmLand.mX, esmLand.mY);
if (!mCellBorder)
mCellBorder.reset(new CellBorder(mCellNode, mCoordinates));
mCellBorder->buildShape(esmLand);
return;
}
}
// No land data
mLandDeleted = true;
unloadLand();
}
void CSVRender::Cell::unloadLand()
{
if (mTerrain)
mTerrain->unloadCell(mCoordinates.getX(), mCoordinates.getY());
if (mCellBorder)
mCellBorder.reset();
}
CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::string& id,
bool deleted)
: mData (data), mId (Misc::StringUtils::lowerCase (id)), mDeleted (deleted), mSubMode (0),
mSubModeElementMask (0)
mSubModeElementMask (0), mUpdateLand(true), mLandDeleted(false)
{
std::pair<CSMWorld::CellCoordinates, bool> result = CSMWorld::CellCoordinates::fromId (id);
@ -86,6 +172,8 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st
mCoordinates = result.first;
mCellNode = new osg::Group;
mCellNode->setUserData(new CellNodeContainer(this));
mCellNode->setUpdateCallback(new CellNodeCallback);
rootNode->addChild(mCellNode);
setCellMarker();
@ -99,22 +187,7 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st
addObjects (0, rows-1);
const CSMWorld::IdCollection<CSMWorld::Land>& land = mData.getLand();
int landIndex = land.searchId(mId);
if (landIndex != -1)
{
const ESM::Land& esmLand = land.getRecord(mId).get();
if (esmLand.getLandData (ESM::Land::DATA_VHGT))
{
mTerrain.reset(new Terrain::TerrainGrid(mCellNode, mCellNode, data.getResourceSystem().get(), new TerrainStorage(mData), Mask_Terrain));
mTerrain->loadCell(esmLand.mX,
esmLand.mY);
mCellBorder.reset(new CellBorder(mCellNode, mCoordinates));
mCellBorder->buildShape(esmLand);
}
}
updateLand();
mPathgrid.reset(new Pathgrid(mData, mCellNode, mId, mCoordinates));
mCellWater.reset(new CellWater(mData, mCellNode, mId, mCoordinates));
@ -285,6 +358,38 @@ void CSVRender::Cell::pathgridRemoved()
mPathgrid->removeGeometry();
}
void CSVRender::Cell::landDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
mUpdateLand = true;
}
void CSVRender::Cell::landAboutToBeRemoved (const QModelIndex& parent, int start, int end)
{
mLandDeleted = true;
unloadLand();
}
void CSVRender::Cell::landAdded (const QModelIndex& parent, int start, int end)
{
mUpdateLand = true;
mLandDeleted = false;
}
void CSVRender::Cell::landTextureChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
mUpdateLand = true;
}
void CSVRender::Cell::landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end)
{
mUpdateLand = true;
}
void CSVRender::Cell::landTextureAdded (const QModelIndex& parent, int start, int end)
{
mUpdateLand = true;
}
void CSVRender::Cell::reloadAssets()
{
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());

@ -57,6 +57,7 @@ namespace CSVRender
bool mDeleted;
int mSubMode;
unsigned int mSubModeElementMask;
bool mUpdateLand, mLandDeleted;
/// Ignored if cell does not have an object with the given ID.
///
@ -72,6 +73,9 @@ namespace CSVRender
/// \return Have any objects been added?
bool addObjects (int start, int end);
void updateLand();
void unloadLand();
public:
enum Selection
@ -118,6 +122,18 @@ namespace CSVRender
void pathgridRemoved();
void landDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void landAboutToBeRemoved (const QModelIndex& parent, int start, int end);
void landAdded (const QModelIndex& parent, int start, int end);
void landTextureChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end);
void landTextureAdded (const QModelIndex& parent, int start, int end);
void reloadAssets();
void setSelection (int elementMask, Selection mode);
@ -145,6 +161,8 @@ namespace CSVRender
/// Erase all overrides and restore the visual representation of the cell to its
/// true state.
void reset (unsigned int elementMask);
friend class CellNodeCallback;
};
}

@ -351,6 +351,72 @@ void CSVRender::PagedWorldspaceWidget::pathgridAdded(const QModelIndex& parent,
}
}
void CSVRender::PagedWorldspaceWidget::landDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
for (int r = topLeft.row(); r <= bottomRight.row(); ++r)
{
std::string id = mDocument.getData().getLand().getId(r);
auto cellIt = mCells.find(CSMWorld::CellCoordinates::fromId(id).first);
if (cellIt != mCells.end())
{
cellIt->second->landDataChanged(topLeft, bottomRight);
flagAsModified();
}
}
}
void CSVRender::PagedWorldspaceWidget::landAboutToBeRemoved (const QModelIndex& parent, int start, int end)
{
for (int r = start; r <= end; ++r)
{
std::string id = mDocument.getData().getLand().getId(r);
auto cellIt = mCells.find(CSMWorld::CellCoordinates::fromId(id).first);
if (cellIt != mCells.end())
{
cellIt->second->landAboutToBeRemoved(parent, start, end);
flagAsModified();
}
}
}
void CSVRender::PagedWorldspaceWidget::landAdded (const QModelIndex& parent, int start, int end)
{
for (int r = start; r <= end; ++r)
{
std::string id = mDocument.getData().getLand().getId(r);
auto cellIt = mCells.find(CSMWorld::CellCoordinates::fromId(id).first);
if (cellIt != mCells.end())
{
cellIt->second->landAdded(parent, start, end);
flagAsModified();
}
}
}
void CSVRender::PagedWorldspaceWidget::landTextureDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
for (auto cellIt : mCells)
cellIt.second->landTextureChanged(topLeft, bottomRight);
flagAsModified();
}
void CSVRender::PagedWorldspaceWidget::landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end)
{
for (auto cellIt : mCells)
cellIt.second->landTextureAboutToBeRemoved(parent, start, end);
flagAsModified();
}
void CSVRender::PagedWorldspaceWidget::landTextureAdded (const QModelIndex& parent, int start, int end)
{
for (auto cellIt : mCells)
cellIt.second->landTextureAdded(parent, start, end);
flagAsModified();
}
std::string CSVRender::PagedWorldspaceWidget::getStartupInstruction()
{
@ -475,6 +541,24 @@ CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc
connect (&document.getData(), SIGNAL (assetTablesChanged ()),
this, SLOT (assetTablesChanged ()));
QAbstractItemModel *lands = document.getData().getTableModel (CSMWorld::UniversalId::Type_Lands);
connect (lands, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
this, SLOT (landDataChanged (const QModelIndex&, const QModelIndex&)));
connect (lands, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
this, SLOT (landAboutToBeRemoved (const QModelIndex&, int, int)));
connect (lands, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (landAdded (const QModelIndex&, int, int)));
QAbstractItemModel *ltexs = document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures);
connect (ltexs, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
this, SLOT (landTextureDataChanged (const QModelIndex&, const QModelIndex&)));
connect (ltexs, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
this, SLOT (landTextureAboutToBeRemoved (const QModelIndex&, int, int)));
connect (ltexs, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (landTextureAdded (const QModelIndex&, int, int)));
// Shortcuts
CSMPrefs::Shortcut* loadCameraCellShortcut = new CSMPrefs::Shortcut("scene-load-cam-cell", this);
connect(loadCameraCellShortcut, SIGNAL(activated()), this, SLOT(loadCameraCell()));

@ -155,6 +155,14 @@ namespace CSVRender
virtual void cellAdded (const QModelIndex& index, int start, int end);
virtual void landDataChanged (const QModelIndex& topLeft, const QModelIndex& botomRight);
virtual void landAboutToBeRemoved (const QModelIndex& parent, int start, int end);
virtual void landAdded (const QModelIndex& parent, int start, int end);
virtual void landTextureDataChanged (const QModelIndex& topLeft, const QModelIndex& botomRight);
virtual void landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end);
virtual void landTextureAdded (const QModelIndex& parent, int start, int end);
void assetTablesChanged ();
void loadCameraCell();

@ -1,5 +1,8 @@
#include "terrainstorage.hpp"
#include "../../model/world/land.hpp"
#include "../../model/world/landtexture.hpp"
namespace CSVRender
{
@ -11,12 +14,9 @@ namespace CSVRender
osg::ref_ptr<const ESMTerrain::LandObject> TerrainStorage::getLand(int cellX, int cellY)
{
std::ostringstream stream;
stream << "#" << cellX << " " << cellY;
// The cell isn't guaranteed to have Land. This is because the terrain implementation
// has to wrap the vertices of the last row and column to the next cell, which may be a nonexisting cell
int index = mData.getLand().searchId(stream.str());
int index = mData.getLand().searchId(CSMWorld::Land::createUniqueRecordId(cellX, cellY));
if (index == -1)
return NULL;
@ -26,16 +26,11 @@ namespace CSVRender
const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin)
{
int numRecords = mData.getLandTextures().getSize();
for (int i=0; i<numRecords; ++i)
{
const CSMWorld::LandTexture* ltex = &mData.getLandTextures().getRecord(i).get();
if (ltex->mIndex == index && ltex->mPluginIndex == plugin)
return ltex;
}
int row = mData.getLandTextures().searchId(CSMWorld::LandTexture::createUniqueRecordId(plugin, index));
if (row == -1)
return nullptr;
return NULL;
return &mData.getLandTextures().getRecord(row).get();
}
void TerrainStorage::getBounds(float &minX, float &maxX, float &minY, float &maxY)

@ -31,6 +31,9 @@ namespace CSVWorld
virtual void cloneMode(const std::string& originId,
const CSMWorld::UniversalId::Type type) = 0;
/// Touches a record, if the creator supports it.
virtual void touch(const std::vector<CSMWorld::UniversalId>& ids) = 0;
virtual void setEditLock (bool locked) = 0;
virtual void toggleWidgets(bool active = true) = 0;

@ -51,6 +51,11 @@ std::string CSVWorld::GenericCreator::getId() const
return mId->text().toUtf8().constData();
}
std::string CSVWorld::GenericCreator::getClonedId() const
{
return mClonedId;
}
std::string CSVWorld::GenericCreator::getIdValidatorResult() const
{
std::string errors;
@ -254,6 +259,22 @@ void CSVWorld::GenericCreator::cloneMode(const std::string& originId,
mClonedType = type;
}
void CSVWorld::GenericCreator::touch(const std::vector<CSMWorld::UniversalId>& ids)
{
// Combine multiple touch commands into one "macro" command
mUndoStack.beginMacro("Touch Records");
CSMWorld::IdTable& table = dynamic_cast<CSMWorld::IdTable&>(*mData.getTableModel(mListId));
for (const CSMWorld::UniversalId& uid : ids)
{
CSMWorld::TouchCommand* touchCmd = new CSMWorld::TouchCommand(table, uid.getId());
mUndoStack.push(touchCmd);
}
// Execute
mUndoStack.endMacro();
}
void CSVWorld::GenericCreator::toggleWidgets(bool active)
{
}

@ -64,6 +64,8 @@ namespace CSVWorld
virtual std::string getId() const;
std::string getClonedId() const;
virtual std::string getIdValidatorResult() const;
/// Allow subclasses to add additional data to \a command.
@ -103,6 +105,8 @@ namespace CSVWorld
virtual void cloneMode(const std::string& originId,
const CSMWorld::UniversalId::Type type);
virtual void touch(const std::vector<CSMWorld::UniversalId>& ids);
virtual std::string getErrors() const;
///< Return formatted error descriptions for the current state of the creator. if an empty
/// string is returned, there is no error.

@ -0,0 +1,120 @@
#include "landcreator.hpp"
#include <limits>
#include <QLabel>
#include <QSpinBox>
#include "../../model/world/commands.hpp"
#include "../../model/world/idtable.hpp"
#include "../../model/world/land.hpp"
namespace CSVWorld
{
LandCreator::LandCreator(CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id)
: GenericCreator(data, undoStack, id)
, mXLabel(nullptr)
, mYLabel(nullptr)
, mX(nullptr)
, mY(nullptr)
{
const int MaxInt = std::numeric_limits<int>::max();
const int MinInt = std::numeric_limits<int>::min();
setManualEditing(false);
mXLabel = new QLabel("X: ");
mX = new QSpinBox();
mX->setMinimum(MinInt);
mX->setMaximum(MaxInt);
insertBeforeButtons(mXLabel, false);
insertBeforeButtons(mX, true);
mYLabel = new QLabel("Y: ");
mY = new QSpinBox();
mY->setMinimum(MinInt);
mY->setMaximum(MaxInt);
insertBeforeButtons(mYLabel, false);
insertBeforeButtons(mY, true);
connect (mX, SIGNAL(valueChanged(int)), this, SLOT(coordChanged(int)));
connect (mY, SIGNAL(valueChanged(int)), this, SLOT(coordChanged(int)));
}
void LandCreator::cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type)
{
GenericCreator::cloneMode(originId, type);
int x = 0, y = 0;
CSMWorld::Land::parseUniqueRecordId(originId, x, y);
mX->setValue(x);
mY->setValue(y);
}
void LandCreator::touch(const std::vector<CSMWorld::UniversalId>& ids)
{
// Combine multiple touch commands into one "macro" command
getUndoStack().beginMacro("Touch records");
CSMWorld::IdTable& lands = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(CSMWorld::UniversalId::Type_Lands));
CSMWorld::IdTable& ltexs = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(CSMWorld::UniversalId::Type_LandTextures));
for (const CSMWorld::UniversalId& uid : ids)
{
CSMWorld::TouchLandCommand* touchCmd = new CSMWorld::TouchLandCommand(lands, ltexs, uid.getId());
getUndoStack().push(touchCmd);
}
// Execute
getUndoStack().endMacro();
}
void LandCreator::focus()
{
mX->setFocus();
}
void LandCreator::reset()
{
GenericCreator::reset();
mX->setValue(0);
mY->setValue(0);
}
std::string LandCreator::getErrors() const
{
if (getData().getLand().searchId(getId()) >= 0)
return "A land with that name already exists.";
return "";
}
std::string LandCreator::getId() const
{
return CSMWorld::Land::createUniqueRecordId(mX->value(), mY->value());
}
void LandCreator::pushCommand(std::unique_ptr<CSMWorld::CreateCommand> command, const std::string& id)
{
if (mCloneMode)
{
CSMWorld::IdTable& lands = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(CSMWorld::UniversalId::Type_Lands));
CSMWorld::IdTable& ltexs = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(CSMWorld::UniversalId::Type_LandTextures));
getUndoStack().beginMacro(("Clone " + id).c_str());
getUndoStack().push(command.release());
CSMWorld::CopyLandTexturesCommand* ltexCopy = new CSMWorld::CopyLandTexturesCommand(lands, ltexs, getClonedId(), getId());
getUndoStack().push(ltexCopy);
getUndoStack().endMacro();
}
else
getUndoStack().push (command.release());
}
void LandCreator::coordChanged(int value)
{
update();
}
}

@ -0,0 +1,47 @@
#ifndef CSV_WORLD_LANDCREATOR_H
#define CSV_WORLD_LANDCREATOR_H
#include "genericcreator.hpp"
class QLabel;
class QSpinBox;
namespace CSVWorld
{
class LandCreator : public GenericCreator
{
Q_OBJECT
QLabel* mXLabel;
QLabel* mYLabel;
QSpinBox* mX;
QSpinBox* mY;
public:
LandCreator(CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id);
void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) override;
void touch(const std::vector<CSMWorld::UniversalId>& ids) override;
void focus() override;
void reset() override;
std::string getErrors() const override;
protected:
std::string getId() const override;
void pushCommand(std::unique_ptr<CSMWorld::CreateCommand> command,
const std::string& id) override;
private slots:
void coordChanged(int value);
};
}
#endif

@ -0,0 +1,101 @@
#include "landtexturecreator.hpp"
#include <cstdint>
#include <limits>
#include <QLabel>
#include <QLineEdit>
#include <QSpinBox>
#include "../../model/world/commands.hpp"
#include "../../model/world/idtable.hpp"
#include "../../model/world/landtexture.hpp"
namespace CSVWorld
{
LandTextureCreator::LandTextureCreator(CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id)
: GenericCreator(data, undoStack, id)
{
// One index is reserved for a default texture
const size_t MaxIndex = std::numeric_limits<uint16_t>::max() - 1;
setManualEditing(false);
QLabel* nameLabel = new QLabel("Name");
insertBeforeButtons(nameLabel, false);
mNameEdit = new QLineEdit(this);
insertBeforeButtons(mNameEdit, true);
QLabel* indexLabel = new QLabel("Index");
insertBeforeButtons(indexLabel, false);
mIndexBox = new QSpinBox(this);
mIndexBox->setMinimum(0);
mIndexBox->setMaximum(MaxIndex);
insertBeforeButtons(mIndexBox, true);
connect(mNameEdit, SIGNAL(textChanged(const QString&)), this, SLOT(nameChanged(const QString&)));
connect(mIndexBox, SIGNAL(valueChanged(int)), this, SLOT(indexChanged(int)));
}
void LandTextureCreator::cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type)
{
GenericCreator::cloneMode(originId, type);
CSMWorld::IdTable& table = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(getCollectionId()));
int column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureNickname);
mNameEdit->setText((table.data(table.getModelIndex(originId, column)).toString()));
column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureIndex);
mIndexBox->setValue((table.data(table.getModelIndex(originId, column)).toInt()));
}
void LandTextureCreator::focus()
{
mIndexBox->setFocus();
}
void LandTextureCreator::reset()
{
GenericCreator::reset();
mNameEdit->setText("");
mIndexBox->setValue(0);
}
std::string LandTextureCreator::getErrors() const
{
if (getData().getLandTextures().searchId(getId()) >= 0)
{
return "Index is already in use";
}
return "";
}
void LandTextureCreator::configureCreateCommand(CSMWorld::CreateCommand& command) const
{
GenericCreator::configureCreateCommand(command);
CSMWorld::IdTable& table = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(getCollectionId()));
int column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureNickname);
command.addValue(column, mName.c_str());
}
std::string LandTextureCreator::getId() const
{
return CSMWorld::LandTexture::createUniqueRecordId(0, mIndexBox->value());
}
void LandTextureCreator::nameChanged(const QString& value)
{
mName = value.toUtf8().constData();
update();
}
void LandTextureCreator::indexChanged(int value)
{
update();
}
}

@ -0,0 +1,49 @@
#ifndef CSV_WORLD_LANDTEXTURECREATOR_H
#define CSV_WORLD_LANDTEXTURECREATOR_H
#include <string>
#include "genericcreator.hpp"
class QLineEdit;
class QSpinBox;
namespace CSVWorld
{
class LandTextureCreator : public GenericCreator
{
Q_OBJECT
public:
LandTextureCreator(CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id);
void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) override;
void focus() override;
void reset() override;
std::string getErrors() const override;
protected:
void configureCreateCommand(CSMWorld::CreateCommand& command) const override;
std::string getId() const override;
private slots:
void nameChanged(const QString& val);
void indexChanged(int val);
private:
QLineEdit* mNameEdit;
QSpinBox* mIndexBox;
std::string mName;
};
}
#endif

@ -18,6 +18,8 @@
#include "pathgridcreator.hpp"
#include "previewsubview.hpp"
#include "bodypartcreator.hpp"
#include "landcreator.hpp"
#include "landtexturecreator.hpp"
void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
{
@ -81,6 +83,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
manager.add (CSMWorld::UniversalId::Type_Pathgrids,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, PathgridCreatorFactory>);
manager.add (CSMWorld::UniversalId::Type_Lands,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<LandCreator> >);
manager.add (CSMWorld::UniversalId::Type_LandTextures,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<LandTextureCreator> >);
manager.add (CSMWorld::UniversalId::Type_Globals,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<GlobalCreator> >);
@ -181,6 +189,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
manager.add (CSMWorld::UniversalId::Type_Pathgrid,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, PathgridCreatorFactory> (false));
manager.add (CSMWorld::UniversalId::Type_Land,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<LandCreator> >(false));
manager.add (CSMWorld::UniversalId::Type_LandTexture,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<LandTextureCreator> >(false));
manager.add (CSMWorld::UniversalId::Type_DebugProfile,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator, CSMWorld::Scope_Project | CSMWorld::Scope_Session> > (false));

@ -16,6 +16,7 @@
#include "../../model/world/idtableproxymodel.hpp"
#include "../../model/world/idtablebase.hpp"
#include "../../model/world/idtable.hpp"
#include "../../model/world/landtexturetableproxymodel.hpp"
#include "../../model/world/record.hpp"
#include "../../model/world/columns.hpp"
#include "../../model/world/commanddispatcher.hpp"
@ -60,6 +61,9 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
menu.addAction(mCloneAction);
}
if (mTouchAction)
menu.addAction (mTouchAction);
if (mCreateAction)
menu.addAction (mCreateAction);
@ -226,17 +230,22 @@ void CSVWorld::Table::mouseDoubleClickEvent (QMouseEvent *event)
CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
bool createAndDelete, bool sorting, CSMDoc::Document& document)
: DragRecordTable(document), mCreateAction (0),
mCloneAction(0), mRecordStatusDisplay (0), mJumpToAddedRecord(false), mUnselectAfterJump(false)
: DragRecordTable(document), mCreateAction (nullptr), mCloneAction(nullptr), mTouchAction(nullptr),
mRecordStatusDisplay (0), mJumpToAddedRecord(false), mUnselectAfterJump(false)
{
mModel = &dynamic_cast<CSMWorld::IdTableBase&> (*mDocument.getData().getTableModel (id));
bool isInfoTable = id.getType() == CSMWorld::UniversalId::Type_TopicInfos ||
id.getType() == CSMWorld::UniversalId::Type_JournalInfos;
bool isLtexTable = (id.getType() == CSMWorld::UniversalId::Type_LandTextures);
if (isInfoTable)
{
mProxyModel = new CSMWorld::InfoTableProxyModel(id.getType(), this);
}
else if (isLtexTable)
{
mProxyModel = new CSMWorld::LandTextureTableProxyModel (this);
}
else
{
mProxyModel = new CSMWorld::IdTableProxyModel (this);
@ -302,6 +311,15 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
cloneShortcut->associateAction(mCloneAction);
}
if (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_AllowTouch)
{
mTouchAction = new QAction(tr("Touch Record"), this);
connect(mTouchAction, SIGNAL(triggered()), this, SLOT(touchRecord()));
addAction(mTouchAction);
CSMPrefs::Shortcut* touchShortcut = new CSMPrefs::Shortcut("table-touch", this);
touchShortcut->associateAction(mTouchAction);
}
mRevertAction = new QAction (tr ("Revert Record"), this);
connect (mRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeRevert()));
addAction (mRevertAction);
@ -442,6 +460,22 @@ void CSVWorld::Table::cloneRecord()
}
}
void CSVWorld::Table::touchRecord()
{
if (!mEditLock && mModel->getFeatures() & CSMWorld::IdTableBase::Feature_AllowTouch)
{
std::vector<CSMWorld::UniversalId> touchIds;
QModelIndexList selectedRows = selectionModel()->selectedRows();
for (auto it = selectedRows.begin(); it != selectedRows.end(); ++it)
{
touchIds.push_back(getUniversalId(it->row()));
}
emit touchRequest(touchIds);
}
}
void CSVWorld::Table::moveUpRecord()
{
if (mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant))

@ -56,6 +56,7 @@ namespace CSVWorld
QAction *mEditAction;
QAction *mCreateAction;
QAction *mCloneAction;
QAction *mTouchAction;
QAction *mRevertAction;
QAction *mDeleteAction;
QAction *mMoveUpAction;
@ -115,6 +116,8 @@ namespace CSVWorld
void cloneRequest(const CSMWorld::UniversalId&);
void touchRequest(const std::vector<CSMWorld::UniversalId>& ids);
void closeRequest();
void extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds);
@ -129,6 +132,8 @@ namespace CSVWorld
void cloneRecord();
void touchRecord();
void moveUpRecord();
void moveDownRecord();

@ -249,6 +249,11 @@ void CSVWorld::TableBottomBox::cloneRequest(const std::string& id,
mCreator->focus();
}
void CSVWorld::TableBottomBox::touchRequest(const std::vector<CSMWorld::UniversalId>& ids)
{
mCreator->touch(ids);
}
void CSVWorld::TableBottomBox::extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds)
{
extendedConfigRequest(ExtendedCommandConfigurator::Mode_Delete, selectedIds);

@ -102,6 +102,7 @@ namespace CSVWorld
void createRequest();
void cloneRequest(const std::string& id,
const CSMWorld::UniversalId::Type type);
void touchRequest(const std::vector<CSMWorld::UniversalId>&);
void extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds);
void extendedRevertConfigRequest(const std::vector<std::string> &selectedIds);

@ -69,6 +69,9 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D
connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)),
mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)));
connect (mTable, SIGNAL(touchRequest(const std::vector<CSMWorld::UniversalId>&)),
mBottom, SLOT(touchRequest(const std::vector<CSMWorld::UniversalId>&)));
connect (mTable, SIGNAL(extendedDeleteConfigRequest(const std::vector<std::string> &)),
mBottom, SLOT(extendedDeleteConfigRequest(const std::vector<std::string> &)));
connect (mTable, SIGNAL(extendedRevertConfigRequest(const std::vector<std::string> &)),

@ -42,7 +42,7 @@ add_openmw_dir (mwgui
itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview
tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog
recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview
draganddrop timeadvancer jailscreen itemchargeview
draganddrop timeadvancer jailscreen itemchargeview keyboardnavigation
)
add_openmw_dir (mwdialogue

@ -231,11 +231,6 @@ void OMW::Engine::frame(float frametime)
// update GUI
mEnvironment.getWindowManager()->onFrame(frametime);
if (mEnvironment.getStateManager()->getState()!=
MWBase::StateManager::State_NoGame)
{
mEnvironment.getWindowManager()->update();
}
unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber();
osg::Stats* stats = mViewer->getViewerStats();

@ -2,6 +2,8 @@
#define GAME_MWBASE_DIALOGUEMANAGER_H
#include <string>
#include <vector>
#include <list>
#include <stdint.h>
@ -42,7 +44,8 @@ namespace MWBase
virtual bool isInChoice() const = 0;
virtual void startDialogue (const MWWorld::Ptr& actor) = 0;
typedef std::pair<std::string, std::string> Response; // title, text
virtual bool startDialogue (const MWWorld::Ptr& actor, Response& response) = 0;
virtual void addTopic (const std::string& topic) = 0;
@ -57,20 +60,24 @@ namespace MWBase
End of tes3mp addition
*/
virtual void askQuestion (const std::string& question,int choice) = 0;
virtual void addChoice (const std::string& text,int choice) = 0;
virtual const std::vector<std::pair<std::string, int> >& getChoices() = 0;
virtual bool isGoodbye() = 0;
virtual void goodbye() = 0;
virtual void say(const MWWorld::Ptr &actor, const std::string &topic) = 0;
//calbacks for the GUI
virtual void keywordSelected (const std::string& keyword) = 0;
virtual Response keywordSelected (const std::string& keyword) = 0;
virtual void goodbyeSelected() = 0;
virtual void questionAnswered (int answer) = 0;
virtual Response questionAnswered (int answer) = 0;
virtual std::list<std::string> getAvailableTopics() = 0;
virtual bool checkServiceRefused () = 0;
virtual bool checkServiceRefused (Response& response) = 0;
virtual void persuade (int type) = 0;
virtual Response persuade (int type) = 0;
virtual int getTemporaryDispositionChange () const = 0;
/// @note This change is temporary and gets discarded when dialogue ends.

@ -248,7 +248,7 @@ namespace MWBase
/// Has the player stolen this item from the given owner?
virtual bool isItemStolenFrom(const std::string& itemid, const std::string& ownerid) = 0;
virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::ConstPtr& item, MWWorld::Ptr& victim) = 0;
virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, MWWorld::Ptr& victim) = 0;
/// Turn actor into werewolf or normal form.
virtual void setWerewolf(const MWWorld::Ptr& actor, bool werewolf) = 0;

@ -7,6 +7,8 @@
#include <map>
#include <set>
#include <MyGUI_KeyCode.h>
#include "../mwgui/mode.hpp"
namespace Loading
@ -100,23 +102,17 @@ namespace MWBase
virtual ~WindowManager() {}
/**
* Should be called each frame to update windows/gui elements.
* This could mean updating sizes of gui elements or opening
* new dialogs.
*/
virtual void update() = 0;
/// @note This method will block until the video finishes playing
/// (and will continually update the window while doing so)
virtual void playVideo(const std::string& name, bool allowSkipping) = 0;
virtual void setNewGame(bool newgame) = 0;
virtual void pushGuiMode (MWGui::GuiMode mode, const MWWorld::Ptr& arg) = 0;
virtual void pushGuiMode (MWGui::GuiMode mode) = 0;
virtual void popGuiMode() = 0;
virtual void popGuiMode(bool noSound=false) = 0;
virtual void removeGuiMode (MWGui::GuiMode mode) = 0;
virtual void removeGuiMode (MWGui::GuiMode mode, bool noSound=false) = 0;
///< can be anywhere in the stack
virtual void goToJail(int days) = 0;
@ -144,7 +140,6 @@ namespace MWBase
virtual bool isAllowed (MWGui::GuiWindow wnd) const = 0;
/// \todo investigate, if we really need to expose every single lousy UI element to the outside world
virtual MWGui::DialogueWindow* getDialogueWindow() = 0;
virtual MWGui::InventoryWindow* getInventoryWindow() = 0;
virtual MWGui::CountDialog* getCountDialog() = 0;
virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0;
@ -195,6 +190,7 @@ namespace MWBase
virtual void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) = 0;
virtual void setCursorVisible(bool visible) = 0;
virtual void setCursorActive(bool active) = 0;
virtual void getMousePosition(int &x, int &y) = 0;
virtual void getMousePosition(float &x, float &y) = 0;
virtual void setDragDrop(bool dragDrop) = 0;
@ -238,7 +234,7 @@ namespace MWBase
virtual void showCrosshair(bool show) = 0;
virtual bool getSubtitlesEnabled() = 0;
virtual bool toggleGui() = 0;
virtual bool toggleHud() = 0;
virtual void disallowMouse() = 0;
virtual void allowMouse() = 0;
@ -302,21 +298,6 @@ namespace MWBase
virtual bool getPlayerSleeping() = 0;
virtual void wakeUpPlayer() = 0;
virtual void showCompanionWindow(MWWorld::Ptr actor) = 0;
virtual void startSpellMaking(MWWorld::Ptr actor) = 0;
virtual void startEnchanting(MWWorld::Ptr actor) = 0;
virtual void startRecharge(MWWorld::Ptr soulgem) = 0;
virtual void startSelfEnchanting(MWWorld::Ptr soulgem) = 0;
virtual void startTraining(MWWorld::Ptr actor) = 0;
virtual void startRepair(MWWorld::Ptr actor) = 0;
virtual void startRepairItem(MWWorld::Ptr item) = 0;
virtual void startTravel(const MWWorld::Ptr& actor) = 0;
virtual void startSpellBuying(const MWWorld::Ptr& actor) = 0;
virtual void startTrade(const MWWorld::Ptr& actor) = 0;
virtual void openContainer(const MWWorld::Ptr& container, bool loot) = 0;
virtual void showBook(const MWWorld::Ptr& item, bool showTakeButton) = 0;
virtual void showScroll(const MWWorld::Ptr& item, bool showTakeButton) = 0;
virtual void showSoulgemDialog (MWWorld::Ptr item) = 0;
virtual void changePointer (const std::string& name) = 0;
@ -358,11 +339,11 @@ namespace MWBase
virtual void pinWindow (MWGui::GuiWindow window) = 0;
/// Fade the screen in, over \a time seconds
virtual void fadeScreenIn(const float time, bool clearQueue=true) = 0;
virtual void fadeScreenIn(const float time, bool clearQueue=true, float delay=0.f) = 0;
/// Fade the screen out to black, over \a time seconds
virtual void fadeScreenOut(const float time, bool clearQueue=true) = 0;
virtual void fadeScreenOut(const float time, bool clearQueue=true, float delay=0.f) = 0;
/// Fade the screen to a specified percentage of black, over \a time seconds
virtual void fadeScreenTo(const int percent, const float time, bool clearQueue=true) = 0;
virtual void fadeScreenTo(const int percent, const float time, bool clearQueue=true, float delay=0.f) = 0;
/// Darken the screen to a specified percentage
virtual void setBlindness(const int percent) = 0;
@ -388,6 +369,8 @@ namespace MWBase
virtual void writeFog(MWWorld::CellStore* cell) = 0;
virtual const MWGui::TextColours& getTextColours() = 0;
virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text) = 0;
};
}

@ -567,11 +567,11 @@ namespace MWClass
// by default user can loot friendly actors during death animation
if (canLoot && !stats.getAiSequence().isInCombat())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));
// otherwise wait until death animation
if(stats.isDeathAnimationFinished())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));
// death animation is not finished, do nothing
return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));
@ -580,6 +580,9 @@ namespace MWClass
if(stats.getAiSequence().isInCombat())
return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction(""));
if(stats.getKnockedDown())
return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction(""));
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr));
}

@ -1008,11 +1008,11 @@ namespace MWClass
// by default user can loot friendly actors during death animation
if (canLoot && !stats.getAiSequence().isInCombat())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));
// otherwise wait until death animation
if(stats.isDeathAnimationFinished())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));
// death animation is not finished, do nothing
return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));

@ -5,6 +5,7 @@
#include <algorithm>
#include <iterator>
#include <list>
#include <iostream>
#include <components/esm/loaddial.hpp>
#include <components/esm/loadinfo.hpp>
@ -46,8 +47,6 @@
#include "../mwworld/containerstore.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwgui/dialogue.hpp"
#include "../mwscript/compilercontext.hpp"
#include "../mwscript/interpretercontext.hpp"
#include "../mwscript/extensions.hpp"
@ -72,16 +71,8 @@ namespace MWDialogue
{
mChoice = -1;
mIsInChoice = false;
mGoodbye = false;
mCompilerContext.setExtensions (&extensions);
const MWWorld::Store<ESM::Dialogue> &dialogs =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
MWWorld::Store<ESM::Dialogue>::iterator it = dialogs.begin();
for (; it != dialogs.end(); ++it)
{
mDialogueMap[Misc::StringUtils::lowerCase(it->mId)] = *it;
}
}
void DialogueManager::clear()
@ -146,17 +137,15 @@ namespace MWDialogue
if (mActorKnownTopics.count( topicId ))
mKnownTopics.insert( topicId );
}
updateTopics();
}
void DialogueManager::startDialogue (const MWWorld::Ptr& actor)
bool DialogueManager::startDialogue (const MWWorld::Ptr& actor, Response& response)
{
updateGlobals();
// Dialogue with dead actor (e.g. through script) should not be allowed.
if (actor.getClass().getCreatureStats(actor).isDead())
return;
return false;
mLastTopic = "";
mPermanentDispositionChange = 0;
@ -164,6 +153,8 @@ namespace MWDialogue
mChoice = -1;
mIsInChoice = false;
mGoodbye = false;
mChoices.clear();
mActor = actor;
@ -172,13 +163,6 @@ namespace MWDialogue
mActorKnownTopics.clear();
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
// If the dialogue window was already open, keep the existing history
bool resetHistory = (!MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Dialogue));
win->startDialogue(actor, actor.getClass().getName (actor), resetHistory);
//greeting
const MWWorld::Store<ESM::Dialogue> &dialogs =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
@ -192,9 +176,6 @@ namespace MWDialogue
// Search a response (we do not accept a fallback to "Info refusal" here)
if (const ESM::DialInfo *info = filter.search (*it, false))
{
//initialise the GUI
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue);
creatureStats.talkedToPlayer();
if (!info->mSound.empty())
@ -203,29 +184,23 @@ namespace MWDialogue
}
// first topics update so that parseText knows the keywords to highlight
updateTopics();
updateActorKnownTopics();
parseText (info->mResponse);
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), "", false);
response = Response ("", Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
executeScript (info->mResultScript, mActor);
mLastTopic = Misc::StringUtils::lowerCase(it->mId);
mLastTopic = it->mId;
// update topics again to accommodate changes resulting from executeScript
updateTopics();
updateActorKnownTopics();
return;
return true;
}
}
}
// No greetings found. The dialogue window should not be shown.
// If this is a companion, we must show the companion window directly (used by BM_bear_be_unique).
bool isCompanion = !mActor.getClass().getScript(mActor).empty()
&& mActor.getRefData().getLocals().getIntVar(mActor.getClass().getScript(mActor), "companion");
if (isCompanion)
MWBase::Environment::get().getWindowManager()->showCompanionWindow(mActor);
return false;
}
bool DialogueManager::compile (const std::string& cmd, std::vector<Interpreter::Type_Code>& code, const MWWorld::Ptr& actor)
@ -303,8 +278,9 @@ namespace MWDialogue
}
}
void DialogueManager::executeTopic (const std::string& topic)
DialogueManager::Response DialogueManager::executeTopic (const std::string& topic)
{
DialogueManager::Response response;
Filter filter (mActor, mChoice, mTalkedTo);
const MWWorld::Store<ESM::Dialogue> &dialogues =
@ -312,8 +288,6 @@ namespace MWDialogue
const ESM::Dialogue& dialogue = *dialogues.find (topic);
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
const ESM::DialInfo* info = filter.search(dialogue, true);
if (info)
{
@ -338,7 +312,7 @@ namespace MWDialogue
title = topic;
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), title);
response = Response(title, Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
if (dialogue.mType == ESM::Dialogue::Topic)
{
@ -349,7 +323,7 @@ namespace MWDialogue
{
if (iter->mId == info->mId)
{
MWBase::Environment::get().getJournal()->addTopic (topic, info->mId, mActor);
MWBase::Environment::get().getJournal()->addTopic (Misc::StringUtils::lowerCase(topic), info->mId, mActor);
break;
}
}
@ -359,11 +333,12 @@ namespace MWDialogue
mLastTopic = topic;
}
else
{
// no response found, print a fallback text
win->addResponse ("", topic);
return response;
}
const ESM::Dialogue *DialogueManager::searchDialogue(const std::string& id)
{
return MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().search(id);
}
void DialogueManager::updateGlobals()
@ -371,19 +346,16 @@ namespace MWDialogue
MWBase::Environment::get().getWorld()->updateDialogueGlobals();
}
void DialogueManager::updateTopics()
void DialogueManager::updateActorKnownTopics()
{
updateGlobals();
std::list<std::string> keywordList;
int choice = mChoice;
mChoice = -1;
mActorKnownTopics.clear();
const MWWorld::Store<ESM::Dialogue> &dialogs =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
Filter filter (mActor, mChoice, mTalkedTo);
Filter filter (mActor, -1, mTalkedTo);
for (MWWorld::Store<ESM::Dialogue>::iterator iter = dialogs.begin(); iter != dialogs.end(); ++iter)
{
@ -393,90 +365,42 @@ namespace MWDialogue
{
std::string lower = Misc::StringUtils::lowerCase(iter->mId);
mActorKnownTopics.insert (lower);
//does the player know the topic?
if (mKnownTopics.count(lower))
{
keywordList.push_back (iter->mId);
}
}
}
}
// check the available services of this actor
int services = 0;
if (mActor.getTypeName() == typeid(ESM::NPC).name())
{
MWWorld::LiveCellRef<ESM::NPC>* ref = mActor.get<ESM::NPC>();
if (ref->mBase->mHasAI)
services = ref->mBase->mAiData.mServices;
}
else if (mActor.getTypeName() == typeid(ESM::Creature).name())
{
MWWorld::LiveCellRef<ESM::Creature>* ref = mActor.get<ESM::Creature>();
if (ref->mBase->mHasAI)
services = ref->mBase->mAiData.mServices;
}
int windowServices = 0;
if (services & ESM::NPC::Weapon
|| services & ESM::NPC::Armor
|| services & ESM::NPC::Clothing
|| services & ESM::NPC::Books
|| services & ESM::NPC::Ingredients
|| services & ESM::NPC::Picks
|| services & ESM::NPC::Probes
|| services & ESM::NPC::Lights
|| services & ESM::NPC::Apparatus
|| services & ESM::NPC::RepairItem
|| services & ESM::NPC::Misc)
windowServices |= MWGui::DialogueWindow::Service_Trade;
if((mActor.getTypeName() == typeid(ESM::NPC).name() && !mActor.get<ESM::NPC>()->mBase->getTransport().empty())
|| (mActor.getTypeName() == typeid(ESM::Creature).name() && !mActor.get<ESM::Creature>()->mBase->getTransport().empty()))
windowServices |= MWGui::DialogueWindow::Service_Travel;
if (services & ESM::NPC::Spells)
windowServices |= MWGui::DialogueWindow::Service_BuySpells;
if (services & ESM::NPC::Spellmaking)
windowServices |= MWGui::DialogueWindow::Service_CreateSpells;
if (services & ESM::NPC::Training)
windowServices |= MWGui::DialogueWindow::Service_Training;
if (services & ESM::NPC::Enchanting)
windowServices |= MWGui::DialogueWindow::Service_Enchant;
if (services & ESM::NPC::Repair)
windowServices |= MWGui::DialogueWindow::Service_Repair;
std::list<std::string> DialogueManager::getAvailableTopics()
{
updateActorKnownTopics();
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
std::list<std::string> keywordList;
win->setServices (windowServices);
for (const std::string& topic : mActorKnownTopics)
{
//does the player know the topic?
if (mKnownTopics.count(Misc::StringUtils::lowerCase(topic)))
keywordList.push_back(topic);
}
// sort again, because the previous sort was case-sensitive
keywordList.sort(Misc::StringUtils::ciLess);
win->setKeywords(keywordList);
mChoice = choice;
return keywordList;
}
void DialogueManager::keywordSelected (const std::string& keyword)
DialogueManager::Response DialogueManager::keywordSelected (const std::string& keyword)
{
Response response;
if(!mIsInChoice)
{
if(mDialogueMap.find(keyword) != mDialogueMap.end())
{
if (mDialogueMap[keyword].mType == ESM::Dialogue::Topic)
const ESM::Dialogue* dialogue = searchDialogue(keyword);
if (dialogue && dialogue->mType == ESM::Dialogue::Topic)
{
executeTopic (keyword);
response = executeTopic (keyword);
}
}
}
updateTopics();
return response;
}
bool DialogueManager::isInChoice() const
@ -486,8 +410,6 @@ namespace MWDialogue
void DialogueManager::goodbyeSelected()
{
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue);
// Apply disposition change to NPC's base disposition
if (mActor.getClass().isNpc())
{
@ -503,37 +425,38 @@ namespace MWDialogue
mTemporaryDispositionChange = 0;
}
void DialogueManager::questionAnswered (int answer)
DialogueManager::Response DialogueManager::questionAnswered (int answer)
{
mChoice = answer;
DialogueManager::Response response;
if (mDialogueMap.find(mLastTopic) != mDialogueMap.end())
const ESM::Dialogue* dialogue = searchDialogue(mLastTopic);
if (dialogue)
{
Filter filter (mActor, mChoice, mTalkedTo);
if (mDialogueMap[mLastTopic].mType == ESM::Dialogue::Topic
|| mDialogueMap[mLastTopic].mType == ESM::Dialogue::Greeting)
if (dialogue->mType == ESM::Dialogue::Topic || dialogue->mType == ESM::Dialogue::Greeting)
{
if (const ESM::DialInfo *info = filter.search (mDialogueMap[mLastTopic], true))
if (const ESM::DialInfo *info = filter.search (*dialogue, true))
{
std::string text = info->mResponse;
parseText (text);
mChoice = -1;
mIsInChoice = false;
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->clearChoices();
mChoices.clear();
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addResponse (Interpreter::fixDefinesDialog(text, interpreterContext));
response = Response("", Interpreter::fixDefinesDialog(text, interpreterContext));
// Make sure the returned DialInfo is from the Dialogue we supplied. If could also be from the Info refusal group,
// in which case it should not be added to the journal.
for (ESM::Dialogue::InfoContainer::const_iterator iter = mDialogueMap[mLastTopic].mInfo.begin();
iter!=mDialogueMap[mLastTopic].mInfo.end(); ++iter)
for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue->mInfo.begin();
iter!=dialogue->mInfo.end(); ++iter)
{
if (iter->mId == info->mId)
{
MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId, mActor);
MWBase::Environment::get().getJournal()->addTopic (Misc::StringUtils::lowerCase(mLastTopic), info->mId, mActor);
break;
}
}
@ -544,32 +467,39 @@ namespace MWDialogue
{
mChoice = -1;
mIsInChoice = false;
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->clearChoices();
mChoices.clear();
}
}
}
updateTopics();
updateActorKnownTopics();
return response;
}
void DialogueManager::askQuestion (const std::string& question, int choice)
void DialogueManager::addChoice (const std::string& text, int choice)
{
mIsInChoice = true;
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
win->addChoice(question, choice);
mChoices.push_back(std::make_pair(text, choice));
}
void DialogueManager::goodbye()
const std::vector<std::pair<std::string, int> >& DialogueManager::getChoices()
{
mIsInChoice = true;
return mChoices;
}
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
bool DialogueManager::isGoodbye()
{
return mGoodbye;
}
win->goodbye();
void DialogueManager::goodbye()
{
mIsInChoice = false;
mGoodbye = true;
}
void DialogueManager::persuade(int type)
DialogueManager::Response DialogueManager::persuade(int type)
{
bool success;
float temp, perm;
@ -618,7 +548,7 @@ namespace MWDialogue
text = "Bribe";
}
executeTopic (text + (success ? " Success" : " Fail"));
return executeTopic (text + (success ? " Success" : " Fail"));
}
int DialogueManager::getTemporaryDispositionChange() const
@ -631,7 +561,7 @@ namespace MWDialogue
mTemporaryDispositionChange += delta;
}
bool DialogueManager::checkServiceRefused()
bool DialogueManager::checkServiceRefused(Response& response)
{
Filter filter (mActor, mChoice, mTalkedTo);
@ -639,7 +569,6 @@ namespace MWDialogue
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
const ESM::Dialogue& dialogue = *dialogues.find ("Service Refusal");
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
std::vector<const ESM::DialInfo *> infos = filter.list (dialogue, false, false, true);
if (!infos.empty())
@ -653,8 +582,7 @@ namespace MWDialogue
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext),
gmsts.find ("sServiceRefusal")->getString());
response = Response(gmsts.find ("sServiceRefusal")->getString(), Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
executeScript (info->mResultScript, mActor);
return true;
@ -677,6 +605,12 @@ namespace MWDialogue
return;
}
if (actor.getClass().getCreatureStats(actor).getKnockedDown())
{
// Unconscious actors can not speak
return;
}
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Dialogue *dial = store.get<ESM::Dialogue>().find(topic);
@ -813,7 +747,7 @@ namespace MWDialogue
if (actor == mActor && !mLastTopic.empty())
{
MWBase::Environment::get().getJournal()->removeLastAddedTopicResponse(
mLastTopic, actor.getClass().getName(actor));
Misc::StringUtils::lowerCase(mLastTopic), actor.getClass().getName(actor));
}
}
}

@ -22,7 +22,6 @@ namespace MWDialogue
{
class DialogueManager : public MWBase::DialogueManager
{
std::map<std::string, ESM::Dialogue> mDialogueMap;
std::set<std::string> mKnownTopics;// Those are the topics the player knows.
// Modified faction reactions. <Faction1, <Faction2, Difference> >
@ -42,19 +41,24 @@ namespace MWDialogue
int mChoice;
std::string mLastTopic; // last topic ID, lowercase
bool mIsInChoice;
bool mGoodbye;
std::vector<std::pair<std::string, int> > mChoices;
float mTemporaryDispositionChange;
float mPermanentDispositionChange;
void parseText (const std::string& text);
void updateTopics();
void updateActorKnownTopics();
void updateGlobals();
bool compile (const std::string& cmd, std::vector<Interpreter::Type_Code>& code, const MWWorld::Ptr& actor);
void executeScript (const std::string& script, const MWWorld::Ptr& actor);
void executeTopic (const std::string& topic);
Response executeTopic (const std::string& topic);
const ESM::Dialogue* searchDialogue(const std::string& id);
public:
@ -64,7 +68,9 @@ namespace MWDialogue
virtual bool isInChoice() const;
virtual void startDialogue (const MWWorld::Ptr& actor);
virtual bool startDialogue (const MWWorld::Ptr& actor, Response& response);
std::list<std::string> getAvailableTopics();
virtual void addTopic (const std::string& topic);
@ -79,20 +85,23 @@ namespace MWDialogue
End of tes3mp addition
*/
virtual void askQuestion (const std::string& question,int choice);
virtual void addChoice (const std::string& text,int choice);
const std::vector<std::pair<std::string, int> >& getChoices();
virtual bool isGoodbye();
virtual void goodbye();
virtual bool checkServiceRefused ();
virtual bool checkServiceRefused (Response& response);
virtual void say(const MWWorld::Ptr &actor, const std::string &topic);
//calbacks for the GUI
virtual void keywordSelected (const std::string& keyword);
virtual Response keywordSelected (const std::string& keyword);
virtual void goodbyeSelected();
virtual void questionAnswered (int answer);
virtual Response questionAnswered (int answer);
virtual void persuade (int type);
virtual Response persuade (int type);
virtual int getTemporaryDispositionChange () const;
/// @note This change is temporary and gets discarded when dialogue ends.

@ -193,7 +193,8 @@ bool MWDialogue::Filter::testFunctionLocal(const MWDialogue::SelectWrapper& sele
return false; // shouldn't happen, we checked that variable has a type above, so must exist
const MWScript::Locals& locals = mActor.getRefData().getLocals();
if (locals.isEmpty())
return select.selectCompare(0);
switch (type)
{
case 's': return select.selectCompare (static_cast<int> (locals.mShorts[index]));

@ -1,6 +1,8 @@
#include "alchemywindow.hpp"
#include <MyGUI_Gui.h>
#include <MyGUI_Button.h>
#include <MyGUI_EditBox.h>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -19,6 +21,7 @@
#include "sortfilteritemmodel.hpp"
#include "itemview.hpp"
#include "itemwidget.hpp"
#include "widgets.hpp"
namespace MWGui
{
@ -54,12 +57,19 @@ namespace MWGui
mCreateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCreateButtonClicked);
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCancelButtonClicked);
mNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &AlchemyWindow::onAccept);
center();
}
void AlchemyWindow::onAccept(MyGUI::EditBox* sender)
{
onCreateButtonClicked(sender);
}
void AlchemyWindow::onCancelButtonClicked(MyGUI::Widget* _sender)
{
exit();
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Alchemy);
}
void AlchemyWindow::onCreateButtonClicked(MyGUI::Widget* _sender)
@ -112,8 +122,9 @@ namespace MWGui
update();
}
void AlchemyWindow::open()
void AlchemyWindow::onOpen()
{
mAlchemy->clear();
mAlchemy->setAlchemist (MWMechanics::getPlayer());
InventoryItemModel* model = new InventoryItemModel(MWMechanics::getPlayer());
@ -138,12 +149,8 @@ namespace MWGui
}
update();
}
void AlchemyWindow::exit() {
mAlchemy->clear();
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Alchemy);
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Inventory);
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit);
}
void AlchemyWindow::onIngredientSelected(MyGUI::Widget* _sender)

@ -5,7 +5,6 @@
#include "../mwmechanics/alchemy.hpp"
#include "widgets.hpp"
#include "windowbase.hpp"
namespace MWMechanics
@ -24,8 +23,9 @@ namespace MWGui
public:
AlchemyWindow();
virtual void open();
virtual void exit();
virtual void onOpen();
void onResChange(int, int) { center(); }
private:
std::string mSuggestedPotionName;
@ -43,6 +43,7 @@ namespace MWGui
void onCancelButtonClicked(MyGUI::Widget* _sender);
void onCreateButtonClicked(MyGUI::Widget* _sender);
void onIngredientSelected(MyGUI::Widget* _sender);
void onAccept(MyGUI::EditBox*);
void onSelectedItem(int index);

@ -64,11 +64,12 @@ namespace MWGui
okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", ""));
}
void BirthDialog::open()
void BirthDialog::onOpen()
{
WindowModal::open();
WindowModal::onOpen();
updateBirths();
updateSpells();
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mBirthList);
}

@ -20,7 +20,9 @@ namespace MWGui
void setBirthId(const std::string &raceId);
void setNextButtonShow(bool shown);
virtual void open();
virtual void onOpen();
bool exit() { return false; }
// Events
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;

@ -1,6 +1,7 @@
#include "bookwindow.hpp"
#include <MyGUI_TextBox.h>
#include <MyGUI_InputManager.h>
#include <components/esm/loadbook.hpp>
@ -11,6 +12,7 @@
#include "../mwmechanics/actorutil.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwworld/class.hpp"
#include "formatting.hpp"
@ -51,6 +53,11 @@ namespace MWGui
mRightPage->setNeedMouseFocus(true);
mRightPage->eventMouseWheel += MyGUI::newDelegate(this, &BookWindow::onMouseWheel);
mNextPageButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &BookWindow::onKeyButtonPressed);
mPrevPageButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &BookWindow::onKeyButtonPressed);
mTakeButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &BookWindow::onKeyButtonPressed);
mCloseButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &BookWindow::onKeyButtonPressed);
if (mNextPageButton->getSize().width == 64)
{
// english button has a 7 pixel wide strip of garbage on its right edge
@ -74,15 +81,16 @@ namespace MWGui
mPages.clear();
}
void BookWindow::openBook (MWWorld::Ptr book, bool showTakeButton)
void BookWindow::setPtr (const MWWorld::Ptr& book)
{
mBook = book;
MWWorld::Ptr player = MWMechanics::getPlayer();
bool showTakeButton = book.getContainerStore() != &player.getClass().getContainerStore(player);
clearPages();
mCurrentPage = 0;
MWBase::Environment::get().getWindowManager()->playSound("book open");
MWWorld::LiveCellRef<ESM::Book> *ref = mBook.get<ESM::Book>();
Formatting::BookFormatter formatter;
@ -92,14 +100,8 @@ namespace MWGui
updatePages();
setTakeButtonShow(showTakeButton);
}
void BookWindow::exit()
{
// no 3d sounds because the object could be in a container.
MWBase::Environment::get().getWindowManager()->playSound("book close");
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book);
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton);
}
void BookWindow::setTakeButtonShow(bool show)
@ -108,6 +110,14 @@ namespace MWGui
mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed);
}
void BookWindow::onKeyButtonPressed(MyGUI::Widget *sender, MyGUI::KeyCode key, MyGUI::Char character)
{
if (key == MyGUI::KeyCode::ArrowUp)
prevPage();
else if (key == MyGUI::KeyCode::ArrowDown)
nextPage();
}
void BookWindow::setInventoryAllowed(bool allowed)
{
mTakeButtonAllowed = allowed;
@ -116,7 +126,7 @@ namespace MWGui
void BookWindow::onCloseButtonClicked (MyGUI::Widget* sender)
{
exit();
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book);
}
void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender)
@ -144,20 +154,16 @@ namespace MWGui
mLeftPageNumber->setCaption( MyGUI::utility::toString(mCurrentPage*2 + 1) );
mRightPageNumber->setCaption( MyGUI::utility::toString(mCurrentPage*2 + 2) );
//If it is the last page, hide the button "Next Page"
if ( (mCurrentPage+1)*2 == mPages.size()
|| (mCurrentPage+1)*2 == mPages.size() + 1)
{
mNextPageButton->setVisible(false);
} else {
mNextPageButton->setVisible(true);
}
//If it is the fist page, hide the button "Prev Page"
if (mCurrentPage == 0) {
mPrevPageButton->setVisible(false);
} else {
mPrevPageButton->setVisible(true);
}
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
bool nextPageVisible = (mCurrentPage+1)*2 < mPages.size();
mNextPageButton->setVisible(nextPageVisible);
bool prevPageVisible = mCurrentPage != 0;
mPrevPageButton->setVisible(prevPageVisible);
if (focus == mNextPageButton && !nextPageVisible && prevPageVisible)
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mPrevPageButton);
else if (focus == mPrevPageButton && !prevPageVisible && nextPageVisible)
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNextPageButton);
if (mPages.empty())
return;

@ -14,11 +14,11 @@ namespace MWGui
public:
BookWindow();
virtual void exit();
void openBook(MWWorld::Ptr book, bool showTakeButton);
void setPtr(const MWWorld::Ptr& book);
void setInventoryAllowed(bool allowed);
void onResChange(int, int) { center(); }
protected:
void onNextPageButtonClicked (MyGUI::Widget* sender);
void onPrevPageButtonClicked (MyGUI::Widget* sender);
@ -27,6 +27,8 @@ namespace MWGui
void onMouseWheel(MyGUI::Widget* _sender, int _rel);
void setTakeButtonShow(bool show);
void onKeyButtonPressed(MyGUI::Widget* sender, MyGUI::KeyCode key, MyGUI::Char character);
void nextPage();
void prevPage();

@ -125,11 +125,12 @@ namespace MWGui
okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", ""));
}
void PickClassDialog::open()
void PickClassDialog::onOpen()
{
WindowModal::open ();
WindowModal::onOpen ();
updateClasses();
updateStats();
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mClassList);
}
@ -341,9 +342,9 @@ namespace MWGui
}
}
void InfoBoxDialog::open()
void InfoBoxDialog::onOpen()
{
WindowModal::open();
WindowModal::onOpen();
// Fix layout
layoutVertically(mTextBox, 4);
layoutVertically(mButtonBar, 6);
@ -730,9 +731,10 @@ namespace MWGui
exit();
}
void SelectSpecializationDialog::exit()
bool SelectSpecializationDialog::exit()
{
eventCancel();
return true;
}
/* SelectAttributeDialog */
@ -778,9 +780,10 @@ namespace MWGui
exit();
}
void SelectAttributeDialog::exit()
bool SelectAttributeDialog::exit()
{
eventCancel();
return true;
}
@ -869,9 +872,10 @@ namespace MWGui
exit();
}
void SelectSkillDialog::exit()
bool SelectSkillDialog::exit()
{
eventCancel();
return true;
}
/* DescriptionDialog */

@ -21,7 +21,9 @@ namespace MWGui
std::string getText() const;
void setButtons(ButtonList &buttons);
virtual void open();
virtual void onOpen();
bool exit() { return false; }
// Events
typedef MyGUI::delegates::CMultiDelegate1<int> EventHandle_Int;
@ -67,6 +69,8 @@ namespace MWGui
std::string getClassId() const;
void setClassId(const std::string &classId);
bool exit() { return false; }
// Events
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;
@ -100,7 +104,9 @@ namespace MWGui
void setClassId(const std::string &classId);
void setNextButtonShow(bool shown);
virtual void open();
virtual void onOpen();
bool exit() { return false; }
// Events
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;
@ -142,7 +148,7 @@ namespace MWGui
SelectSpecializationDialog();
~SelectSpecializationDialog();
virtual void exit();
virtual bool exit();
ESM::Class::Specialization getSpecializationId() const { return mSpecializationId; }
@ -175,7 +181,7 @@ namespace MWGui
SelectAttributeDialog();
~SelectAttributeDialog();
virtual void exit();
virtual bool exit();
ESM::Attribute::AttributeID getAttributeId() const { return mAttributeId; }
@ -206,7 +212,7 @@ namespace MWGui
SelectSkillDialog();
~SelectSkillDialog();
virtual void exit();
virtual bool exit();
ESM::Skill::SkillEnum getSkillId() const { return mSkillId; }
@ -262,6 +268,8 @@ namespace MWGui
CreateClassDialog();
virtual ~CreateClassDialog();
bool exit() { return false; }
std::string getName() const;
std::string getDescription() const;
ESM::Class::Specialization getSpecializationId() const;

@ -13,6 +13,7 @@
#include "companionitemmodel.hpp"
#include "draganddrop.hpp"
#include "countdialog.hpp"
#include "widgets.hpp"
namespace
{
@ -103,7 +104,7 @@ void CompanionWindow::onBackgroundSelected()
}
}
void CompanionWindow::openCompanion(const MWWorld::Ptr& npc)
void CompanionWindow::setPtr(const MWWorld::Ptr& npc)
{
mPtr = npc;
updateEncumbranceBar();
@ -116,8 +117,9 @@ void CompanionWindow::openCompanion(const MWWorld::Ptr& npc)
setTitle(npc.getClass().getName(npc));
}
void CompanionWindow::onFrame()
void CompanionWindow::onFrame(float dt)
{
checkReferenceAvailable();
updateEncumbranceBar();
}
@ -139,10 +141,11 @@ void CompanionWindow::updateEncumbranceBar()
void CompanionWindow::onCloseButtonClicked(MyGUI::Widget* _sender)
{
exit();
if (exit())
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion);
}
void CompanionWindow::exit()
bool CompanionWindow::exit()
{
if (mModel && mModel->hasProfit(mPtr) && getProfit(mPtr) < 0)
{
@ -151,9 +154,9 @@ void CompanionWindow::exit()
buttons.push_back("#{sCompanionWarningButtonTwo}");
mMessageBoxManager->createInteractiveMessageBox("#{sCompanionWarningMessage}", buttons);
mMessageBoxManager->eventButtonPressed += MyGUI::newDelegate(this, &CompanionWindow::onMessageBoxButtonClicked);
return false;
}
else
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion);
return true;
}
void CompanionWindow::onMessageBoxButtonClicked(int button)
@ -162,7 +165,7 @@ void CompanionWindow::onMessageBoxButtonClicked(int button)
{
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion);
// Important for Calvus' contract script to work properly
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue);
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
}
}

@ -1,12 +1,16 @@
#ifndef OPENMW_MWGUI_COMPANIONWINDOW_H
#define OPENMW_MWGUI_COMPANIONWINDOW_H
#include "widgets.hpp"
#include "windowbase.hpp"
#include "referenceinterface.hpp"
namespace MWGui
{
namespace Widgets
{
class MWDynamicStat;
}
class MessageBoxManager;
class ItemView;
class DragAndDrop;
@ -18,12 +22,13 @@ namespace MWGui
public:
CompanionWindow(DragAndDrop* dragAndDrop, MessageBoxManager* manager);
virtual void exit();
virtual bool exit();
virtual void resetReference();
void openCompanion(const MWWorld::Ptr& npc);
void onFrame ();
void setPtr(const MWWorld::Ptr& npc);
void onFrame (float dt);
void clear() { resetReference(); }
private:
ItemView* mItemView;

@ -3,6 +3,9 @@
#include <MyGUI_Button.h>
#include <MyGUI_EditBox.h>
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
namespace MWGui
{
ConfirmationDialog::ConfirmationDialog() :
@ -16,14 +19,6 @@ namespace MWGui
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ConfirmationDialog::onOkButtonClicked);
}
void ConfirmationDialog::askForConfirmation(const std::string& message, const std::string& confirmMessage, const std::string& cancelMessage)
{
mCancelButton->setCaptionWithReplacing(cancelMessage);
mOkButton->setCaptionWithReplacing(confirmMessage);
askForConfirmation(message);
}
void ConfirmationDialog::askForConfirmation(const std::string& message)
{
setVisible(true);
@ -38,18 +33,21 @@ namespace MWGui
mMessage->setSize(mMessage->getWidth(), mMessage->getTextSize().height + 24);
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mOkButton);
center();
}
void ConfirmationDialog::exit()
bool ConfirmationDialog::exit()
{
setVisible(false);
eventCancelClicked();
return true;
}
void ConfirmationDialog::onCancelButtonClicked(MyGUI::Widget* _sender)
{
setVisible(false);
exit();
}

@ -10,8 +10,7 @@ namespace MWGui
public:
ConfirmationDialog();
void askForConfirmation(const std::string& message);
void askForConfirmation(const std::string& message, const std::string& confirmMessage, const std::string& cancelMessage);
virtual void exit();
virtual bool exit();
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;

@ -145,25 +145,13 @@ namespace MWGui
mCompilerContext.setExtensions (&mExtensions);
}
void Console::open()
void Console::onOpen()
{
// Give keyboard focus to the combo box whenever the console is
// turned on
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine);
}
void Console::close()
{
// Apparently, hidden widgets can retain key focus
// Remove for MyGUI 3.2.2
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL);
}
void Console::exit()
{
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Console);
}
void Console::setFont(const std::string &fntName)
{
mHistory->setFontName(fntName);
@ -227,6 +215,11 @@ namespace MWGui
}
}
void Console::clear()
{
resetReference();
}
void Console::keyPress(MyGUI::Widget* _sender,
MyGUI::KeyCode key,
MyGUI::Char _char)

@ -39,10 +39,7 @@ namespace MWGui
Console(int w, int h, bool consoleOnlyScripts);
virtual void open();
virtual void close();
virtual void exit();
virtual void onOpen();
void setFont(const std::string &fntName);
@ -63,6 +60,8 @@ namespace MWGui
void executeFile (const std::string& path);
void clear();
virtual void resetReference ();
protected:

@ -36,6 +36,7 @@
#include "itemview.hpp"
#include "itemwidget.hpp"
#include "inventoryitemmodel.hpp"
#include "containeritemmodel.hpp"
#include "sortfilteritemmodel.hpp"
#include "pickpocketitemmodel.hpp"
#include "draganddrop.hpp"
@ -61,7 +62,6 @@ namespace MWGui
mDisposeCorpseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onDisposeCorpseButtonClicked);
mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onCloseButtonClicked);
mCloseButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ContainerWindow::onKeyPressed);
mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onTakeAllButtonClicked);
setCoord(200,0,600,300);
@ -219,14 +219,26 @@ namespace MWGui
dropItem();
}
void ContainerWindow::openContainer(const MWWorld::Ptr& container, bool loot)
void ContainerWindow::setPtr(const MWWorld::Ptr& container)
{
/*
Start of tes3mp addition
Mark this container as open for multiplayer logic purposes
*/
mwmp::Main::get().getCellController()->openContainer(container, loot);
/*
End of tes3mp addition
*/
mPickpocketDetected = false;
mPtr = container;
if (mPtr.getTypeName() == typeid(ESM::NPC).name() && !loot)
bool loot = mPtr.getClass().isActor() && mPtr.getClass().getCreatureStats(mPtr).isDead();
if (mPtr.getClass().hasInventoryStore(mPtr))
{
if (mPtr.getClass().isNpc() && !loot)
{
// we are stealing stuff
MWWorld::Ptr player = MWMechanics::getPlayer();
@ -235,6 +247,11 @@ namespace MWGui
}
else
mModel = new InventoryItemModel(container);
}
else
{
mModel = new ContainerItemModel(container);
}
mDisposeCorpseButton->setVisible(loot);
@ -248,14 +265,6 @@ namespace MWGui
setTitle(container.getClass().getName(container));
}
void ContainerWindow::onKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char)
{
if (_key == MyGUI::KeyCode::Space)
onCloseButtonClicked(mCloseButton);
if (_key == MyGUI::KeyCode::Return || _key == MyGUI::KeyCode::NumpadEnter)
onTakeAllButtonClicked(mTakeButton);
}
void ContainerWindow::resetReference()
{
ReferenceInterface::resetReference();
@ -264,10 +273,19 @@ namespace MWGui
mSortModel = NULL;
}
void ContainerWindow::close()
void ContainerWindow::onClose()
{
/*
Start of tes3mp addition
Mark this container as closed for multiplayer logic purposes
*/
mwmp::Main::get().getCellController()->closeContainer(mPtr);
WindowBase::close();
/*
End of tes3mp addition
*/
WindowBase::onClose();
if (dynamic_cast<PickpocketItemModel*>(mModel)
// Make sure we were actually closed, rather than just temporarily hidden (e.g. console or main menu opened)
@ -289,17 +307,9 @@ namespace MWGui
}
}
void ContainerWindow::exit()
{
if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop)
{
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
}
}
void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender)
{
exit();
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
}
void ContainerWindow::onTakeAllButtonClicked(MyGUI::Widget* _sender)
@ -420,9 +430,8 @@ namespace MWGui
MWMechanics::Pickpocket pickpocket(player, mPtr);
if (pickpocket.pick(item.mBase, count))
{
int value = item.mBase.getClass().getValue(item.mBase) * count;
MWBase::Environment::get().getMechanicsManager()->commitCrime(
player, mPtr, MWBase::MechanicsManager::OT_Theft, value, true);
player, mPtr, MWBase::MechanicsManager::OT_Pickpocket, 0, true);
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);
mPickpocketDetected = true;
return false;

@ -33,12 +33,13 @@ namespace MWGui
public:
ContainerWindow(DragAndDrop* dragAndDrop);
void openContainer(const MWWorld::Ptr& container, bool loot=false);
virtual void close();
void setPtr(const MWWorld::Ptr& container);
virtual void onClose();
void clear() { resetReference(); }
virtual void resetReference();
void onFrame(float dt) { checkReferenceAvailable(); }
virtual void exit();
virtual void resetReference();
private:
DragAndDrop* mDragAndDrop;
@ -61,7 +62,6 @@ namespace MWGui
void onCloseButtonClicked(MyGUI::Widget* _sender);
void onTakeAllButtonClicked(MyGUI::Widget* _sender);
void onDisposeCorpseButtonClicked(MyGUI::Widget* sender);
void onKeyPressed(MyGUI::Widget* _sender, MyGUI::KeyCode _key, MyGUI::Char _char);
/// @return is taking the item allowed?
bool onTakeItem(const ItemStack& item, int count);

@ -6,8 +6,11 @@
#include "../mwworld/class.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/environment.hpp"
#include "../mwmechanics/actorutil.hpp"
namespace
{
@ -47,6 +50,19 @@ ContainerItemModel::ContainerItemModel (const MWWorld::Ptr& source)
mItemSources.push_back(source);
}
bool ContainerItemModel::allowedToUseItems() const
{
if (mItemSources.size() == 0)
return true;
MWWorld::Ptr ptr = MWMechanics::getPlayer();
MWWorld::Ptr victim;
// Check if the player is allowed to use items from opened container
MWBase::MechanicsManager* mm = MWBase::Environment::get().getMechanicsManager();
return mm->isAllowedToUse(ptr, mItemSources[0], victim);
}
ItemStack ContainerItemModel::getItem (ModelIndex index)
{
if (index < 0)

@ -17,6 +17,7 @@ namespace MWGui
ContainerItemModel (const MWWorld::Ptr& source);
virtual bool allowedToUseItems() const;
virtual ItemStack getItem (ModelIndex index);
virtual ModelIndex getIndex (ItemStack item);
virtual size_t getItemCount();

@ -56,19 +56,9 @@ namespace MWGui
mItemEdit->setValue(maxCount);
}
void CountDialog::cancel() //Keeping this here as I don't know if anything else relies on it.
{
exit();
}
void CountDialog::exit()
{
setVisible(false);
}
void CountDialog::onCancelButtonClicked(MyGUI::Widget* _sender)
{
cancel();
setVisible(false);
}
void CountDialog::onOkButtonClicked(MyGUI::Widget* _sender)

@ -15,8 +15,6 @@ namespace MWGui
public:
CountDialog();
void openCountDialog(const std::string& item, const std::string& message, const int maxCount);
void cancel();
virtual void exit();
typedef MyGUI::delegates::CMultiDelegate2<MyGUI::Widget*, int> EventHandle_WidgetInt;

@ -3,6 +3,8 @@
#include <MyGUI_LanguageManager.h>
#include <MyGUI_Window.h>
#include <MyGUI_ProgressBar.h>
#include <MyGUI_ScrollBar.h>
#include <MyGUI_Button.h>
#include <components/widgets/list.hpp>
#include <components/translation/translation.hpp>
@ -20,7 +22,6 @@
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "widgets.hpp"
#include "bookpage.hpp"
#include "textcolours.hpp"
@ -52,7 +53,7 @@ namespace MWGui
void PersuasionDialog::onCancel(MyGUI::Widget *sender)
{
exit();
setVisible(false);
}
void PersuasionDialog::onPersuade(MyGUI::Widget *sender)
@ -68,14 +69,15 @@ namespace MWGui
else /*if (sender == mBribe1000Button)*/
type = MWBase::MechanicsManager::PT_Bribe1000;
MWBase::Environment::get().getDialogueManager()->persuade(type);
MWBase::DialogueManager::Response response = MWBase::Environment::get().getDialogueManager()->persuade(type);
eventPersuadeMsg(response.first, response.second);
setVisible(false);
}
void PersuasionDialog::open()
void PersuasionDialog::onOpen()
{
WindowModal::open();
center();
MWWorld::Ptr player = MWMechanics::getPlayer();
@ -86,11 +88,12 @@ namespace MWGui
mBribe1000Button->setEnabled (playerGold >= 1000);
mGoldLabel->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold));
WindowModal::onOpen();
}
void PersuasionDialog::exit()
MyGUI::Widget* PersuasionDialog::getDefaultKeyFocus()
{
setVisible(false);
return mAdmireButton;
}
// --------------------------------------------------------------------------------------------------
@ -219,31 +222,27 @@ namespace MWGui
void Choice::activated()
{
MWBase::Environment::get().getWindowManager()->playSound("Menu Click");
MWBase::Environment::get().getDialogueManager()->questionAnswered(mChoiceId);
eventChoiceActivated(mChoiceId);
}
void Topic::activated()
{
MWBase::Environment::get().getWindowManager()->playSound("Menu Click");
MWBase::Environment::get().getDialogueManager()->keywordSelected(Misc::StringUtils::lowerCase(mTopicId));
eventTopicActivated(mTopicId);
}
void Goodbye::activated()
{
MWBase::Environment::get().getWindowManager()->playSound("Menu Click");
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
eventActivated();
}
// --------------------------------------------------------------------------------------------------
DialogueWindow::DialogueWindow()
: WindowBase("openmw_dialogue_window.layout")
, mServices(0)
, mEnabled(false)
, mIsCompanion(false)
, mGoodbye(false)
, mPersuasionDialog()
{
@ -251,17 +250,17 @@ namespace MWGui
center();
mPersuasionDialog.setVisible(false);
mPersuasionDialog.eventPersuadeMsg += MyGUI::newDelegate(this, &DialogueWindow::onPersuadeResult);
//History view
getWidget(mHistory, "History");
//Topics list
getWidget(mTopicsList, "TopicsList");
mTopicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic);
mTopicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectListItem);
MyGUI::Button* byeButton;
getWidget(byeButton, "ByeButton");
byeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked);
getWidget(mGoodbyeButton, "ByeButton");
mGoodbyeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked);
getWidget(mDispositionBar, "Disposition");
getWidget(mDispositionText,"DispositionText");
@ -276,18 +275,36 @@ namespace MWGui
mMainWidget->castType<MyGUI::Window>()->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize);
}
void DialogueWindow::exit()
DialogueWindow::~DialogueWindow()
{
if ((!mEnabled || MWBase::Environment::get().getDialogueManager()->isInChoice())
&& !mGoodbye)
mPersuasionDialog.eventPersuadeMsg.clear();
deleteLater();
for (Link* link : mLinks)
delete link;
for (auto link : mTopicLinks)
delete link.second;
for (auto history : mHistoryContents)
delete history;
}
void DialogueWindow::onTradeComplete()
{
// in choice, not allowed to escape, but give access to main menu to allow loading other saves
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
addResponse("", MyGUI::LanguageManager::getInstance().replaceTags("#{sBarterDialog5}"));
}
bool DialogueWindow::exit()
{
if ((MWBase::Environment::get().getDialogueManager()->isInChoice()))
{
return false;
}
else
{
resetReference();
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
mTopicsList->scrollToTop();
return true;
}
}
@ -312,12 +329,13 @@ namespace MWGui
void DialogueWindow::onByeClicked(MyGUI::Widget* _sender)
{
exit();
if (exit())
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue);
}
void DialogueWindow::onSelectTopic(const std::string& topic, int id)
void DialogueWindow::onSelectListItem(const std::string& topic, int id)
{
if (!mEnabled || MWBase::Environment::get().getDialogueManager()->isInChoice())
if (mGoodbye || MWBase::Environment::get().getDialogueManager()->isInChoice())
return;
int separatorPos = 0;
@ -328,63 +346,84 @@ namespace MWGui
}
if (id >= separatorPos)
MWBase::Environment::get().getDialogueManager()->keywordSelected(Misc::StringUtils::lowerCase(topic));
{
onTopicActivated(topic);
if (mGoodbyeButton->getEnabled())
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mGoodbyeButton);
}
else
{
const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
MWBase::DialogueManager::Response response;
if (topic == gmst.find("sPersuasion")->getString())
mPersuasionDialog.setVisible(true);
else if (topic == gmst.find("sCompanionShare")->getString())
MWBase::Environment::get().getWindowManager()->showCompanionWindow(mPtr);
else if (!MWBase::Environment::get().getDialogueManager()->checkServiceRefused())
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Companion, mPtr);
else if (!MWBase::Environment::get().getDialogueManager()->checkServiceRefused(response))
{
if (topic == gmst.find("sBarter")->getString())
MWBase::Environment::get().getWindowManager()->startTrade(mPtr);
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Barter, mPtr);
else if (topic == gmst.find("sSpells")->getString())
MWBase::Environment::get().getWindowManager()->startSpellBuying(mPtr);
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellBuying, mPtr);
else if (topic == gmst.find("sTravel")->getString())
MWBase::Environment::get().getWindowManager()->startTravel(mPtr);
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Travel, mPtr);
else if (topic == gmst.find("sSpellMakingMenuTitle")->getString())
MWBase::Environment::get().getWindowManager()->startSpellMaking (mPtr);
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellCreation, mPtr);
else if (topic == gmst.find("sEnchanting")->getString())
MWBase::Environment::get().getWindowManager()->startEnchanting (mPtr);
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting, mPtr);
else if (topic == gmst.find("sServiceTrainingTitle")->getString())
MWBase::Environment::get().getWindowManager()->startTraining (mPtr);
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Training, mPtr);
else if (topic == gmst.find("sRepair")->getString())
MWBase::Environment::get().getWindowManager()->startRepair (mPtr);
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_MerchantRepair, mPtr);
}
else
addResponse(response.first, response.second);
}
}
void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName, bool resetHistory)
void DialogueWindow::setPtr(const MWWorld::Ptr& actor)
{
MWBase::DialogueManager::Response response;
if (!MWBase::Environment::get().getDialogueManager()->startDialogue(actor, response))
{
// No greetings found. The dialogue window should not be shown.
// If this is a companion, we must show the companion window directly (used by BM_bear_be_unique).
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue);
if (isCompanion())
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Companion, mPtr);
return;
}
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mGoodbyeButton);
mGoodbye = false;
mEnabled = true;
bool sameActor = (mPtr == actor);
mPtr = actor;
mTopicsList->setEnabled(true);
setTitle(npcName);
clearChoices();
setTitle(mPtr.getClass().getName(mPtr));
mTopicsList->clear();
if (resetHistory || !sameActor)
if (!sameActor)
{
for (std::vector<DialogueText*>::iterator it = mHistoryContents.begin(); it != mHistoryContents.end(); ++it)
delete (*it);
mHistoryContents.clear();
mKeywords.clear();
updateTopicsPane();
}
for (std::vector<Link*>::iterator it = mLinks.begin(); it != mLinks.end(); ++it)
delete (*it);
mDeleteLater.push_back(*it); // Links are not deleted right away to prevent issues with event handlers
mLinks.clear();
updateOptions();
updateDisposition();
restock();
addResponse(response.first, response.second, false);
}
void DialogueWindow::restock()
@ -401,16 +440,35 @@ namespace MWGui
}
}
void DialogueWindow::deleteLater()
{
for (Link* link : mDeleteLater)
delete link;
mDeleteLater.clear();
}
void DialogueWindow::setKeywords(std::list<std::string> keyWords)
{
if (mKeywords == keyWords && isCompanion() == mIsCompanion)
return;
mIsCompanion = isCompanion();
mKeywords = keyWords;
updateTopicsPane();
}
void DialogueWindow::updateTopicsPane()
{
mTopicsList->clear();
for (std::map<std::string, Link*>::iterator it = mTopicLinks.begin(); it != mTopicLinks.end(); ++it)
delete it->second;
mDeleteLater.push_back(it->second);
mTopicLinks.clear();
mKeywordSearch.clear();
bool isCompanion = !mPtr.getClass().getScript(mPtr).empty()
&& mPtr.getRefData().getLocals().getIntVar(mPtr.getClass().getScript(mPtr), "companion");
int services = mPtr.getClass().getServices(mPtr);
bool travel = (mPtr.getTypeName() == typeid(ESM::NPC).name() && !mPtr.get<ESM::NPC>()->mBase->getTransport().empty())
|| (mPtr.getTypeName() == typeid(ESM::Creature).name() && !mPtr.get<ESM::Creature>()->mBase->getTransport().empty());
const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
@ -418,39 +476,40 @@ namespace MWGui
if (mPtr.getTypeName() == typeid(ESM::NPC).name())
mTopicsList->addItem(gmst.find("sPersuasion")->getString());
if (mServices & Service_Trade)
if (services & ESM::NPC::AllItems)
mTopicsList->addItem(gmst.find("sBarter")->getString());
if (mServices & Service_BuySpells)
if (services & ESM::NPC::Spells)
mTopicsList->addItem(gmst.find("sSpells")->getString());
if (mServices & Service_Travel)
if (travel)
mTopicsList->addItem(gmst.find("sTravel")->getString());
if (mServices & Service_CreateSpells)
if (services & ESM::NPC::Spellmaking)
mTopicsList->addItem(gmst.find("sSpellmakingMenuTitle")->getString());
if (mServices & Service_Enchant)
if (services & ESM::NPC::Enchanting)
mTopicsList->addItem(gmst.find("sEnchanting")->getString());
if (mServices & Service_Training)
if (services & ESM::NPC::Training)
mTopicsList->addItem(gmst.find("sServiceTrainingTitle")->getString());
if (mServices & Service_Repair)
if (services & ESM::NPC::Repair)
mTopicsList->addItem(gmst.find("sRepair")->getString());
if (isCompanion)
if (isCompanion())
mTopicsList->addItem(gmst.find("sCompanionShare")->getString());
if (mTopicsList->getItemCount() > 0)
mTopicsList->addSeparator();
for(std::list<std::string>::iterator it = keyWords.begin(); it != keyWords.end(); ++it)
for(std::list<std::string>::iterator it = mKeywords.begin(); it != mKeywords.end(); ++it)
{
mTopicsList->addItem(*it);
Topic* t = new Topic(*it);
t->eventTopicActivated += MyGUI::newDelegate(this, &DialogueWindow::onTopicActivated);
mTopicLinks[Misc::StringUtils::lowerCase(*it)] = t;
mKeywordSearch.seed(Misc::StringUtils::lowerCase(*it), intptr_t(t));
@ -484,9 +543,11 @@ namespace MWGui
typesetter->sectionBreak(9);
// choices
const TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours();
for (std::vector<std::pair<std::string, int> >::iterator it = mChoices.begin(); it != mChoices.end(); ++it)
mChoices = MWBase::Environment::get().getDialogueManager()->getChoices();
for (std::vector<std::pair<std::string, int> >::const_iterator it = mChoices.begin(); it != mChoices.end(); ++it)
{
Choice* link = new Choice(it->second);
link->eventChoiceActivated += MyGUI::newDelegate(this, &DialogueWindow::onChoiceActivated);
mLinks.push_back(link);
typesetter->lineBreak();
@ -496,9 +557,11 @@ namespace MWGui
typesetter->write(questionStyle, to_utf8_span(it->first.c_str()));
}
mGoodbye = MWBase::Environment::get().getDialogueManager()->isGoodbye();
if (mGoodbye)
{
Goodbye* link = new Goodbye();
link->eventActivated += MyGUI::newDelegate(this, &DialogueWindow::onGoodbyeActivated);
mLinks.push_back(link);
std::string goodbye = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sGoodbye")->getString();
BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, textColours.answer, textColours.answerOver,
@ -528,10 +591,11 @@ namespace MWGui
onScrollbarMoved(mScrollBar, 0);
}
MyGUI::Button* byeButton;
getWidget(byeButton, "ByeButton");
bool goodbyeEnabled = !MWBase::Environment::get().getDialogueManager()->isInChoice() || mGoodbye;
byeButton->setEnabled(goodbyeEnabled);
bool goodbyeWasEnabled = mGoodbyeButton->getEnabled();
mGoodbyeButton->setEnabled(goodbyeEnabled);
if (goodbyeEnabled && !goodbyeWasEnabled)
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mGoodbyeButton);
bool topicsEnabled = !MWBase::Environment::get().getDialogueManager()->isInChoice() && !mGoodbye;
mTopicsList->setEnabled(topicsEnabled);
@ -542,59 +606,47 @@ namespace MWGui
reinterpret_cast<Link*>(link)->activated();
}
void DialogueWindow::onScrollbarMoved(MyGUI::ScrollBar *sender, size_t pos)
void DialogueWindow::onTopicActivated(const std::string &topicId)
{
mHistory->setPosition(0, static_cast<int>(pos) * -1);
MWBase::DialogueManager::Response response = MWBase::Environment::get().getDialogueManager()->keywordSelected(topicId);
addResponse(response.first, response.second);
}
void DialogueWindow::addResponse(const std::string &text, const std::string &title, bool needMargin)
{
// This is called from the dialogue manager, so text is
// case-smashed - thus we have to retrieve the correct case
// of the title through the topic list.
std::string realTitle = title;
if (realTitle != "")
{
for (size_t i=0; i<mTopicsList->getItemCount(); ++i)
void DialogueWindow::onChoiceActivated(int id)
{
std::string item = mTopicsList->getItemNameAt(i);
if (Misc::StringUtils::ciEqual(item, title))
{
realTitle = item;
break;
}
}
MWBase::DialogueManager::Response response = MWBase::Environment::get().getDialogueManager()->questionAnswered(id);
addResponse(response.first, response.second);
}
mHistoryContents.push_back(new Response(text, realTitle, needMargin));
updateHistory();
void DialogueWindow::onGoodbyeActivated()
{
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue);
resetReference();
}
void DialogueWindow::addMessageBox(const std::string& text)
void DialogueWindow::onScrollbarMoved(MyGUI::ScrollBar *sender, size_t pos)
{
mHistoryContents.push_back(new Message(text));
updateHistory();
mHistory->setPosition(0, static_cast<int>(pos) * -1);
}
void DialogueWindow::addChoice(const std::string& choice, int id)
void DialogueWindow::addResponse(const std::string &title, const std::string &text, bool needMargin)
{
mChoices.push_back(std::make_pair(choice, id));
mHistoryContents.push_back(new Response(text, title, needMargin));
updateHistory();
updateTopics();
}
void DialogueWindow::clearChoices()
void DialogueWindow::addMessageBox(const std::string& text)
{
mChoices.clear();
mHistoryContents.push_back(new Message(text));
updateHistory();
}
void DialogueWindow::updateOptions()
void DialogueWindow::updateDisposition()
{
//Clear the list of topics
mTopicsList->clear();
bool dispositionVisible = false;
if (mPtr.getClass().isNpc())
if (!mPtr.isEmpty() && mPtr.getClass().isNpc())
{
dispositionVisible = true;
mDispositionBar->setProgressRange(100);
@ -620,26 +672,38 @@ namespace MWGui
}
}
void DialogueWindow::goodbye()
void DialogueWindow::onReferenceUnavailable()
{
mGoodbye = true;
mEnabled = false;
updateHistory();
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue);
}
void DialogueWindow::onReferenceUnavailable()
void DialogueWindow::onFrame(float dt)
{
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue);
checkReferenceAvailable();
if (mPtr.isEmpty())
return;
updateDisposition();
deleteLater();
if (mChoices != MWBase::Environment::get().getDialogueManager()->getChoices()
|| mGoodbye != MWBase::Environment::get().getDialogueManager()->isGoodbye())
updateHistory();
}
void DialogueWindow::onFrame()
void DialogueWindow::updateTopics()
{
if(mMainWidget->getVisible() && mPtr.getTypeName() == typeid(ESM::NPC).name())
setKeywords(MWBase::Environment::get().getDialogueManager()->getAvailableTopics());
}
bool DialogueWindow::isCompanion()
{
int disp = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr);
mDispositionBar->setProgressRange(100);
mDispositionBar->setProgressPosition(disp);
mDispositionText->setCaption(MyGUI::utility::toString(disp)+std::string("/100"));
return !mPtr.getClass().getScript(mPtr).empty()
&& mPtr.getRefData().getLocals().getIntVar(mPtr.getClass().getScript(mPtr), "companion");
}
void DialogueWindow::onPersuadeResult(const std::string &title, const std::string &text)
{
addResponse(title, text);
}
}

@ -8,6 +8,8 @@
#include "../mwdialogue/keywordsearch.hpp"
#include <MyGUI_Delegate.h>
namespace Gui
{
class MWList;
@ -20,16 +22,17 @@ namespace MWGui
namespace MWGui
{
class DialogueHistoryViewModel;
class BookPage;
class PersuasionDialog : public WindowModal
{
public:
PersuasionDialog();
virtual void open();
virtual void exit();
typedef MyGUI::delegates::CMultiDelegate2<const std::string&, const std::string&> EventHandle_Result;
EventHandle_Result eventPersuadeMsg;
virtual void onOpen();
virtual MyGUI::Widget* getDefaultKeyFocus();
private:
MyGUI::Button* mCancelButton;
@ -54,6 +57,8 @@ namespace MWGui
struct Topic : Link
{
typedef MyGUI::delegates::CMultiDelegate1<const std::string&> EventHandle_TopicId;
EventHandle_TopicId eventTopicActivated;
Topic(const std::string& id) : mTopicId(id) {}
std::string mTopicId;
virtual void activated ();
@ -61,6 +66,8 @@ namespace MWGui
struct Choice : Link
{
typedef MyGUI::delegates::CMultiDelegate1<int> EventHandle_ChoiceId;
EventHandle_ChoiceId eventChoiceActivated;
Choice(int id) : mChoiceId(id) {}
int mChoiceId;
virtual void activated ();
@ -68,6 +75,8 @@ namespace MWGui
struct Goodbye : Link
{
typedef MyGUI::delegates::CMultiDelegate0 Event_Activated;
Event_Activated eventActivated;
virtual void activated ();
};
@ -99,46 +108,41 @@ namespace MWGui
{
public:
DialogueWindow();
~DialogueWindow();
virtual void exit();
void onTradeComplete();
virtual bool exit();
// Events
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;
void notifyLinkClicked (TypesetBook::InteractiveId link);
void startDialogue(MWWorld::Ptr actor, std::string npcName, bool resetHistory);
void setPtr(const MWWorld::Ptr& actor);
void setKeywords(std::list<std::string> keyWord);
void addResponse (const std::string& text, const std::string& title="", bool needMargin = true);
void addResponse (const std::string& title, const std::string& text, bool needMargin = true);
void addMessageBox(const std::string& text);
void addChoice(const std::string& choice, int id);
void clearChoices();
void goodbye();
void onFrame();
// make sure to call these before setKeywords()
void setServices(int services) { mServices = services; }
enum Services
{
Service_Trade = 0x01,
Service_BuySpells = 0x02,
Service_CreateSpells = 0x04,
Service_Enchant = 0x08,
Service_Training = 0x10,
Service_Travel = 0x20,
Service_Repair = 0x40
};
void onFrame(float dt);
void clear() { resetReference(); }
protected:
void onSelectTopic(const std::string& topic, int id);
void updateTopics();
void updateTopicsPane();
bool isCompanion();
void onPersuadeResult(const std::string& title, const std::string& text);
void onSelectListItem(const std::string& topic, int id);
void onByeClicked(MyGUI::Widget* _sender);
void onMouseWheel(MyGUI::Widget* _sender, int _rel);
void onWindowResize(MyGUI::Window* _sender);
void onTopicActivated(const std::string& topicId);
void onChoiceActivated(int id);
void onGoodbyeActivated();
void onScrollbarMoved (MyGUI::ScrollBar* sender, size_t pos);
@ -147,21 +151,24 @@ namespace MWGui
virtual void onReferenceUnavailable();
private:
void updateOptions();
void updateDisposition();
void restock();
int mServices;
void deleteLater();
bool mEnabled;
bool mGoodbye;
bool mIsCompanion;
std::list<std::string> mKeywords;
std::vector<DialogueText*> mHistoryContents;
std::vector<std::pair<std::string, int> > mChoices;
bool mGoodbye;
std::vector<Link*> mLinks;
std::map<std::string, Link*> mTopicLinks;
std::vector<Link*> mDeleteLater;
KeywordSearchT mKeywordSearch;
BookPage* mHistory;
@ -169,6 +176,7 @@ namespace MWGui
MyGUI::ScrollBar* mScrollBar;
MyGUI::ProgressBar* mDispositionBar;
MyGUI::TextBox* mDispositionText;
MyGUI::Button* mGoodbyeButton;
PersuasionDialog mPersuasionDialog;

@ -121,10 +121,18 @@ void DragAndDrop::drop(ItemModel *targetModel, ItemView *targetView)
mSourceView->update();
}
void DragAndDrop::onFrame()
{
if (mIsOnDragAndDrop && mItem.mBase.getRefData().getCount() == 0)
finish();
}
void DragAndDrop::finish()
{
mIsOnDragAndDrop = false;
mSourceSortModel->clearDragItems();
// since mSourceView doesn't get updated in drag()
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView();
MyGUI::Gui::getInstance().destroyWidget(mDraggedWidget);
mDraggedWidget = 0;

@ -29,6 +29,7 @@ namespace MWGui
void startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count);
void drop (ItemModel* targetModel, ItemView* targetView);
void onFrame();
void finish();
};

@ -4,11 +4,11 @@
#include <MyGUI_Button.h>
#include <MyGUI_ScrollView.h>
#include <MyGUI_EditBox.h>
#include <components/widgets/list.hpp>
#include <components/settings/settings.hpp>
#include "../mwbase/dialoguemanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/containerstore.hpp"
@ -53,6 +53,7 @@ namespace MWGui
mSoulBox->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onSelectSoul);
mBuyButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onBuyButtonClicked);
mTypeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onTypeButtonClicked);
mName->eventEditSelectAccept += MyGUI::newDelegate(this, &EnchantingDialog::onAccept);
}
EnchantingDialog::~EnchantingDialog()
@ -60,9 +61,10 @@ namespace MWGui
delete mItemSelectionDialog;
}
void EnchantingDialog::open()
void EnchantingDialog::onOpen()
{
center();
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mName);
}
void EnchantingDialog::setSoulGem(const MWWorld::Ptr &gem)
@ -100,11 +102,6 @@ namespace MWGui
}
}
void EnchantingDialog::exit()
{
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting);
}
void EnchantingDialog::updateLabels()
{
std::stringstream enchantCost;
@ -143,57 +140,41 @@ namespace MWGui
}
}
void EnchantingDialog::startEnchanting (MWWorld::Ptr actor)
void EnchantingDialog::setPtr (const MWWorld::Ptr& ptr)
{
mName->setCaption("");
if (ptr.getClass().isActor())
{
mEnchanting.setSelfEnchanting(false);
mEnchanting.setEnchanter(actor);
mEnchanting.setEnchanter(ptr);
mBuyButton->setCaptionWithReplacing("#{sBuy}");
mChanceLayout->setVisible(false);
mPtr = actor;
mPtr = ptr;
setSoulGem(MWWorld::Ptr());
setItem(MWWorld::Ptr());
startEditing ();
mPrice->setVisible(true);
mPriceText->setVisible(true);
updateLabels();
}
void EnchantingDialog::startSelfEnchanting(MWWorld::Ptr soulgem)
else
{
mName->setCaption("");
MWWorld::Ptr player = MWMechanics::getPlayer();
mEnchanting.setSelfEnchanting(true);
mEnchanting.setEnchanter(player);
mEnchanting.setEnchanter(MWMechanics::getPlayer());
mBuyButton->setCaptionWithReplacing("#{sCreate}");
bool enabled = Settings::Manager::getBool("show enchant chance","Game");
mChanceLayout->setVisible(enabled);
mPtr = player;
startEditing();
setSoulGem(soulgem);
setItem(MWWorld::Ptr());
mPtr = MWMechanics::getPlayer();
setSoulGem(ptr);
mPrice->setVisible(false);
mPriceText->setVisible(false);
}
setItem(MWWorld::Ptr());
startEditing ();
updateLabels();
}
void EnchantingDialog::onReferenceUnavailable ()
{
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue);
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting);
resetReference();
}
@ -209,7 +190,7 @@ namespace MWGui
void EnchantingDialog::onCancelButtonClicked(MyGUI::Widget* sender)
{
exit();
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Enchanting);
}
void EnchantingDialog::onSelectItem(MyGUI::Widget *sender)
@ -304,6 +285,11 @@ namespace MWGui
updateEffectsView();
}
void EnchantingDialog::onAccept(MyGUI::EditBox *sender)
{
onBuyButtonClicked(sender);
}
void EnchantingDialog::onBuyButtonClicked(MyGUI::Widget* sender)
{
if (mEffects.size() <= 0)
@ -364,7 +350,7 @@ namespace MWGui
MWBase::Environment::get().getMechanicsManager()->confiscateStolenItemToOwner(player, item, mPtr, 1);
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting);
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
return;
}
}

@ -19,15 +19,17 @@ namespace MWGui
EnchantingDialog();
virtual ~EnchantingDialog();
virtual void open();
virtual void onOpen();
virtual void exit();
void onFrame(float dt) { checkReferenceAvailable(); }
void clear() { resetReference(); }
void setSoulGem (const MWWorld::Ptr& gem);
void setItem (const MWWorld::Ptr& item);
void startEnchanting(MWWorld::Ptr actor);
void startSelfEnchanting(MWWorld::Ptr soulgem);
/// Actor Ptr: buy enchantment from this actor
/// Soulgem Ptr: player self-enchant
void setPtr(const MWWorld::Ptr& ptr);
virtual void resetReference();
@ -46,6 +48,7 @@ namespace MWGui
void onBuyButtonClicked(MyGUI::Widget* sender);
void updateLabels();
void onTypeButtonClicked(MyGUI::Widget* sender);
void onAccept(MyGUI::EditBox* sender);
ItemSelectionDialog* mItemSelectionDialog;
@ -58,7 +61,7 @@ namespace MWGui
MyGUI::Button* mTypeButton;
MyGUI::Button* mBuyButton;
MyGUI::TextBox* mName;
MyGUI::EditBox* mName;
MyGUI::TextBox* mEnchantmentPoints;
MyGUI::TextBox* mCastCost;
MyGUI::TextBox* mCharge;

@ -406,10 +406,11 @@ namespace MWGui
MyGUI::EditBox* box = parent->createWidget<MyGUI::EditBox>("NormalText",
MyGUI::IntCoord(0, pag.getCurrentTop(), pag.getPageWidth(), 0), MyGUI::Align::Left | MyGUI::Align::Top,
parent->getName() + MyGUI::utility::toString(parent->getChildCount()));
box->setProperty("Static", "true");
box->setProperty("MultiLine", "true");
box->setProperty("WordWrap", "true");
box->setProperty("NeedMouse", "false");
box->setEditStatic(true);
box->setEditMultiLine(true);
box->setEditWordWrap(true);
box->setNeedMouseFocus(false);
box->setNeedKeyFocus(false);
box->setMaxTextLength(text.size());
box->setTextAlign(mBlockStyle.mAlign);
box->setTextColour(mTextStyle.mColour);

@ -108,7 +108,7 @@ namespace MWGui
HUD::HUD(CustomMarkerCollection &customMarkers, DragAndDrop* dragAndDrop, MWRender::LocalMap* localMapRender)
: Layout("openmw_hud.layout")
: WindowBase("openmw_hud.layout")
, LocalMapBase(customMarkers, localMapRender, Settings::Manager::getBool("local map hud fog of war", "Map"))
, mHealth(NULL)
, mMagicka(NULL)
@ -422,6 +422,20 @@ namespace MWGui
if (mIsDrowning)
mDrowningFlashTheta += dt * osg::PI*2;
mSpellIcons->updateWidgets(mEffectBox, true);
if (mEnemyActorId != -1 && mEnemyHealth->getVisible())
{
updateEnemyHealthBar();
}
if (mIsDrowning)
{
float intensity = (cos(mDrowningFlashTheta) + 2.0f) / 3.0f;
mDrowningFlash->setAlpha(intensity);
}
}
void HUD::setSelectedSpell(const std::string& spellId, int successChancePercent)
@ -653,23 +667,6 @@ namespace MWGui
}
void HUD::update()
{
mSpellIcons->updateWidgets(mEffectBox, true);
if (mEnemyActorId != -1 && mEnemyHealth->getVisible())
{
updateEnemyHealthBar();
}
if (mIsDrowning)
{
float intensity = (cos(mDrowningFlashTheta) + 2.0f) / 3.0f;
mDrowningFlash->setAlpha(intensity);
}
}
void HUD::setEnemy(const MWWorld::Ptr &enemy)
{
mEnemyActorId = enemy.getClass().getCreatureStats(enemy).getActorId();
@ -686,6 +683,13 @@ namespace MWGui
mEnemyHealthTimer = -1;
}
void HUD::clear()
{
unsetSelectedSpell();
unsetSelectedWeapon();
resetEnemy();
}
void HUD::customMarkerCreated(MyGUI::Widget *marker)
{
marker->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked);

@ -16,7 +16,7 @@ namespace MWGui
class SpellIcons;
class ItemWidget;
class HUD : public Layout, public LocalMapBase
class HUD : public WindowBase, public LocalMapBase
{
public:
HUD(CustomMarkerCollection& customMarkers, DragAndDrop* dragAndDrop, MWRender::LocalMap* localMapRender);
@ -55,11 +55,11 @@ namespace MWGui
MyGUI::Widget* getEffectBox() { return mEffectBox; }
void update();
void setEnemy(const MWWorld::Ptr& enemy);
void resetEnemy();
void clear();
private:
MyGUI::ProgressBar *mHealth, *mMagicka, *mStamina, *mEnemyHealth, *mDrowning;
MyGUI::Widget* mHealthFrame;

@ -377,7 +377,7 @@ namespace MWGui
dirtyPreview();
}
void InventoryWindow::open()
void InventoryWindow::onOpen()
{
if (!mPtr.isEmpty())
{
@ -603,11 +603,8 @@ namespace MWGui
mEncumbranceBar->setValue(static_cast<int>(encumbrance), static_cast<int>(capacity));
}
void InventoryWindow::onFrame()
void InventoryWindow::onFrame(float dt)
{
if (!mMainWidget->getVisible())
return;
updateEncumbranceBar();
}

@ -35,12 +35,12 @@ namespace MWGui
public:
InventoryWindow(DragAndDrop* dragAndDrop, osg::Group* parent, Resource::ResourceSystem* resourceSystem);
virtual void open();
virtual void onOpen();
/// start trading, disables item drag&drop
void setTrading(bool trading);
void onFrame();
void onFrame(float dt);
void pickUpObject (MWWorld::Ptr object);

@ -119,6 +119,11 @@ namespace MWGui
return ret;
}
bool ItemModel::allowedToUseItems() const
{
return true;
}
bool ItemModel::allowedToInsertItems() const
{
return true;
@ -135,6 +140,11 @@ namespace MWGui
delete mSourceModel;
}
bool ProxyItemModel::allowedToUseItems() const
{
return mSourceModel->allowedToUseItems();
}
MWWorld::Ptr ProxyItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner)
{
return mSourceModel->copyItem (item, count, setNewOwner);

@ -70,6 +70,9 @@ namespace MWGui
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) = 0;
virtual void removeItem (const ItemStack& item, size_t count) = 0;
/// Is the player allowed to use items from this item model? (default true)
virtual bool allowedToUseItems() const;
/// Is the player allowed to insert items into this model? (default true)
virtual bool allowedToInsertItems() const;
@ -85,6 +88,9 @@ namespace MWGui
public:
ProxyItemModel();
virtual ~ProxyItemModel();
bool allowedToUseItems() const;
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false);
virtual void removeItem (const ItemStack& item, size_t count);
virtual ModelIndex getIndex (ItemStack item);

@ -29,9 +29,10 @@ namespace MWGui
center();
}
void ItemSelectionDialog::exit()
bool ItemSelectionDialog::exit()
{
eventDialogCanceled();
return true;
}
void ItemSelectionDialog::openContainer(const MWWorld::Ptr& container)

@ -21,7 +21,7 @@ namespace MWGui
public:
ItemSelectionDialog(const std::string& label);
virtual void exit();
virtual bool exit();
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;
typedef MyGUI::delegates::CMultiDelegate1<MWWorld::Ptr> EventHandle_Item;

@ -37,8 +37,6 @@ namespace MWGui
{
getWidget(mProgressBar, "ProgressBar");
setVisible(false);
mTimeAdvancer.eventProgressChanged += MyGUI::newDelegate(this, &JailScreen::onJailProgressChanged);
mTimeAdvancer.eventFinished += MyGUI::newDelegate(this, &JailScreen::onJailFinished);

@ -14,6 +14,8 @@ namespace MWGui
void onFrame(float dt);
bool exit() { return false; }
private:
int mDays;

@ -8,6 +8,7 @@
#include <MyGUI_TextBox.h>
#include <MyGUI_Button.h>
#include <MyGUI_InputManager.h>
#include <components/misc/stringops.hpp>
#include <components/widgets/imagebutton.hpp>
@ -44,7 +45,7 @@ namespace
static char const LeftTopicIndex [] = "LeftTopicIndex";
static char const RightTopicIndex [] = "RightTopicIndex";
struct JournalWindowImpl : MWGui::WindowBase, MWGui::JournalBooks, MWGui::JournalWindow
struct JournalWindowImpl : MWGui::JournalBooks, MWGui::JournalWindow
{
struct DisplayState
{
@ -84,19 +85,24 @@ namespace
void adviseButtonClick (char const * name, void (JournalWindowImpl::*Handler) (MyGUI::Widget* _sender))
{
getWidget <Gui::ImageButton> (name) ->
getWidget <MyGUI::Widget> (name) ->
eventMouseButtonClick += newDelegate(this, Handler);
}
void adviseKeyPress (char const * name, void (JournalWindowImpl::*Handler) (MyGUI::Widget* _sender, MyGUI::KeyCode key, MyGUI::Char character))
{
getWidget <MyGUI::Widget> (name) ->
eventKeyButtonPressed += newDelegate(this, Handler);
}
MWGui::BookPage* getPage (char const * name)
{
return getWidget <MWGui::BookPage> (name);
}
JournalWindowImpl (MWGui::JournalViewModel::Ptr Model, bool questList)
: WindowBase("openmw_journal.layout"), JournalBooks (Model)
: JournalBooks (Model), JournalWindow()
{
mMainWidget->setVisible(false);
center();
adviseButtonClick (OptionsBTN, &JournalWindowImpl::notifyOptions );
@ -112,6 +118,12 @@ namespace
adviseButtonClick (ShowAllBTN, &JournalWindowImpl::notifyShowAll );
adviseButtonClick (ShowActiveBTN, &JournalWindowImpl::notifyShowActive);
adviseKeyPress (OptionsBTN, &JournalWindowImpl::notifyKeyPress);
adviseKeyPress (PrevPageBTN, &JournalWindowImpl::notifyKeyPress);
adviseKeyPress (NextPageBTN, &JournalWindowImpl::notifyKeyPress);
adviseKeyPress (CloseBTN, &JournalWindowImpl::notifyKeyPress);
adviseKeyPress (JournalBTN, &JournalWindowImpl::notifyKeyPress);
Gui::MWList* list = getWidget<Gui::MWList>(QuestsList);
list->eventItemSelected += MyGUI::newDelegate(this, &JournalWindowImpl::notifyQuestClicked);
@ -211,7 +223,7 @@ namespace
button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0));
}
void open()
void onOpen()
{
if (!MWBase::Environment::get().getWindowManager ()->getJournalAllowed ())
{
@ -238,9 +250,11 @@ namespace
--page;
}
updateShowingPages();
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(getWidget<MyGUI::Widget>(CloseBTN));
}
void close()
void onClose()
{
mModel->unload ();
@ -349,8 +363,19 @@ namespace
relPages = 0;
}
setVisible (PrevPageBTN, page > 0);
setVisible (NextPageBTN, relPages > 2);
MyGUI::Widget* nextPageBtn = getWidget<MyGUI::Widget>(NextPageBTN);
MyGUI::Widget* prevPageBtn = getWidget<MyGUI::Widget>(PrevPageBTN);
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
bool nextPageVisible = relPages > 2;
nextPageBtn->setVisible(nextPageVisible);
bool prevPageVisible = page > 0;
prevPageBtn->setVisible(prevPageVisible);
if (focus == nextPageBtn && !nextPageVisible && prevPageVisible)
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(prevPageBtn);
else if (focus == prevPageBtn && !prevPageVisible && nextPageVisible)
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nextPageBtn);
setVisible (PageOneNum, relPages > 0);
setVisible (PageTwoNum, relPages > 1);
@ -362,6 +387,14 @@ namespace
setText (PageTwoNum, page + 2);
}
void notifyKeyPress(MyGUI::Widget* sender, MyGUI::KeyCode key, MyGUI::Char character)
{
if (key == MyGUI::KeyCode::ArrowUp)
notifyPrevPage(sender);
else if (key == MyGUI::KeyCode::ArrowDown)
notifyNextPage(sender);
}
void notifyTopicClicked (intptr_t linkId)
{
Book topicBook = createTopicBook (linkId);
@ -603,3 +636,9 @@ MWGui::JournalWindow * MWGui::JournalWindow::create (JournalViewModel::Ptr Model
{
return new JournalWindowImpl (Model, questList);
}
MWGui::JournalWindow::JournalWindow()
:WindowBase("openmw_journal.layout")
{
}

@ -1,6 +1,8 @@
#ifndef MWGUI_JOURNAL_H
#define MWGUI_JOURNAL_H
#include "windowbase.hpp"
#include <memory>
namespace MWBase { class WindowManager; }
@ -9,8 +11,10 @@ namespace MWGui
{
struct JournalViewModel;
struct JournalWindow
struct JournalWindow : public WindowBase
{
JournalWindow();
/// construct a new instance of the one JournalWindow implementation
static JournalWindow * create (std::shared_ptr <JournalViewModel> Model, bool questList);

@ -0,0 +1,283 @@
#include "keyboardnavigation.hpp"
#include <MyGUI_InputManager.h>
#include <MyGUI_WidgetManager.h>
#include <MyGUI_Button.h>
#include <MyGUI_Gui.h>
#include <MyGUI_Window.h>
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/environment.hpp"
namespace MWGui
{
bool shouldAcceptKeyFocus(MyGUI::Widget* w)
{
return w && !w->castType<MyGUI::Window>(false) && w->getInheritedEnabled() && w->getInheritedVisible() && w->getVisible() && w->getEnabled();
}
/// Recursively get all child widgets that accept keyboard input
void getKeyFocusWidgets(MyGUI::Widget* parent, std::vector<MyGUI::Widget*>& results)
{
if (!parent->getVisible() || !parent->getEnabled())
return;
MyGUI::EnumeratorWidgetPtr enumerator = parent->getEnumerator();
while (enumerator.next())
{
MyGUI::Widget* w = enumerator.current();
if (!w->getVisible() || !w->getEnabled())
continue;
if (w->getNeedKeyFocus() && shouldAcceptKeyFocus(w))
results.push_back(w);
else
getKeyFocusWidgets(w, results);
}
}
KeyboardNavigation::KeyboardNavigation()
: mCurrentFocus(nullptr)
, mModalWindow(nullptr)
{
MyGUI::WidgetManager::getInstance().registerUnlinker(this);
}
KeyboardNavigation::~KeyboardNavigation()
{
MyGUI::WidgetManager::getInstance().unregisterUnlinker(this);
}
void KeyboardNavigation::saveFocus(int mode)
{
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
if (shouldAcceptKeyFocus(focus))
{
mKeyFocus[mode] = focus;
}
else
{
mKeyFocus[mode] = mCurrentFocus;
}
}
void KeyboardNavigation::restoreFocus(int mode)
{
std::map<int, MyGUI::Widget*>::const_iterator found = mKeyFocus.find(mode);
if (found != mKeyFocus.end())
{
MyGUI::Widget* w = found->second;
if (w && w->getVisible() && w->getEnabled())
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(found->second);
}
}
void KeyboardNavigation::_unlinkWidget(MyGUI::Widget *widget)
{
for (std::pair<const int, MyGUI::Widget*>& w : mKeyFocus)
if (w.second == widget)
w.second = nullptr;
if (widget == mCurrentFocus)
mCurrentFocus = nullptr;
}
void styleFocusedButton(MyGUI::Widget* w)
{
if (w)
{
if (MyGUI::Button* b = w->castType<MyGUI::Button>(false))
{
b->_setWidgetState("highlighted");
}
}
}
bool isRootParent(MyGUI::Widget* widget, MyGUI::Widget* root)
{
while (widget && widget->getParent())
widget = widget->getParent();
return widget == root;
}
void KeyboardNavigation::onFrame()
{
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
if (focus == mCurrentFocus)
{
styleFocusedButton(mCurrentFocus);
return;
}
// workaround incorrect key focus resets (fix in MyGUI TBD)
if (!shouldAcceptKeyFocus(focus) && shouldAcceptKeyFocus(mCurrentFocus) && (!mModalWindow || isRootParent(mCurrentFocus, mModalWindow)))
{
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCurrentFocus);
focus = mCurrentFocus;
}
// style highlighted button (won't be needed for MyGUI 3.2.3)
if (focus != mCurrentFocus)
{
if (mCurrentFocus)
{
if (MyGUI::Button* b = mCurrentFocus->castType<MyGUI::Button>(false))
b->_setWidgetState("normal");
}
mCurrentFocus = focus;
}
styleFocusedButton(mCurrentFocus);
}
void KeyboardNavigation::setDefaultFocus(MyGUI::Widget *window, MyGUI::Widget *defaultFocus)
{
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
if (!focus || !shouldAcceptKeyFocus(focus))
{
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(defaultFocus);
}
else
{
if (!isRootParent(focus, window))
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(defaultFocus);
}
}
void KeyboardNavigation::setModalWindow(MyGUI::Widget *window)
{
mModalWindow = window;
}
enum Direction
{
D_Left,
D_Up,
D_Right,
D_Down,
D_Next,
D_Prev
};
bool KeyboardNavigation::injectKeyPress(MyGUI::KeyCode key, unsigned int text)
{
switch (key.getValue())
{
case MyGUI::KeyCode::ArrowLeft:
return switchFocus(D_Left, false);
case MyGUI::KeyCode::ArrowRight:
return switchFocus(D_Right, false);
case MyGUI::KeyCode::ArrowUp:
return switchFocus(D_Up, false);
case MyGUI::KeyCode::ArrowDown:
return switchFocus(D_Down, false);
case MyGUI::KeyCode::Tab:
return switchFocus(MyGUI::InputManager::getInstance().isShiftPressed() ? D_Prev : D_Next, true);
case MyGUI::KeyCode::Return:
case MyGUI::KeyCode::NumpadEnter:
case MyGUI::KeyCode::Space:
return accept();
default:
return false;
}
}
bool KeyboardNavigation::switchFocus(int direction, bool wrap)
{
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
bool isCycle = (direction == D_Prev || direction == D_Next);
if ((focus && focus->getTypeName().find("Button") == std::string::npos) && !isCycle)
return false;
if (focus && isCycle && focus->getUserString("AcceptTab") == "true")
return false;
if ((!focus || !focus->getNeedKeyFocus()) && isCycle)
{
// if nothing is selected, select the first widget
return selectFirstWidget();
}
if (!focus)
return false;
MyGUI::Widget* window = focus;
while (window && window->getParent())
window = window->getParent();
MyGUI::VectorWidgetPtr keyFocusList;
getKeyFocusWidgets(window, keyFocusList);
if (keyFocusList.empty())
return false;
MyGUI::VectorWidgetPtr::iterator found = std::find(keyFocusList.begin(), keyFocusList.end(), focus);
if (found == keyFocusList.end())
{
if (isCycle)
return selectFirstWidget();
else
return false;
}
bool forward = (direction == D_Next || direction == D_Right || direction == D_Down);
int index = found - keyFocusList.begin();
index = forward ? (index+1) : (index-1);
if (wrap)
index = (index + keyFocusList.size())%keyFocusList.size();
else
index = std::min(std::max(0, index), static_cast<int>(keyFocusList.size())-1);
MyGUI::Widget* next = keyFocusList[index];
int vertdiff = next->getTop() - focus->getTop();
int horizdiff = next->getLeft() - focus->getLeft();
bool isVertical = std::abs(vertdiff) > std::abs(horizdiff);
if (direction == D_Right && (horizdiff <= 0 || isVertical))
return false;
else if (direction == D_Left && (horizdiff >= 0 || isVertical))
return false;
else if (direction == D_Down && (vertdiff <= 0 || !isVertical))
return false;
else if (direction == D_Up && (vertdiff >= 0 || !isVertical))
return false;
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(keyFocusList[index]);
return true;
}
bool KeyboardNavigation::selectFirstWidget()
{
MyGUI::VectorWidgetPtr keyFocusList;
MyGUI::EnumeratorWidgetPtr enumerator = MyGUI::Gui::getInstance().getEnumerator();
if (mModalWindow)
enumerator = mModalWindow->getEnumerator();
while (enumerator.next())
getKeyFocusWidgets(enumerator.current(), keyFocusList);
if (!keyFocusList.empty())
{
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(keyFocusList[0]);
return true;
}
return false;
}
bool KeyboardNavigation::accept()
{
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
if (!focus)
return false;
//MyGUI::Button* button = focus->castType<MyGUI::Button>(false);
//if (button && button->getEnabled())
if (focus->getTypeName().find("Button") != std::string::npos && focus->getEnabled())
{
focus->eventMouseButtonClick(focus);
return true;
}
return false;
}
}

@ -0,0 +1,47 @@
#ifndef OPENMW_MWGUI_KEYBOARDNAVIGATION_H
#define OPENMW_MWGUI_KEYBOARDNAVIGATION_H
#include <MyGUI_KeyCode.h>
#include <MyGUI_IUnlinkWidget.h>
namespace MWGui
{
class KeyboardNavigation : public MyGUI::IUnlinkWidget
{
public:
KeyboardNavigation();
~KeyboardNavigation();
/// @return Was the key handled by this class?
bool injectKeyPress(MyGUI::KeyCode key, unsigned int text);
void saveFocus(int mode);
void restoreFocus(int mode);
void _unlinkWidget(MyGUI::Widget* widget);
void onFrame();
/// Set a key focus widget for this window, if one isn't already set.
void setDefaultFocus(MyGUI::Widget* window, MyGUI::Widget* defaultFocus);
void setModalWindow(MyGUI::Widget* window);
private:
bool switchFocus(int direction, bool wrap);
bool selectFirstWidget();
/// Send button press event to focused button
bool accept();
std::map<int, MyGUI::Widget*> mKeyFocus;
MyGUI::Widget* mCurrentFocus;
MyGUI::Widget* mModalWindow;
};
}
#endif

@ -41,6 +41,7 @@ namespace MWGui
{
MyGUI::TextBox* t;
getWidget(t, "AttribVal" + MyGUI::utility::toString(i));
mAttributeValues.push_back(t);
MyGUI::Button* b;
getWidget(b, "Attrib" + MyGUI::utility::toString(i));
@ -48,10 +49,7 @@ namespace MWGui
b->eventMouseButtonClick += MyGUI::newDelegate(this, &LevelupDialog::onAttributeClicked);
mAttributes.push_back(b);
mAttributeValues.push_back(t);
getWidget(t, "AttribMultiplier" + MyGUI::utility::toString(i));
mAttributeMultipliers.push_back(t);
}
@ -128,7 +126,7 @@ namespace MWGui
setAttributeValues();
}
void LevelupDialog::open()
void LevelupDialog::onOpen()
{
MWBase::World *world = MWBase::Environment::get().getWorld();
MWWorld::Ptr player = world->getPlayerPtr();

@ -11,7 +11,7 @@ namespace MWGui
public:
LevelupDialog();
virtual void open();
virtual void onOpen();
private:
MyGUI::Button* mOkButton;

@ -50,8 +50,8 @@ namespace MWGui
mBackgroundImage = MyGUI::Gui::getInstance().createWidgetReal<BackgroundImage>("ImageBox", 0,0,1,1,
MyGUI::Align::Stretch, "Menu");
setVisible(false);
mSceneImage = MyGUI::Gui::getInstance().createWidgetReal<BackgroundImage>("ImageBox", 0,0,1,1,
MyGUI::Align::Stretch, "Scene");
findSplashScreens();
}
@ -112,6 +112,7 @@ namespace MWGui
{
WindowBase::setVisible(visible);
mBackgroundImage->setVisible(visible);
mSceneImage->setVisible(visible);
}
double LoadingScreen::getTargetFrameRate() const
@ -216,8 +217,11 @@ namespace MWGui
// TODO: add option (filename pattern?) to use image aspect ratio instead of 4:3
// we can't do this by default, because the Morrowind splash screens are 1024x1024, but should be displayed as 4:3
bool stretch = Settings::Manager::getBool("stretch menu background", "GUI");
mBackgroundImage->setVisible(true);
mBackgroundImage->setBackgroundImage(randomSplash, true, stretch);
}
mSceneImage->setBackgroundImage("");
mSceneImage->setVisible(false);
}
void LoadingScreen::setProgressRange (size_t range)
@ -294,9 +298,11 @@ namespace MWGui
mViewer->getCamera()->setInitialDrawCallback(new CopyFramebufferToTextureCallback(mTexture));
mBackgroundImage->setBackgroundImage("");
mBackgroundImage->setVisible(false);
mBackgroundImage->setRenderItemTexture(mGuiTexture.get());
mBackgroundImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f));
mSceneImage->setRenderItemTexture(mGuiTexture.get());
mSceneImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f));
mSceneImage->setVisible(true);
}
void LoadingScreen::draw()

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

Loading…
Cancel
Save