1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-05-17 10:41:29 +00:00

Merge remote-tracking branch 'upstream/master' into osgshadow-test

This commit is contained in:
AnyOldName3 2017-10-08 00:18:02 +01:00
commit b94bbe00cb
87 changed files with 2108 additions and 227 deletions

View file

@ -6,5 +6,5 @@ brew outdated cmake || brew upgrade cmake
brew outdated pkgconfig || brew upgrade pkgconfig brew outdated pkgconfig || brew upgrade pkgconfig
brew install $macos_qt_formula 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 unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null

View file

@ -18,7 +18,7 @@ opencs_hdrs_noqt (model/doc
opencs_units (model/world 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 cellcreator pathgridcreator referenceablecreator startscriptcreator referencecreator scenesubview
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator
bodypartcreator bodypartcreator landtexturecreator landcreator
) )
opencs_units_noqt (view/world opencs_units_noqt (view/world

View file

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

View file

@ -6,6 +6,7 @@
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
#include <stdexcept> #include <stdexcept>
#include <string>
#include <functional> #include <functional>
#include <QVariant> #include <QVariant>
@ -13,8 +14,9 @@
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include "columnbase.hpp" #include "columnbase.hpp"
#include "collectionbase.hpp" #include "collectionbase.hpp"
#include "land.hpp"
#include "landtexture.hpp"
namespace CSMWorld namespace CSMWorld
{ {
@ -22,15 +24,14 @@ namespace CSMWorld
template<typename ESXRecordT> template<typename ESXRecordT>
struct IdAccessor struct IdAccessor
{ {
std::string& getId (ESXRecordT& record); void setId(ESXRecordT& record, const std::string& id) const;
const std::string getId (const ESXRecordT& record) const; const std::string getId (const ESXRecordT& record) const;
}; };
template<typename ESXRecordT> 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> template<typename ESXRecordT>
@ -39,6 +40,39 @@ namespace CSMWorld
return record.mId; 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 /// \brief Single-type record collection
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> > template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
class Collection : public CollectionBase class Collection : public CollectionBase
@ -69,6 +103,13 @@ namespace CSMWorld
/// ///
/// \return Success? /// \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: public:
Collection(); Collection();
@ -108,6 +149,10 @@ namespace CSMWorld
const std::string& destination, const std::string& destination,
const UniversalId::Type type); 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; virtual int searchId (const std::string& id) const;
////< Search record with \a id. ////< Search record with \a id.
/// \return index of record (if found) or -1 (not found) /// \return index of record (if found) or -1 (not found)
@ -206,16 +251,71 @@ namespace CSMWorld
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::cloneRecord(const std::string& origin, int Collection<ESXRecordT, IdAccessorT>::cloneRecordImp(const std::string& origin,
const std::string& destination, const std::string& destination, UniversalId::Type type)
const UniversalId::Type type)
{ {
Record<ESXRecordT> copy; Record<ESXRecordT> copy;
copy.mModified = getRecord(origin).get(); copy.mModified = getRecord(origin).get();
copy.mState = RecordBase::State_ModifiedOnly; copy.mState = RecordBase::State_ModifiedOnly;
copy.get().mId = destination; IdAccessorT().setId(copy.get(), destination);
int index = getAppendIndex(destination, type);
insertRecord(copy, 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> template<typename ESXRecordT, typename IdAccessorT>
@ -366,7 +466,7 @@ namespace CSMWorld
UniversalId::Type type) UniversalId::Type type)
{ {
ESXRecordT record; ESXRecordT record;
IdAccessorT().getId (record) = id; IdAccessorT().setId(record, id);
record.blank(); record.blank();
Record<ESXRecordT> record2; Record<ESXRecordT> record2;

View file

@ -78,6 +78,8 @@ namespace CSMWorld
const std::string& destination, const std::string& destination,
const UniversalId::Type type) = 0; 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 (const std::string& id) const = 0;
virtual const RecordBase& getRecord (int index) const = 0; virtual const RecordBase& getRecord (int index) const = 0;

View file

@ -1,10 +1,322 @@
#include "columnimp.hpp" #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) : 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) 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); 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(); ESM::BodyPart record2 = record.get();
@ -22,7 +334,8 @@ void CSMWorld::BodyPartRaceColumn::set(Record<ESM::BodyPart> &record, const QVar
record.setModified(record2); record.setModified(record2);
} }
bool CSMWorld::BodyPartRaceColumn::isEditable() const bool BodyPartRaceColumn::isEditable() const
{ {
return true; return true;
} }
}

View file

@ -2,10 +2,12 @@
#define CSM_WOLRD_COLUMNIMP_H #define CSM_WOLRD_COLUMNIMP_H
#include <cassert> #include <cassert>
#include <cstdint>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include <QColor> #include <QColor>
#include <QVector>
#include <components/esm/loadbody.hpp> #include <components/esm/loadbody.hpp>
#include <components/esm/loadskil.hpp> #include <components/esm/loadskil.hpp>
@ -15,6 +17,9 @@
#include "columns.hpp" #include "columns.hpp"
#include "info.hpp" #include "info.hpp"
#include "land.hpp"
#include "landtexture.hpp"
namespace CSMWorld namespace CSMWorld
{ {
/// \note Shares ID with VarValueColumn. A table can not have both. /// \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> template<typename ESXRecordT>
struct RecordStateColumn : public Column<ESXRecordT> struct RecordStateColumn : public Column<ESXRecordT>
{ {
@ -673,32 +692,23 @@ namespace CSMWorld
} }
}; };
/// \todo QColor is a GUI class and should not be in model. Need to think of an alternative
/// solution.
template<typename ESXRecordT> template<typename ESXRecordT>
struct MapColourColumn : public Column<ESXRecordT> struct MapColourColumn : public Column<ESXRecordT>
{ {
/// \todo Replace Display_Integer with something that displays the colour value more directly.
MapColourColumn() MapColourColumn()
: Column<ESXRecordT> (Columns::ColumnId_MapColour, ColumnBase::Display_Colour) : Column<ESXRecordT> (Columns::ColumnId_MapColour, ColumnBase::Display_Colour)
{} {}
virtual QVariant get (const Record<ESXRecordT>& record) const virtual QVariant get (const Record<ESXRecordT>& record) const
{ {
int colour = record.get().mMapColor; return record.get().mMapColor;
return QColor (colour & 0xff, (colour>>8) & 0xff, (colour>>16) & 0xff);
} }
virtual void set (Record<ESXRecordT>& record, const QVariant& data) virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{ {
ESXRecordT record2 = record.get(); ESXRecordT copy = record.get();
copy.mMapColor = data.toInt();
QColor colour = data.value<QColor>(); record.setModified (copy);
record2.mMapColor = (colour.blue() << 16) | (colour.green() << 8) | colour.red();
record.setModified (record2);
} }
virtual bool isEditable() const virtual bool isEditable() const
@ -2418,6 +2428,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> struct BodyPartRaceColumn : public RaceColumn<ESM::BodyPart>
{ {
const MeshTypeColumn<ESM::BodyPart> *mMeshType; const MeshTypeColumn<ESM::BodyPart> *mMeshType;
@ -2430,4 +2528,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 #endif

View file

@ -330,6 +330,14 @@ namespace CSMWorld
{ ColumnId_WeatherChance, "Percent Chance" }, { ColumnId_WeatherChance, "Percent Chance" },
{ ColumnId_Text, "Text" }, { 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_UseValue1, "Use value 1" },
{ ColumnId_UseValue2, "Use value 2" }, { ColumnId_UseValue2, "Use value 2" },

View file

@ -329,6 +329,15 @@ namespace CSMWorld
ColumnId_Text = 297, 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 // Allocated to a separate value range, so we don't get a collision should we ever need
// to extend the number of use values. // to extend the number of use values.
ColumnId_UseValue1 = 0x10000, ColumnId_UseValue1 = 0x10000,

View file

@ -2,6 +2,7 @@
#include <cmath> #include <cmath>
#include <sstream> #include <sstream>
#include <unordered_set>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
@ -15,6 +16,175 @@
#include "nestedtablewrapper.hpp" #include "nestedtablewrapper.hpp"
#include "pathgrid.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, CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
const QVariant& new_, QUndoCommand* parent) const QVariant& new_, QUndoCommand* parent)
: QUndoCommand (parent), mModel (&model), mIndex (index), mNew (new_), mHasRecordState(false), mOldRecordState(CSMWorld::RecordBase::State_BaseOnly) : QUndoCommand (parent), mModel (&model), mIndex (index), mNew (new_), mHasRecordState(false), mOldRecordState(CSMWorld::RecordBase::State_BaseOnly)

View file

@ -5,12 +5,14 @@
#include <string> #include <string>
#include <map> #include <map>
#include <memory>
#include <vector> #include <vector>
#include <QVariant> #include <QVariant>
#include <QUndoCommand> #include <QUndoCommand>
#include <QModelIndex> #include <QModelIndex>
#include "columnimp.hpp"
#include "universalid.hpp" #include "universalid.hpp"
#include "nestedtablewrapper.hpp" #include "nestedtablewrapper.hpp"
@ -24,6 +26,91 @@ namespace CSMWorld
struct RecordBase; struct RecordBase;
struct NestedTableWrapperBase; 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 class ModifyCommand : public QUndoCommand
{ {
QAbstractItemModel *mModel; QAbstractItemModel *mModel;

View file

@ -412,6 +412,24 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat
Columns::ColumnId_NegativeLight, ESM::MagicEffect::NegativeLight)); Columns::ColumnId_NegativeLight, ESM::MagicEffect::NegativeLight));
mMagicEffects.addColumn (new DescriptionColumn<ESM::MagicEffect>); 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 StringIdColumn<Pathgrid>);
mPathgrids.addColumn (new RecordStateColumn<Pathgrid>); mPathgrids.addColumn (new RecordStateColumn<Pathgrid>);
mPathgrids.addColumn (new FixedRecordTypeColumn<Pathgrid> (UniversalId::Type_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 (&mBodyParts), UniversalId::Type_BodyPart);
addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen); addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen);
addModel (new IdTable (&mMagicEffects), UniversalId::Type_MagicEffect); 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 IdTree (&mPathgrids, &mPathgrids), UniversalId::Type_Pathgrid);
addModel (new IdTable (&mStartScripts), UniversalId::Type_StartScript); addModel (new IdTable (&mStartScripts), UniversalId::Type_StartScript);
addModel (new IdTree (&mReferenceables, &mReferenceables, IdTable::Feature_Preview), 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_LTEX: mLandTextures.load (*mReader, mBase); break;
case ESM::REC_LAND: case ESM::REC_LAND: mLand.load(*mReader, mBase); break;
{
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_CELL: case ESM::REC_CELL:
{ {

View file

@ -4,6 +4,7 @@
#include <components/esm/esmreader.hpp> #include <components/esm/esmreader.hpp>
#include "collection.hpp" #include "collection.hpp"
#include "land.hpp"
namespace CSMWorld namespace CSMWorld
{ {
@ -39,6 +40,22 @@ namespace CSMWorld
record.load (reader, isDeleted); 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> template<typename ESXRecordT, typename IdAccessorT>
int IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base) int IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base)
{ {

View file

@ -1,11 +1,17 @@
#include "idtable.hpp" #include "idtable.hpp"
#include <algorithm>
#include <cctype>
#include <cstdint>
#include <limits>
#include <map>
#include <stdexcept> #include <stdexcept>
#include <components/esm/cellid.hpp> #include <components/esm/cellid.hpp>
#include "collectionbase.hpp" #include "collectionbase.hpp"
#include "columnbase.hpp" #include "columnbase.hpp"
#include "landtexture.hpp"
CSMWorld::IdTable::IdTable (CollectionBase *idCollection, unsigned int features) CSMWorld::IdTable::IdTable (CollectionBase *idCollection, unsigned int features)
: IdTableBase (features), mIdCollection (idCollection) : IdTableBase (features), mIdCollection (idCollection)
@ -179,6 +185,26 @@ void CSMWorld::IdTable::cloneRecord(const std::string& origin,
endInsertRows(); 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 ///This method can return only indexes to the top level table cells
QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const
{ {
@ -281,3 +307,72 @@ CSMWorld::CollectionBase *CSMWorld::IdTable::idCollection() const
{ {
return mIdCollection; 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;
}

View file

@ -61,6 +61,11 @@ namespace CSMWorld
const std::string& destination, const std::string& destination,
UniversalId::Type type = UniversalId::Type_None); 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; virtual QModelIndex getModelIndex (const std::string& id, int column) const;
void setRecord (const std::string& id, const RecordBase& record, void setRecord (const std::string& id, const RecordBase& record,
@ -93,6 +98,29 @@ namespace CSMWorld
virtual CollectionBase *idCollection() const; 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 #endif

View file

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

View file

@ -1,15 +1,30 @@
#include "land.hpp" #include "land.hpp"
#include <sstream> #include <sstream>
#include <stdexcept>
namespace CSMWorld namespace CSMWorld
{ {
void Land::load(ESM::ESMReader &esm, bool &isDeleted) void Land::load(ESM::ESMReader &esm, bool &isDeleted)
{ {
ESM::Land::load(esm, isDeleted); ESM::Land::load(esm, isDeleted);
}
std::string Land::createUniqueRecordId(int x, int y)
{
std::ostringstream stream; std::ostringstream stream;
stream << "#" << mX << " " << mY; stream << "#" << x << " " << y;
mId = stream.str(); 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));
} }
} }

View file

@ -12,10 +12,11 @@ namespace CSMWorld
/// \todo Add worldspace support to the Land record. /// \todo Add worldspace support to the Land record.
struct Land : public ESM::Land struct Land : public ESM::Land
{ {
std::string mId;
/// Loads the metadata and ID /// Loads the metadata and ID
void load (ESM::ESMReader &esm, bool &isDeleted); 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);
}; };
} }

View file

@ -1,5 +1,8 @@
#include "landtexture.hpp" #include "landtexture.hpp"
#include <sstream>
#include <stdexcept>
#include <components/esm/esmreader.hpp> #include <components/esm/esmreader.hpp>
namespace CSMWorld namespace CSMWorld
@ -11,4 +14,21 @@ namespace CSMWorld
mPluginIndex = esm.getIndex(); 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));
}
} }

View file

@ -13,6 +13,11 @@ namespace CSMWorld
int mPluginIndex; int mPluginIndex;
void load (ESM::ESMReader &esm, bool &isDeleted); 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);
}; };
} }

