#ifndef CSM_WOLRD_REFIDADAPTERIMP_H #define CSM_WOLRD_REFIDADAPTERIMP_H #include <algorithm> #include <map> #include <memory> #include <stdexcept> #include <string> #include <string_view> #include <utility> #include <vector> #include <QVariant> #include <components/esm/defs.hpp> #include <components/esm/esmcommon.hpp> #include <components/esm3/aipackage.hpp> #include <components/esm3/loadalch.hpp> #include <components/esm3/loadappa.hpp> #include <components/esm3/loadarmo.hpp> #include <components/esm3/loadbook.hpp> #include <components/esm3/loadclot.hpp> #include <components/esm3/loadcont.hpp> #include <components/esm3/loadcrea.hpp> #include <components/esm3/loaddoor.hpp> #include <components/esm3/loadingr.hpp> #include <components/esm3/loadlevlist.hpp> #include <components/esm3/loadligh.hpp> #include <components/esm3/loadmisc.hpp> #include <components/esm3/loadnpc.hpp> #include <components/esm3/loadweap.hpp> #include <components/esm3/transport.hpp> #include "columnbase.hpp" #include "nestedtablewrapper.hpp" #include "record.hpp" #include "refidadapter.hpp" #include "refiddata.hpp" #include "universalid.hpp" namespace CSMWorld { class RefIdColumn; struct BaseColumns { const RefIdColumn* mId; const RefIdColumn* mModified; const RefIdColumn* mType; const RefIdColumn* mBlocked; BaseColumns() : mId(nullptr) , mModified(nullptr) , mType(nullptr) , mBlocked(nullptr) { } }; /// \brief Base adapter for all refereceable record types /// Adapters that can handle nested tables, needs to return valid qvariant for parent columns template <typename RecordT> class BaseRefIdAdapter : public RefIdAdapter { UniversalId::Type mType; BaseColumns mBase; public: BaseRefIdAdapter(UniversalId::Type type, const BaseColumns& base); ESM::RefId getId(const RecordBase& record) const override; void setId(RecordBase& record, const std::string& id) override; QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. UniversalId::Type getType() const; }; template <typename RecordT> BaseRefIdAdapter<RecordT>::BaseRefIdAdapter(UniversalId::Type type, const BaseColumns& base) : mType(type) , mBase(base) { } template <typename RecordT> void BaseRefIdAdapter<RecordT>::setId(RecordBase& record, const std::string& id) { (dynamic_cast<Record<RecordT>&>(record).get().mId) = ESM::RefId::stringRefId(id); } template <typename RecordT> ESM::RefId BaseRefIdAdapter<RecordT>::getId(const RecordBase& record) const { return dynamic_cast<const Record<RecordT>&>(record).get().mId; } template <typename RecordT> QVariant BaseRefIdAdapter<RecordT>::getData(const RefIdColumn* column, const RefIdData& data, int index) const { const Record<RecordT>& record = static_cast<const Record<RecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); if (column == mBase.mId) return QString::fromStdString(record.get().mId.toString()); if (column == mBase.mModified) { if (record.mState == Record<RecordT>::State_Erased) return static_cast<int>(Record<RecordT>::State_Deleted); return static_cast<int>(record.mState); } if (column == mBase.mType) return static_cast<int>(mType); if (column == mBase.mBlocked) return (record.get().mRecordFlags & ESM::FLAG_Blocked) != 0; return QVariant(); } template <typename RecordT> void BaseRefIdAdapter<RecordT>::setData( const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const { Record<RecordT>& record = static_cast<Record<RecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); if (column == mBase.mModified) record.mState = static_cast<RecordBase::State>(value.toInt()); else if (column == mBase.mBlocked) { RecordT record2 = record.get(); if (value.toInt() != 0) record2.mRecordFlags |= ESM::FLAG_Blocked; else record2.mRecordFlags &= ~ESM::FLAG_Blocked; record.setModified(record2); } } template <typename RecordT> UniversalId::Type BaseRefIdAdapter<RecordT>::getType() const { return mType; } // NOTE: Body Part should not have persistence (but BodyPart is not listed in the Objects // table at the moment). // // Spellmaking - not persistent - currently not part of objects table // Enchanting - not persistent - currently not part of objects table // // Leveled Creature - no model, so not persistent // Leveled Item - no model, so not persistent struct ModelColumns : public BaseColumns { const RefIdColumn* mModel; const RefIdColumn* mPersistence; ModelColumns(const BaseColumns& base) : BaseColumns(base) , mModel(nullptr) , mPersistence(nullptr) { } }; /// \brief Adapter for IDs with models (all but levelled lists) template <typename RecordT> class ModelRefIdAdapter : public BaseRefIdAdapter<RecordT> { ModelColumns mModel; public: ModelRefIdAdapter(UniversalId::Type type, const ModelColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; template <typename RecordT> ModelRefIdAdapter<RecordT>::ModelRefIdAdapter(UniversalId::Type type, const ModelColumns& columns) : BaseRefIdAdapter<RecordT>(type, columns) , mModel(columns) { } template <typename RecordT> QVariant ModelRefIdAdapter<RecordT>::getData(const RefIdColumn* column, const RefIdData& data, int index) const { const Record<RecordT>& record = static_cast<const Record<RecordT>&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter<RecordT>::getType()))); if (column == mModel.mModel) return QString::fromUtf8(record.get().mModel.c_str()); if (column == mModel.mPersistence) return (record.get().mRecordFlags & ESM::FLAG_Persistent) != 0; return BaseRefIdAdapter<RecordT>::getData(column, data, index); } template <typename RecordT> void ModelRefIdAdapter<RecordT>::setData( const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const { Record<RecordT>& record = static_cast<Record<RecordT>&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter<RecordT>::getType()))); RecordT record2 = record.get(); if (column == mModel.mModel) record2.mModel = value.toString().toUtf8().constData(); else if (column == mModel.mPersistence) { if (value.toInt() != 0) record2.mRecordFlags |= ESM::FLAG_Persistent; else record2.mRecordFlags &= ~ESM::FLAG_Persistent; } else { BaseRefIdAdapter<RecordT>::setData(column, data, index, value); return; } record.setModified(record2); } struct NameColumns : public ModelColumns { const RefIdColumn* mName; const RefIdColumn* mScript; NameColumns(const ModelColumns& base) : ModelColumns(base) , mName(nullptr) , mScript(nullptr) { } }; /// \brief Adapter for IDs with names (all but levelled lists and statics) template <typename RecordT> class NameRefIdAdapter : public ModelRefIdAdapter<RecordT> { NameColumns mName; public: NameRefIdAdapter(UniversalId::Type type, const NameColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; template <typename RecordT> NameRefIdAdapter<RecordT>::NameRefIdAdapter(UniversalId::Type type, const NameColumns& columns) : ModelRefIdAdapter<RecordT>(type, columns) , mName(columns) { } template <typename RecordT> QVariant NameRefIdAdapter<RecordT>::getData(const RefIdColumn* column, const RefIdData& data, int index) const { const Record<RecordT>& record = static_cast<const Record<RecordT>&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter<RecordT>::getType()))); if (column == mName.mName) return QString::fromUtf8(record.get().mName.c_str()); if (column == mName.mScript) return QString::fromUtf8(record.get().mScript.getRefIdString().c_str()); return ModelRefIdAdapter<RecordT>::getData(column, data, index); } template <typename RecordT> void NameRefIdAdapter<RecordT>::setData( const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const { Record<RecordT>& record = static_cast<Record<RecordT>&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter<RecordT>::getType()))); RecordT record2 = record.get(); if (column == mName.mName) record2.mName = value.toString().toUtf8().constData(); else if (column == mName.mScript) record2.mScript = ESM::RefId::stringRefId(value.toString().toUtf8().constData()); else { ModelRefIdAdapter<RecordT>::setData(column, data, index, value); return; } record.setModified(record2); } struct InventoryColumns : public NameColumns { const RefIdColumn* mIcon; const RefIdColumn* mWeight; const RefIdColumn* mValue; InventoryColumns(const NameColumns& base) : NameColumns(base) , mIcon(nullptr) , mWeight(nullptr) , mValue(nullptr) { } }; /// \brief Adapter for IDs that can go into an inventory template <typename RecordT> class InventoryRefIdAdapter : public NameRefIdAdapter<RecordT> { InventoryColumns mInventory; public: InventoryRefIdAdapter(UniversalId::Type type, const InventoryColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; template <typename RecordT> InventoryRefIdAdapter<RecordT>::InventoryRefIdAdapter(UniversalId::Type type, const InventoryColumns& columns) : NameRefIdAdapter<RecordT>(type, columns) , mInventory(columns) { } template <typename RecordT> QVariant InventoryRefIdAdapter<RecordT>::getData(const RefIdColumn* column, const RefIdData& data, int index) const { const Record<RecordT>& record = static_cast<const Record<RecordT>&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter<RecordT>::getType()))); if (column == mInventory.mIcon) return QString::fromUtf8(record.get().mIcon.c_str()); if (column == mInventory.mWeight) return record.get().mData.mWeight; if (column == mInventory.mValue) return record.get().mData.mValue; return NameRefIdAdapter<RecordT>::getData(column, data, index); } template <typename RecordT> void InventoryRefIdAdapter<RecordT>::setData( const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const { Record<RecordT>& record = static_cast<Record<RecordT>&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter<RecordT>::getType()))); RecordT record2 = record.get(); if (column == mInventory.mIcon) record2.mIcon = value.toString().toUtf8().constData(); else if (column == mInventory.mWeight) record2.mData.mWeight = value.toFloat(); else if (column == mInventory.mValue) record2.mData.mValue = value.toInt(); else { NameRefIdAdapter<RecordT>::setData(column, data, index, value); return; } record.setModified(record2); } struct PotionColumns : public InventoryColumns { const RefIdColumn* mEffects; PotionColumns(const InventoryColumns& columns); }; class PotionRefIdAdapter : public InventoryRefIdAdapter<ESM::Potion> { PotionColumns mColumns; const RefIdColumn* mAutoCalc; public: PotionRefIdAdapter(const PotionColumns& columns, const RefIdColumn* autoCalc); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; struct IngredientColumns : public InventoryColumns { const RefIdColumn* mEffects; IngredientColumns(const InventoryColumns& columns); }; class IngredientRefIdAdapter : public InventoryRefIdAdapter<ESM::Ingredient> { IngredientColumns mColumns; public: IngredientRefIdAdapter(const IngredientColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; class IngredEffectRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; public: IngredEffectRefIdAdapter(); IngredEffectRefIdAdapter(const IngredEffectRefIdAdapter&) = delete; IngredEffectRefIdAdapter& operator=(const IngredEffectRefIdAdapter&) = delete; ~IngredEffectRefIdAdapter() override = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override; void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override; void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override; NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override; QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override; void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override; int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override; int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override; }; struct EnchantableColumns : public InventoryColumns { const RefIdColumn* mEnchantment; const RefIdColumn* mEnchantmentPoints; EnchantableColumns(const InventoryColumns& base) : InventoryColumns(base) , mEnchantment(nullptr) , mEnchantmentPoints(nullptr) { } }; /// \brief Adapter for enchantable IDs template <typename RecordT> class EnchantableRefIdAdapter : public InventoryRefIdAdapter<RecordT> { EnchantableColumns mEnchantable; public: EnchantableRefIdAdapter(UniversalId::Type type, const EnchantableColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; template <typename RecordT> EnchantableRefIdAdapter<RecordT>::EnchantableRefIdAdapter(UniversalId::Type type, const EnchantableColumns& columns) : InventoryRefIdAdapter<RecordT>(type, columns) , mEnchantable(columns) { } template <typename RecordT> QVariant EnchantableRefIdAdapter<RecordT>::getData( const RefIdColumn* column, const RefIdData& data, int index) const { const Record<RecordT>& record = static_cast<const Record<RecordT>&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter<RecordT>::getType()))); if (column == mEnchantable.mEnchantment) return QString::fromUtf8(record.get().mEnchant.getRefIdString().c_str()); if (column == mEnchantable.mEnchantmentPoints) return static_cast<int>(record.get().mData.mEnchant); return InventoryRefIdAdapter<RecordT>::getData(column, data, index); } template <typename RecordT> void EnchantableRefIdAdapter<RecordT>::setData( const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const { Record<RecordT>& record = static_cast<Record<RecordT>&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter<RecordT>::getType()))); RecordT record2 = record.get(); if (column == mEnchantable.mEnchantment) record2.mEnchant = ESM::RefId::stringRefId(value.toString().toUtf8().constData()); else if (column == mEnchantable.mEnchantmentPoints) record2.mData.mEnchant = value.toInt(); else { InventoryRefIdAdapter<RecordT>::setData(column, data, index, value); return; } record.setModified(record2); } struct ToolColumns : public InventoryColumns { const RefIdColumn* mQuality; const RefIdColumn* mUses; ToolColumns(const InventoryColumns& base) : InventoryColumns(base) , mQuality(nullptr) , mUses(nullptr) { } }; /// \brief Adapter for tools with limited uses IDs (lockpick, repair, probes) template <typename RecordT> class ToolRefIdAdapter : public InventoryRefIdAdapter<RecordT> { ToolColumns mTools; public: ToolRefIdAdapter(UniversalId::Type type, const ToolColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; template <typename RecordT> ToolRefIdAdapter<RecordT>::ToolRefIdAdapter(UniversalId::Type type, const ToolColumns& columns) : InventoryRefIdAdapter<RecordT>(type, columns) , mTools(columns) { } template <typename RecordT> QVariant ToolRefIdAdapter<RecordT>::getData(const RefIdColumn* column, const RefIdData& data, int index) const { const Record<RecordT>& record = static_cast<const Record<RecordT>&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter<RecordT>::getType()))); if (column == mTools.mQuality) return record.get().mData.mQuality; if (column == mTools.mUses) return record.get().mData.mUses; return InventoryRefIdAdapter<RecordT>::getData(column, data, index); } template <typename RecordT> void ToolRefIdAdapter<RecordT>::setData( const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const { Record<RecordT>& record = static_cast<Record<RecordT>&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter<RecordT>::getType()))); RecordT record2 = record.get(); if (column == mTools.mQuality) record2.mData.mQuality = value.toFloat(); else if (column == mTools.mUses) record2.mData.mUses = value.toInt(); else { InventoryRefIdAdapter<RecordT>::setData(column, data, index, value); return; } record.setModified(record2); } struct ActorColumns : public NameColumns { const RefIdColumn* mHello; const RefIdColumn* mFlee; const RefIdColumn* mFight; const RefIdColumn* mAlarm; const RefIdColumn* mInventory; const RefIdColumn* mSpells; const RefIdColumn* mDestinations; const RefIdColumn* mAiPackages; std::map<const RefIdColumn*, unsigned int> mServices; ActorColumns(const NameColumns& base) : NameColumns(base) , mHello(nullptr) , mFlee(nullptr) , mFight(nullptr) , mAlarm(nullptr) , mInventory(nullptr) , mSpells(nullptr) , mDestinations(nullptr) , mAiPackages(nullptr) { } }; /// \brief Adapter for actor IDs (handles common AI functionality) template <typename RecordT> class ActorRefIdAdapter : public NameRefIdAdapter<RecordT> { ActorColumns mActors; public: ActorRefIdAdapter(UniversalId::Type type, const ActorColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; template <typename RecordT> ActorRefIdAdapter<RecordT>::ActorRefIdAdapter(UniversalId::Type type, const ActorColumns& columns) : NameRefIdAdapter<RecordT>(type, columns) , mActors(columns) { } template <typename RecordT> QVariant ActorRefIdAdapter<RecordT>::getData(const RefIdColumn* column, const RefIdData& data, int index) const { const Record<RecordT>& record = static_cast<const Record<RecordT>&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter<RecordT>::getType()))); if (column == mActors.mHello) return record.get().mAiData.mHello; if (column == mActors.mFlee) return record.get().mAiData.mFlee; if (column == mActors.mFight) return record.get().mAiData.mFight; if (column == mActors.mAlarm) return record.get().mAiData.mAlarm; if (column == mActors.mInventory) return QVariant::fromValue(ColumnBase::TableEdit_Full); if (column == mActors.mSpells) return QVariant::fromValue(ColumnBase::TableEdit_Full); if (column == mActors.mDestinations) return QVariant::fromValue(ColumnBase::TableEdit_Full); if (column == mActors.mAiPackages) return QVariant::fromValue(ColumnBase::TableEdit_Full); std::map<const RefIdColumn*, unsigned int>::const_iterator iter = mActors.mServices.find(column); if (iter != mActors.mServices.end()) return (record.get().mAiData.mServices & iter->second) != 0; return NameRefIdAdapter<RecordT>::getData(column, data, index); } template <typename RecordT> void ActorRefIdAdapter<RecordT>::setData( const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const { Record<RecordT>& record = static_cast<Record<RecordT>&>( data.getRecord(RefIdData::LocalIndex(index, BaseRefIdAdapter<RecordT>::getType()))); RecordT record2 = record.get(); if (column == mActors.mHello) record2.mAiData.mHello = value.toInt(); else if (column == mActors.mFlee) // Flee, Fight and Alarm ratings are probabilities. record2.mAiData.mFlee = std::min(100, value.toInt()); else if (column == mActors.mFight) record2.mAiData.mFight = std::min(100, value.toInt()); else if (column == mActors.mAlarm) record2.mAiData.mAlarm = std::min(100, value.toInt()); else { typename std::map<const RefIdColumn*, unsigned int>::const_iterator iter = mActors.mServices.find(column); if (iter != mActors.mServices.end()) { if (value.toInt() != 0) record2.mAiData.mServices |= iter->second; else record2.mAiData.mServices &= ~iter->second; } else { NameRefIdAdapter<RecordT>::setData(column, data, index, value); return; } } record.setModified(record2); } class ApparatusRefIdAdapter : public InventoryRefIdAdapter<ESM::Apparatus> { const RefIdColumn* mType; const RefIdColumn* mQuality; public: ApparatusRefIdAdapter(const InventoryColumns& columns, const RefIdColumn* type, const RefIdColumn* quality); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; class ArmorRefIdAdapter : public EnchantableRefIdAdapter<ESM::Armor> { const RefIdColumn* mType; const RefIdColumn* mHealth; const RefIdColumn* mArmor; const RefIdColumn* mPartRef; public: ArmorRefIdAdapter(const EnchantableColumns& columns, const RefIdColumn* type, const RefIdColumn* health, const RefIdColumn* armor, const RefIdColumn* partRef); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; class BookRefIdAdapter : public EnchantableRefIdAdapter<ESM::Book> { const RefIdColumn* mBookType; const RefIdColumn* mSkill; const RefIdColumn* mText; public: BookRefIdAdapter(const EnchantableColumns& columns, const RefIdColumn* bookType, const RefIdColumn* skill, const RefIdColumn* text); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; class ClothingRefIdAdapter : public EnchantableRefIdAdapter<ESM::Clothing> { const RefIdColumn* mType; const RefIdColumn* mPartRef; public: ClothingRefIdAdapter(const EnchantableColumns& columns, const RefIdColumn* type, const RefIdColumn* partRef); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; class ContainerRefIdAdapter : public NameRefIdAdapter<ESM::Container> { const RefIdColumn* mWeight; const RefIdColumn* mOrganic; const RefIdColumn* mRespawn; const RefIdColumn* mContent; public: ContainerRefIdAdapter(const NameColumns& columns, const RefIdColumn* weight, const RefIdColumn* organic, const RefIdColumn* respawn, const RefIdColumn* content); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; struct CreatureColumns : public ActorColumns { std::map<const RefIdColumn*, unsigned int> mFlags; const RefIdColumn* mType; const RefIdColumn* mScale; const RefIdColumn* mOriginal; const RefIdColumn* mAttributes; const RefIdColumn* mAttacks; const RefIdColumn* mMisc; const RefIdColumn* mBloodType; CreatureColumns(const ActorColumns& actorColumns); }; class CreatureRefIdAdapter : public ActorRefIdAdapter<ESM::Creature> { CreatureColumns mColumns; public: CreatureRefIdAdapter(const CreatureColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; class DoorRefIdAdapter : public NameRefIdAdapter<ESM::Door> { const RefIdColumn* mOpenSound; const RefIdColumn* mCloseSound; public: DoorRefIdAdapter(const NameColumns& columns, const RefIdColumn* openSound, const RefIdColumn* closeSound); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; struct LightColumns : public InventoryColumns { const RefIdColumn* mTime; const RefIdColumn* mRadius; const RefIdColumn* mColor; const RefIdColumn* mSound; const RefIdColumn* mEmitterType; std::map<const RefIdColumn*, unsigned int> mFlags; LightColumns(const InventoryColumns& columns); }; class LightRefIdAdapter : public InventoryRefIdAdapter<ESM::Light> { LightColumns mColumns; public: LightRefIdAdapter(const LightColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; class MiscRefIdAdapter : public InventoryRefIdAdapter<ESM::Miscellaneous> { const RefIdColumn* mKey; public: MiscRefIdAdapter(const InventoryColumns& columns, const RefIdColumn* key); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; struct NpcColumns : public ActorColumns { std::map<const RefIdColumn*, unsigned int> mFlags; const RefIdColumn* mRace; const RefIdColumn* mClass; const RefIdColumn* mFaction; const RefIdColumn* mHair; const RefIdColumn* mHead; const RefIdColumn* mAttributes; // depends on npc type const RefIdColumn* mSkills; // depends on npc type const RefIdColumn* mMisc; // may depend on npc type, e.g. FactionID const RefIdColumn* mBloodType; const RefIdColumn* mGender; NpcColumns(const ActorColumns& actorColumns); }; class NpcRefIdAdapter : public ActorRefIdAdapter<ESM::NPC> { NpcColumns mColumns; public: NpcRefIdAdapter(const NpcColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; struct WeaponColumns : public EnchantableColumns { const RefIdColumn* mType; const RefIdColumn* mHealth; const RefIdColumn* mSpeed; const RefIdColumn* mReach; const RefIdColumn* mChop[2]; const RefIdColumn* mSlash[2]; const RefIdColumn* mThrust[2]; std::map<const RefIdColumn*, unsigned int> mFlags; WeaponColumns(const EnchantableColumns& columns); }; class WeaponRefIdAdapter : public EnchantableRefIdAdapter<ESM::Weapon> { WeaponColumns mColumns; public: WeaponRefIdAdapter(const WeaponColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; class NpcAttributesRefIdAdapter : public NestedRefIdAdapterBase { public: NpcAttributesRefIdAdapter() = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override; void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override; void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override; NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override; QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override; void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override; int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override; int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override; }; class NpcSkillsRefIdAdapter : public NestedRefIdAdapterBase { public: NpcSkillsRefIdAdapter() = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override; void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override; void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override; NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override; QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override; void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override; int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override; int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override; }; class NpcMiscRefIdAdapter : public NestedRefIdAdapterBase { public: NpcMiscRefIdAdapter() = default; NpcMiscRefIdAdapter(const NpcMiscRefIdAdapter&) = delete; NpcMiscRefIdAdapter& operator=(const NpcMiscRefIdAdapter&) = delete; ~NpcMiscRefIdAdapter() override = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override; void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override; void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override; NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override; QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override; void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override; int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override; int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override; }; class CreatureAttributesRefIdAdapter : public NestedRefIdAdapterBase { public: CreatureAttributesRefIdAdapter() = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override; void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override; void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override; NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override; QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override; void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override; int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override; int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override; }; class CreatureAttackRefIdAdapter : public NestedRefIdAdapterBase { public: CreatureAttackRefIdAdapter() = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override; void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override; void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override; NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override; QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override; void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override; int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override; int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override; }; class CreatureMiscRefIdAdapter : public NestedRefIdAdapterBase { public: CreatureMiscRefIdAdapter() = default; CreatureMiscRefIdAdapter(const CreatureMiscRefIdAdapter&) = delete; CreatureMiscRefIdAdapter& operator=(const CreatureMiscRefIdAdapter&) = delete; ~CreatureMiscRefIdAdapter() override = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override; void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override; void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override; NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override; QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override; void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override; int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override; int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override; }; template <typename ESXRecordT> class EffectsListAdapter; template <typename ESXRecordT> class EffectsRefIdAdapter : public EffectsListAdapter<ESXRecordT>, public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented EffectsRefIdAdapter(const EffectsRefIdAdapter&); EffectsRefIdAdapter& operator=(const EffectsRefIdAdapter&); public: EffectsRefIdAdapter(UniversalId::Type type) : mType(type) { } virtual ~EffectsRefIdAdapter() = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); EffectsListAdapter<ESXRecordT>::addRow(record, position); } void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); EffectsListAdapter<ESXRecordT>::removeRow(record, rowToRemove); } void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); EffectsListAdapter<ESXRecordT>::setTable(record, nestedTable); } NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record<ESXRecordT>& record = static_cast<const Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); return EffectsListAdapter<ESXRecordT>::table(record); } QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override { const Record<ESXRecordT>& record = static_cast<const Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); return EffectsListAdapter<ESXRecordT>::getData(record, subRowIndex, subColIndex); } void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(row, mType))); EffectsListAdapter<ESXRecordT>::setData(record, value, subRowIndex, subColIndex); } int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override { const Record<ESXRecordT> record; // not used, just a dummy return EffectsListAdapter<ESXRecordT>::getColumnsCount(record); } int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record<ESXRecordT>& record = static_cast<const Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); return EffectsListAdapter<ESXRecordT>::getRowsCount(record); } }; template <typename ESXRecordT> class NestedInventoryRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented NestedInventoryRefIdAdapter(const NestedInventoryRefIdAdapter&); NestedInventoryRefIdAdapter& operator=(const NestedInventoryRefIdAdapter&); public: NestedInventoryRefIdAdapter(UniversalId::Type type) : mType(type) { } virtual ~NestedInventoryRefIdAdapter() = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT container = record.get(); std::vector<ESM::ContItem>& list = container.mInventory.mList; ESM::ContItem newRow = ESM::ContItem(); if (position >= (int)list.size()) list.push_back(newRow); else list.insert(list.begin() + position, newRow); record.setModified(container); } void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT container = record.get(); std::vector<ESM::ContItem>& list = container.mInventory.mList; if (rowToRemove < 0 || rowToRemove >= static_cast<int>(list.size())) throw std::runtime_error("index out of range"); list.erase(list.begin() + rowToRemove); record.setModified(container); } void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT container = record.get(); container.mInventory.mList = static_cast<const NestedTableWrapper<std::vector<typename ESM::ContItem>>&>(nestedTable).mNestedTable; record.setModified(container); } NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record<ESXRecordT>& record = static_cast<const Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); // deleted by dtor of NestedTableStoring return new NestedTableWrapper<std::vector<typename ESM::ContItem>>(record.get().mInventory.mList); } QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override { const Record<ESXRecordT>& record = static_cast<const Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); const std::vector<ESM::ContItem>& list = record.get().mInventory.mList; if (subRowIndex < 0 || subRowIndex >= static_cast<int>(list.size())) throw std::runtime_error("index out of range"); const ESM::ContItem& content = list.at(subRowIndex); switch (subColIndex) { case 0: return QString::fromUtf8(content.mItem.getRefIdString().c_str()); case 1: return content.mCount; default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } } void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(row, mType))); ESXRecordT container = record.get(); std::vector<ESM::ContItem>& list = container.mInventory.mList; if (subRowIndex < 0 || subRowIndex >= static_cast<int>(list.size())) throw std::runtime_error("index out of range"); switch (subColIndex) { case 0: list.at(subRowIndex).mItem = ESM::RefId::stringRefId(value.toString().toUtf8().constData()); break; case 1: list.at(subRowIndex).mCount = value.toInt(); break; default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } record.setModified(container); } int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override { return 2; } int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record<ESXRecordT>& record = static_cast<const Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); return static_cast<int>(record.get().mInventory.mList.size()); } }; template <typename ESXRecordT> class NestedSpellRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented NestedSpellRefIdAdapter(const NestedSpellRefIdAdapter&); NestedSpellRefIdAdapter& operator=(const NestedSpellRefIdAdapter&); public: NestedSpellRefIdAdapter(UniversalId::Type type) : mType(type) { } virtual ~NestedSpellRefIdAdapter() = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT caster = record.get(); std::vector<ESM::RefId>& list = caster.mSpells.mList; ESM::RefId newString; if (position >= (int)list.size()) list.push_back(newString); else list.insert(list.begin() + position, newString); record.setModified(caster); } void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT caster = record.get(); std::vector<ESM::RefId>& list = caster.mSpells.mList; if (rowToRemove < 0 || rowToRemove >= static_cast<int>(list.size())) throw std::runtime_error("index out of range"); list.erase(list.begin() + rowToRemove); record.setModified(caster); } void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT caster = record.get(); caster.mSpells.mList = static_cast<const NestedTableWrapper<std::vector<typename ESM::RefId>>&>(nestedTable).mNestedTable; record.setModified(caster); } NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record<ESXRecordT>& record = static_cast<const Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); // deleted by dtor of NestedTableStoring return new NestedTableWrapper<std::vector<typename ESM::RefId>>(record.get().mSpells.mList); } QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override { const Record<ESXRecordT>& record = static_cast<const Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); const std::vector<ESM::RefId>& list = record.get().mSpells.mList; if (subRowIndex < 0 || subRowIndex >= static_cast<int>(list.size())) throw std::runtime_error("index out of range"); const ESM::RefId& content = list.at(subRowIndex); if (subColIndex == 0) return QString::fromUtf8(content.getRefIdString().c_str()); else throw std::runtime_error("Trying to access non-existing column in the nested table!"); } void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(row, mType))); ESXRecordT caster = record.get(); std::vector<ESM::RefId>& list = caster.mSpells.mList; if (subRowIndex < 0 || subRowIndex >= static_cast<int>(list.size())) throw std::runtime_error("index out of range"); if (subColIndex == 0) list.at(subRowIndex) = ESM::RefId::stringRefId(value.toString().toUtf8().constData()); else throw std::runtime_error("Trying to access non-existing column in the nested table!"); record.setModified(caster); } int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override { return 1; } int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record<ESXRecordT>& record = static_cast<const Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); return static_cast<int>(record.get().mSpells.mList.size()); } }; template <typename ESXRecordT> class NestedTravelRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented NestedTravelRefIdAdapter(const NestedTravelRefIdAdapter&); NestedTravelRefIdAdapter& operator=(const NestedTravelRefIdAdapter&); public: NestedTravelRefIdAdapter(UniversalId::Type type) : mType(type) { } virtual ~NestedTravelRefIdAdapter() = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT traveller = record.get(); std::vector<ESM::Transport::Dest>& list = traveller.mTransport.mList; ESM::Position newPos; for (unsigned i = 0; i < 3; ++i) { newPos.pos[i] = 0; newPos.rot[i] = 0; } ESM::Transport::Dest newRow; newRow.mPos = newPos; newRow.mCellName.clear(); if (position >= (int)list.size()) list.push_back(newRow); else list.insert(list.begin() + position, newRow); record.setModified(traveller); } void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT traveller = record.get(); std::vector<ESM::Transport::Dest>& list = traveller.mTransport.mList; if (rowToRemove < 0 || rowToRemove >= static_cast<int>(list.size())) throw std::runtime_error("index out of range"); list.erase(list.begin() + rowToRemove); record.setModified(traveller); } void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT traveller = record.get(); traveller.mTransport.mList = static_cast<const NestedTableWrapper<std::vector<typename ESM::Transport::Dest>>&>(nestedTable) .mNestedTable; record.setModified(traveller); } NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record<ESXRecordT>& record = static_cast<const Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); // deleted by dtor of NestedTableStoring return new NestedTableWrapper<std::vector<typename ESM::Transport::Dest>>(record.get().mTransport.mList); } QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override { const Record<ESXRecordT>& record = static_cast<const Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); const std::vector<ESM::Transport::Dest>& list = record.get().mTransport.mList; if (subRowIndex < 0 || subRowIndex >= static_cast<int>(list.size())) throw std::runtime_error("index out of range"); const ESM::Transport::Dest& content = list.at(subRowIndex); switch (subColIndex) { case 0: return QString::fromUtf8(content.mCellName.c_str()); case 1: return content.mPos.pos[0]; case 2: return content.mPos.pos[1]; case 3: return content.mPos.pos[2]; case 4: return content.mPos.rot[0]; case 5: return content.mPos.rot[1]; case 6: return content.mPos.rot[2]; default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } } void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(row, mType))); ESXRecordT traveller = record.get(); std::vector<ESM::Transport::Dest>& list = traveller.mTransport.mList; if (subRowIndex < 0 || subRowIndex >= static_cast<int>(list.size())) throw std::runtime_error("index out of range"); switch (subColIndex) { case 0: list.at(subRowIndex).mCellName = value.toString().toUtf8().constData(); break; case 1: list.at(subRowIndex).mPos.pos[0] = value.toFloat(); break; case 2: list.at(subRowIndex).mPos.pos[1] = value.toFloat(); break; case 3: list.at(subRowIndex).mPos.pos[2] = value.toFloat(); break; case 4: list.at(subRowIndex).mPos.rot[0] = value.toFloat(); break; case 5: list.at(subRowIndex).mPos.rot[1] = value.toFloat(); break; case 6: list.at(subRowIndex).mPos.rot[2] = value.toFloat(); break; default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } record.setModified(traveller); } int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override { return 7; } int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record<ESXRecordT>& record = static_cast<const Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); return static_cast<int>(record.get().mTransport.mList.size()); } }; template <typename ESXRecordT> class ActorAiRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented ActorAiRefIdAdapter(const ActorAiRefIdAdapter&); ActorAiRefIdAdapter& operator=(const ActorAiRefIdAdapter&); public: ActorAiRefIdAdapter(UniversalId::Type type) : mType(type) { } virtual ~ActorAiRefIdAdapter() = default; // FIXME: should check if the AI package type is already in the list and use a default // that wasn't used already (in extreme case do not add anything at all? void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT actor = record.get(); std::vector<ESM::AIPackage>& list = actor.mAiPackage.mList; ESM::AIPackage newRow; newRow.mType = ESM::AI_Wander; newRow.mWander.mDistance = 0; newRow.mWander.mDuration = 0; newRow.mWander.mTimeOfDay = 0; for (int i = 0; i < 8; ++i) newRow.mWander.mIdle[i] = 0; newRow.mWander.mShouldRepeat = 1; newRow.mCellName.clear(); if (position >= (int)list.size()) list.push_back(newRow); else list.insert(list.begin() + position, newRow); record.setModified(actor); } void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT actor = record.get(); std::vector<ESM::AIPackage>& list = actor.mAiPackage.mList; if (rowToRemove < 0 || rowToRemove >= static_cast<int>(list.size())) throw std::runtime_error("index out of range"); list.erase(list.begin() + rowToRemove); record.setModified(actor); } void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT actor = record.get(); actor.mAiPackage.mList = static_cast<const NestedTableWrapper<std::vector<typename ESM::AIPackage>>&>(nestedTable) .mNestedTable; record.setModified(actor); } NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record<ESXRecordT>& record = static_cast<const Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); // deleted by dtor of NestedTableStoring return new NestedTableWrapper<std::vector<typename ESM::AIPackage>>(record.get().mAiPackage.mList); } QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override { const Record<ESXRecordT>& record = static_cast<const Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); const std::vector<ESM::AIPackage>& list = record.get().mAiPackage.mList; if (subRowIndex < 0 || subRowIndex >= static_cast<int>(list.size())) throw std::runtime_error("index out of range"); const ESM::AIPackage& content = list.at(subRowIndex); switch (subColIndex) { case 0: // FIXME: should more than one AI package type be allowed? Check vanilla switch (content.mType) { case ESM::AI_Wander: return 0; case ESM::AI_Travel: return 1; case ESM::AI_Follow: return 2; case ESM::AI_Escort: return 3; case ESM::AI_Activate: return 4; default: return QVariant(); } case 1: // wander dist if (content.mType == ESM::AI_Wander) return content.mWander.mDistance; else return QVariant(); case 2: // wander/follow dur if (content.mType == ESM::AI_Wander) return content.mWander.mDuration; else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) return content.mTarget.mDuration; else return QVariant(); case 3: // wander ToD if (content.mType == ESM::AI_Wander) return content.mWander.mTimeOfDay; // FIXME: not sure of the format else return QVariant(); case 4: // wander idle case 5: case 6: case 7: case 8: case 9: case 10: case 11: if (content.mType == ESM::AI_Wander) return static_cast<int>(content.mWander.mIdle[subColIndex - 4]); else return QVariant(); case 12: // repeat if (content.mType == ESM::AI_Wander) return content.mWander.mShouldRepeat != 0; else if (content.mType == ESM::AI_Travel) return content.mTravel.mShouldRepeat != 0; else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) return content.mTarget.mShouldRepeat != 0; else if (content.mType == ESM::AI_Activate) return content.mActivate.mShouldRepeat != 0; else return QVariant(); case 13: // activate name if (content.mType == ESM::AI_Activate) return QString(content.mActivate.mName.toString().c_str()); else return QVariant(); case 14: // target id if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) return QString(content.mTarget.mId.toString().c_str()); else return QVariant(); case 15: // target cell if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) return QString::fromUtf8(content.mCellName.c_str()); else return QVariant(); case 16: if (content.mType == ESM::AI_Travel) return content.mTravel.mX; else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) return content.mTarget.mX; else return QVariant(); case 17: if (content.mType == ESM::AI_Travel) return content.mTravel.mY; else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) return content.mTarget.mY; else return QVariant(); case 18: if (content.mType == ESM::AI_Travel) return content.mTravel.mZ; else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) return content.mTarget.mZ; else return QVariant(); default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } } void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(row, mType))); ESXRecordT actor = record.get(); std::vector<ESM::AIPackage>& list = actor.mAiPackage.mList; if (subRowIndex < 0 || subRowIndex >= static_cast<int>(list.size())) throw std::runtime_error("index out of range"); ESM::AIPackage& content = list.at(subRowIndex); switch (subColIndex) { case 0: // ai package type switch (value.toInt()) { case 0: content.mType = ESM::AI_Wander; break; case 1: content.mType = ESM::AI_Travel; break; case 2: content.mType = ESM::AI_Follow; break; case 3: content.mType = ESM::AI_Escort; break; case 4: content.mType = ESM::AI_Activate; break; default: return; // return without saving } break; // always save case 1: if (content.mType == ESM::AI_Wander) content.mWander.mDistance = static_cast<short>(value.toInt()); else return; // return without saving break; // always save case 2: if (content.mType == ESM::AI_Wander) content.mWander.mDuration = static_cast<short>(value.toInt()); else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) content.mTarget.mDuration = static_cast<short>(value.toInt()); else return; // return without saving break; case 3: if (content.mType == ESM::AI_Wander) content.mWander.mTimeOfDay = static_cast<unsigned char>(value.toInt()); else return; // return without saving break; // always save case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: if (content.mType == ESM::AI_Wander) content.mWander.mIdle[subColIndex - 4] = static_cast<unsigned char>(value.toInt()); else return; // return without saving break; // always save case 12: if (content.mType == ESM::AI_Wander) content.mWander.mShouldRepeat = static_cast<unsigned char>(value.toInt()); else if (content.mType == ESM::AI_Travel) content.mTravel.mShouldRepeat = static_cast<unsigned char>(value.toInt()); else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) content.mTarget.mShouldRepeat = static_cast<unsigned char>(value.toInt()); else if (content.mType == ESM::AI_Activate) content.mActivate.mShouldRepeat = static_cast<unsigned char>(value.toInt()); else return; // return without saving break; // always save case 13: // NAME32 if (content.mType == ESM::AI_Activate) { const QByteArray name = value.toString().toUtf8(); content.mActivate.mName.assign(std::string_view(name.constData(), name.size())); } else return; // return without saving break; // always save case 14: // NAME32 if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) { const QByteArray id = value.toString().toUtf8(); content.mTarget.mId.assign(std::string_view(id.constData(), id.size())); } else return; // return without saving break; // always save case 15: if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) content.mCellName = std::string(value.toString().toUtf8().constData()); else return; // return without saving break; // always save case 16: if (content.mType == ESM::AI_Travel) content.mTravel.mX = value.toFloat(); else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) content.mTarget.mX = value.toFloat(); else return; // return without saving break; // always save case 17: if (content.mType == ESM::AI_Travel) content.mTravel.mY = value.toFloat(); else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) content.mTarget.mY = value.toFloat(); else return; // return without saving break; // always save case 18: if (content.mType == ESM::AI_Travel) content.mTravel.mZ = value.toFloat(); else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) content.mTarget.mZ = value.toFloat(); else return; // return without saving break; // always save default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } record.setModified(actor); } int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override { return 19; } int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record<ESXRecordT>& record = static_cast<const Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); return static_cast<int>(record.get().mAiPackage.mList.size()); } }; template <typename ESXRecordT> class BodyPartRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented BodyPartRefIdAdapter(const BodyPartRefIdAdapter&); BodyPartRefIdAdapter& operator=(const BodyPartRefIdAdapter&); public: BodyPartRefIdAdapter(UniversalId::Type type) : mType(type) { } virtual ~BodyPartRefIdAdapter() = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT apparel = record.get(); std::vector<ESM::PartReference>& list = apparel.mParts.mParts; ESM::PartReference newPart; newPart.mPart = 0; // 0 == head newPart.mMale = ESM::RefId(); newPart.mFemale = ESM::RefId(); if (position >= (int)list.size()) list.push_back(newPart); else list.insert(list.begin() + position, newPart); record.setModified(apparel); } void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT apparel = record.get(); std::vector<ESM::PartReference>& list = apparel.mParts.mParts; if (rowToRemove < 0 || rowToRemove >= static_cast<int>(list.size())) throw std::runtime_error("index out of range"); list.erase(list.begin() + rowToRemove); record.setModified(apparel); } void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT apparel = record.get(); apparel.mParts.mParts = static_cast<const NestedTableWrapper<std::vector<typename ESM::PartReference>>&>(nestedTable) .mNestedTable; record.setModified(apparel); } NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record<ESXRecordT>& record = static_cast<const Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); // deleted by dtor of NestedTableStoring return new NestedTableWrapper<std::vector<typename ESM::PartReference>>(record.get().mParts.mParts); } QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override { const Record<ESXRecordT>& record = static_cast<const Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); const std::vector<ESM::PartReference>& list = record.get().mParts.mParts; if (subRowIndex < 0 || subRowIndex >= static_cast<int>(list.size())) throw std::runtime_error("index out of range"); const ESM::PartReference& content = list.at(subRowIndex); switch (subColIndex) { case 0: { if (content.mPart < ESM::PRT_Count) return content.mPart; else throw std::runtime_error("Part Reference Type unexpected value"); } case 1: return QString(content.mMale.getRefIdString().c_str()); case 2: return QString(content.mFemale.getRefIdString().c_str()); default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } } void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(row, mType))); ESXRecordT apparel = record.get(); std::vector<ESM::PartReference>& list = apparel.mParts.mParts; if (subRowIndex < 0 || subRowIndex >= static_cast<int>(list.size())) throw std::runtime_error("index out of range"); switch (subColIndex) { case 0: list.at(subRowIndex).mPart = static_cast<unsigned char>(value.toInt()); break; case 1: list.at(subRowIndex).mMale = ESM::RefId::stringRefId(value.toString().toStdString()); break; case 2: list.at(subRowIndex).mFemale = ESM::RefId::stringRefId(value.toString().toStdString()); break; default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } record.setModified(apparel); } int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override { return 3; } int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record<ESXRecordT>& record = static_cast<const Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); return static_cast<int>(record.get().mParts.mParts.size()); } }; struct LevListColumns : public BaseColumns { const RefIdColumn* mLevList; const RefIdColumn* mNestedListLevList; LevListColumns(const BaseColumns& base) : BaseColumns(base) , mLevList(nullptr) , mNestedListLevList(nullptr) { } }; template <typename RecordT> class LevelledListRefIdAdapter : public BaseRefIdAdapter<RecordT> { LevListColumns mLevList; public: LevelledListRefIdAdapter(UniversalId::Type type, const LevListColumns& columns); QVariant getData(const RefIdColumn* column, const RefIdData& data, int index) const override; void setData(const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const override; ///< If the data type does not match an exception is thrown. }; template <typename RecordT> LevelledListRefIdAdapter<RecordT>::LevelledListRefIdAdapter(UniversalId::Type type, const LevListColumns& columns) : BaseRefIdAdapter<RecordT>(type, columns) , mLevList(columns) { } template <typename RecordT> QVariant LevelledListRefIdAdapter<RecordT>::getData( const RefIdColumn* column, const RefIdData& data, int index) const { if (column == mLevList.mLevList || column == mLevList.mNestedListLevList) return QVariant::fromValue(ColumnBase::TableEdit_Full); return BaseRefIdAdapter<RecordT>::getData(column, data, index); } template <typename RecordT> void LevelledListRefIdAdapter<RecordT>::setData( const RefIdColumn* column, RefIdData& data, int index, const QVariant& value) const { BaseRefIdAdapter<RecordT>::setData(column, data, index, value); return; } // for non-tables template <typename ESXRecordT> class NestedListLevListRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented NestedListLevListRefIdAdapter(const NestedListLevListRefIdAdapter&); NestedListLevListRefIdAdapter& operator=(const NestedListLevListRefIdAdapter&); public: NestedListLevListRefIdAdapter(UniversalId::Type type) : mType(type) { } virtual ~NestedListLevListRefIdAdapter() = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override { throw std::logic_error("cannot add a row to a fixed table"); } void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override { throw std::logic_error("cannot remove a row to a fixed table"); } void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override { throw std::logic_error("table operation not supported"); } NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override { throw std::logic_error("table operation not supported"); } QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override { const Record<ESXRecordT>& record = static_cast<const Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); if (mType == UniversalId::Type_CreatureLevelledList) { switch (subColIndex) { case 0: return QVariant(); // disable the checkbox editor case 1: return record.get().mFlags & ESM::CreatureLevList::AllLevels; case 2: return static_cast<int>(record.get().mChanceNone); default: throw std::runtime_error("Trying to access non-existing column in levelled creatues!"); } } else { switch (subColIndex) { case 0: return record.get().mFlags & ESM::ItemLevList::Each; case 1: return record.get().mFlags & ESM::ItemLevList::AllLevels; case 2: return static_cast<int>(record.get().mChanceNone); default: throw std::runtime_error("Trying to access non-existing column in levelled items!"); } } } void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(row, mType))); ESXRecordT leveled = record.get(); if (mType == UniversalId::Type_CreatureLevelledList) { switch (subColIndex) { case 0: return; // return without saving case 1: { if (value.toBool()) { leveled.mFlags |= ESM::CreatureLevList::AllLevels; break; } else { leveled.mFlags &= ~ESM::CreatureLevList::AllLevels; break; } } case 2: leveled.mChanceNone = static_cast<unsigned char>(value.toInt()); break; default: throw std::runtime_error("Trying to set non-existing column in levelled creatures!"); } } else { switch (subColIndex) { case 0: { if (value.toBool()) { leveled.mFlags |= ESM::ItemLevList::Each; break; } else { leveled.mFlags &= ~ESM::ItemLevList::Each; break; } } case 1: { if (value.toBool()) { leveled.mFlags |= ESM::ItemLevList::AllLevels; break; } else { leveled.mFlags &= ~ESM::ItemLevList::AllLevels; break; } } case 2: leveled.mChanceNone = static_cast<unsigned char>(value.toInt()); break; default: throw std::runtime_error("Trying to set non-existing column in levelled items!"); } } record.setModified(leveled); } int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override { return 3; } int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override { return 1; // fixed at size 1 } }; // for tables template <typename ESXRecordT> class NestedLevListRefIdAdapter : public NestedRefIdAdapterBase { UniversalId::Type mType; // not implemented NestedLevListRefIdAdapter(const NestedLevListRefIdAdapter&); NestedLevListRefIdAdapter& operator=(const NestedLevListRefIdAdapter&); public: NestedLevListRefIdAdapter(UniversalId::Type type) : mType(type) { } virtual ~NestedLevListRefIdAdapter() = default; void addNestedRow(const RefIdColumn* column, RefIdData& data, int index, int position) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT leveled = record.get(); std::vector<ESM::LevelledListBase::LevelItem>& list = leveled.mList; ESM::LevelledListBase::LevelItem newItem; newItem.mId = ESM::RefId(); newItem.mLevel = 0; if (position >= (int)list.size()) list.push_back(newItem); else list.insert(list.begin() + position, newItem); record.setModified(leveled); } void removeNestedRow(const RefIdColumn* column, RefIdData& data, int index, int rowToRemove) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT leveled = record.get(); std::vector<ESM::LevelledListBase::LevelItem>& list = leveled.mList; if (rowToRemove < 0 || rowToRemove >= static_cast<int>(list.size())) throw std::runtime_error("index out of range"); list.erase(list.begin() + rowToRemove); record.setModified(leveled); } void setNestedTable(const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); ESXRecordT leveled = record.get(); leveled.mList = static_cast<const NestedTableWrapper<std::vector<typename ESM::LevelledListBase::LevelItem>>&>( nestedTable) .mNestedTable; record.setModified(leveled); } NestedTableWrapperBase* nestedTable(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record<ESXRecordT>& record = static_cast<const Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); // deleted by dtor of NestedTableStoring return new NestedTableWrapper<std::vector<typename ESM::LevelledListBase::LevelItem>>(record.get().mList); } QVariant getNestedData(const RefIdColumn* column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const override { const Record<ESXRecordT>& record = static_cast<const Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); const std::vector<ESM::LevelledListBase::LevelItem>& list = record.get().mList; if (subRowIndex < 0 || subRowIndex >= static_cast<int>(list.size())) throw std::runtime_error("index out of range"); const ESM::LevelledListBase::LevelItem& content = list.at(subRowIndex); switch (subColIndex) { case 0: return QString(content.mId.getRefIdString().c_str()); case 1: return content.mLevel; default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } } void setNestedData(const RefIdColumn* column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const override { Record<ESXRecordT>& record = static_cast<Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(row, mType))); ESXRecordT leveled = record.get(); std::vector<ESM::LevelledListBase::LevelItem>& list = leveled.mList; if (subRowIndex < 0 || subRowIndex >= static_cast<int>(list.size())) throw std::runtime_error("index out of range"); switch (subColIndex) { case 0: list.at(subRowIndex).mId = ESM::RefId::stringRefId(value.toString().toStdString()); break; case 1: list.at(subRowIndex).mLevel = static_cast<short>(value.toInt()); break; default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); } record.setModified(leveled); } int getNestedColumnsCount(const RefIdColumn* column, const RefIdData& data) const override { return 2; } int getNestedRowsCount(const RefIdColumn* column, const RefIdData& data, int index) const override { const Record<ESXRecordT>& record = static_cast<const Record<ESXRecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType))); return static_cast<int>(record.get().mList.size()); } }; } #endif