2013-06-17 12:28:09 +00:00
|
|
|
#ifndef CSM_WOLRD_COLLECTION_H
|
|
|
|
#define CSM_WOLRD_COLLECTION_H
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <cctype>
|
|
|
|
#include <functional>
|
2022-09-22 18:26:05 +00:00
|
|
|
#include <map>
|
2021-07-23 04:21:21 +00:00
|
|
|
#include <memory>
|
2022-09-22 18:26:05 +00:00
|
|
|
#include <stdexcept>
|
|
|
|
#include <string>
|
2021-09-04 16:07:23 +00:00
|
|
|
#include <string_view>
|
2023-03-08 01:17:15 +00:00
|
|
|
#include <unordered_set>
|
2022-09-22 18:26:05 +00:00
|
|
|
#include <vector>
|
2013-06-17 12:28:09 +00:00
|
|
|
|
|
|
|
#include <QVariant>
|
|
|
|
|
2022-08-02 22:00:54 +00:00
|
|
|
#include <components/misc/strings/lower.hpp>
|
2013-06-17 12:28:09 +00:00
|
|
|
|
|
|
|
#include "collectionbase.hpp"
|
2022-09-22 18:26:05 +00:00
|
|
|
#include "columnbase.hpp"
|
2023-03-08 01:17:15 +00:00
|
|
|
#include "info.hpp"
|
2017-09-04 05:06:58 +00:00
|
|
|
#include "land.hpp"
|
2017-08-25 23:51:30 +00:00
|
|
|
#include "landtexture.hpp"
|
2022-10-19 17:02:00 +00:00
|
|
|
#include "record.hpp"
|
2019-01-21 19:30:11 +00:00
|
|
|
#include "ref.hpp"
|
2013-06-17 12:28:09 +00:00
|
|
|
|
|
|
|
namespace CSMWorld
|
|
|
|
{
|
2023-03-08 01:17:15 +00:00
|
|
|
inline std::pair<std::string_view, std::string_view> parseInfoRefId(const ESM::RefId& infoId)
|
|
|
|
{
|
|
|
|
const auto separator = infoId.getRefIdString().find('#');
|
|
|
|
if (separator == std::string::npos)
|
|
|
|
throw std::runtime_error("Invalid info id: " + infoId.getRefIdString());
|
|
|
|
const std::string_view view(infoId.getRefIdString());
|
|
|
|
return { view.substr(0, separator), view.substr(separator + 1) };
|
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename T>
|
|
|
|
void setRecordId(const decltype(T::mId)& id, T& record)
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
2017-08-25 23:51:30 +00:00
|
|
|
record.mId = id;
|
2013-06-17 12:28:09 +00:00
|
|
|
}
|
|
|
|
|
2023-03-08 01:17:15 +00:00
|
|
|
inline void setRecordId(const ESM::RefId& id, Info& record)
|
|
|
|
{
|
|
|
|
record.mId = id;
|
|
|
|
const auto [topicId, originalId] = parseInfoRefId(id);
|
|
|
|
record.mTopicId = ESM::RefId::stringRefId(topicId);
|
|
|
|
record.mOriginalId = ESM::RefId::stringRefId(originalId);
|
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename T>
|
|
|
|
auto getRecordId(const T& record)
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
|
|
|
return record.mId;
|
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
inline void setRecordId(const ESM::RefId& id, Land& record)
|
2017-09-04 05:06:58 +00:00
|
|
|
{
|
2023-02-25 12:11:31 +00:00
|
|
|
int x = 0;
|
|
|
|
int y = 0;
|
2017-09-04 05:06:58 +00:00
|
|
|
|
2022-10-06 17:39:46 +00:00
|
|
|
Land::parseUniqueRecordId(id.getRefIdString(), x, y);
|
2017-09-04 05:06:58 +00:00
|
|
|
record.mX = x;
|
|
|
|
record.mY = y;
|
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
inline ESM::RefId getRecordId(const Land& record)
|
|
|
|
{
|
|
|
|
return ESM::RefId::stringRefId(Land::createUniqueRecordId(record.mX, record.mY));
|
|
|
|
}
|
|
|
|
|
2023-02-27 22:35:48 +00:00
|
|
|
inline void setRecordId(const ESM::RefId& id, LandTexture& record)
|
2017-08-25 23:51:30 +00:00
|
|
|
{
|
|
|
|
int plugin = 0;
|
|
|
|
int index = 0;
|
|
|
|
|
2022-10-06 17:39:46 +00:00
|
|
|
LandTexture::parseUniqueRecordId(id.getRefIdString(), plugin, index);
|
2017-08-25 23:51:30 +00:00
|
|
|
record.mPluginIndex = plugin;
|
|
|
|
record.mIndex = index;
|
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
inline ESM::RefId getRecordId(const LandTexture& record)
|
2017-08-25 23:51:30 +00:00
|
|
|
{
|
2022-10-06 17:39:46 +00:00
|
|
|
return ESM::RefId::stringRefId(LandTexture::createUniqueRecordId(record.mPluginIndex, record.mIndex));
|
2017-08-25 23:51:30 +00:00
|
|
|
}
|
|
|
|
|
2013-06-17 12:28:09 +00:00
|
|
|
/// \brief Single-type record collection
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
2013-06-17 12:28:09 +00:00
|
|
|
class Collection : public CollectionBase
|
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
public:
|
|
|
|
typedef ESXRecordT ESXRecord;
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
private:
|
|
|
|
std::vector<std::unique_ptr<Record<ESXRecordT>>> mRecords;
|
2023-02-16 20:37:33 +00:00
|
|
|
std::map<ESM::RefId, int> mIndex;
|
2022-09-22 18:26:05 +00:00
|
|
|
std::vector<Column<ESXRecordT>*> mColumns;
|
2014-01-27 18:40:05 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
// not implemented
|
|
|
|
Collection(const Collection&);
|
|
|
|
Collection& operator=(const Collection&);
|
2013-11-08 10:52:30 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
protected:
|
|
|
|
const std::vector<std::unique_ptr<Record<ESXRecordT>>>& getRecords() const;
|
2013-11-10 12:00:46 +00:00
|
|
|
|
2023-03-08 01:17:15 +00:00
|
|
|
void reorderRowsImp(const std::vector<int>& indexOrder);
|
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
bool reorderRowsImp(int baseIndex, const std::vector<int>& newOrder);
|
|
|
|
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
|
|
|
|
/// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex).
|
|
|
|
///
|
|
|
|
/// \return Success?
|
2013-11-14 10:39:14 +00:00
|
|
|
|
2023-03-03 10:30:50 +00:00
|
|
|
int cloneRecordImp(const ESM::RefId& origin, const ESM::RefId& dest, UniversalId::Type type);
|
2022-09-22 18:26:05 +00:00
|
|
|
///< Returns the index of the clone.
|
2017-09-04 05:06:58 +00:00
|
|
|
|
2023-03-03 10:30:50 +00:00
|
|
|
int touchRecordImp(const ESM::RefId& id);
|
2022-09-22 18:26:05 +00:00
|
|
|
///< Returns the index of the record on success, -1 on failure.
|
2017-09-04 05:06:58 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
public:
|
|
|
|
Collection();
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2023-02-27 23:22:47 +00:00
|
|
|
~Collection() override;
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
void add(const ESXRecordT& record);
|
|
|
|
///< Add a new record (modified)
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
int getSize() const override;
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-10-06 17:39:46 +00:00
|
|
|
ESM::RefId getId(int index) const override;
|
2014-06-09 08:35:39 +00:00
|
|
|
|
2022-10-06 17:39:46 +00:00
|
|
|
int getIndex(const ESM::RefId& id) const override;
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
int getColumns() const override;
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
QVariant getData(int index, int column) const override;
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
void setData(int index, int column, const QVariant& data) override;
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
const ColumnBase& getColumn(int column) const override;
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2023-02-27 23:22:47 +00:00
|
|
|
void merge();
|
2022-09-22 18:26:05 +00:00
|
|
|
///< Merge modified into base.
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2023-02-27 23:22:47 +00:00
|
|
|
void purge();
|
2022-09-22 18:26:05 +00:00
|
|
|
///< Remove records that are flagged as erased.
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
void removeRows(int index, int count) override;
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-10-06 17:39:46 +00:00
|
|
|
void appendBlankRecord(const ESM::RefId& id, UniversalId::Type type = UniversalId::Type_None) override;
|
2022-09-22 18:26:05 +00:00
|
|
|
///< \param type Will be ignored, unless the collection supports multiple record types
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
void cloneRecord(
|
2022-10-06 17:39:46 +00:00
|
|
|
const ESM::RefId& origin, const ESM::RefId& destination, const UniversalId::Type type) override;
|
2014-01-27 18:40:05 +00:00
|
|
|
|
2022-10-06 17:39:46 +00:00
|
|
|
bool touchRecord(const ESM::RefId& id) override;
|
2022-09-22 18:26:05 +00:00
|
|
|
///< Change the state of a record from base to modified, if it is not already.
|
|
|
|
/// \return True if the record was changed.
|
2014-01-27 18:40:05 +00:00
|
|
|
|
2022-10-06 17:39:46 +00:00
|
|
|
int searchId(const ESM::RefId& id) const override;
|
|
|
|
////< Search record with \a id.
|
|
|
|
/// \return index of record (if found) or -1 (not found)
|
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
void replace(int index, std::unique_ptr<RecordBase> record) override;
|
|
|
|
///< If the record type does not match, an exception is thrown.
|
|
|
|
///
|
|
|
|
/// \attention \a record must not change the ID.
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
void appendRecord(std::unique_ptr<RecordBase> record, UniversalId::Type type = UniversalId::Type_None) override;
|
|
|
|
///< If the record type does not match, an exception is thrown.
|
|
|
|
///< \param type Will be ignored, unless the collection supports multiple record types
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-10-06 17:39:46 +00:00
|
|
|
const Record<ESXRecordT>& getRecord(const ESM::RefId& id) const override;
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
const Record<ESXRecordT>& getRecord(int index) const override;
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-10-06 17:39:46 +00:00
|
|
|
int getAppendIndex(const ESM::RefId& id, UniversalId::Type type = UniversalId::Type_None) const override;
|
2022-09-22 18:26:05 +00:00
|
|
|
///< \param type Will be ignored, unless the collection supports multiple record types
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-10-06 17:39:46 +00:00
|
|
|
std::vector<ESM::RefId> getIds(bool listDeleted = true) const override;
|
2022-09-22 18:26:05 +00:00
|
|
|
///< Return a sorted collection of all IDs
|
|
|
|
///
|
|
|
|
/// \param listDeleted include deleted record in the list
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
virtual void insertRecord(
|
|
|
|
std::unique_ptr<RecordBase> record, int index, UniversalId::Type type = UniversalId::Type_None);
|
|
|
|
///< Insert record before index.
|
|
|
|
///
|
|
|
|
/// If the record type does not match, an exception is thrown.
|
|
|
|
///
|
|
|
|
/// If the index is invalid either generally (by being out of range) or for the particular
|
|
|
|
/// record, an exception is thrown.
|
2013-09-19 10:11:27 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
bool reorderRows(int baseIndex, const std::vector<int>& newOrder) override;
|
|
|
|
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
|
|
|
|
/// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex).
|
|
|
|
///
|
|
|
|
/// \return Success?
|
2013-10-27 13:00:25 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
void addColumn(Column<ESXRecordT>* column);
|
2013-11-14 10:39:14 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
void setRecord(int index, std::unique_ptr<Record<ESXRecordT>> record);
|
|
|
|
///< \attention This function must not change the ID.
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
NestableColumn* getNestableColumn(int column) const;
|
2013-06-17 12:28:09 +00:00
|
|
|
};
|
2014-01-27 18:40:05 +00:00
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
const std::vector<std::unique_ptr<Record<ESXRecordT>>>& Collection<ESXRecordT>::getRecords() const
|
2013-11-10 12:00:46 +00:00
|
|
|
{
|
|
|
|
return mRecords;
|
|
|
|
}
|
|
|
|
|
2023-03-08 01:17:15 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
void Collection<ESXRecordT>::reorderRowsImp(const std::vector<int>& indexOrder)
|
|
|
|
{
|
|
|
|
assert(indexOrder.size() == mRecords.size());
|
|
|
|
assert(std::unordered_set(indexOrder.begin(), indexOrder.end()).size() == indexOrder.size());
|
|
|
|
std::vector<std::unique_ptr<Record<ESXRecordT>>> orderedRecords;
|
|
|
|
for (const int index : indexOrder)
|
|
|
|
{
|
|
|
|
mIndex.at(mRecords[index]->get().mId) = static_cast<int>(orderedRecords.size());
|
|
|
|
orderedRecords.push_back(std::move(mRecords[index]));
|
|
|
|
}
|
|
|
|
mRecords = std::move(orderedRecords);
|
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
bool Collection<ESXRecordT>::reorderRowsImp(int baseIndex, const std::vector<int>& newOrder)
|
2013-11-14 10:39:14 +00:00
|
|
|
{
|
|
|
|
if (!newOrder.empty())
|
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
int size = static_cast<int>(newOrder.size());
|
2013-11-14 10:39:14 +00:00
|
|
|
|
|
|
|
// check that all indices are present
|
2022-09-22 18:26:05 +00:00
|
|
|
std::vector<int> test(newOrder);
|
|
|
|
std::sort(test.begin(), test.end());
|
|
|
|
if (*test.begin() != 0 || *--test.end() != size - 1)
|
2013-11-14 10:39:14 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// reorder records
|
2022-09-22 18:26:05 +00:00
|
|
|
std::vector<std::unique_ptr<Record<ESXRecordT>>> buffer(size);
|
2013-11-14 10:39:14 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
for (int i = 0; i < size; ++i)
|
2013-11-14 11:21:10 +00:00
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
buffer[newOrder[i]] = std::move(mRecords[baseIndex + i]);
|
2022-07-12 08:56:53 +00:00
|
|
|
if (buffer[newOrder[i]])
|
2022-09-22 18:26:05 +00:00
|
|
|
buffer[newOrder[i]]->setModified(buffer[newOrder[i]]->get());
|
2013-11-14 11:21:10 +00:00
|
|
|
}
|
2013-11-14 10:39:14 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
std::move(buffer.begin(), buffer.end(), mRecords.begin() + baseIndex);
|
2013-11-14 10:39:14 +00:00
|
|
|
|
|
|
|
// adjust index
|
2023-02-16 20:37:33 +00:00
|
|
|
for (auto& [id, index] : mIndex)
|
|
|
|
if (index >= baseIndex && index < baseIndex + size)
|
|
|
|
index = newOrder.at(index - baseIndex) + baseIndex;
|
2013-11-14 10:39:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
int Collection<ESXRecordT>::cloneRecordImp(
|
2023-03-03 10:30:50 +00:00
|
|
|
const ESM::RefId& origin, const ESM::RefId& destination, UniversalId::Type type)
|
2014-01-20 12:59:00 +00:00
|
|
|
{
|
2022-05-29 11:25:17 +00:00
|
|
|
auto copy = std::make_unique<Record<ESXRecordT>>();
|
2023-03-03 10:30:50 +00:00
|
|
|
copy->mModified = getRecord(origin).get();
|
2022-05-29 11:25:17 +00:00
|
|
|
copy->mState = RecordBase::State_ModifiedOnly;
|
2023-03-03 10:30:50 +00:00
|
|
|
setRecordId(destination, copy->get());
|
2014-01-27 18:40:05 +00:00
|
|
|
|
2022-05-29 11:25:17 +00:00
|
|
|
if (type == UniversalId::Type_Reference)
|
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
CSMWorld::CellRef* ptr = (CSMWorld::CellRef*)©->mModified;
|
2019-01-21 19:30:11 +00:00
|
|
|
ptr->mRefNum.mIndex = 0;
|
|
|
|
}
|
2023-03-03 10:30:50 +00:00
|
|
|
const int index = getAppendIndex(destination, type);
|
|
|
|
insertRecord(std::move(copy), getAppendIndex(destination, type));
|
2017-09-04 05:06:58 +00:00
|
|
|
|
|
|
|
return index;
|
2014-01-20 12:59:00 +00:00
|
|
|
}
|
2014-01-27 18:40:05 +00:00
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
2023-03-03 10:30:50 +00:00
|
|
|
int Collection<ESXRecordT>::touchRecordImp(const ESM::RefId& id)
|
2017-09-01 02:01:38 +00:00
|
|
|
{
|
2023-03-03 10:30:50 +00:00
|
|
|
const int index = getIndex(id);
|
2021-07-23 04:21:21 +00:00
|
|
|
Record<ESXRecordT>& record = *mRecords.at(index);
|
2017-09-01 02:01:38 +00:00
|
|
|
if (record.isDeleted())
|
|
|
|
{
|
|
|
|
throw std::runtime_error("attempt to touch deleted record");
|
|
|
|
}
|
|
|
|
|
2017-09-04 05:06:58 +00:00
|
|
|
if (!record.isModified())
|
2017-09-01 02:01:38 +00:00
|
|
|
{
|
|
|
|
record.setModified(record.get());
|
2017-09-04 05:06:58 +00:00
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
void Collection<ESXRecordT>::cloneRecord(
|
2022-10-06 17:39:46 +00:00
|
|
|
const ESM::RefId& origin, const ESM::RefId& destination, const UniversalId::Type type)
|
2017-09-04 05:06:58 +00:00
|
|
|
{
|
2023-03-03 10:30:50 +00:00
|
|
|
cloneRecordImp(origin, destination, type);
|
2017-09-04 05:06:58 +00:00
|
|
|
}
|
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
template <>
|
2023-02-25 12:11:31 +00:00
|
|
|
inline void Collection<Land>::cloneRecord(
|
2022-10-06 17:39:46 +00:00
|
|
|
const ESM::RefId& origin, const ESM::RefId& destination, const UniversalId::Type type)
|
2017-09-04 05:06:58 +00:00
|
|
|
{
|
2023-03-03 10:30:50 +00:00
|
|
|
const int index = cloneRecordImp(origin, destination, type);
|
2021-11-04 15:55:32 +00:00
|
|
|
mRecords.at(index)->get().setPlugin(0);
|
2017-09-04 05:06:58 +00:00
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
bool Collection<ESXRecordT>::touchRecord(const ESM::RefId& id)
|
2017-09-04 05:06:58 +00:00
|
|
|
{
|
2023-03-03 10:30:50 +00:00
|
|
|
return touchRecordImp(id) != -1;
|
2017-09-04 05:06:58 +00:00
|
|
|
}
|
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
template <>
|
2023-02-25 12:11:31 +00:00
|
|
|
inline bool Collection<Land>::touchRecord(const ESM::RefId& id)
|
2017-09-04 05:06:58 +00:00
|
|
|
{
|
2023-03-03 10:30:50 +00:00
|
|
|
const int index = touchRecordImp(id);
|
2017-09-04 05:06:58 +00:00
|
|
|
if (index >= 0)
|
|
|
|
{
|
2021-11-04 15:55:32 +00:00
|
|
|
mRecords.at(index)->get().setPlugin(0);
|
2017-09-01 02:01:38 +00:00
|
|
|
return true;
|
|
|
|
}
|
2017-09-04 05:06:58 +00:00
|
|
|
|
|
|
|
return false;
|
2017-09-01 02:01:38 +00:00
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
Collection<ESXRecordT>::Collection()
|
2022-09-22 18:26:05 +00:00
|
|
|
{
|
|
|
|
}
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
Collection<ESXRecordT>::~Collection()
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
for (typename std::vector<Column<ESXRecordT>*>::iterator iter(mColumns.begin()); iter != mColumns.end(); ++iter)
|
2013-06-17 12:28:09 +00:00
|
|
|
delete *iter;
|
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
void Collection<ESXRecordT>::add(const ESXRecordT& record)
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
2023-02-25 12:11:31 +00:00
|
|
|
const ESM::RefId id = getRecordId(record);
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2023-02-16 20:37:33 +00:00
|
|
|
auto iter = mIndex.find(id);
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
if (iter == mIndex.end())
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
2022-05-29 11:25:17 +00:00
|
|
|
auto record2 = std::make_unique<Record<ESXRecordT>>();
|
2021-07-23 04:21:21 +00:00
|
|
|
record2->mState = Record<ESXRecordT>::State_ModifiedOnly;
|
|
|
|
record2->mModified = record;
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
insertRecord(std::move(record2), getAppendIndex(id));
|
2013-06-17 12:28:09 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
mRecords[iter->second]->setModified(record);
|
2013-06-17 12:28:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
int Collection<ESXRecordT>::getSize() const
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
|
|
|
return mRecords.size();
|
|
|
|
}
|
2014-07-17 11:03:53 +00:00
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
ESM::RefId Collection<ESXRecordT>::getId(int index) const
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
2023-02-25 12:11:31 +00:00
|
|
|
return getRecordId(mRecords.at(index)->get());
|
2013-06-17 12:28:09 +00:00
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
int Collection<ESXRecordT>::getIndex(const ESM::RefId& id) const
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
int index = searchId(id);
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
if (index == -1)
|
2022-10-06 17:39:46 +00:00
|
|
|
throw std::runtime_error("invalid ID: " + id.getRefIdString());
|
2013-06-17 12:28:09 +00:00
|
|
|
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
int Collection<ESXRecordT>::getColumns() const
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
|
|
|
return mColumns.size();
|
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
QVariant Collection<ESXRecordT>::getData(int index, int column) const
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
return mColumns.at(column)->get(*mRecords.at(index));
|
2013-06-17 12:28:09 +00:00
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
void Collection<ESXRecordT>::setData(int index, int column, const QVariant& data)
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
return mColumns.at(column)->set(*mRecords.at(index), data);
|
2013-06-17 12:28:09 +00:00
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
const ColumnBase& Collection<ESXRecordT>::getColumn(int column) const
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
return *mColumns.at(column);
|
2013-06-17 12:28:09 +00:00
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
NestableColumn* Collection<ESXRecordT>::getNestableColumn(int column) const
|
2015-04-09 09:11:19 +00:00
|
|
|
{
|
|
|
|
if (column < 0 || column >= static_cast<int>(mColumns.size()))
|
|
|
|
throw std::runtime_error("column index out of range");
|
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
return mColumns.at(column);
|
2015-04-09 09:11:19 +00:00
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
void Collection<ESXRecordT>::addColumn(Column<ESXRecordT>* column)
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
mColumns.push_back(column);
|
2013-06-17 12:28:09 +00:00
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
void Collection<ESXRecordT>::merge()
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
for (typename std::vector<std::unique_ptr<Record<ESXRecordT>>>::iterator iter(mRecords.begin());
|
|
|
|
iter != mRecords.end(); ++iter)
|
2021-07-23 04:21:21 +00:00
|
|
|
(*iter)->merge();
|
2013-06-17 12:28:09 +00:00
|
|
|
|
|
|
|
purge();
|
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
void Collection<ESXRecordT>::purge()
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
while (i < static_cast<int>(mRecords.size()))
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
2021-07-23 04:21:21 +00:00
|
|
|
if (mRecords[i]->isErased())
|
2022-09-22 18:26:05 +00:00
|
|
|
removeRows(i, 1);
|
2013-06-17 12:28:09 +00:00
|
|
|
else
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
void Collection<ESXRecordT>::removeRows(int index, int count)
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
mRecords.erase(mRecords.begin() + index, mRecords.begin() + index + count);
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2023-02-16 20:37:33 +00:00
|
|
|
auto iter = mIndex.begin();
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
while (iter != mIndex.end())
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
if (iter->second >= index)
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
if (iter->second >= index + count)
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
|
|
|
iter->second -= count;
|
2013-08-26 10:25:19 +00:00
|
|
|
++iter;
|
2013-06-17 12:28:09 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-02-16 20:37:33 +00:00
|
|
|
iter = mIndex.erase(iter);
|
2013-06-17 12:28:09 +00:00
|
|
|
}
|
|
|
|
}
|
2013-08-26 10:25:19 +00:00
|
|
|
else
|
|
|
|
++iter;
|
2013-06-17 12:28:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
void Collection<ESXRecordT>::appendBlankRecord(const ESM::RefId& id, UniversalId::Type type)
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
|
|
|
ESXRecordT record;
|
2023-02-25 12:11:31 +00:00
|
|
|
setRecordId(id, record);
|
2013-06-17 12:28:09 +00:00
|
|
|
record.blank();
|
2013-10-27 13:13:10 +00:00
|
|
|
|
2022-05-29 11:25:17 +00:00
|
|
|
auto record2 = std::make_unique<Record<ESXRecordT>>();
|
2021-07-23 04:21:21 +00:00
|
|
|
record2->mState = Record<ESXRecordT>::State_ModifiedOnly;
|
|
|
|
record2->mModified = record;
|
2013-10-27 13:13:10 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
insertRecord(std::move(record2), getAppendIndex(id, type), type);
|
2013-06-17 12:28:09 +00:00
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
int Collection<ESXRecordT>::searchId(const ESM::RefId& id) const
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
2023-02-16 20:37:33 +00:00
|
|
|
const auto iter = mIndex.find(id);
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
if (iter == mIndex.end())
|
2013-06-17 12:28:09 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
return iter->second;
|
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
void Collection<ESXRecordT>::replace(int index, std::unique_ptr<RecordBase> record)
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
std::unique_ptr<Record<ESXRecordT>> tmp(static_cast<Record<ESXRecordT>*>(record.release()));
|
|
|
|
mRecords.at(index) = std::move(tmp);
|
2013-06-17 12:28:09 +00:00
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
void Collection<ESXRecordT>::appendRecord(std::unique_ptr<RecordBase> record, UniversalId::Type type)
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
2023-02-25 12:11:31 +00:00
|
|
|
int index = getAppendIndex(getRecordId(static_cast<Record<ESXRecordT>*>(record.get())->get()), type);
|
2022-09-22 18:26:05 +00:00
|
|
|
insertRecord(std::move(record), index, type);
|
2013-06-17 12:28:09 +00:00
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
int Collection<ESXRecordT>::getAppendIndex(const ESM::RefId& id, UniversalId::Type type) const
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
return static_cast<int>(mRecords.size());
|
2013-06-17 12:28:09 +00:00
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
std::vector<ESM::RefId> Collection<ESXRecordT>::getIds(bool listDeleted) const
|
2013-09-19 10:11:27 +00:00
|
|
|
{
|
2022-10-06 17:39:46 +00:00
|
|
|
std::vector<ESM::RefId> ids;
|
2013-09-19 10:11:27 +00:00
|
|
|
|
2022-10-06 17:39:46 +00:00
|
|
|
for (auto iter = mIndex.begin(); iter != mIndex.end(); ++iter)
|
2013-09-19 10:11:27 +00:00
|
|
|
{
|
2021-07-23 04:21:21 +00:00
|
|
|
if (listDeleted || !mRecords[iter->second]->isDeleted())
|
2023-02-25 12:11:31 +00:00
|
|
|
ids.push_back(getRecordId(mRecords[iter->second]->get()));
|
2013-09-19 10:11:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ids;
|
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
const Record<ESXRecordT>& Collection<ESXRecordT>::getRecord(const ESM::RefId& id) const
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
int index = getIndex(id);
|
|
|
|
return *mRecords.at(index);
|
2013-06-17 12:28:09 +00:00
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
const Record<ESXRecordT>& Collection<ESXRecordT>::getRecord(int index) const
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
return *mRecords.at(index);
|
2013-06-17 12:28:09 +00:00
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
void Collection<ESXRecordT>::insertRecord(std::unique_ptr<RecordBase> record, int index, UniversalId::Type type)
|
2013-10-27 13:00:25 +00:00
|
|
|
{
|
2021-07-23 13:17:16 +00:00
|
|
|
int size = static_cast<int>(mRecords.size());
|
|
|
|
if (index < 0 || index > size)
|
2022-09-22 18:26:05 +00:00
|
|
|
throw std::runtime_error("index out of range");
|
2013-10-27 13:00:25 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
std::unique_ptr<Record<ESXRecordT>> record2(static_cast<Record<ESXRecordT>*>(record.release()));
|
2023-02-25 12:11:31 +00:00
|
|
|
ESM::RefId id = getRecordId(record2->get());
|
2013-10-27 13:00:25 +00:00
|
|
|
|
2021-07-23 13:17:16 +00:00
|
|
|
if (index == size)
|
2022-09-22 18:26:05 +00:00
|
|
|
mRecords.push_back(std::move(record2));
|
2021-07-23 13:17:16 +00:00
|
|
|
else
|
2022-09-22 18:26:05 +00:00
|
|
|
mRecords.insert(mRecords.begin() + index, std::move(record2));
|
2013-10-27 13:00:25 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
if (index < size - 1)
|
2013-10-27 13:00:25 +00:00
|
|
|
{
|
2023-02-16 20:37:33 +00:00
|
|
|
for (auto& [key, value] : mIndex)
|
2021-07-23 13:17:16 +00:00
|
|
|
{
|
2023-02-16 20:37:33 +00:00
|
|
|
if (value >= index)
|
|
|
|
++value;
|
2021-07-23 13:17:16 +00:00
|
|
|
}
|
2013-10-27 13:00:25 +00:00
|
|
|
}
|
|
|
|
|
2022-10-06 17:39:46 +00:00
|
|
|
mIndex.insert(std::make_pair(id, index));
|
2013-10-27 13:00:25 +00:00
|
|
|
}
|
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
void Collection<ESXRecordT>::setRecord(int index, std::unique_ptr<Record<ESXRecordT>> record)
|
2013-06-17 12:28:09 +00:00
|
|
|
{
|
2023-02-25 12:11:31 +00:00
|
|
|
if (getRecordId(mRecords.at(index)->get()) != getRecordId(record->get()))
|
2022-09-22 18:26:05 +00:00
|
|
|
throw std::runtime_error("attempt to change the ID of a record");
|
2013-06-17 12:28:09 +00:00
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
mRecords.at(index) = std::move(record);
|
2013-06-17 12:28:09 +00:00
|
|
|
}
|
2013-11-14 10:39:14 +00:00
|
|
|
|
2023-02-25 12:11:31 +00:00
|
|
|
template <typename ESXRecordT>
|
|
|
|
bool Collection<ESXRecordT>::reorderRows(int baseIndex, const std::vector<int>& newOrder)
|
2013-11-14 10:39:14 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2013-06-17 12:28:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|