View file

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

View file

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

View file

@ -813,6 +813,12 @@ void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin,
mData.insertRecord(*newRecord, type, destination); 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, void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record,
UniversalId::Type type) UniversalId::Type type)
{ {

View file

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

View file

@ -53,6 +53,8 @@ namespace
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", 0 }, { 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_SoundGens, "Sound Generators", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", 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_Pathgrids, "Pathgrids", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Meta Data Table", 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_DebugProfile, "Debug Profile", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_SoundGen, "Sound Generator", 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_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_Pathgrid, "Pathgrid", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Meta Data", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Meta Data", 0 },

View file

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

View file

@ -172,6 +172,16 @@ void CSVDoc::View::setupWorldMenu()
setupShortcut("document-world-references", references); setupShortcut("document-world-references", references);
world->addAction (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); QAction *grid = new QAction (tr ("Pathgrid"), this);
connect (grid, SIGNAL (triggered()), this, SLOT (addPathgridSubView())); connect (grid, SIGNAL (triggered()), this, SLOT (addPathgridSubView()));
setupShortcut("document-world-pathgrid", grid); setupShortcut("document-world-pathgrid", grid);
@ -876,6 +886,16 @@ void CSVDoc::View::addRunLogSubView()
addSubView (CSMWorld::UniversalId::Type_RunLog); 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() void CSVDoc::View::addPathgridSubView()
{ {
addSubView (CSMWorld::UniversalId::Type_Pathgrids); addSubView (CSMWorld::UniversalId::Type_Pathgrids);

View file

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

View file

@ -26,6 +26,33 @@
#include "terrainstorage.hpp" #include "terrainstorage.hpp"
#include "object.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) bool CSVRender::Cell::removeObject (const std::string& id)
{ {
std::map<std::string, Object *>::iterator iter = std::map<std::string, Object *>::iterator iter =
@ -75,10 +102,69 @@ bool CSVRender::Cell::addObjects (int start, int end)
return modified; 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, CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::string& id,
bool deleted) bool deleted)
: mData (data), mId (Misc::StringUtils::lowerCase (id)), mDeleted (deleted), mSubMode (0), : 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); 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; mCoordinates = result.first;
mCellNode = new osg::Group; mCellNode = new osg::Group;
mCellNode->setUserData(new CellNodeContainer(this));
mCellNode->setUpdateCallback(new CellNodeCallback);
rootNode->addChild(mCellNode); rootNode->addChild(mCellNode);
setCellMarker(); setCellMarker();
@ -99,22 +187,7 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st
addObjects (0, rows-1); addObjects (0, rows-1);
const CSMWorld::IdCollection<CSMWorld::Land>& land = mData.getLand(); updateLand();
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);
}
}
mPathgrid.reset(new Pathgrid(mData, mCellNode, mId, mCoordinates)); mPathgrid.reset(new Pathgrid(mData, mCellNode, mId, mCoordinates));
mCellWater.reset(new CellWater(mData, mCellNode, mId, mCoordinates)); mCellWater.reset(new CellWater(mData, mCellNode, mId, mCoordinates));
@ -285,6 +358,38 @@ void CSVRender::Cell::pathgridRemoved()
mPathgrid->removeGeometry(); 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() void CSVRender::Cell::reloadAssets()
{ {
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin()); for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());

View file

@ -57,6 +57,7 @@ namespace CSVRender
bool mDeleted; bool mDeleted;
int mSubMode; int mSubMode;
unsigned int mSubModeElementMask; unsigned int mSubModeElementMask;
bool mUpdateLand, mLandDeleted;
/// Ignored if cell does not have an object with the given ID. /// Ignored if cell does not have an object with the given ID.
/// ///
@ -72,6 +73,9 @@ namespace CSVRender
/// \return Have any objects been added? /// \return Have any objects been added?
bool addObjects (int start, int end); bool addObjects (int start, int end);
void updateLand();
void unloadLand();
public: public:
enum Selection enum Selection
@ -118,6 +122,18 @@ namespace CSVRender
void pathgridRemoved(); 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 reloadAssets();
void setSelection (int elementMask, Selection mode); 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 /// Erase all overrides and restore the visual representation of the cell to its
/// true state. /// true state.
void reset (unsigned int elementMask); void reset (unsigned int elementMask);
friend class CellNodeCallback;
}; };
} }

View file

@ -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() std::string CSVRender::PagedWorldspaceWidget::getStartupInstruction()
{ {
@ -475,6 +541,24 @@ CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc
connect (&document.getData(), SIGNAL (assetTablesChanged ()), connect (&document.getData(), SIGNAL (assetTablesChanged ()),
this, SLOT (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 // Shortcuts
CSMPrefs::Shortcut* loadCameraCellShortcut = new CSMPrefs::Shortcut("scene-load-cam-cell", this); CSMPrefs::Shortcut* loadCameraCellShortcut = new CSMPrefs::Shortcut("scene-load-cam-cell", this);
connect(loadCameraCellShortcut, SIGNAL(activated()), this, SLOT(loadCameraCell())); connect(loadCameraCellShortcut, SIGNAL(activated()), this, SLOT(loadCameraCell()));

View file

@ -155,6 +155,14 @@ namespace CSVRender
virtual void cellAdded (const QModelIndex& index, int start, int end); 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 assetTablesChanged ();
void loadCameraCell(); void loadCameraCell();

View file

@ -1,5 +1,8 @@
#include "terrainstorage.hpp" #include "terrainstorage.hpp"
#include "../../model/world/land.hpp"
#include "../../model/world/landtexture.hpp"
namespace CSVRender namespace CSVRender
{ {
@ -11,12 +14,9 @@ namespace CSVRender
osg::ref_ptr<const ESMTerrain::LandObject> TerrainStorage::getLand(int cellX, int cellY) 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 // 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 // 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) if (index == -1)
return NULL; return NULL;
@ -26,16 +26,11 @@ namespace CSVRender
const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin) const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin)
{ {
int numRecords = mData.getLandTextures().getSize(); int row = mData.getLandTextures().searchId(CSMWorld::LandTexture::createUniqueRecordId(plugin, index));
if (row == -1)
return nullptr;
for (int i=0; i<numRecords; ++i) return &mData.getLandTextures().getRecord(row).get();
{
const CSMWorld::LandTexture* ltex = &mData.getLandTextures().getRecord(i).get();
if (ltex->mIndex == index && ltex->mPluginIndex == plugin)
return ltex;
}
return NULL;
} }
void TerrainStorage::getBounds(float &minX, float &maxX, float &minY, float &maxY) void TerrainStorage::getBounds(float &minX, float &maxX, float &minY, float &maxY)

View file

@ -15,12 +15,15 @@ void CSVWorld::ColorDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option, const QStyleOptionViewItem &option,
const QModelIndex &index) const const QModelIndex &index) const
{ {
int colorInt = index.data().toInt();
QColor color(colorInt & 0xff, (colorInt >> 8) & 0xff, (colorInt >> 16) & 0xff);
QRect coloredRect(option.rect.x() + qRound(option.rect.width() / 4.0), QRect coloredRect(option.rect.x() + qRound(option.rect.width() / 4.0),
option.rect.y() + qRound(option.rect.height() / 4.0), option.rect.y() + qRound(option.rect.height() / 4.0),
option.rect.width() / 2, option.rect.width() / 2,
option.rect.height() / 2); option.rect.height() / 2);
painter->save(); painter->save();
painter->fillRect(coloredRect, index.data().value<QColor>()); painter->fillRect(coloredRect, color);
painter->setPen(Qt::black); painter->setPen(Qt::black);
painter->drawRect(coloredRect); painter->drawRect(coloredRect);
painter->restore(); painter->restore();

View file

@ -31,6 +31,9 @@ namespace CSVWorld
virtual void cloneMode(const std::string& originId, virtual void cloneMode(const std::string& originId,
const CSMWorld::UniversalId::Type type) = 0; 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 setEditLock (bool locked) = 0;
virtual void toggleWidgets(bool active = true) = 0; virtual void toggleWidgets(bool active = true) = 0;

View file

@ -51,6 +51,11 @@ std::string CSVWorld::GenericCreator::getId() const
return mId->text().toUtf8().constData(); return mId->text().toUtf8().constData();
} }
std::string CSVWorld::GenericCreator::getClonedId() const
{
return mClonedId;
}
std::string CSVWorld::GenericCreator::getIdValidatorResult() const std::string CSVWorld::GenericCreator::getIdValidatorResult() const
{ {
std::string errors; std::string errors;
@ -254,6 +259,22 @@ void CSVWorld::GenericCreator::cloneMode(const std::string& originId,
mClonedType = type; 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) void CSVWorld::GenericCreator::toggleWidgets(bool active)
{ {
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -16,6 +16,7 @@
#include "../../model/world/idtableproxymodel.hpp" #include "../../model/world/idtableproxymodel.hpp"
#include "../../model/world/idtablebase.hpp" #include "../../model/world/idtablebase.hpp"
#include "../../model/world/idtable.hpp" #include "../../model/world/idtable.hpp"
#include "../../model/world/landtexturetableproxymodel.hpp"
#include "../../model/world/record.hpp" #include "../../model/world/record.hpp"
#include "../../model/world/columns.hpp" #include "../../model/world/columns.hpp"
#include "../../model/world/commanddispatcher.hpp" #include "../../model/world/commanddispatcher.hpp"
@ -60,6 +61,9 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
menu.addAction(mCloneAction); menu.addAction(mCloneAction);
} }
if (mTouchAction)
menu.addAction (mTouchAction);
if (mCreateAction) if (mCreateAction)
menu.addAction (mCreateAction); menu.addAction (mCreateAction);
@ -226,17 +230,22 @@ void CSVWorld::Table::mouseDoubleClickEvent (QMouseEvent *event)
CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
bool createAndDelete, bool sorting, CSMDoc::Document& document) bool createAndDelete, bool sorting, CSMDoc::Document& document)
: DragRecordTable(document), mCreateAction (0), : DragRecordTable(document), mCreateAction (nullptr), mCloneAction(nullptr), mTouchAction(nullptr),
mCloneAction(0), mRecordStatusDisplay (0), mJumpToAddedRecord(false), mUnselectAfterJump(false) mRecordStatusDisplay (0), mJumpToAddedRecord(false), mUnselectAfterJump(false)
{ {
mModel = &dynamic_cast<CSMWorld::IdTableBase&> (*mDocument.getData().getTableModel (id)); mModel = &dynamic_cast<CSMWorld::IdTableBase&> (*mDocument.getData().getTableModel (id));
bool isInfoTable = id.getType() == CSMWorld::UniversalId::Type_TopicInfos || bool isInfoTable = id.getType() == CSMWorld::UniversalId::Type_TopicInfos ||
id.getType() == CSMWorld::UniversalId::Type_JournalInfos; id.getType() == CSMWorld::UniversalId::Type_JournalInfos;
bool isLtexTable = (id.getType() == CSMWorld::UniversalId::Type_LandTextures);
if (isInfoTable) if (isInfoTable)
{ {
mProxyModel = new CSMWorld::InfoTableProxyModel(id.getType(), this); mProxyModel = new CSMWorld::InfoTableProxyModel(id.getType(), this);
} }
else if (isLtexTable)
{
mProxyModel = new CSMWorld::LandTextureTableProxyModel (this);
}
else else
{ {
mProxyModel = new CSMWorld::IdTableProxyModel (this); mProxyModel = new CSMWorld::IdTableProxyModel (this);
@ -302,6 +311,15 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
cloneShortcut->associateAction(mCloneAction); 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); mRevertAction = new QAction (tr ("Revert Record"), this);
connect (mRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeRevert())); connect (mRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeRevert()));
addAction (mRevertAction); 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() void CSVWorld::Table::moveUpRecord()
{ {
if (mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant)) if (mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant))

View file

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

View file

@ -249,6 +249,11 @@ void CSVWorld::TableBottomBox::cloneRequest(const std::string& id,
mCreator->focus(); 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) void CSVWorld::TableBottomBox::extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds)
{ {
extendedConfigRequest(ExtendedCommandConfigurator::Mode_Delete, selectedIds); extendedConfigRequest(ExtendedCommandConfigurator::Mode_Delete, selectedIds);

View file

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

View file

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

View file

@ -22,6 +22,7 @@
#include "itemview.hpp" #include "itemview.hpp"
#include "itemwidget.hpp" #include "itemwidget.hpp"
#include "inventoryitemmodel.hpp" #include "inventoryitemmodel.hpp"
#include "containeritemmodel.hpp"
#include "sortfilteritemmodel.hpp" #include "sortfilteritemmodel.hpp"
#include "pickpocketitemmodel.hpp" #include "pickpocketitemmodel.hpp"
#include "draganddrop.hpp" #include "draganddrop.hpp"
@ -136,6 +137,8 @@ namespace MWGui
bool loot = mPtr.getClass().isActor() && mPtr.getClass().getCreatureStats(mPtr).isDead(); bool loot = mPtr.getClass().isActor() && mPtr.getClass().getCreatureStats(mPtr).isDead();
if (mPtr.getClass().hasInventoryStore(mPtr))
{
if (mPtr.getClass().isNpc() && !loot) if (mPtr.getClass().isNpc() && !loot)
{ {
// we are stealing stuff // we are stealing stuff
@ -145,6 +148,11 @@ namespace MWGui
} }
else else
mModel = new InventoryItemModel(container); mModel = new InventoryItemModel(container);
}
else
{
mModel = new ContainerItemModel(container);
}
mDisposeCorpseButton->setVisible(loot); mDisposeCorpseButton->setVisible(loot);

View file

@ -6,8 +6,11 @@
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwmechanics/actorutil.hpp"
namespace namespace
{ {
@ -47,6 +50,19 @@ ContainerItemModel::ContainerItemModel (const MWWorld::Ptr& source)
mItemSources.push_back(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) ItemStack ContainerItemModel::getItem (ModelIndex index)
{ {
if (index < 0) if (index < 0)

View file

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

View file

@ -390,6 +390,7 @@ namespace MWGui
{ {
// No greetings found. The dialogue window should not be shown. // 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). // 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()) if (isCompanion())
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Companion, mPtr); MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Companion, mPtr);
return; return;

View file

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

View file

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

View file

@ -12,6 +12,11 @@
namespace MWGui 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 /// Recursively get all child widgets that accept keyboard input
void getKeyFocusWidgets(MyGUI::Widget* parent, std::vector<MyGUI::Widget*>& results) void getKeyFocusWidgets(MyGUI::Widget* parent, std::vector<MyGUI::Widget*>& results)
{ {
@ -24,18 +29,13 @@ void getKeyFocusWidgets(MyGUI::Widget* parent, std::vector<MyGUI::Widget*>& resu
MyGUI::Widget* w = enumerator.current(); MyGUI::Widget* w = enumerator.current();
if (!w->getVisible() || !w->getEnabled()) if (!w->getVisible() || !w->getEnabled())
continue; continue;
if (w->getNeedKeyFocus()) if (w->getNeedKeyFocus() && shouldAcceptKeyFocus(w))
results.push_back(w); results.push_back(w);
else else
getKeyFocusWidgets(w, results); getKeyFocusWidgets(w, results);
} }
} }
bool shouldAcceptKeyFocus(MyGUI::Widget* w)
{
return w && !w->castType<MyGUI::Window>(false) && w->getInheritedEnabled() && w->getInheritedVisible() && w->getVisible() && w->getEnabled();
}
KeyboardNavigation::KeyboardNavigation() KeyboardNavigation::KeyboardNavigation()
: mCurrentFocus(nullptr) : mCurrentFocus(nullptr)
, mModalWindow(nullptr) , mModalWindow(nullptr)

View file

@ -50,6 +50,8 @@ namespace MWGui
mBackgroundImage = MyGUI::Gui::getInstance().createWidgetReal<BackgroundImage>("ImageBox", 0,0,1,1, mBackgroundImage = MyGUI::Gui::getInstance().createWidgetReal<BackgroundImage>("ImageBox", 0,0,1,1,
MyGUI::Align::Stretch, "Menu"); MyGUI::Align::Stretch, "Menu");
mSceneImage = MyGUI::Gui::getInstance().createWidgetReal<BackgroundImage>("ImageBox", 0,0,1,1,
MyGUI::Align::Stretch, "Scene");
findSplashScreens(); findSplashScreens();
} }
@ -110,6 +112,7 @@ namespace MWGui
{ {
WindowBase::setVisible(visible); WindowBase::setVisible(visible);
mBackgroundImage->setVisible(visible); mBackgroundImage->setVisible(visible);
mSceneImage->setVisible(visible);
} }
double LoadingScreen::getTargetFrameRate() const double LoadingScreen::getTargetFrameRate() const
@ -214,8 +217,11 @@ namespace MWGui
// TODO: add option (filename pattern?) to use image aspect ratio instead of 4:3 // 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 // 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"); bool stretch = Settings::Manager::getBool("stretch menu background", "GUI");
mBackgroundImage->setVisible(true);
mBackgroundImage->setBackgroundImage(randomSplash, true, stretch); mBackgroundImage->setBackgroundImage(randomSplash, true, stretch);
} }
mSceneImage->setBackgroundImage("");
mSceneImage->setVisible(false);
} }
void LoadingScreen::setProgressRange (size_t range) void LoadingScreen::setProgressRange (size_t range)
@ -292,9 +298,11 @@ namespace MWGui
mViewer->getCamera()->setInitialDrawCallback(new CopyFramebufferToTextureCallback(mTexture)); mViewer->getCamera()->setInitialDrawCallback(new CopyFramebufferToTextureCallback(mTexture));
mBackgroundImage->setBackgroundImage(""); mBackgroundImage->setBackgroundImage("");
mBackgroundImage->setVisible(false);
mBackgroundImage->setRenderItemTexture(mGuiTexture.get()); mSceneImage->setRenderItemTexture(mGuiTexture.get());
mBackgroundImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); mSceneImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f));
mSceneImage->setVisible(true);
} }
void LoadingScreen::draw() void LoadingScreen::draw()

View file

@ -72,6 +72,7 @@ namespace MWGui
MyGUI::TextBox* mLoadingText; MyGUI::TextBox* mLoadingText;
MyGUI::ScrollBar* mProgressBar; MyGUI::ScrollBar* mProgressBar;
BackgroundImage* mBackgroundImage; BackgroundImage* mBackgroundImage;
BackgroundImage* mSceneImage;
std::vector<std::string> mSplashScreens; std::vector<std::string> mSplashScreens;

View file

@ -210,6 +210,11 @@ namespace MWGui
} }
} }
bool MainMenu::exit()
{
return MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_Running;
}
void MainMenu::updateMenu() void MainMenu::updateMenu()
{ {
setCoord(0,0, mWidth, mHeight); setCoord(0,0, mWidth, mHeight);

View file

@ -38,6 +38,8 @@ namespace MWGui
void onFrame(float dt); void onFrame(float dt);
bool exit();
private: private:
const VFS::Manager* mVFS; const VFS::Manager* mVFS;

View file

@ -26,6 +26,11 @@ namespace MWGui
} }
} }
bool PickpocketItemModel::allowedToUseItems() const
{
return false;
}
ItemStack PickpocketItemModel::getItem (ModelIndex index) ItemStack PickpocketItemModel::getItem (ModelIndex index)
{ {
if (index < 0) if (index < 0)

View file

@ -11,6 +11,8 @@ namespace MWGui
{ {
public: public:
PickpocketItemModel (const MWWorld::Ptr& thief, ItemModel* sourceModel, bool hideItems=true); PickpocketItemModel (const MWWorld::Ptr& thief, ItemModel* sourceModel, bool hideItems=true);
virtual bool allowedToUseItems() const;
virtual ItemStack getItem (ModelIndex index); virtual ItemStack getItem (ModelIndex index);
virtual size_t getItemCount(); virtual size_t getItemCount();
virtual void update(); virtual void update();

View file

@ -50,7 +50,8 @@ namespace MWGui
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onOkButtonClicked); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onOkButtonClicked);
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked);
mDeleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onDeleteButtonClicked); mDeleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onDeleteButtonClicked);
mCharacterSelection->eventComboAccept += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected); mCharacterSelection->eventComboChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected);
mCharacterSelection->eventComboAccept += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterAccept);
mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected); mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected);
mSaveList->eventListMouseItemActivate += MyGUI::newDelegate(this, &SaveGameDialog::onSlotMouseClick); mSaveList->eventListMouseItemActivate += MyGUI::newDelegate(this, &SaveGameDialog::onSlotMouseClick);
mSaveList->eventListSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onSlotActivated); mSaveList->eventListSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onSlotActivated);
@ -132,6 +133,8 @@ namespace MWGui
mSaveNameEdit->setCaption (""); mSaveNameEdit->setCaption ("");
if (mSaving) if (mSaving)
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveNameEdit); MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveNameEdit);
else
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList);
center(); center();
@ -322,6 +325,12 @@ namespace MWGui
fillSaveList(); fillSaveList();
} }
void SaveGameDialog::onCharacterAccept(MyGUI::ComboBox* sender, size_t pos)
{
// Give key focus to save list so we can confirm the selection with Enter
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList);
}
void SaveGameDialog::fillSaveList() void SaveGameDialog::fillSaveList()
{ {
mSaveList->removeAllItems(); mSaveList->removeAllItems();
@ -336,8 +345,6 @@ namespace MWGui
{ {
mSaveList->setIndexSelected(0); mSaveList->setIndexSelected(0);
onSlotSelected(mSaveList, 0); onSlotSelected(mSaveList, 0);
// Give key focus to save list so we can confirm the selection with Enter
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList);
} }
else else
onSlotSelected(mSaveList, MyGUI::ITEM_NONE); onSlotSelected(mSaveList, MyGUI::ITEM_NONE);

View file

@ -29,6 +29,7 @@ namespace MWGui
void onOkButtonClicked (MyGUI::Widget* sender); void onOkButtonClicked (MyGUI::Widget* sender);
void onDeleteButtonClicked (MyGUI::Widget* sender); void onDeleteButtonClicked (MyGUI::Widget* sender);
void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos); void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos);
void onCharacterAccept(MyGUI::ComboBox* sender, size_t pos);
// Slot selected (mouse click or arrow keys) // Slot selected (mouse click or arrow keys)
void onSlotSelected (MyGUI::ListBox* sender, size_t pos); void onSlotSelected (MyGUI::ListBox* sender, size_t pos);
// Slot activated (double click or enter key) // Slot activated (double click or enter key)

View file

@ -2,6 +2,7 @@
#include <MyGUI_RenderManager.h> #include <MyGUI_RenderManager.h>
#include <MyGUI_ImageBox.h> #include <MyGUI_ImageBox.h>
#include <MyGUI_Gui.h>
namespace MWGui namespace MWGui
{ {
@ -80,6 +81,8 @@ namespace MWGui
, mFactor(1.f) , mFactor(1.f)
, mRepeat(false) , mRepeat(false)
{ {
MyGUI::Gui::getInstance().eventFrameStart += MyGUI::newDelegate(this, &ScreenFader::onFrameStart);
mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize()); mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize());
MyGUI::ImageBox* imageBox = mMainWidget->castType<MyGUI::ImageBox>(false); MyGUI::ImageBox* imageBox = mMainWidget->castType<MyGUI::ImageBox>(false);
@ -94,7 +97,12 @@ namespace MWGui
} }
} }
void ScreenFader::update(float dt) ScreenFader::~ScreenFader()
{
MyGUI::Gui::getInstance().eventFrameStart -= MyGUI::newDelegate(this, &ScreenFader::onFrameStart);
}
void ScreenFader::onFrameStart(float dt)
{ {
if (!mQueue.empty()) if (!mQueue.empty())
{ {

View file

@ -36,8 +36,9 @@ namespace MWGui
{ {
public: public:
ScreenFader(const std::string & texturePath, const std::string& layout = "openmw_screen_fader.layout", const MyGUI::FloatCoord& texCoordOverride = MyGUI::FloatCoord(0,0,1,1)); ScreenFader(const std::string & texturePath, const std::string& layout = "openmw_screen_fader.layout", const MyGUI::FloatCoord& texCoordOverride = MyGUI::FloatCoord(0,0,1,1));
~ScreenFader();
void update(float dt); void onFrameStart(float dt);
void fadeIn(const float time, float delay=0); void fadeIn(const float time, float delay=0);
void fadeOut(const float time, float delay=0); void fadeOut(const float time, float delay=0);

View file

@ -159,6 +159,11 @@ namespace MWGui
mSourceModel = sourceModel; mSourceModel = sourceModel;
} }
bool SortFilterItemModel::allowedToUseItems() const
{
return mSourceModel->allowedToUseItems();
}
void SortFilterItemModel::addDragItem (const MWWorld::Ptr& dragItem, size_t count) void SortFilterItemModel::addDragItem (const MWWorld::Ptr& dragItem, size_t count)
{ {
mDragItems.push_back(std::make_pair(dragItem, count)); mDragItems.push_back(std::make_pair(dragItem, count));

View file

@ -15,6 +15,7 @@ namespace MWGui
bool filterAccepts (const ItemStack& item); bool filterAccepts (const ItemStack& item);
bool allowedToUseItems() const;
virtual ItemStack getItem (ModelIndex index); virtual ItemStack getItem (ModelIndex index);
virtual size_t getItemCount(); virtual size_t getItemCount();

View file

@ -120,7 +120,7 @@ namespace MWGui
if (info.caption.empty()) if (info.caption.empty())
info.caption=mFocusObject.getCellRef().getRefId(); info.caption=mFocusObject.getCellRef().getRefId();
info.icon=""; info.icon="";
tooltipSize = createToolTip(info, true); tooltipSize = createToolTip(info, checkOwned());
} }
else else
tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), true); tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), true);
@ -186,22 +186,23 @@ namespace MWGui
ToolTipInfo info; ToolTipInfo info;
info.text = data.caption; info.text = data.caption;
info.notes = data.notes; info.notes = data.notes;
tooltipSize = createToolTip(info, false); tooltipSize = createToolTip(info);
} }
else if (type == "ItemPtr") else if (type == "ItemPtr")
{ {
mFocusObject = *focus->getUserData<MWWorld::Ptr>(); mFocusObject = *focus->getUserData<MWWorld::Ptr>();
tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), false); tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), false, checkOwned());
} }
else if (type == "ItemModelIndex") else if (type == "ItemModelIndex")
{ {
std::pair<ItemModel::ModelIndex, ItemModel*> pair = *focus->getUserData<std::pair<ItemModel::ModelIndex, ItemModel*> >(); std::pair<ItemModel::ModelIndex, ItemModel*> pair = *focus->getUserData<std::pair<ItemModel::ModelIndex, ItemModel*> >();
mFocusObject = pair.second->getItem(pair.first).mBase; mFocusObject = pair.second->getItem(pair.first).mBase;
tooltipSize = getToolTipViaPtr(pair.second->getItem(pair.first).mCount, false); bool isAllowedToUse = pair.second->allowedToUseItems();
tooltipSize = getToolTipViaPtr(pair.second->getItem(pair.first).mCount, false, !isAllowedToUse);
} }
else if (type == "ToolTipInfo") else if (type == "ToolTipInfo")
{ {
tooltipSize = createToolTip(*focus->getUserData<MWGui::ToolTipInfo>(), false); tooltipSize = createToolTip(*focus->getUserData<MWGui::ToolTipInfo>());
} }
else if (type == "AvatarItemSelection") else if (type == "AvatarItemSelection")
{ {
@ -244,7 +245,7 @@ namespace MWGui
info.text = "#{sSchool}: " + sSchoolNames[school]; info.text = "#{sSchool}: " + sSchoolNames[school];
} }
info.effects = effects; info.effects = effects;
tooltipSize = createToolTip(info, false); tooltipSize = createToolTip(info);
} }
else if (type == "Layout") else if (type == "Layout")
{ {
@ -298,7 +299,7 @@ namespace MWGui
{ {
if (!mFocusObject.isEmpty()) if (!mFocusObject.isEmpty())
{ {
MyGUI::IntSize tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount()); MyGUI::IntSize tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), true, checkOwned());
setCoord(viewSize.width/2 - tooltipSize.width/2, setCoord(viewSize.width/2 - tooltipSize.width/2,
std::max(0, int(mFocusToolTipY*viewSize.height - tooltipSize.height)), std::max(0, int(mFocusToolTipY*viewSize.height - tooltipSize.height)),
@ -332,7 +333,7 @@ namespace MWGui
update(mFrameDuration); update(mFrameDuration);
} }
MyGUI::IntSize ToolTips::getToolTipViaPtr (int count, bool image) MyGUI::IntSize ToolTips::getToolTipViaPtr (int count, bool image, bool isOwned)
{ {
// this the maximum width of the tooltip before it starts word-wrapping // this the maximum width of the tooltip before it starts word-wrapping
setCoord(0, 0, 300, 300); setCoord(0, 0, 300, 300);
@ -351,7 +352,7 @@ namespace MWGui
ToolTipInfo info = object.getToolTipInfo(mFocusObject, count); ToolTipInfo info = object.getToolTipInfo(mFocusObject, count);
if (!image) if (!image)
info.icon = ""; info.icon = "";
tooltipSize = createToolTip(info, true); tooltipSize = createToolTip(info, isOwned);
} }
return tooltipSize; return tooltipSize;
@ -359,27 +360,21 @@ namespace MWGui
bool ToolTips::checkOwned() bool ToolTips::checkOwned()
{ {
if(!mFocusObject.isEmpty()) if(mFocusObject.isEmpty())
{ return false;
MWWorld::Ptr ptr = MWMechanics::getPlayer(); MWWorld::Ptr ptr = MWMechanics::getPlayer();
MWWorld::Ptr victim; MWWorld::Ptr victim;
MWBase::MechanicsManager* mm = MWBase::Environment::get().getMechanicsManager(); MWBase::MechanicsManager* mm = MWBase::Environment::get().getMechanicsManager();
bool allowed = mm->isAllowedToUse(ptr, mFocusObject, victim); return !mm->isAllowedToUse(ptr, mFocusObject, victim);
return !allowed;
}
else
{
return false;
}
} }
MyGUI::IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info, bool isFocusObject) MyGUI::IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info, bool isOwned)
{ {
mDynamicToolTipBox->setVisible(true); mDynamicToolTipBox->setVisible(true);
if((mShowOwned == 1 || mShowOwned == 3) && isFocusObject && checkOwned()) if((mShowOwned == 1 || mShowOwned == 3) && isOwned)
mDynamicToolTipBox->changeWidgetSkin(MWBase::Environment::get().getWindowManager()->isGuiMode() ? "HUD_Box_NoTransp_Owned" : "HUD_Box_Owned"); mDynamicToolTipBox->changeWidgetSkin(MWBase::Environment::get().getWindowManager()->isGuiMode() ? "HUD_Box_NoTransp_Owned" : "HUD_Box_Owned");
else else
mDynamicToolTipBox->changeWidgetSkin(MWBase::Environment::get().getWindowManager()->isGuiMode() ? "HUD_Box_NoTransp" : "HUD_Box"); mDynamicToolTipBox->changeWidgetSkin(MWBase::Environment::get().getWindowManager()->isGuiMode() ? "HUD_Box_NoTransp" : "HUD_Box");

View file

@ -98,10 +98,10 @@ namespace MWGui
MWWorld::Ptr mFocusObject; MWWorld::Ptr mFocusObject;
MyGUI::IntSize getToolTipViaPtr (int count, bool image=true); MyGUI::IntSize getToolTipViaPtr (int count, bool image = true, bool isOwned = false);
///< @return requested tooltip size ///< @return requested tooltip size
MyGUI::IntSize createToolTip(const ToolTipInfo& info, bool isFocusObject); MyGUI::IntSize createToolTip(const ToolTipInfo& info, bool isOwned = false);
///< @return requested tooltip size ///< @return requested tooltip size
/// @param isFocusObject Is the object this tooltips originates from mFocusObject? /// @param isFocusObject Is the object this tooltips originates from mFocusObject?

View file

@ -15,6 +15,11 @@ namespace MWGui
mSourceModel = sourceModel; mSourceModel = sourceModel;
} }
bool TradeItemModel::allowedToUseItems() const
{
return true;
}
ItemStack TradeItemModel::getItem (ModelIndex index) ItemStack TradeItemModel::getItem (ModelIndex index)
{ {
if (index < 0) if (index < 0)

View file

@ -15,6 +15,8 @@ namespace MWGui
public: public:
TradeItemModel (ItemModel* sourceModel, const MWWorld::Ptr& merchant); TradeItemModel (ItemModel* sourceModel, const MWWorld::Ptr& merchant);
bool allowedToUseItems() const;
virtual ItemStack getItem (ModelIndex index); virtual ItemStack getItem (ModelIndex index);
virtual size_t getItemCount(); virtual size_t getItemCount();

View file

@ -178,6 +178,8 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel);
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
MWBase::Environment::get().getWindowManager()->fadeScreenOut(1);
// Teleports any followers, too. // Teleports any followers, too.
MWWorld::ActionTeleport action(interior ? cellname : "", pos, true); MWWorld::ActionTeleport action(interior ? cellname : "", pos, true);
action.execute(player); action.execute(player);

View file

@ -606,25 +606,25 @@ namespace MWGui
mMap->setVisible(false); mMap->setVisible(false);
mStatsWindow->setVisible(false); mStatsWindow->setVisible(false);
mSpellWindow->setVisible(false); mSpellWindow->setVisible(false);
mInventoryWindow->setVisible(getMode() == GM_Container || getMode() == GM_Barter); mInventoryWindow->setVisible(getMode() == GM_Container || getMode() == GM_Barter || getMode() == GM_Companion);
} }
GuiMode mode = mGuiModes.back(); GuiMode mode = mGuiModes.back();
mInventoryWindow->setTrading(mode == GM_Barter); mInventoryWindow->setTrading(mode == GM_Barter);
if (getMode() == GM_Inventory)
{
// For the inventory mode, compute the effective set of windows to show. // For the inventory mode, compute the effective set of windows to show.
// This is controlled both by what windows the // This is controlled both by what windows the
// user has opened/closed (the 'shown' variable) and by what // user has opened/closed (the 'shown' variable) and by what
// windows we are allowed to show (the 'allowed' var.) // windows we are allowed to show (the 'allowed' var.)
int eff = mShown & mAllowed & ~mForceHidden; int eff = mShown & mAllowed & ~mForceHidden;
mGuiModeStates[GM_Inventory].mVisibilityMask.resize(4); mMap->setVisible(eff & GW_Map);
mGuiModeStates[GM_Inventory].mVisibilityMask[0] = eff & GW_Map; mInventoryWindow->setVisible(eff & GW_Inventory);
mGuiModeStates[GM_Inventory].mVisibilityMask[1] = eff & GW_Inventory; mSpellWindow->setVisible(eff & GW_Magic);
mGuiModeStates[GM_Inventory].mVisibilityMask[2] = eff & GW_Magic; mStatsWindow->setVisible(eff & GW_Stats);
mGuiModeStates[GM_Inventory].mVisibilityMask[3] = eff & GW_Stats; }
if (getMode() == GM_Inventory)
mGuiModeStates[GM_Inventory].update(true);
switch (mode) switch (mode)
{ {
@ -750,8 +750,8 @@ namespace MWGui
if (!window->exit()) if (!window->exit())
{ {
// unable to exit window, but give access to main menu // unable to exit window, but give access to main menu
if (!MyGUI::InputManager::getInstance().isModalAny()) if (!MyGUI::InputManager::getInstance().isModalAny() && getMode() != GM_MainMenu)
pushGuiMode (MWGui::GM_MainMenu); pushGuiMode (GM_MainMenu);
return; return;
} }
} }
@ -864,8 +864,16 @@ namespace MWGui
for (WindowBase* window : state.mWindows) for (WindowBase* window : state.mWindows)
window->onFrame(frameDuration); window->onFrame(frameDuration);
} }
else
{
// update pinned windows if visible
for (WindowBase* window : mGuiModeStates[GM_Inventory].mWindows)
if (window->isVisible())
window->onFrame(frameDuration);
}
if (!mCurrentModals.empty()) if (!mCurrentModals.empty())
mCurrentModals.top()->onFrame(frameDuration); mCurrentModals.back()->onFrame(frameDuration);
mKeyboardNavigation->onFrame(); mKeyboardNavigation->onFrame();
@ -886,12 +894,6 @@ namespace MWGui
mHud->onFrame(frameDuration); mHud->onFrame(frameDuration);
if (mWerewolfFader)
mWerewolfFader->update(frameDuration);
mBlindnessFader->update(frameDuration);
mHitFader->update(frameDuration);
mScreenFader->update(frameDuration);
mDebugWindow->onFrame(frameDuration); mDebugWindow->onFrame(frameDuration);
if (mCharGen) if (mCharGen)
@ -1727,9 +1729,10 @@ namespace MWGui
{ {
if (!mCurrentModals.empty()) if (!mCurrentModals.empty())
{ {
if (!mCurrentModals.top()->exit()) WindowModal* window = mCurrentModals.back();
if (!window->exit())
return; return;
mCurrentModals.top()->setVisible(false); window->setVisible(false);
} }
} }
@ -1738,7 +1741,7 @@ namespace MWGui
if (mCurrentModals.empty()) if (mCurrentModals.empty())
mKeyboardNavigation->saveFocus(getMode()); mKeyboardNavigation->saveFocus(getMode());
mCurrentModals.push(input); mCurrentModals.push_back(input);
mKeyboardNavigation->restoreFocus(-1); mKeyboardNavigation->restoreFocus(-1);
mKeyboardNavigation->setModalWindow(input->mMainWidget); mKeyboardNavigation->setModalWindow(input->mMainWidget);
@ -1747,17 +1750,21 @@ namespace MWGui
void WindowManager::removeCurrentModal(WindowModal* input) void WindowManager::removeCurrentModal(WindowModal* input)
{ {
// Only remove the top if it matches the current pointer. A lot of things hide their visibility before showing it,
//so just popping the top would cause massive issues.
if(!mCurrentModals.empty()) if(!mCurrentModals.empty())
{ {
if(input == mCurrentModals.top()) if(input == mCurrentModals.back())
{ {
mCurrentModals.pop(); mCurrentModals.pop_back();
mKeyboardNavigation->saveFocus(-1); mKeyboardNavigation->saveFocus(-1);
} }
else else
std::cout << " warning: modal widget " << input << " " << typeid(input).name() << " not found " << std::endl; {
auto found = std::find(mCurrentModals.begin(), mCurrentModals.end(), input);
if (found != mCurrentModals.end())
mCurrentModals.erase(found);
else
std::cerr << " warning: can't find modal window " << input << std::endl;
}
} }
if (mCurrentModals.empty()) if (mCurrentModals.empty())
{ {
@ -1765,7 +1772,7 @@ namespace MWGui
mKeyboardNavigation->restoreFocus(getMode()); mKeyboardNavigation->restoreFocus(getMode());
} }
else else
mKeyboardNavigation->setModalWindow(mCurrentModals.top()->mMainWidget); mKeyboardNavigation->setModalWindow(mCurrentModals.back()->mMainWidget);
} }
void WindowManager::onVideoKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char) void WindowManager::onVideoKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char)
@ -2059,12 +2066,7 @@ namespace MWGui
void WindowManager::GuiModeState::update(bool visible) void WindowManager::GuiModeState::update(bool visible)
{ {
for (unsigned int i=0; i<mWindows.size(); ++i) for (unsigned int i=0; i<mWindows.size(); ++i)
{ mWindows[i]->setVisible(visible);
bool visibilityMask = true;
if (i < mVisibilityMask.size())
visibilityMask = mVisibilityMask[i];
mWindows[i]->setVisible(visible && visibilityMask);
}
} }
} }

View file

@ -401,7 +401,7 @@ namespace MWGui
MWWorld::Ptr mSelectedEnchantItem; MWWorld::Ptr mSelectedEnchantItem;
MWWorld::Ptr mSelectedWeapon; MWWorld::Ptr mSelectedWeapon;
std::stack<WindowModal*> mCurrentModals; std::vector<WindowModal*> mCurrentModals;
// Markers placed manually by the player. Must be shared between both map views (the HUD map and the map window). // Markers placed manually by the player. Must be shared between both map views (the HUD map and the map window).
CustomMarkerCollection mCustomMarkers; CustomMarkerCollection mCustomMarkers;
@ -477,7 +477,6 @@ namespace MWGui
void update(bool visible); void update(bool visible);
std::vector<WindowBase*> mWindows; std::vector<WindowBase*> mWindows;
std::vector<bool> mVisibilityMask; // optional, may be used to temporarily exclude windows from this mode.
std::string mCloseSound; std::string mCloseSound;
std::string mOpenSound; std::string mOpenSound;

View file

@ -241,8 +241,6 @@ namespace MWInput
switch (action) switch (action)
{ {
case A_GameMenu: case A_GameMenu:
if(!(MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running
&& MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_MainMenu))
toggleMainMenu (); toggleMainMenu ();
break; break;
case A_Screenshot: case A_Screenshot:

View file

@ -70,11 +70,7 @@ void adjustBoundItem (const std::string& item, bool bound, const MWWorld::Ptr& a
} }
} }
else else
{ actor.getClass().getInventoryStore(actor).remove(item, 1, actor, true);
MWWorld::Ptr itemPtr = actor.getClass().getInventoryStore(actor).search(item);
if (!itemPtr.isEmpty())
actor.getClass().getInventoryStore(actor).remove(itemPtr, 1, actor, true);
}
} }
class CheckActorCommanded : public MWMechanics::EffectSourceVisitor class CheckActorCommanded : public MWMechanics::EffectSourceVisitor

View file

@ -842,6 +842,9 @@ namespace MWMechanics
bool MechanicsManager::isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, MWWorld::Ptr& victim) bool MechanicsManager::isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, MWWorld::Ptr& victim)
{ {
if (target.isEmpty())
return true;
const MWWorld::CellRef& cellref = target.getCellRef(); const MWWorld::CellRef& cellref = target.getCellRef();
// there is no harm to use unlocked doors // there is no harm to use unlocked doors
if (target.getClass().isDoor() && cellref.getLockLevel() <= 0 && ptr.getCellRef().getTrap().empty()) if (target.getClass().isDoor() && cellref.getLockLevel() <= 0 && ptr.getCellRef().getTrap().empty())

View file

@ -654,11 +654,30 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSelectedEnchantItem(
return mSelectedEnchantItem; return mSelectedEnchantItem;
} }
int MWWorld::InventoryStore::remove(const std::string& itemId, int count, const Ptr& actor)
{
return remove(itemId, count, actor, false);
}
int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor) int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor)
{ {
return remove(item, count, actor, false); return remove(item, count, actor, false);
} }
int MWWorld::InventoryStore::remove(const std::string& itemId, int count, const Ptr& actor, bool equipReplacement)
{
int toRemove = count;
for (ContainerStoreIterator iter(begin()); iter != end() && toRemove > 0; ++iter)
if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), itemId))
toRemove -= remove(*iter, toRemove, actor, equipReplacement);
flagAsModified();
// number of removed items
return count - toRemove;
}
int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor, bool equipReplacement) int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor, bool equipReplacement)
{ {
int retCount = ContainerStore::remove(item, count, actor); int retCount = ContainerStore::remove(item, count, actor);

View file

@ -177,6 +177,9 @@ namespace MWWorld
virtual bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2) const; virtual bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2) const;
///< @return true if the two specified objects can stack with each other ///< @return true if the two specified objects can stack with each other
virtual int remove(const std::string& itemId, int count, const Ptr& actor);
virtual int remove(const std::string& itemId, int count, const Ptr& actor, bool equipReplacement);
virtual int remove(const Ptr& item, int count, const Ptr& actor); virtual int remove(const Ptr& item, int count, const Ptr& actor);
virtual int remove(const Ptr& item, int count, const Ptr& actor, bool equipReplacement); virtual int remove(const Ptr& item, int count, const Ptr& actor, bool equipReplacement);
///< Remove \a count item(s) designated by \a item from this inventory. ///< Remove \a count item(s) designated by \a item from this inventory.

View file

@ -580,10 +580,16 @@ namespace MWWorld
MWBase::Environment::get().getWorld()->positionToIndex (position.pos[0], position.pos[1], x, y); MWBase::Environment::get().getWorld()->positionToIndex (position.pos[0], position.pos[1], x, y);
if (changeEvent)
MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5);
changeCellGrid(x, y, changeEvent); changeCellGrid(x, y, changeEvent);
CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y); CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y);
changePlayerCell(current, position, adjustPlayerPos); changePlayerCell(current, position, adjustPlayerPos);
if (changeEvent)
MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5);
} }
CellStore* Scene::getCurrentCell () CellStore* Scene::getCurrentCell ()

View file

@ -175,6 +175,45 @@ namespace ESM
} }
void Land::blank()
{
mPlugin = 0;
for (int i = 0; i < LAND_GLOBAL_MAP_LOD_SIZE; ++i)
mWnam[0] = 0;
if (!mLandData)
mLandData = new LandData;
mLandData->mHeightOffset = 0;
for (int i = 0; i < LAND_NUM_VERTS; ++i)
mLandData->mHeights[i] = 0;
mLandData->mMinHeight = 0;
mLandData->mMaxHeight = 0;
for (int i = 0; i < LAND_NUM_VERTS; ++i)
{
mLandData->mNormals[i*3+0] = 0;
mLandData->mNormals[i*3+1] = 0;
mLandData->mNormals[i*3+2] = 127;
}
for (int i = 0; i < LAND_NUM_TEXTURES; ++i)
mLandData->mTextures[i] = 0;
for (int i = 0; i < LAND_NUM_VERTS; ++i)
{
mLandData->mColours[i*3+0] = -1;
mLandData->mColours[i*3+1] = -1;
mLandData->mColours[i*3+2] = -1;
}
mLandData->mUnk1 = 0;
mLandData->mUnk2 = 0;
mLandData->mDataLoaded = Land::DATA_VNML | Land::DATA_VHGT | Land::DATA_WNAM |
Land::DATA_VCLR | Land::DATA_VTEX;
mDataTypes = mLandData->mDataLoaded;
// No file associated with the land now
mContext.filename.clear();
}
void Land::loadData(int flags, LandData* target) const void Land::loadData(int flags, LandData* target) const
{ {
// Create storage if nothing is loaded // Create storage if nothing is loaded
@ -193,6 +232,16 @@ namespace ESM
return; return;
} }
// Copy data to target if no file
if (mContext.filename.empty())
{
// Make sure there is data, and that it doesn't point to the same object.
if (mLandData && mLandData != target)
*target = *mLandData;
return;
}
ESM::ESMReader reader; ESM::ESMReader reader;
reader.restoreContext(mContext); reader.restoreContext(mContext);
@ -269,6 +318,15 @@ namespace ESM
return mLandData && (mLandData->mDataLoaded & flags) == (flags & mDataTypes); return mLandData && (mLandData->mDataLoaded & flags) == (flags & mDataTypes);
} }
void Land::setDataLoaded(int flags)
{
if (!mLandData)
mLandData = new LandData;
mDataTypes |= flags;
mLandData->mDataLoaded |= flags;
}
Land::Land (const Land& land) Land::Land (const Land& land)
: mFlags (land.mFlags), mX (land.mX), mY (land.mY), mPlugin (land.mPlugin), : mFlags (land.mFlags), mX (land.mX), mY (land.mY), mPlugin (land.mPlugin),
mContext (land.mContext), mDataTypes (land.mDataTypes), mContext (land.mContext), mDataTypes (land.mDataTypes),

View file

@ -31,6 +31,8 @@ struct Land
// File context. This allows the ESM reader to be 'reset' to this // File context. This allows the ESM reader to be 'reset' to this
// location later when we are ready to load the full data set. // location later when we are ready to load the full data set.
// In the editor, there may not be a file associated with the Land,
// in which case the filename will be empty.
ESM_Context mContext; ESM_Context mContext;
int mDataTypes; int mDataTypes;
@ -64,6 +66,8 @@ struct Land
//total number of textures per land //total number of textures per land
static const int LAND_NUM_TEXTURES = LAND_TEXTURE_SIZE * LAND_TEXTURE_SIZE; static const int LAND_NUM_TEXTURES = LAND_TEXTURE_SIZE * LAND_TEXTURE_SIZE;
static const int LAND_GLOBAL_MAP_LOD_SIZE = 81;
#pragma pack(push,1) #pragma pack(push,1)
struct VHGT struct VHGT
{ {
@ -109,12 +113,12 @@ struct Land
}; };
// low-LOD heightmap (used for rendering the global map) // low-LOD heightmap (used for rendering the global map)
signed char mWnam[81]; signed char mWnam[LAND_GLOBAL_MAP_LOD_SIZE];
void load(ESMReader &esm, bool &isDeleted); void load(ESMReader &esm, bool &isDeleted);
void save(ESMWriter &esm, bool isDeleted = false) const; void save(ESMWriter &esm, bool isDeleted = false) const;
void blank() {} void blank();
/** /**
* Actually loads data into target * Actually loads data into target
@ -131,6 +135,9 @@ struct Land
/// @note We only check data types that *can* be loaded (present in mDataTypes) /// @note We only check data types that *can* be loaded (present in mDataTypes)
bool isDataLoaded(int flags) const; bool isDataLoaded(int flags) const;
/// Sets the flags and creates a LandData if needed
void setDataLoaded(int flags);
Land (const Land& land); Land (const Land& land);
Land& operator= (Land land); Land& operator= (Land land);

View file

@ -59,7 +59,7 @@ namespace ESM
void LandTexture::blank() void LandTexture::blank()
{ {
mId.clear();
mTexture.clear(); mTexture.clear();
mIndex = -1;
} }
} }

View file

@ -31,14 +31,15 @@ struct LandTexture
/// Return a string descriptor for this record type. Currently used for debugging / error logs only. /// Return a string descriptor for this record type. Currently used for debugging / error logs only.
static std::string getRecordType() { return "LandTexture"; } static std::string getRecordType() { return "LandTexture"; }
// mId is merely a user friendly name for the texture in the editor.
std::string mId, mTexture; std::string mId, mTexture;
int mIndex; int mIndex;
void load(ESMReader &esm, bool &isDeleted); void load(ESMReader &esm, bool &isDeleted);
void save(ESMWriter &esm, bool isDeleted = false) const; void save(ESMWriter &esm, bool isDeleted = false) const;
/// Sets the record to the default state. Does not touch the index. Does touch mID.
void blank(); void blank();
///< Set record to default state (does not touch the ID).
}; };
} }
#endif #endif

View file

@ -75,4 +75,9 @@ void World::updateTextureFiltering()
mTextureManager->updateTextureFiltering(); mTextureManager->updateTextureFiltering();
} }
void World::clearAssociatedCaches()
{
mChunkManager->clearCache();
}
} }

View file

@ -63,6 +63,10 @@ namespace Terrain
float getHeightAt (const osg::Vec3f& worldPos); float getHeightAt (const osg::Vec3f& worldPos);
/// Clears the cached land and landtexture data.
/// @note Thread safe.
virtual void clearAssociatedCaches();
/// Load a terrain cell at maximum LOD and store it in the View for later use. /// Load a terrain cell at maximum LOD and store it in the View for later use.
/// @note Thread safe. /// @note Thread safe.
virtual void cacheCell(View* view, int x, int y) {} virtual void cacheCell(View* view, int x, int y) {}

View file

@ -14,20 +14,16 @@
<Property key="ImagePushed" value="textures\tx_menubook_take_pressed.dds"/> <Property key="ImagePushed" value="textures\tx_menubook_take_pressed.dds"/>
</Widget> </Widget>
<Widget type="Widget" position="0 0 292 398">
<Widget type="ImageButton" skin="ImageBox" position="205 358 48 32" name="PrevPageBTN"> <Widget type="ImageButton" skin="ImageBox" position="205 358 48 32" name="PrevPageBTN">
<Property key="ImageHighlighted" value="textures\tx_menubook_prev_over.dds"/> <Property key="ImageHighlighted" value="textures\tx_menubook_prev_over.dds"/>
<Property key="ImageNormal" value="textures\tx_menubook_prev_idle.dds"/> <Property key="ImageNormal" value="textures\tx_menubook_prev_idle.dds"/>
<Property key="ImagePushed" value="textures\tx_menubook_prev_pressed.dds"/> <Property key="ImagePushed" value="textures\tx_menubook_prev_pressed.dds"/>
</Widget> </Widget>
</Widget> <Widget type="ImageButton" skin="ImageBox" position="330 358 48 32" name="NextPageBTN">
<Widget type="Widget" position="292 0 292 398">
<Widget type="ImageButton" skin="ImageBox" position="38 358 48 32" name="NextPageBTN">
<Property key="ImageHighlighted" value="textures\tx_menubook_next_over.dds"/> <Property key="ImageHighlighted" value="textures\tx_menubook_next_over.dds"/>
<Property key="ImageNormal" value="textures\tx_menubook_next_idle.dds"/> <Property key="ImageNormal" value="textures\tx_menubook_next_idle.dds"/>
<Property key="ImagePushed" value="textures\tx_menubook_next_pressed.dds"/> <Property key="ImagePushed" value="textures\tx_menubook_next_pressed.dds"/>
</Widget> </Widget>
</Widget>
<Widget type="ImageButton" skin="ImageBox" position="488 358 48 32" name="CloseButton"> <Widget type="ImageButton" skin="ImageBox" position="488 358 48 32" name="CloseButton">
<Property key="ImageHighlighted" value="textures\tx_menubook_close_over.dds"/> <Property key="ImageHighlighted" value="textures\tx_menubook_close_over.dds"/>

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<MyGUI type="Layer" version="1.0"> <MyGUI type="Layer" version="1.0">
<Layer name="Scene" overlapped="false" pick="false"/>
<Layer name="Overlay" overlapped="false" pick="false"/> <Layer name="Overlay" overlapped="false" pick="false"/>
<Layer name="AdditiveOverlay" type="AdditiveLayer" pick="false"/> <Layer name="AdditiveOverlay" type="AdditiveLayer" pick="false"/>
<Layer name="HUD" overlapped="false" pick="true"/> <Layer name="HUD" overlapped="false" pick="true"/>