mirror of
https://github.com/OpenMW/openmw.git
synced 2025-12-13 03:13:08 +00:00
Merge branch 'OpenCS-loading-opt' into 'master'
OpenCS loading time improvements See merge request OpenMW/openmw!1044
This commit is contained in:
commit
141095b850
32 changed files with 873 additions and 398 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
#include "document.hpp"
|
#include "document.hpp"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/filesystem/fstream.hpp>
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
|
@ -115,10 +116,10 @@ void CSMDoc::Document::addOptionalGmst (const ESM::GameSetting& gmst)
|
||||||
{
|
{
|
||||||
if (getData().getGmsts().searchId (gmst.mId)==-1)
|
if (getData().getGmsts().searchId (gmst.mId)==-1)
|
||||||
{
|
{
|
||||||
CSMWorld::Record<ESM::GameSetting> record;
|
std::unique_ptr<CSMWorld::Record<ESM::GameSetting> > record(new CSMWorld::Record<ESM::GameSetting>);
|
||||||
record.mBase = gmst;
|
record->mBase = gmst;
|
||||||
record.mState = CSMWorld::RecordBase::State_BaseOnly;
|
record->mState = CSMWorld::RecordBase::State_BaseOnly;
|
||||||
getData().getGmsts().appendRecord (record);
|
getData().getGmsts().appendRecord (std::move(record));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,10 +127,10 @@ void CSMDoc::Document::addOptionalGlobal (const ESM::Global& global)
|
||||||
{
|
{
|
||||||
if (getData().getGlobals().searchId (global.mId)==-1)
|
if (getData().getGlobals().searchId (global.mId)==-1)
|
||||||
{
|
{
|
||||||
CSMWorld::Record<ESM::Global> record;
|
std::unique_ptr<CSMWorld::Record<ESM::Global> > record(new CSMWorld::Record<ESM::Global>);
|
||||||
record.mBase = global;
|
record->mBase = global;
|
||||||
record.mState = CSMWorld::RecordBase::State_BaseOnly;
|
record->mState = CSMWorld::RecordBase::State_BaseOnly;
|
||||||
getData().getGlobals().appendRecord (record);
|
getData().getGlobals().appendRecord (std::move(record));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,10 +138,10 @@ void CSMDoc::Document::addOptionalMagicEffect (const ESM::MagicEffect& magicEffe
|
||||||
{
|
{
|
||||||
if (getData().getMagicEffects().searchId (magicEffect.mId)==-1)
|
if (getData().getMagicEffects().searchId (magicEffect.mId)==-1)
|
||||||
{
|
{
|
||||||
CSMWorld::Record<ESM::MagicEffect> record;
|
std::unique_ptr<CSMWorld::Record<ESM::MagicEffect> > record(new CSMWorld::Record<ESM::MagicEffect>);
|
||||||
record.mBase = magicEffect;
|
record->mBase = magicEffect;
|
||||||
record.mState = CSMWorld::RecordBase::State_BaseOnly;
|
record->mState = CSMWorld::RecordBase::State_BaseOnly;
|
||||||
getData().getMagicEffects().appendRecord (record);
|
getData().getMagicEffects().appendRecord (std::move(record));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,19 +85,19 @@ void CSMDoc::Loader::load()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iter->second.mFile<size)
|
if (iter->second.mFile<size) // start loading the files
|
||||||
{
|
{
|
||||||
boost::filesystem::path path = document->getContentFiles()[iter->second.mFile];
|
boost::filesystem::path path = document->getContentFiles()[iter->second.mFile];
|
||||||
|
|
||||||
int steps = document->getData().startLoading (path, iter->second.mFile!=editedIndex, false);
|
int steps = document->getData().startLoading (path, iter->second.mFile!=editedIndex, /*project*/false);
|
||||||
iter->second.mRecordsLeft = true;
|
iter->second.mRecordsLeft = true;
|
||||||
iter->second.mRecordsLoaded = 0;
|
iter->second.mRecordsLoaded = 0;
|
||||||
|
|
||||||
emit nextStage (document, path.filename().string(), steps);
|
emit nextStage (document, path.filename().string(), steps);
|
||||||
}
|
}
|
||||||
else if (iter->second.mFile==size)
|
else if (iter->second.mFile==size) // start loading the last (project) file
|
||||||
{
|
{
|
||||||
int steps = document->getData().startLoading (document->getProjectPath(), false, true);
|
int steps = document->getData().startLoading (document->getProjectPath(), /*base*/false, true);
|
||||||
iter->second.mRecordsLeft = true;
|
iter->second.mRecordsLeft = true;
|
||||||
iter->second.mRecordsLoaded = 0;
|
iter->second.mRecordsLoaded = 0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message
|
||||||
|
|
||||||
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)
|
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)
|
||||||
{
|
{
|
||||||
if (iter->isModified() || iter->mState == CSMWorld::RecordBase::State_Deleted)
|
if ((*iter)->isModified() || (*iter)->mState == CSMWorld::RecordBase::State_Deleted)
|
||||||
{
|
{
|
||||||
infoModified = true;
|
infoModified = true;
|
||||||
break;
|
break;
|
||||||
|
|
@ -140,9 +140,9 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message
|
||||||
// write modified selected info records
|
// write modified selected info records
|
||||||
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)
|
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)
|
||||||
{
|
{
|
||||||
if (iter->isModified() || iter->mState == CSMWorld::RecordBase::State_Deleted)
|
if ((*iter)->isModified() || (*iter)->mState == CSMWorld::RecordBase::State_Deleted)
|
||||||
{
|
{
|
||||||
ESM::DialInfo info = iter->get();
|
ESM::DialInfo info = (*iter)->get();
|
||||||
info.mId = info.mId.substr (info.mId.find_last_of ('#')+1);
|
info.mId = info.mId.substr (info.mId.find_last_of ('#')+1);
|
||||||
|
|
||||||
info.mPrev = "";
|
info.mPrev = "";
|
||||||
|
|
@ -151,7 +151,7 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message
|
||||||
CSMWorld::InfoCollection::RecordConstIterator prev = iter;
|
CSMWorld::InfoCollection::RecordConstIterator prev = iter;
|
||||||
--prev;
|
--prev;
|
||||||
|
|
||||||
info.mPrev = prev->get().mId.substr (prev->get().mId.find_last_of ('#')+1);
|
info.mPrev = (*prev)->get().mId.substr ((*prev)->get().mId.find_last_of ('#')+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMWorld::InfoCollection::RecordConstIterator next = iter;
|
CSMWorld::InfoCollection::RecordConstIterator next = iter;
|
||||||
|
|
@ -160,11 +160,11 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message
|
||||||
info.mNext = "";
|
info.mNext = "";
|
||||||
if (next!=range.second)
|
if (next!=range.second)
|
||||||
{
|
{
|
||||||
info.mNext = next->get().mId.substr (next->get().mId.find_last_of ('#')+1);
|
info.mNext = (*next)->get().mId.substr ((*next)->get().mId.find_last_of ('#')+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.startRecord (info.sRecordId);
|
writer.startRecord (info.sRecordId);
|
||||||
info.save (writer, iter->mState == CSMWorld::RecordBase::State_Deleted);
|
info.save (writer, (*iter)->mState == CSMWorld::RecordBase::State_Deleted);
|
||||||
writer.endRecord (info.sRecordId);
|
writer.endRecord (info.sRecordId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages)
|
||||||
|
|
||||||
for (CSMWorld::InfoCollection::RecordConstIterator it = range.first; it != range.second; ++it)
|
for (CSMWorld::InfoCollection::RecordConstIterator it = range.first; it != range.second; ++it)
|
||||||
{
|
{
|
||||||
const CSMWorld::Record<CSMWorld::Info> infoRecord = (*it);
|
const CSMWorld::Record<CSMWorld::Info> infoRecord = (*it->get());
|
||||||
|
|
||||||
if (infoRecord.isDeleted())
|
if (infoRecord.isDeleted())
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -103,10 +103,9 @@ void CSMTools::MergeReferencesStage::perform (int stage, CSMDoc::Messages& messa
|
||||||
ref.mRefNum.mContentFile = 0;
|
ref.mRefNum.mContentFile = 0;
|
||||||
ref.mNew = false;
|
ref.mNew = false;
|
||||||
|
|
||||||
CSMWorld::Record<CSMWorld::CellRef> newRecord (
|
mState.mTarget->getData().getReferences().appendRecord (
|
||||||
CSMWorld::RecordBase::State_ModifiedOnly, nullptr, &ref);
|
std::make_unique<CSMWorld::Record<CSMWorld::CellRef> >(
|
||||||
|
CSMWorld::Record<CSMWorld::CellRef>(CSMWorld::RecordBase::State_ModifiedOnly, nullptr, &ref)));
|
||||||
mState.mTarget->getData().getReferences().appendRecord (newRecord);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -128,7 +127,9 @@ void CSMTools::PopulateLandTexturesMergeStage::perform (int stage, CSMDoc::Messa
|
||||||
|
|
||||||
if (!record.isDeleted())
|
if (!record.isDeleted())
|
||||||
{
|
{
|
||||||
mState.mTarget->getData().getLandTextures().appendRecord(record);
|
mState.mTarget->getData().getLandTextures().appendRecord(
|
||||||
|
std::make_unique<CSMWorld::Record<CSMWorld::LandTexture> >(
|
||||||
|
CSMWorld::Record<CSMWorld::LandTexture>(CSMWorld::RecordBase::State_ModifiedOnly, nullptr, &record.get())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -150,7 +151,9 @@ void CSMTools::MergeLandStage::perform (int stage, CSMDoc::Messages& messages)
|
||||||
|
|
||||||
if (!record.isDeleted())
|
if (!record.isDeleted())
|
||||||
{
|
{
|
||||||
mState.mTarget->getData().getLand().appendRecord (record);
|
mState.mTarget->getData().getLand().appendRecord (
|
||||||
|
std::make_unique<CSMWorld::Record<CSMWorld::Land> >(
|
||||||
|
CSMWorld::Record<CSMWorld::Land>(CSMWorld::RecordBase::State_ModifiedOnly, nullptr, &record.get())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -185,10 +188,9 @@ void CSMTools::FixLandsAndLandTexturesMergeStage::perform (int stage, CSMDoc::Me
|
||||||
const CSMWorld::Record<CSMWorld::Land>& oldRecord =
|
const CSMWorld::Record<CSMWorld::Land>& oldRecord =
|
||||||
mState.mTarget->getData().getLand().getRecord (stage);
|
mState.mTarget->getData().getLand().getRecord (stage);
|
||||||
|
|
||||||
CSMWorld::Record<CSMWorld::Land> newRecord(CSMWorld::RecordBase::State_ModifiedOnly,
|
mState.mTarget->getData().getLand().setRecord (stage,
|
||||||
nullptr, &oldRecord.get());
|
std::make_unique<CSMWorld::Record<CSMWorld::Land> >(
|
||||||
|
CSMWorld::Record<CSMWorld::Land>(CSMWorld::RecordBase::State_ModifiedOnly, nullptr, &oldRecord.get())));
|
||||||
mState.mTarget->getData().getLand().setRecord(stage, newRecord);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <components/to_utf8/to_utf8.hpp>
|
#include <components/to_utf8/to_utf8.hpp>
|
||||||
|
|
||||||
|
|
@ -82,7 +83,8 @@ namespace CSMTools
|
||||||
const CSMWorld::Record<RecordType>& record = source.getRecord (stage);
|
const CSMWorld::Record<RecordType>& record = source.getRecord (stage);
|
||||||
|
|
||||||
if (!record.isDeleted())
|
if (!record.isDeleted())
|
||||||
target.appendRecord (CSMWorld::Record<RecordType> (CSMWorld::RecordBase::State_ModifiedOnly, nullptr, &record.get()));
|
target.appendRecord (std::make_unique<CSMWorld::Record<RecordType> >(
|
||||||
|
CSMWorld::Record<RecordType>(CSMWorld::RecordBase::State_ModifiedOnly, nullptr, &record.get())));
|
||||||
}
|
}
|
||||||
|
|
||||||
class MergeRefIdsStage : public CSMDoc::Stage
|
class MergeRefIdsStage : public CSMDoc::Stage
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
|
|
@ -84,7 +85,7 @@ namespace CSMWorld
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::vector<Record<ESXRecordT> > mRecords;
|
std::vector<std::unique_ptr<Record<ESXRecordT> > > mRecords;
|
||||||
std::map<std::string, int> mIndex;
|
std::map<std::string, int> mIndex;
|
||||||
std::vector<Column<ESXRecordT> *> mColumns;
|
std::vector<Column<ESXRecordT> *> mColumns;
|
||||||
|
|
||||||
|
|
@ -94,9 +95,7 @@ namespace CSMWorld
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
const std::map<std::string, int>& getIdMap() const;
|
const std::vector<std::unique_ptr<Record<ESXRecordT> > >& getRecords() const;
|
||||||
|
|
||||||
const std::vector<Record<ESXRecordT> >& getRecords() const;
|
|
||||||
|
|
||||||
bool reorderRowsImp (int baseIndex, const std::vector<int>& newOrder);
|
bool reorderRowsImp (int baseIndex, const std::vector<int>& newOrder);
|
||||||
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
|
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
|
||||||
|
|
@ -158,12 +157,12 @@ namespace CSMWorld
|
||||||
////< Search record with \a id.
|
////< Search record with \a id.
|
||||||
/// \return index of record (if found) or -1 (not found)
|
/// \return index of record (if found) or -1 (not found)
|
||||||
|
|
||||||
void replace (int index, const RecordBase& record) override;
|
void replace (int index, std::unique_ptr<RecordBase> record) override;
|
||||||
///< If the record type does not match, an exception is thrown.
|
///< If the record type does not match, an exception is thrown.
|
||||||
///
|
///
|
||||||
/// \attention \a record must not change the ID.
|
/// \attention \a record must not change the ID.
|
||||||
|
|
||||||
void appendRecord (const RecordBase& record,
|
void appendRecord (std::unique_ptr<RecordBase> record,
|
||||||
UniversalId::Type type = UniversalId::Type_None) override;
|
UniversalId::Type type = UniversalId::Type_None) override;
|
||||||
///< If the record type does not match, an exception is thrown.
|
///< If the record type does not match, an exception is thrown.
|
||||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||||
|
|
@ -181,7 +180,7 @@ namespace CSMWorld
|
||||||
///
|
///
|
||||||
/// \param listDeleted include deleted record in the list
|
/// \param listDeleted include deleted record in the list
|
||||||
|
|
||||||
virtual void insertRecord (const RecordBase& record, int index,
|
virtual void insertRecord (std::unique_ptr<RecordBase> record, int index,
|
||||||
UniversalId::Type type = UniversalId::Type_None);
|
UniversalId::Type type = UniversalId::Type_None);
|
||||||
///< Insert record before index.
|
///< Insert record before index.
|
||||||
///
|
///
|
||||||
|
|
@ -198,20 +197,14 @@ namespace CSMWorld
|
||||||
|
|
||||||
void addColumn (Column<ESXRecordT> *column);
|
void addColumn (Column<ESXRecordT> *column);
|
||||||
|
|
||||||
void setRecord (int index, const Record<ESXRecordT>& record);
|
void setRecord (int index, std::unique_ptr<Record<ESXRecordT> > record);
|
||||||
///< \attention This function must not change the ID.
|
///< \attention This function must not change the ID.
|
||||||
|
|
||||||
NestableColumn *getNestableColumn (int column) const;
|
NestableColumn *getNestableColumn (int column) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
const std::map<std::string, int>& Collection<ESXRecordT, IdAccessorT>::getIdMap() const
|
const std::vector<std::unique_ptr<Record<ESXRecordT> > >& Collection<ESXRecordT, IdAccessorT>::getRecords() const
|
||||||
{
|
|
||||||
return mIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
|
||||||
const std::vector<Record<ESXRecordT> >& Collection<ESXRecordT, IdAccessorT>::getRecords() const
|
|
||||||
{
|
{
|
||||||
return mRecords;
|
return mRecords;
|
||||||
}
|
}
|
||||||
|
|
@ -231,15 +224,15 @@ namespace CSMWorld
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// reorder records
|
// reorder records
|
||||||
std::vector<Record<ESXRecordT> > buffer (size);
|
std::vector<std::unique_ptr<Record<ESXRecordT> > > buffer (size);
|
||||||
|
|
||||||
for (int i=0; i<size; ++i)
|
for (int i=0; i<size; ++i)
|
||||||
{
|
{
|
||||||
buffer[newOrder[i]] = mRecords [baseIndex+i];
|
buffer[newOrder[i]] = std::move(mRecords [baseIndex+i]);
|
||||||
buffer[newOrder[i]].setModified (buffer[newOrder[i]].get());
|
buffer[newOrder[i]]->setModified (buffer[newOrder[i]]->get());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::copy (buffer.begin(), buffer.end(), mRecords.begin()+baseIndex);
|
std::move (buffer.begin(), buffer.end(), mRecords.begin()+baseIndex);
|
||||||
|
|
||||||
// adjust index
|
// adjust index
|
||||||
for (std::map<std::string, int>::iterator iter (mIndex.begin()); iter!=mIndex.end();
|
for (std::map<std::string, int>::iterator iter (mIndex.begin()); iter!=mIndex.end();
|
||||||
|
|
@ -255,18 +248,18 @@ namespace CSMWorld
|
||||||
int Collection<ESXRecordT, IdAccessorT>::cloneRecordImp(const std::string& origin,
|
int Collection<ESXRecordT, IdAccessorT>::cloneRecordImp(const std::string& origin,
|
||||||
const std::string& destination, UniversalId::Type type)
|
const std::string& destination, UniversalId::Type type)
|
||||||
{
|
{
|
||||||
Record<ESXRecordT> copy;
|
std::unique_ptr<Record<ESXRecordT> > copy(new Record<ESXRecordT>);
|
||||||
copy.mModified = getRecord(origin).get();
|
copy->mModified = getRecord(origin).get();
|
||||||
copy.mState = RecordBase::State_ModifiedOnly;
|
copy->mState = RecordBase::State_ModifiedOnly;
|
||||||
IdAccessorT().setId(copy.get(), destination);
|
IdAccessorT().setId(copy->get(), destination);
|
||||||
|
|
||||||
if (type == UniversalId::Type_Reference) {
|
if (type == UniversalId::Type_Reference) {
|
||||||
CSMWorld::CellRef* ptr = (CSMWorld::CellRef*) ©.mModified;
|
CSMWorld::CellRef* ptr = (CSMWorld::CellRef*) ©->mModified;
|
||||||
ptr->mRefNum.mIndex = 0;
|
ptr->mRefNum.mIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int index = getAppendIndex(destination, type);
|
int index = getAppendIndex(destination, type);
|
||||||
insertRecord(copy, getAppendIndex(destination, type));
|
insertRecord(std::move(copy), getAppendIndex(destination, type));
|
||||||
|
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
@ -275,7 +268,7 @@ namespace CSMWorld
|
||||||
int Collection<ESXRecordT, IdAccessorT>::touchRecordImp(const std::string& id)
|
int Collection<ESXRecordT, IdAccessorT>::touchRecordImp(const std::string& id)
|
||||||
{
|
{
|
||||||
int index = getIndex(id);
|
int index = getIndex(id);
|
||||||
Record<ESXRecordT>& record = mRecords.at(index);
|
Record<ESXRecordT>& record = *mRecords.at(index);
|
||||||
if (record.isDeleted())
|
if (record.isDeleted())
|
||||||
{
|
{
|
||||||
throw std::runtime_error("attempt to touch deleted record");
|
throw std::runtime_error("attempt to touch deleted record");
|
||||||
|
|
@ -302,7 +295,7 @@ namespace CSMWorld
|
||||||
const std::string& destination, const UniversalId::Type type)
|
const std::string& destination, const UniversalId::Type type)
|
||||||
{
|
{
|
||||||
int index = cloneRecordImp(origin, destination, type);
|
int index = cloneRecordImp(origin, destination, type);
|
||||||
mRecords.at(index).get().mPlugin = 0;
|
mRecords.at(index)->get().mPlugin = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
|
|
@ -317,7 +310,7 @@ namespace CSMWorld
|
||||||
int index = touchRecordImp(id);
|
int index = touchRecordImp(id);
|
||||||
if (index >= 0)
|
if (index >= 0)
|
||||||
{
|
{
|
||||||
mRecords.at(index).get().mPlugin = 0;
|
mRecords.at(index)->get().mPlugin = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -344,15 +337,15 @@ namespace CSMWorld
|
||||||
|
|
||||||
if (iter==mIndex.end())
|
if (iter==mIndex.end())
|
||||||
{
|
{
|
||||||
Record<ESXRecordT> record2;
|
std::unique_ptr<Record<ESXRecordT> > record2(new Record<ESXRecordT>);
|
||||||
record2.mState = Record<ESXRecordT>::State_ModifiedOnly;
|
record2->mState = Record<ESXRecordT>::State_ModifiedOnly;
|
||||||
record2.mModified = record;
|
record2->mModified = record;
|
||||||
|
|
||||||
insertRecord (record2, getAppendIndex (id));
|
insertRecord (std::move(record2), getAppendIndex (id));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mRecords[iter->second].setModified (record);
|
mRecords[iter->second]->setModified (record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -365,7 +358,7 @@ namespace CSMWorld
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
std::string Collection<ESXRecordT, IdAccessorT>::getId (int index) const
|
std::string Collection<ESXRecordT, IdAccessorT>::getId (int index) const
|
||||||
{
|
{
|
||||||
return IdAccessorT().getId (mRecords.at (index).get());
|
return IdAccessorT().getId (mRecords.at (index)->get());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
|
|
@ -388,13 +381,13 @@ namespace CSMWorld
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
QVariant Collection<ESXRecordT, IdAccessorT>::getData (int index, int column) const
|
QVariant Collection<ESXRecordT, IdAccessorT>::getData (int index, int column) const
|
||||||
{
|
{
|
||||||
return mColumns.at (column)->get (mRecords.at (index));
|
return mColumns.at (column)->get (*mRecords.at (index));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
void Collection<ESXRecordT, IdAccessorT>::setData (int index, int column, const QVariant& data)
|
void Collection<ESXRecordT, IdAccessorT>::setData (int index, int column, const QVariant& data)
|
||||||
{
|
{
|
||||||
return mColumns.at (column)->set (mRecords.at (index), data);
|
return mColumns.at (column)->set (*mRecords.at (index), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
|
|
@ -421,8 +414,8 @@ namespace CSMWorld
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
void Collection<ESXRecordT, IdAccessorT>::merge()
|
void Collection<ESXRecordT, IdAccessorT>::merge()
|
||||||
{
|
{
|
||||||
for (typename std::vector<Record<ESXRecordT> >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter)
|
for (typename std::vector<std::unique_ptr<Record<ESXRecordT> > >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter)
|
||||||
iter->merge();
|
(*iter)->merge();
|
||||||
|
|
||||||
purge();
|
purge();
|
||||||
}
|
}
|
||||||
|
|
@ -434,7 +427,7 @@ namespace CSMWorld
|
||||||
|
|
||||||
while (i<static_cast<int> (mRecords.size()))
|
while (i<static_cast<int> (mRecords.size()))
|
||||||
{
|
{
|
||||||
if (mRecords[i].isErased())
|
if (mRecords[i]->isErased())
|
||||||
removeRows (i, 1);
|
removeRows (i, 1);
|
||||||
else
|
else
|
||||||
++i;
|
++i;
|
||||||
|
|
@ -475,11 +468,11 @@ namespace CSMWorld
|
||||||
IdAccessorT().setId(record, id);
|
IdAccessorT().setId(record, id);
|
||||||
record.blank();
|
record.blank();
|
||||||
|
|
||||||
Record<ESXRecordT> record2;
|
std::unique_ptr<Record<ESXRecordT> > record2(new Record<ESXRecordT>);
|
||||||
record2.mState = Record<ESXRecordT>::State_ModifiedOnly;
|
record2->mState = Record<ESXRecordT>::State_ModifiedOnly;
|
||||||
record2.mModified = record;
|
record2->mModified = record;
|
||||||
|
|
||||||
insertRecord (record2, getAppendIndex (id, type), type);
|
insertRecord (std::move(record2), getAppendIndex (id, type), type);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
|
|
@ -496,18 +489,19 @@ namespace CSMWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
void Collection<ESXRecordT, IdAccessorT>::replace (int index, const RecordBase& record)
|
void Collection<ESXRecordT, IdAccessorT>::replace (int index, std::unique_ptr<RecordBase> record)
|
||||||
{
|
{
|
||||||
mRecords.at (index) = dynamic_cast<const Record<ESXRecordT>&> (record);
|
std::unique_ptr<Record<ESXRecordT> > tmp(static_cast<Record<ESXRecordT>*>(record.release()));
|
||||||
|
mRecords.at (index) = std::move(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
void Collection<ESXRecordT, IdAccessorT>::appendRecord (const RecordBase& record,
|
void Collection<ESXRecordT, IdAccessorT>::appendRecord (std::unique_ptr<RecordBase> record,
|
||||||
UniversalId::Type type)
|
UniversalId::Type type)
|
||||||
{
|
{
|
||||||
insertRecord (record,
|
int index =
|
||||||
getAppendIndex (IdAccessorT().getId (
|
getAppendIndex(IdAccessorT().getId(static_cast<Record<ESXRecordT>*>(record.get())->get()), type);
|
||||||
dynamic_cast<const Record<ESXRecordT>&> (record).get()), type), type);
|
insertRecord (std::move(record), index, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
|
|
@ -525,8 +519,8 @@ namespace CSMWorld
|
||||||
for (typename std::map<std::string, int>::const_iterator iter = mIndex.begin();
|
for (typename std::map<std::string, int>::const_iterator iter = mIndex.begin();
|
||||||
iter!=mIndex.end(); ++iter)
|
iter!=mIndex.end(); ++iter)
|
||||||
{
|
{
|
||||||
if (listDeleted || !mRecords[iter->second].isDeleted())
|
if (listDeleted || !mRecords[iter->second]->isDeleted())
|
||||||
ids.push_back (IdAccessorT().getId (mRecords[iter->second].get()));
|
ids.push_back (IdAccessorT().getId (mRecords[iter->second]->get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ids;
|
return ids;
|
||||||
|
|
@ -536,46 +530,52 @@ namespace CSMWorld
|
||||||
const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (const std::string& id) const
|
const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (const std::string& id) const
|
||||||
{
|
{
|
||||||
int index = getIndex (id);
|
int index = getIndex (id);
|
||||||
return mRecords.at (index);
|
return *mRecords.at (index);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (int index) const
|
const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (int index) const
|
||||||
{
|
{
|
||||||
return mRecords.at (index);
|
return *mRecords.at (index);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
void Collection<ESXRecordT, IdAccessorT>::insertRecord (const RecordBase& record, int index,
|
void Collection<ESXRecordT, IdAccessorT>::insertRecord (std::unique_ptr<RecordBase> record, int index,
|
||||||
UniversalId::Type type)
|
UniversalId::Type type)
|
||||||
{
|
{
|
||||||
if (index<0 || index>static_cast<int> (mRecords.size()))
|
int size = static_cast<int>(mRecords.size());
|
||||||
|
if (index < 0 || index > size)
|
||||||
throw std::runtime_error ("index out of range");
|
throw std::runtime_error ("index out of range");
|
||||||
|
|
||||||
const Record<ESXRecordT>& record2 = dynamic_cast<const Record<ESXRecordT>&> (record);
|
std::unique_ptr<Record<ESXRecordT> > record2(static_cast<Record<ESXRecordT>*>(record.release()));
|
||||||
|
std::string lowerId = Misc::StringUtils::lowerCase(IdAccessorT().getId(record2->get()));
|
||||||
|
|
||||||
mRecords.insert (mRecords.begin()+index, record2);
|
if (index == size)
|
||||||
|
mRecords.push_back (std::move(record2));
|
||||||
|
else
|
||||||
|
mRecords.insert (mRecords.begin()+index, std::move(record2));
|
||||||
|
|
||||||
if (index<static_cast<int> (mRecords.size())-1)
|
if (index < size-1)
|
||||||
{
|
{
|
||||||
for (std::map<std::string, int>::iterator iter (mIndex.begin()); iter!=mIndex.end();
|
for (std::map<std::string, int>::iterator iter (mIndex.begin()); iter!=mIndex.end(); ++iter)
|
||||||
++iter)
|
{
|
||||||
if (iter->second>=index)
|
if (iter->second >= index)
|
||||||
++(iter->second);
|
++(iter->second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (IdAccessorT().getId (
|
mIndex.insert (std::make_pair (lowerId, index));
|
||||||
record2.get())), index));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
void Collection<ESXRecordT, IdAccessorT>::setRecord (int index, const Record<ESXRecordT>& record)
|
void Collection<ESXRecordT, IdAccessorT>::setRecord (int index,
|
||||||
|
std::unique_ptr<Record<ESXRecordT> > record)
|
||||||
{
|
{
|
||||||
if (Misc::StringUtils::lowerCase (IdAccessorT().getId (mRecords.at (index).get()))!=
|
if (Misc::StringUtils::lowerCase (IdAccessorT().getId (mRecords.at (index)->get())) !=
|
||||||
Misc::StringUtils::lowerCase (IdAccessorT().getId (record.get())))
|
Misc::StringUtils::lowerCase (IdAccessorT().getId (record->get())))
|
||||||
throw std::runtime_error ("attempt to change the ID of a record");
|
throw std::runtime_error ("attempt to change the ID of a record");
|
||||||
|
|
||||||
mRecords.at (index) = record;
|
mRecords.at (index) = std::move(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,11 @@ CSMWorld::CollectionBase::CollectionBase() {}
|
||||||
|
|
||||||
CSMWorld::CollectionBase::~CollectionBase() {}
|
CSMWorld::CollectionBase::~CollectionBase() {}
|
||||||
|
|
||||||
|
int CSMWorld::CollectionBase::getInsertIndex (const std::string& id, UniversalId::Type type, RecordBase *record) const
|
||||||
|
{
|
||||||
|
return getAppendIndex(id, type);
|
||||||
|
}
|
||||||
|
|
||||||
int CSMWorld::CollectionBase::searchColumnIndex (Columns::ColumnId id) const
|
int CSMWorld::CollectionBase::searchColumnIndex (Columns::ColumnId id) const
|
||||||
{
|
{
|
||||||
int columns = getColumns();
|
int columns = getColumns();
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "universalid.hpp"
|
#include "universalid.hpp"
|
||||||
#include "columns.hpp"
|
#include "columns.hpp"
|
||||||
|
|
@ -64,13 +65,13 @@ namespace CSMWorld
|
||||||
////< Search record with \a id.
|
////< Search record with \a id.
|
||||||
/// \return index of record (if found) or -1 (not found)
|
/// \return index of record (if found) or -1 (not found)
|
||||||
|
|
||||||
virtual void replace (int index, const RecordBase& record) = 0;
|
virtual void replace (int index, std::unique_ptr<RecordBase> record) = 0;
|
||||||
///< If the record type does not match, an exception is thrown.
|
///< If the record type does not match, an exception is thrown.
|
||||||
///
|
///
|
||||||
/// \attention \a record must not change the ID.
|
/// \attention \a record must not change the ID.
|
||||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||||
|
|
||||||
virtual void appendRecord (const RecordBase& record,
|
virtual void appendRecord (std::unique_ptr<RecordBase> record,
|
||||||
UniversalId::Type type = UniversalId::Type_None) = 0;
|
UniversalId::Type type = UniversalId::Type_None) = 0;
|
||||||
///< If the record type does not match, an exception is thrown.
|
///< If the record type does not match, an exception is thrown.
|
||||||
|
|
||||||
|
|
@ -99,6 +100,12 @@ namespace CSMWorld
|
||||||
///
|
///
|
||||||
/// \return Success?
|
/// \return Success?
|
||||||
|
|
||||||
|
virtual int getInsertIndex (const std::string& id,
|
||||||
|
UniversalId::Type type = UniversalId::Type_None,
|
||||||
|
RecordBase *record = nullptr) const;
|
||||||
|
///< Works like getAppendIndex unless an overloaded method uses the record pointer
|
||||||
|
/// to get additional info about the record that results in an alternative index.
|
||||||
|
|
||||||
int searchColumnIndex (Columns::ColumnId id) const;
|
int searchColumnIndex (Columns::ColumnId id) const;
|
||||||
///< Return index of column with the given \a id. If no such column exists, -1 is returned.
|
///< Return index of column with the given \a id. If no such column exists, -1 is returned.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ CSMWorld::TouchCommand::TouchCommand(IdTable& table, const std::string& id, QUnd
|
||||||
, mChanged(false)
|
, mChanged(false)
|
||||||
{
|
{
|
||||||
setText(("Touch " + mId).c_str());
|
setText(("Touch " + mId).c_str());
|
||||||
mOld.reset(mTable.getRecord(mId).clone());
|
mOld.reset(mTable.getRecord(mId).clone().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::TouchCommand::redo()
|
void CSMWorld::TouchCommand::redo()
|
||||||
|
|
@ -36,7 +36,7 @@ void CSMWorld::TouchCommand::undo()
|
||||||
{
|
{
|
||||||
if (mChanged)
|
if (mChanged)
|
||||||
{
|
{
|
||||||
mTable.setRecord(mId, *mOld);
|
mTable.setRecord(mId, std::move(mOld));
|
||||||
mChanged = false;
|
mChanged = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -159,7 +159,7 @@ CSMWorld::TouchLandCommand::TouchLandCommand(IdTable& landTable, IdTable& ltexTa
|
||||||
, mChanged(false)
|
, mChanged(false)
|
||||||
{
|
{
|
||||||
setText(("Touch " + mId).c_str());
|
setText(("Touch " + mId).c_str());
|
||||||
mOld.reset(mLands.getRecord(mId).clone());
|
mOld.reset(mLands.getRecord(mId).clone().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& CSMWorld::TouchLandCommand::getOriginId() const
|
const std::string& CSMWorld::TouchLandCommand::getOriginId() const
|
||||||
|
|
@ -181,7 +181,7 @@ void CSMWorld::TouchLandCommand::onUndo()
|
||||||
{
|
{
|
||||||
if (mChanged)
|
if (mChanged)
|
||||||
{
|
{
|
||||||
mLands.setRecord(mId, *mOld);
|
mLands.setRecord(mId, std::move(mOld));
|
||||||
mChanged = false;
|
mChanged = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -291,16 +291,15 @@ void CSMWorld::CreateCommand::undo()
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand* parent)
|
CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand* parent)
|
||||||
: QUndoCommand (parent), mModel (model), mId (id), mOld (nullptr)
|
: QUndoCommand (parent), mModel (model), mId (id)
|
||||||
{
|
{
|
||||||
setText (("Revert record " + id).c_str());
|
setText (("Revert record " + id).c_str());
|
||||||
|
|
||||||
mOld = model.getRecord (id).clone();
|
mOld = std::move(model.getRecord (id).clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMWorld::RevertCommand::~RevertCommand()
|
CSMWorld::RevertCommand::~RevertCommand()
|
||||||
{
|
{
|
||||||
delete mOld;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::RevertCommand::redo()
|
void CSMWorld::RevertCommand::redo()
|
||||||
|
|
@ -322,21 +321,20 @@ void CSMWorld::RevertCommand::redo()
|
||||||
|
|
||||||
void CSMWorld::RevertCommand::undo()
|
void CSMWorld::RevertCommand::undo()
|
||||||
{
|
{
|
||||||
mModel.setRecord (mId, *mOld);
|
mModel.setRecord (mId, std::move(mOld));
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMWorld::DeleteCommand::DeleteCommand (IdTable& model,
|
CSMWorld::DeleteCommand::DeleteCommand (IdTable& model,
|
||||||
const std::string& id, CSMWorld::UniversalId::Type type, QUndoCommand* parent)
|
const std::string& id, CSMWorld::UniversalId::Type type, QUndoCommand* parent)
|
||||||
: QUndoCommand (parent), mModel (model), mId (id), mOld (nullptr), mType(type)
|
: QUndoCommand (parent), mModel (model), mId (id), mType(type)
|
||||||
{
|
{
|
||||||
setText (("Delete record " + id).c_str());
|
setText (("Delete record " + id).c_str());
|
||||||
|
|
||||||
mOld = model.getRecord (id).clone();
|
mOld = std::move(model.getRecord (id).clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMWorld::DeleteCommand::~DeleteCommand()
|
CSMWorld::DeleteCommand::~DeleteCommand()
|
||||||
{
|
{
|
||||||
delete mOld;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::DeleteCommand::redo()
|
void CSMWorld::DeleteCommand::redo()
|
||||||
|
|
@ -358,7 +356,7 @@ void CSMWorld::DeleteCommand::redo()
|
||||||
|
|
||||||
void CSMWorld::DeleteCommand::undo()
|
void CSMWorld::DeleteCommand::undo()
|
||||||
{
|
{
|
||||||
mModel.setRecord (mId, *mOld, mType);
|
mModel.setRecord (mId, std::move(mOld), mType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -424,18 +422,19 @@ void CSMWorld::CreatePathgridCommand::redo()
|
||||||
{
|
{
|
||||||
CreateCommand::redo();
|
CreateCommand::redo();
|
||||||
|
|
||||||
Record<Pathgrid> record = static_cast<const Record<Pathgrid>& >(mModel.getRecord(mId));
|
std::unique_ptr<Record<Pathgrid> > record
|
||||||
record.get().blank();
|
= std::make_unique<Record<Pathgrid> >(static_cast<const Record<Pathgrid>& >(mModel.getRecord(mId)));
|
||||||
record.get().mCell = mId;
|
record->get().blank();
|
||||||
|
record->get().mCell = mId;
|
||||||
|
|
||||||
std::pair<CellCoordinates, bool> coords = CellCoordinates::fromId(mId);
|
std::pair<CellCoordinates, bool> coords = CellCoordinates::fromId(mId);
|
||||||
if (coords.second)
|
if (coords.second)
|
||||||
{
|
{
|
||||||
record.get().mData.mX = coords.first.getX();
|
record->get().mData.mX = coords.first.getX();
|
||||||
record.get().mData.mY = coords.first.getY();
|
record->get().mData.mY = coords.first.getY();
|
||||||
}
|
}
|
||||||
|
|
||||||
mModel.setRecord(mId, record, mType);
|
mModel.setRecord(mId, std::move(record), mType);
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMWorld::UpdateCellCommand::UpdateCellCommand (IdTable& model, int row, QUndoCommand *parent)
|
CSMWorld::UpdateCellCommand::UpdateCellCommand (IdTable& model, int row, QUndoCommand *parent)
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,7 @@ namespace CSMWorld
|
||||||
{
|
{
|
||||||
IdTable& mModel;
|
IdTable& mModel;
|
||||||
std::string mId;
|
std::string mId;
|
||||||
RecordBase *mOld;
|
std::unique_ptr<RecordBase> mOld;
|
||||||
|
|
||||||
// not implemented
|
// not implemented
|
||||||
RevertCommand (const RevertCommand&);
|
RevertCommand (const RevertCommand&);
|
||||||
|
|
@ -224,7 +224,7 @@ namespace CSMWorld
|
||||||
{
|
{
|
||||||
IdTable& mModel;
|
IdTable& mModel;
|
||||||
std::string mId;
|
std::string mId;
|
||||||
RecordBase *mOld;
|
std::unique_ptr<RecordBase> mOld;
|
||||||
UniversalId::Type mType;
|
UniversalId::Type mType;
|
||||||
|
|
||||||
// not implemented
|
// not implemented
|
||||||
|
|
|
||||||
|
|
@ -917,8 +917,8 @@ const CSMWorld::MetaData& CSMWorld::Data::getMetaData() const
|
||||||
|
|
||||||
void CSMWorld::Data::setMetaData (const MetaData& metaData)
|
void CSMWorld::Data::setMetaData (const MetaData& metaData)
|
||||||
{
|
{
|
||||||
Record<MetaData> record (RecordBase::State_ModifiedOnly, nullptr, &metaData);
|
mMetaData.setRecord (0, std::make_unique<Record<MetaData> >(
|
||||||
mMetaData.setRecord (0, record);
|
Record<MetaData>(RecordBase::State_ModifiedOnly, nullptr, &metaData)));
|
||||||
}
|
}
|
||||||
|
|
||||||
QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId& id)
|
QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId& id)
|
||||||
|
|
@ -958,6 +958,25 @@ void CSMWorld::Data::merge()
|
||||||
mGlobals.merge();
|
mGlobals.merge();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CSMWorld::Data::getTotalRecords (const std::vector<boost::filesystem::path>& files)
|
||||||
|
{
|
||||||
|
int records = 0;
|
||||||
|
|
||||||
|
std::unique_ptr<ESM::ESMReader> reader = std::unique_ptr<ESM::ESMReader>(new ESM::ESMReader);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < files.size(); ++i)
|
||||||
|
{
|
||||||
|
if (!boost::filesystem::exists(files[i]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
reader->open(files[i].string());
|
||||||
|
records += reader->getRecordCount();
|
||||||
|
reader->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base, bool project)
|
int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base, bool project)
|
||||||
{
|
{
|
||||||
// Don't delete the Reader yet. Some record types store a reference to the Reader to handle on-demand loading
|
// Don't delete the Reader yet. Some record types store a reference to the Reader to handle on-demand loading
|
||||||
|
|
@ -983,7 +1002,8 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
|
||||||
metaData.mId = "sys::meta";
|
metaData.mId = "sys::meta";
|
||||||
metaData.load (*mReader);
|
metaData.load (*mReader);
|
||||||
|
|
||||||
mMetaData.setRecord (0, Record<MetaData> (RecordBase::State_ModifiedOnly, nullptr, &metaData));
|
mMetaData.setRecord (0, std::make_unique<Record<MetaData> >(
|
||||||
|
Record<MetaData> (RecordBase::State_ModifiedOnly, nullptr, &metaData)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return mReader->getRecordCount();
|
return mReader->getRecordCount();
|
||||||
|
|
@ -1011,10 +1031,10 @@ void CSMWorld::Data::loadFallbackEntries()
|
||||||
ESM::Static newMarker;
|
ESM::Static newMarker;
|
||||||
newMarker.mId = marker.first;
|
newMarker.mId = marker.first;
|
||||||
newMarker.mModel = marker.second;
|
newMarker.mModel = marker.second;
|
||||||
CSMWorld::Record<ESM::Static> record;
|
std::unique_ptr<CSMWorld::Record<ESM::Static> > record(new CSMWorld::Record<ESM::Static>);
|
||||||
record.mBase = newMarker;
|
record->mBase = newMarker;
|
||||||
record.mState = CSMWorld::RecordBase::State_BaseOnly;
|
record->mState = CSMWorld::RecordBase::State_BaseOnly;
|
||||||
mReferenceables.appendRecord (record, CSMWorld::UniversalId::Type_Static);
|
mReferenceables.appendRecord (std::move(record), CSMWorld::UniversalId::Type_Static);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1025,10 +1045,10 @@ void CSMWorld::Data::loadFallbackEntries()
|
||||||
ESM::Door newMarker;
|
ESM::Door newMarker;
|
||||||
newMarker.mId = marker.first;
|
newMarker.mId = marker.first;
|
||||||
newMarker.mModel = marker.second;
|
newMarker.mModel = marker.second;
|
||||||
CSMWorld::Record<ESM::Door> record;
|
std::unique_ptr<CSMWorld::Record<ESM::Door> > record(new CSMWorld::Record<ESM::Door>);
|
||||||
record.mBase = newMarker;
|
record->mBase = newMarker;
|
||||||
record.mState = CSMWorld::RecordBase::State_BaseOnly;
|
record->mState = CSMWorld::RecordBase::State_BaseOnly;
|
||||||
mReferenceables.appendRecord (record, CSMWorld::UniversalId::Type_Door);
|
mReferenceables.appendRecord (std::move(record), CSMWorld::UniversalId::Type_Door);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ namespace CSMWorld
|
||||||
const ESM::Dialogue *mDialogue; // last loaded dialogue
|
const ESM::Dialogue *mDialogue; // last loaded dialogue
|
||||||
bool mBase;
|
bool mBase;
|
||||||
bool mProject;
|
bool mProject;
|
||||||
std::map<std::string, std::map<ESM::RefNum, std::string> > mRefLoadCache;
|
std::map<std::string, std::map<unsigned int, unsigned int> > mRefLoadCache;
|
||||||
int mReaderIndex;
|
int mReaderIndex;
|
||||||
|
|
||||||
bool mFsStrict;
|
bool mFsStrict;
|
||||||
|
|
@ -292,6 +292,8 @@ namespace CSMWorld
|
||||||
void merge();
|
void merge();
|
||||||
///< Merge modified into base.
|
///< Merge modified into base.
|
||||||
|
|
||||||
|
int getTotalRecords (const std::vector<boost::filesystem::path>& files); // for better loading bar
|
||||||
|
|
||||||
int startLoading (const boost::filesystem::path& path, bool base, bool project);
|
int startLoading (const boost::filesystem::path& path, bool base, bool project);
|
||||||
///< Begin merging content of a file into base or modified.
|
///< Begin merging content of a file into base or modified.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -83,9 +83,9 @@ namespace CSMWorld
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Record<ESXRecordT> baseRecord = this->getRecord (index);
|
std::unique_ptr<Record<ESXRecordT> > baseRecord(new Record<ESXRecordT>(this->getRecord(index)));
|
||||||
baseRecord.mState = RecordBase::State_Deleted;
|
baseRecord->mState = RecordBase::State_Deleted;
|
||||||
this->setRecord (index, baseRecord);
|
this->setRecord(index, std::move(baseRecord));
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,30 +96,31 @@ namespace CSMWorld
|
||||||
int IdCollection<ESXRecordT, IdAccessorT>::load (const ESXRecordT& record, bool base,
|
int IdCollection<ESXRecordT, IdAccessorT>::load (const ESXRecordT& record, bool base,
|
||||||
int index)
|
int index)
|
||||||
{
|
{
|
||||||
if (index==-2)
|
if (index==-2) // index unknown
|
||||||
index = this->searchId (IdAccessorT().getId (record));
|
index = this->searchId (IdAccessorT().getId (record));
|
||||||
|
|
||||||
if (index==-1)
|
if (index==-1)
|
||||||
{
|
{
|
||||||
// new record
|
// new record
|
||||||
Record<ESXRecordT> record2;
|
std::unique_ptr<Record<ESXRecordT> > record2(new Record<ESXRecordT>);
|
||||||
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
record2->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
||||||
(base ? record2.mBase : record2.mModified) = record;
|
(base ? record2->mBase : record2->mModified) = record;
|
||||||
|
|
||||||
index = this->getSize();
|
index = this->getSize();
|
||||||
this->appendRecord (record2);
|
this->appendRecord(std::move(record2));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// old record
|
// old record
|
||||||
Record<ESXRecordT> record2 = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
|
std::unique_ptr<Record<ESXRecordT> > record2(
|
||||||
|
new Record<ESXRecordT>(Collection<ESXRecordT, IdAccessorT>::getRecord(index)));
|
||||||
|
|
||||||
if (base)
|
if (base)
|
||||||
record2.mBase = record;
|
record2->mBase = record;
|
||||||
else
|
else
|
||||||
record2.setModified (record);
|
record2->setModified(record);
|
||||||
|
|
||||||
this->setRecord (index, record2);
|
this->setRecord(index, std::move(record2));
|
||||||
}
|
}
|
||||||
|
|
||||||
return index;
|
return index;
|
||||||
|
|
@ -133,7 +134,7 @@ namespace CSMWorld
|
||||||
if (index==-1)
|
if (index==-1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Record<ESXRecordT> record = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
|
const Record<ESXRecordT>& record = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
|
||||||
|
|
||||||
if (record.isDeleted())
|
if (record.isDeleted())
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -144,8 +145,10 @@ namespace CSMWorld
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
record.mState = RecordBase::State_Deleted;
|
std::unique_ptr<Record<ESXRecordT> > record2(
|
||||||
this->setRecord (index, record);
|
new Record<ESXRecordT>(Collection<ESXRecordT, IdAccessorT>::getRecord(index)));
|
||||||
|
record2->mState = RecordBase::State_Deleted;
|
||||||
|
this->setRecord(index, std::move(record2));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -228,23 +228,30 @@ QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column)
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& record, CSMWorld::UniversalId::Type type)
|
void CSMWorld::IdTable::setRecord (const std::string& id,
|
||||||
|
std::unique_ptr<RecordBase> record, CSMWorld::UniversalId::Type type)
|
||||||
{
|
{
|
||||||
int index = mIdCollection->searchId (id);
|
int index = mIdCollection->searchId (id);
|
||||||
|
|
||||||
if (index==-1)
|
if (index==-1)
|
||||||
{
|
{
|
||||||
index = mIdCollection->getAppendIndex (id, type);
|
// For info records, appendRecord may use a different index than the one returned by
|
||||||
|
// getAppendIndex (because of prev/next links). This can result in the display not
|
||||||
|
// updating correctly after an undo
|
||||||
|
//
|
||||||
|
// Use an alternative method to get the correct index. For non-Info records the
|
||||||
|
// record pointer is ignored and internally calls getAppendIndex.
|
||||||
|
int index2 = mIdCollection->getInsertIndex (id, type, record.get());
|
||||||
|
|
||||||
beginInsertRows (QModelIndex(), index, index);
|
beginInsertRows (QModelIndex(), index2, index2);
|
||||||
|
|
||||||
mIdCollection->appendRecord (record, type);
|
mIdCollection->appendRecord (std::move(record), type);
|
||||||
|
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mIdCollection->replace (index, record);
|
mIdCollection->replace (index, std::move(record));
|
||||||
emit dataChanged (CSMWorld::IdTable::index (index, 0),
|
emit dataChanged (CSMWorld::IdTable::index (index, 0),
|
||||||
CSMWorld::IdTable::index (index, mIdCollection->getColumns()-1));
|
CSMWorld::IdTable::index (index, mIdCollection->getColumns()-1));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#define CSM_WOLRD_IDTABLE_H
|
#define CSM_WOLRD_IDTABLE_H
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "idtablebase.hpp"
|
#include "idtablebase.hpp"
|
||||||
#include "universalid.hpp"
|
#include "universalid.hpp"
|
||||||
|
|
@ -67,7 +68,7 @@ namespace CSMWorld
|
||||||
|
|
||||||
QModelIndex getModelIndex (const std::string& id, int column) const override;
|
QModelIndex getModelIndex (const std::string& id, int column) const override;
|
||||||
|
|
||||||
void setRecord (const std::string& id, const RecordBase& record,
|
void setRecord (const std::string& id, std::unique_ptr<RecordBase> record,
|
||||||
UniversalId::Type type = UniversalId::Type_None);
|
UniversalId::Type type = UniversalId::Type_None);
|
||||||
///< Add record or overwrite existing record.
|
///< Add record or overwrite existing record.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,67 @@
|
||||||
|
|
||||||
#include <components/misc/stringops.hpp>
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
template<>
|
||||||
|
void Collection<Info, IdAccessor<Info> >::removeRows (int index, int count)
|
||||||
|
{
|
||||||
|
mRecords.erase(mRecords.begin()+index, mRecords.begin()+index+count);
|
||||||
|
|
||||||
|
// index map is updated in InfoCollection::removeRows()
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void Collection<Info, IdAccessor<Info> >::insertRecord (std::unique_ptr<RecordBase> record,
|
||||||
|
int index, UniversalId::Type type)
|
||||||
|
{
|
||||||
|
int size = static_cast<int>(mRecords.size());
|
||||||
|
if (index < 0 || index > size)
|
||||||
|
throw std::runtime_error("index out of range");
|
||||||
|
|
||||||
|
std::unique_ptr<Record<Info> > record2(static_cast<Record<Info>*>(record.release()));
|
||||||
|
|
||||||
|
if (index == size)
|
||||||
|
mRecords.push_back(std::move(record2));
|
||||||
|
else
|
||||||
|
mRecords.insert(mRecords.begin()+index, std::move(record2));
|
||||||
|
|
||||||
|
// index map is updated in InfoCollection::insertRecord()
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
bool Collection<Info, IdAccessor<Info> >::reorderRowsImp (int baseIndex,
|
||||||
|
const std::vector<int>& newOrder)
|
||||||
|
{
|
||||||
|
if (!newOrder.empty())
|
||||||
|
{
|
||||||
|
int size = static_cast<int>(newOrder.size());
|
||||||
|
|
||||||
|
// check that all indices are present
|
||||||
|
std::vector<int> test(newOrder);
|
||||||
|
std::sort(test.begin(), test.end());
|
||||||
|
if (*test.begin() != 0 || *--test.end() != size-1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// reorder records
|
||||||
|
std::vector<std::unique_ptr<Record<Info> > > buffer(size);
|
||||||
|
|
||||||
|
// FIXME: BUG: undo does not remove modified flag
|
||||||
|
for (int i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
buffer[newOrder[i]] = std::move(mRecords[baseIndex+i]);
|
||||||
|
buffer[newOrder[i]]->setModified(buffer[newOrder[i]]->get());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::move(buffer.begin(), buffer.end(), mRecords.begin()+baseIndex);
|
||||||
|
|
||||||
|
// index map is updated in InfoCollection::reorderRows()
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CSMWorld::InfoCollection::load (const Info& record, bool base)
|
void CSMWorld::InfoCollection::load (const Info& record, bool base)
|
||||||
{
|
{
|
||||||
int index = searchId (record.mId);
|
int index = searchId (record.mId);
|
||||||
|
|
@ -15,74 +76,96 @@ void CSMWorld::InfoCollection::load (const Info& record, bool base)
|
||||||
if (index==-1)
|
if (index==-1)
|
||||||
{
|
{
|
||||||
// new record
|
// new record
|
||||||
Record<Info> record2;
|
std::unique_ptr<Record<Info> > record2(new Record<Info>);
|
||||||
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
record2->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
||||||
(base ? record2.mBase : record2.mModified) = record;
|
(base ? record2->mBase : record2->mModified) = record;
|
||||||
|
|
||||||
std::string topic = Misc::StringUtils::lowerCase (record2.get().mTopicId);
|
appendRecord(std::move(record2));
|
||||||
|
|
||||||
if (!record2.get().mPrev.empty())
|
|
||||||
{
|
|
||||||
index = getInfoIndex (record2.get().mPrev, topic);
|
|
||||||
|
|
||||||
if (index!=-1)
|
|
||||||
++index;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index==-1 && !record2.get().mNext.empty())
|
|
||||||
{
|
|
||||||
index = getInfoIndex (record2.get().mNext, topic);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index==-1)
|
|
||||||
{
|
|
||||||
Range range = getTopicRange (topic);
|
|
||||||
|
|
||||||
index = std::distance (getRecords().begin(), range.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
insertRecord (record2, index);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// old record
|
// old record
|
||||||
Record<Info> record2 = getRecord (index);
|
std::unique_ptr<Record<Info> > record2(new Record<Info>(getRecord(index)));
|
||||||
|
|
||||||
if (base)
|
if (base)
|
||||||
record2.mBase = record;
|
record2->mBase = record;
|
||||||
else
|
else
|
||||||
record2.setModified (record);
|
record2->setModified (record);
|
||||||
|
|
||||||
setRecord (index, record2);
|
setRecord (index, std::move(record2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int CSMWorld::InfoCollection::getInfoIndex (const std::string& id, const std::string& topic) const
|
int CSMWorld::InfoCollection::getInfoIndex (const std::string& id, const std::string& topic) const
|
||||||
{
|
{
|
||||||
std::string fullId = Misc::StringUtils::lowerCase (topic) + "#" + id;
|
// find the topic first
|
||||||
|
std::unordered_map<std::string, std::vector<std::pair<std::string, int> > >::const_iterator iter
|
||||||
|
= mInfoIndex.find(Misc::StringUtils::lowerCase(topic));
|
||||||
|
|
||||||
std::pair<RecordConstIterator, RecordConstIterator> range = getTopicRange (topic);
|
if (iter == mInfoIndex.end())
|
||||||
|
return -1;
|
||||||
|
|
||||||
for (; range.first!=range.second; ++range.first)
|
// brute force loop
|
||||||
if (Misc::StringUtils::ciEqual(range.first->get().mId, fullId))
|
for (std::vector<std::pair<std::string, int> >::const_iterator it = iter->second.begin();
|
||||||
return std::distance (getRecords().begin(), range.first);
|
it != iter->second.end(); ++it)
|
||||||
|
{
|
||||||
|
if (Misc::StringUtils::ciEqual(it->first, id))
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CSMWorld::InfoCollection::getAppendIndex (const std::string& id, UniversalId::Type type) const
|
// Calling insertRecord() using index from getInsertIndex() needs to take into account of
|
||||||
|
// prev/next records; an example is deleting a record then undo
|
||||||
|
int CSMWorld::InfoCollection::getInsertIndex (const std::string& id,
|
||||||
|
UniversalId::Type type, RecordBase *record) const
|
||||||
{
|
{
|
||||||
std::string::size_type separator = id.find_last_of ('#');
|
if (record == nullptr)
|
||||||
|
{
|
||||||
|
std::string::size_type separator = id.find_last_of('#');
|
||||||
|
|
||||||
if (separator==std::string::npos)
|
if (separator == std::string::npos)
|
||||||
throw std::runtime_error ("invalid info ID: " + id);
|
throw std::runtime_error("invalid info ID: " + id);
|
||||||
|
|
||||||
std::pair<RecordConstIterator, RecordConstIterator> range = getTopicRange (id.substr (0, separator));
|
std::pair<RecordConstIterator, RecordConstIterator> range = getTopicRange(id.substr(0, separator));
|
||||||
|
|
||||||
if (range.first==range.second)
|
if (range.first == range.second)
|
||||||
return Collection<Info, IdAccessor<Info> >::getAppendIndex (id, type);
|
return Collection<Info, IdAccessor<Info> >::getAppendIndex(id, type);
|
||||||
|
|
||||||
return std::distance (getRecords().begin(), range.second);
|
return std::distance(getRecords().begin(), range.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = -1;
|
||||||
|
|
||||||
|
const Info& info = static_cast<Record<Info>*>(record)->get();
|
||||||
|
std::string topic = info.mTopicId;
|
||||||
|
|
||||||
|
// if the record has a prev, find its index value
|
||||||
|
if (!info.mPrev.empty())
|
||||||
|
{
|
||||||
|
index = getInfoIndex(info.mPrev, topic);
|
||||||
|
|
||||||
|
if (index != -1)
|
||||||
|
++index; // if prev exists, set current index to one above prev
|
||||||
|
}
|
||||||
|
|
||||||
|
// if prev doesn't exist or not found and the record has a next, find its index value
|
||||||
|
if (index == -1 && !info.mNext.empty())
|
||||||
|
{
|
||||||
|
// if next exists, use its index as the current index
|
||||||
|
index = getInfoIndex(info.mNext, topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if next doesn't exist or not found (i.e. neither exist yet) then start a new one
|
||||||
|
if (index == -1)
|
||||||
|
{
|
||||||
|
Range range = getTopicRange(topic); // getTopicRange converts topic to lower case first
|
||||||
|
|
||||||
|
index = std::distance(getRecords().begin(), range.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector<int>& newOrder)
|
bool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector<int>& newOrder)
|
||||||
|
|
@ -99,7 +182,17 @@ bool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector<int
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// reorder
|
// reorder
|
||||||
return reorderRowsImp (baseIndex, newOrder);
|
if (!Collection<Info, IdAccessor<Info> >::reorderRowsImp(baseIndex, newOrder))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// adjust index
|
||||||
|
int size = static_cast<int>(newOrder.size());
|
||||||
|
for (auto& [hash, infos] : mInfoIndex)
|
||||||
|
for (auto& [a, b] : infos)
|
||||||
|
if (b >= baseIndex && b < baseIndex + size)
|
||||||
|
b = newOrder.at(b - baseIndex) + baseIndex;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue)
|
void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue)
|
||||||
|
|
@ -126,9 +219,9 @@ void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ES
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Record<Info> record = getRecord (index);
|
std::unique_ptr<Record<Info> > record(new Record<Info>(getRecord(index)));
|
||||||
record.mState = RecordBase::State_Deleted;
|
record->mState = RecordBase::State_Deleted;
|
||||||
setRecord (index, record);
|
setRecord (index, std::move(record));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -142,73 +235,54 @@ void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ES
|
||||||
CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const std::string& topic)
|
CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const std::string& topic)
|
||||||
const
|
const
|
||||||
{
|
{
|
||||||
std::string topic2 = Misc::StringUtils::lowerCase (topic);
|
std::string lowerTopic = Misc::StringUtils::lowerCase (topic);
|
||||||
|
|
||||||
std::map<std::string, int>::const_iterator iter = getIdMap().lower_bound (topic2);
|
// find the topic
|
||||||
|
std::unordered_map<std::string, std::vector<std::pair<std::string, int> > >::const_iterator iter
|
||||||
|
= mInfoIndex.find(lowerTopic);
|
||||||
|
|
||||||
// Skip invalid records: The beginning of a topic string could be identical to another topic
|
if (iter == mInfoIndex.end())
|
||||||
// string.
|
|
||||||
for (; iter!=getIdMap().end(); ++iter)
|
|
||||||
{
|
|
||||||
std::string testTopicId =
|
|
||||||
Misc::StringUtils::lowerCase (getRecord (iter->second).get().mTopicId);
|
|
||||||
|
|
||||||
if (testTopicId==topic2)
|
|
||||||
break;
|
|
||||||
|
|
||||||
std::size_t size = topic2.size();
|
|
||||||
|
|
||||||
if (testTopicId.size()<size || testTopicId.substr (0, size)!=topic2)
|
|
||||||
return Range (getRecords().end(), getRecords().end());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iter==getIdMap().end())
|
|
||||||
return Range (getRecords().end(), getRecords().end());
|
return Range (getRecords().end(), getRecords().end());
|
||||||
|
|
||||||
RecordConstIterator begin = getRecords().begin()+iter->second;
|
// topic found, find the starting index
|
||||||
|
int low = INT_MAX;
|
||||||
while (begin != getRecords().begin())
|
for (std::vector<std::pair<std::string, int> >::const_iterator it = iter->second.begin();
|
||||||
|
it != iter->second.end(); ++it)
|
||||||
{
|
{
|
||||||
if (!Misc::StringUtils::ciEqual(begin->get().mTopicId, topic2))
|
low = std::min(low, it->second);
|
||||||
{
|
|
||||||
// we've gone one too far, go back
|
|
||||||
++begin;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
--begin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find end
|
RecordConstIterator begin = getRecords().begin() + low;
|
||||||
RecordConstIterator end = begin;
|
|
||||||
|
|
||||||
for (; end!=getRecords().end(); ++end)
|
// Find end (one past the range)
|
||||||
if (!Misc::StringUtils::ciEqual(end->get().mTopicId, topic2))
|
RecordConstIterator end = begin + iter->second.size();
|
||||||
break;
|
if (end != getRecords().end())
|
||||||
|
++end;
|
||||||
|
|
||||||
return Range (begin, end);
|
return Range (begin, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::InfoCollection::removeDialogueInfos(const std::string& dialogueId)
|
void CSMWorld::InfoCollection::removeDialogueInfos(const std::string& dialogueId)
|
||||||
{
|
{
|
||||||
std::string id = Misc::StringUtils::lowerCase(dialogueId);
|
|
||||||
std::vector<int> erasedRecords;
|
std::vector<int> erasedRecords;
|
||||||
|
|
||||||
std::map<std::string, int>::const_iterator current = getIdMap().lower_bound(id);
|
Range range = getTopicRange(dialogueId); // getTopicRange converts dialogueId to lower case first
|
||||||
std::map<std::string, int>::const_iterator end = getIdMap().end();
|
|
||||||
for (; current != end; ++current)
|
for (; range.first != range.second; ++range.first)
|
||||||
{
|
{
|
||||||
Record<Info> record = getRecord(current->second);
|
const Record<Info>& record = **range.first;
|
||||||
|
|
||||||
if (Misc::StringUtils::ciEqual(dialogueId, record.get().mTopicId))
|
if (Misc::StringUtils::ciEqual(dialogueId, record.get().mTopicId))
|
||||||
{
|
{
|
||||||
if (record.mState == RecordBase::State_ModifiedOnly)
|
if (record.mState == RecordBase::State_ModifiedOnly)
|
||||||
{
|
{
|
||||||
erasedRecords.push_back(current->second);
|
erasedRecords.push_back(range.first - getRecords().begin());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
record.mState = RecordBase::State_Deleted;
|
std::unique_ptr<Record<Info> > record2(new Record<Info>(record));
|
||||||
setRecord(current->second, record);
|
record2->mState = RecordBase::State_Deleted;
|
||||||
|
setRecord(range.first - getRecords().begin(), std::move(record2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -223,3 +297,105 @@ void CSMWorld::InfoCollection::removeDialogueInfos(const std::string& dialogueId
|
||||||
erasedRecords.pop_back();
|
erasedRecords.pop_back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: removing a record should adjust prev/next and mark those records as modified
|
||||||
|
// accordingly (also consider undo)
|
||||||
|
void CSMWorld::InfoCollection::removeRows (int index, int count)
|
||||||
|
{
|
||||||
|
Collection<Info, IdAccessor<Info> >::removeRows(index, count); // erase records only
|
||||||
|
|
||||||
|
for (std::unordered_map<std::string, std::vector<std::pair<std::string, int> > >::iterator iter
|
||||||
|
= mInfoIndex.begin(); iter != mInfoIndex.end();)
|
||||||
|
{
|
||||||
|
for (std::vector<std::pair<std::string, int> >::iterator it = iter->second.begin();
|
||||||
|
it != iter->second.end();)
|
||||||
|
{
|
||||||
|
if (it->second >= index)
|
||||||
|
{
|
||||||
|
if (it->second >= index+count)
|
||||||
|
{
|
||||||
|
it->second -= count;
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
iter->second.erase(it);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for an empty vector
|
||||||
|
if (iter->second.empty())
|
||||||
|
mInfoIndex.erase(iter++);
|
||||||
|
else
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::InfoCollection::appendBlankRecord (const std::string& id, UniversalId::Type type)
|
||||||
|
{
|
||||||
|
std::unique_ptr<Record<Info> > record2(new Record<Info>);
|
||||||
|
|
||||||
|
record2->mState = Record<Info>::State_ModifiedOnly;
|
||||||
|
record2->mModified.blank();
|
||||||
|
|
||||||
|
record2->get().mId = id;
|
||||||
|
|
||||||
|
insertRecord(std::move(record2), getInsertIndex(id, type, nullptr), type); // call InfoCollection::insertRecord()
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSMWorld::InfoCollection::searchId (const std::string& id) const
|
||||||
|
{
|
||||||
|
std::string::size_type separator = id.find_last_of('#');
|
||||||
|
|
||||||
|
if (separator == std::string::npos)
|
||||||
|
throw std::runtime_error("invalid info ID: " + id);
|
||||||
|
|
||||||
|
return getInfoIndex(id.substr(separator+1), id.substr(0, separator));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::InfoCollection::appendRecord (std::unique_ptr<RecordBase> record, UniversalId::Type type)
|
||||||
|
{
|
||||||
|
int index = getInsertIndex(static_cast<Record<Info>*>(record.get())->get().mId, type, record.get());
|
||||||
|
|
||||||
|
insertRecord(std::move(record), index, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::InfoCollection::insertRecord (std::unique_ptr<RecordBase> record, int index,
|
||||||
|
UniversalId::Type type)
|
||||||
|
{
|
||||||
|
int size = static_cast<int>(getRecords().size());
|
||||||
|
|
||||||
|
std::string id = static_cast<Record<Info>*>(record.get())->get().mId;
|
||||||
|
std::string::size_type separator = id.find_last_of('#');
|
||||||
|
|
||||||
|
if (separator == std::string::npos)
|
||||||
|
throw std::runtime_error("invalid info ID: " + id);
|
||||||
|
|
||||||
|
Collection<Info, IdAccessor<Info> >::insertRecord(std::move(record), index, type); // add records only
|
||||||
|
|
||||||
|
// adjust index
|
||||||
|
if (index < size-1)
|
||||||
|
{
|
||||||
|
for (std::unordered_map<std::string, std::vector<std::pair<std::string, int> > >::iterator iter
|
||||||
|
= mInfoIndex.begin(); iter != mInfoIndex.end(); ++iter)
|
||||||
|
{
|
||||||
|
for (std::vector<std::pair<std::string, int> >::iterator it = iter->second.begin();
|
||||||
|
it != iter->second.end(); ++it)
|
||||||
|
{
|
||||||
|
if (it->second >= index)
|
||||||
|
++(it->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get iterator for existing topic or a new topic
|
||||||
|
std::string lowerId = Misc::StringUtils::lowerCase(id);
|
||||||
|
std::pair<std::unordered_map<std::string, std::vector<std::pair<std::string, int> > >::iterator, bool> res
|
||||||
|
= mInfoIndex.insert(
|
||||||
|
std::make_pair(lowerId.substr(0, separator),
|
||||||
|
std::vector<std::pair<std::string, int> >())); // empty vector
|
||||||
|
|
||||||
|
// insert info and index
|
||||||
|
res.first->second.push_back(std::make_pair(lowerId.substr(separator+1), index));
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,27 +11,52 @@ namespace ESM
|
||||||
|
|
||||||
namespace CSMWorld
|
namespace CSMWorld
|
||||||
{
|
{
|
||||||
|
template<>
|
||||||
|
void Collection<Info, IdAccessor<Info> >::removeRows (int index, int count);
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void Collection<Info, IdAccessor<Info> >::insertRecord (std::unique_ptr<RecordBase> record,
|
||||||
|
int index, UniversalId::Type type);
|
||||||
|
|
||||||
|
template<>
|
||||||
|
bool Collection<Info, IdAccessor<Info> >::reorderRowsImp (int baseIndex,
|
||||||
|
const std::vector<int>& newOrder);
|
||||||
|
|
||||||
class InfoCollection : public Collection<Info, IdAccessor<Info> >
|
class InfoCollection : public Collection<Info, IdAccessor<Info> >
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
typedef std::vector<Record<Info> >::const_iterator RecordConstIterator;
|
typedef std::vector<std::unique_ptr<Record<Info> > >::const_iterator RecordConstIterator;
|
||||||
typedef std::pair<RecordConstIterator, RecordConstIterator> Range;
|
typedef std::pair<RecordConstIterator, RecordConstIterator> Range;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
// The general strategy is to keep the records in Collection kept in order (within
|
||||||
|
// a topic group) while the index lookup maps are not ordered. It is assumed that
|
||||||
|
// each topic has a small number of infos, which allows the use of vectors for
|
||||||
|
// iterating through them without too much penalty.
|
||||||
|
//
|
||||||
|
// NOTE: topic string as well as id string are stored in lower case.
|
||||||
|
std::unordered_map<std::string, std::vector<std::pair<std::string, int> > > mInfoIndex;
|
||||||
|
|
||||||
void load (const Info& record, bool base);
|
void load (const Info& record, bool base);
|
||||||
|
|
||||||
int getInfoIndex (const std::string& id, const std::string& topic) const;
|
int getInfoIndex (const std::string& id, const std::string& topic) const;
|
||||||
///< Return index for record \a id or -1 (if not present; deleted records are considered)
|
///< Return index for record \a id or -1 (if not present; deleted records are considered)
|
||||||
///
|
///
|
||||||
/// \param id info ID without topic prefix
|
/// \param id info ID without topic prefix
|
||||||
|
//
|
||||||
|
/// \attention id and topic are assumed to be in lower case
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
int getAppendIndex (const std::string& id,
|
int getInsertIndex (const std::string& id,
|
||||||
UniversalId::Type type = UniversalId::Type_None) const override;
|
UniversalId::Type type = UniversalId::Type_None,
|
||||||
|
RecordBase *record = nullptr) const override;
|
||||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||||
|
///
|
||||||
|
/// Works like getAppendIndex unless an overloaded method uses the record pointer
|
||||||
|
/// to get additional info about the record that results in an alternative index.
|
||||||
|
|
||||||
bool reorderRows (int baseIndex, const std::vector<int>& newOrder) override;
|
bool reorderRows (int baseIndex, const std::vector<int>& newOrder) override;
|
||||||
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
|
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
|
||||||
|
|
@ -46,6 +71,20 @@ namespace CSMWorld
|
||||||
/// the given topic.
|
/// the given topic.
|
||||||
|
|
||||||
void removeDialogueInfos(const std::string& dialogueId);
|
void removeDialogueInfos(const std::string& dialogueId);
|
||||||
|
|
||||||
|
void removeRows (int index, int count);
|
||||||
|
|
||||||
|
void appendBlankRecord (const std::string& id,
|
||||||
|
UniversalId::Type type = UniversalId::Type_None);
|
||||||
|
|
||||||
|
int searchId (const std::string& id) const;
|
||||||
|
|
||||||
|
void appendRecord (std::unique_ptr<RecordBase> record,
|
||||||
|
UniversalId::Type type = UniversalId::Type_None);
|
||||||
|
|
||||||
|
void insertRecord (std::unique_ptr<RecordBase> record,
|
||||||
|
int index,
|
||||||
|
UniversalId::Type type = UniversalId::Type_None);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,23 +90,23 @@ namespace CSMWorld
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
void NestedIdCollection<ESXRecordT, IdAccessorT>::addNestedRow(int row, int column, int position)
|
void NestedIdCollection<ESXRecordT, IdAccessorT>::addNestedRow(int row, int column, int position)
|
||||||
{
|
{
|
||||||
Record<ESXRecordT> record;
|
std::unique_ptr<Record<ESXRecordT> > record(new Record<ESXRecordT>);
|
||||||
record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
|
record->assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
|
||||||
|
|
||||||
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).addRow(record, position);
|
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).addRow(*record, position);
|
||||||
|
|
||||||
Collection<ESXRecordT, IdAccessorT>::setRecord(row, record);
|
Collection<ESXRecordT, IdAccessorT>::setRecord(row, std::move(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
void NestedIdCollection<ESXRecordT, IdAccessorT>::removeNestedRows(int row, int column, int subRow)
|
void NestedIdCollection<ESXRecordT, IdAccessorT>::removeNestedRows(int row, int column, int subRow)
|
||||||
{
|
{
|
||||||
Record<ESXRecordT> record;
|
std::unique_ptr<Record<ESXRecordT> > record(new Record<ESXRecordT>);
|
||||||
record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
|
record->assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
|
||||||
|
|
||||||
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).removeRow(record, subRow);
|
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).removeRow(*record, subRow);
|
||||||
|
|
||||||
Collection<ESXRecordT, IdAccessorT>::setRecord(row, record);
|
Collection<ESXRecordT, IdAccessorT>::setRecord(row, std::move(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
|
|
@ -121,13 +121,13 @@ namespace CSMWorld
|
||||||
void NestedIdCollection<ESXRecordT, IdAccessorT>::setNestedData(int row,
|
void NestedIdCollection<ESXRecordT, IdAccessorT>::setNestedData(int row,
|
||||||
int column, const QVariant& data, int subRow, int subColumn)
|
int column, const QVariant& data, int subRow, int subColumn)
|
||||||
{
|
{
|
||||||
Record<ESXRecordT> record;
|
std::unique_ptr<Record<ESXRecordT> > record(new Record<ESXRecordT>);
|
||||||
record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
|
record->assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
|
||||||
|
|
||||||
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).setData(
|
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).setData(
|
||||||
record, data, subRow, subColumn);
|
*record, data, subRow, subColumn);
|
||||||
|
|
||||||
Collection<ESXRecordT, IdAccessorT>::setRecord(row, record);
|
Collection<ESXRecordT, IdAccessorT>::setRecord(row, std::move(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
|
|
@ -142,13 +142,13 @@ namespace CSMWorld
|
||||||
void NestedIdCollection<ESXRecordT, IdAccessorT>::setNestedTable(int row,
|
void NestedIdCollection<ESXRecordT, IdAccessorT>::setNestedTable(int row,
|
||||||
int column, const CSMWorld::NestedTableWrapperBase& nestedTable)
|
int column, const CSMWorld::NestedTableWrapperBase& nestedTable)
|
||||||
{
|
{
|
||||||
Record<ESXRecordT> record;
|
std::unique_ptr<Record<ESXRecordT> > record(new Record<ESXRecordT>);
|
||||||
record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
|
record->assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
|
||||||
|
|
||||||
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).setTable(
|
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).setTable(
|
||||||
record, nestedTable);
|
*record, nestedTable);
|
||||||
|
|
||||||
Collection<ESXRecordT, IdAccessorT>::setRecord(row, record);
|
Collection<ESXRecordT, IdAccessorT>::setRecord(row, std::move(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
|
|
|
||||||
|
|
@ -35,22 +35,22 @@ namespace CSMWorld
|
||||||
|
|
||||||
void NestedInfoCollection::addNestedRow(int row, int column, int position)
|
void NestedInfoCollection::addNestedRow(int row, int column, int position)
|
||||||
{
|
{
|
||||||
Record<Info> record;
|
std::unique_ptr<Record<Info> > record(new Record<Info>);
|
||||||
record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
|
record->assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
|
||||||
|
|
||||||
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).addRow(record, position);
|
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).addRow(*record, position);
|
||||||
|
|
||||||
Collection<Info, IdAccessor<Info> >::setRecord(row, record);
|
Collection<Info, IdAccessor<Info> >::setRecord(row, std::move(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
void NestedInfoCollection::removeNestedRows(int row, int column, int subRow)
|
void NestedInfoCollection::removeNestedRows(int row, int column, int subRow)
|
||||||
{
|
{
|
||||||
Record<Info> record;
|
std::unique_ptr<Record<Info> > record(new Record<Info>);
|
||||||
record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
|
record->assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
|
||||||
|
|
||||||
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).removeRow(record, subRow);
|
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).removeRow(*record, subRow);
|
||||||
|
|
||||||
Collection<Info, IdAccessor<Info> >::setRecord(row, record);
|
Collection<Info, IdAccessor<Info> >::setRecord(row, std::move(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant NestedInfoCollection::getNestedData (int row,
|
QVariant NestedInfoCollection::getNestedData (int row,
|
||||||
|
|
@ -63,13 +63,13 @@ namespace CSMWorld
|
||||||
void NestedInfoCollection::setNestedData(int row,
|
void NestedInfoCollection::setNestedData(int row,
|
||||||
int column, const QVariant& data, int subRow, int subColumn)
|
int column, const QVariant& data, int subRow, int subColumn)
|
||||||
{
|
{
|
||||||
Record<Info> record;
|
std::unique_ptr<Record<Info> > record(new Record<Info>);
|
||||||
record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
|
record->assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
|
||||||
|
|
||||||
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).setData(
|
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).setData(
|
||||||
record, data, subRow, subColumn);
|
*record, data, subRow, subColumn);
|
||||||
|
|
||||||
Collection<Info, IdAccessor<Info> >::setRecord(row, record);
|
Collection<Info, IdAccessor<Info> >::setRecord(row, std::move(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMWorld::NestedTableWrapperBase* NestedInfoCollection::nestedTable(int row,
|
CSMWorld::NestedTableWrapperBase* NestedInfoCollection::nestedTable(int row,
|
||||||
|
|
@ -82,13 +82,13 @@ namespace CSMWorld
|
||||||
void NestedInfoCollection::setNestedTable(int row,
|
void NestedInfoCollection::setNestedTable(int row,
|
||||||
int column, const CSMWorld::NestedTableWrapperBase& nestedTable)
|
int column, const CSMWorld::NestedTableWrapperBase& nestedTable)
|
||||||
{
|
{
|
||||||
Record<Info> record;
|
std::unique_ptr<Record<Info> > record(new Record<Info>);
|
||||||
record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
|
record->assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
|
||||||
|
|
||||||
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).setTable(
|
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).setTable(
|
||||||
record, nestedTable);
|
*record, nestedTable);
|
||||||
|
|
||||||
Collection<Info, IdAccessor<Info> >::setRecord(row, record);
|
Collection<Info, IdAccessor<Info> >::setRecord(row, std::move(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
int NestedInfoCollection::getNestedRowsCount(int row, int column) const
|
int NestedInfoCollection::getNestedRowsCount(int row, int column) const
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef CSM_WOLRD_RECORD_H
|
#ifndef CSM_WOLRD_RECORD_H
|
||||||
#define CSM_WOLRD_RECORD_H
|
#define CSM_WOLRD_RECORD_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace CSMWorld
|
namespace CSMWorld
|
||||||
|
|
@ -20,9 +21,9 @@ namespace CSMWorld
|
||||||
|
|
||||||
virtual ~RecordBase();
|
virtual ~RecordBase();
|
||||||
|
|
||||||
virtual RecordBase *clone() const = 0;
|
virtual std::unique_ptr<RecordBase> clone() const = 0;
|
||||||
|
|
||||||
virtual RecordBase *modifiedCopy() const = 0;
|
virtual std::unique_ptr<RecordBase> modifiedCopy() const = 0;
|
||||||
|
|
||||||
virtual void assign (const RecordBase& record) = 0;
|
virtual void assign (const RecordBase& record) = 0;
|
||||||
///< Will throw an exception if the types don't match.
|
///< Will throw an exception if the types don't match.
|
||||||
|
|
@ -45,9 +46,9 @@ namespace CSMWorld
|
||||||
Record(State state,
|
Record(State state,
|
||||||
const ESXRecordT *base = 0, const ESXRecordT *modified = 0);
|
const ESXRecordT *base = 0, const ESXRecordT *modified = 0);
|
||||||
|
|
||||||
RecordBase *clone() const override;
|
std::unique_ptr<RecordBase> clone() const;
|
||||||
|
|
||||||
RecordBase *modifiedCopy() const override;
|
std::unique_ptr<RecordBase> modifiedCopy() const;
|
||||||
|
|
||||||
void assign (const RecordBase& record) override;
|
void assign (const RecordBase& record) override;
|
||||||
|
|
||||||
|
|
@ -85,15 +86,16 @@ namespace CSMWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ESXRecordT>
|
template <typename ESXRecordT>
|
||||||
RecordBase *Record<ESXRecordT>::modifiedCopy() const
|
std::unique_ptr<RecordBase> Record<ESXRecordT>::modifiedCopy() const
|
||||||
{
|
{
|
||||||
return new Record<ESXRecordT> (State_ModifiedOnly, nullptr, &(this->get()));
|
return std::make_unique<Record<ESXRecordT> >(
|
||||||
|
Record<ESXRecordT>(State_ModifiedOnly, nullptr, &(this->get())));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ESXRecordT>
|
template <typename ESXRecordT>
|
||||||
RecordBase *Record<ESXRecordT>::clone() const
|
std::unique_ptr<RecordBase> Record<ESXRecordT>::clone() const
|
||||||
{
|
{
|
||||||
return new Record<ESXRecordT> (*this);
|
return std::make_unique<Record<ESXRecordT> >(Record<ESXRecordT>(*this));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ESXRecordT>
|
template <typename ESXRecordT>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include "cellcoordinates.hpp"
|
#include "cellcoordinates.hpp"
|
||||||
|
|
||||||
CSMWorld::CellRef::CellRef() : mNew (true)
|
CSMWorld::CellRef::CellRef() : mNew (true), mIdNum(0)
|
||||||
{
|
{
|
||||||
mRefNum.mIndex = 0;
|
mRefNum.mIndex = 0;
|
||||||
mRefNum.mContentFile = 0;
|
mRefNum.mContentFile = 0;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ namespace CSMWorld
|
||||||
std::string mCell;
|
std::string mCell;
|
||||||
std::string mOriginalCell;
|
std::string mOriginalCell;
|
||||||
bool mNew; // new reference, not counted yet, ref num not assigned yet
|
bool mNew; // new reference, not counted yet, ref num not assigned yet
|
||||||
|
unsigned int mIdNum;
|
||||||
|
|
||||||
CellRef();
|
CellRef();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#include "refcollection.hpp"
|
#include "refcollection.hpp"
|
||||||
|
|
||||||
|
#include <charconv>
|
||||||
|
|
||||||
#include <components/esm/loadcell.hpp>
|
#include <components/esm/loadcell.hpp>
|
||||||
|
|
||||||
#include "ref.hpp"
|
#include "ref.hpp"
|
||||||
|
|
@ -7,8 +9,37 @@
|
||||||
#include "universalid.hpp"
|
#include "universalid.hpp"
|
||||||
#include "record.hpp"
|
#include "record.hpp"
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
template<>
|
||||||
|
void Collection<CellRef, IdAccessor<CellRef> >::removeRows (int index, int count)
|
||||||
|
{
|
||||||
|
mRecords.erase(mRecords.begin()+index, mRecords.begin()+index+count);
|
||||||
|
|
||||||
|
// index map is updated in RefCollection::removeRows()
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void Collection<CellRef, IdAccessor<CellRef> >::insertRecord (std::unique_ptr<RecordBase> record, int index,
|
||||||
|
UniversalId::Type type)
|
||||||
|
{
|
||||||
|
int size = static_cast<int>(mRecords.size());
|
||||||
|
if (index < 0 || index > size)
|
||||||
|
throw std::runtime_error("index out of range");
|
||||||
|
|
||||||
|
std::unique_ptr<Record<CellRef> > record2(static_cast<Record<CellRef>*>(record.release()));
|
||||||
|
|
||||||
|
if (index == size)
|
||||||
|
mRecords.push_back(std::move(record2));
|
||||||
|
else
|
||||||
|
mRecords.insert(mRecords.begin()+index, std::move(record2));
|
||||||
|
|
||||||
|
// index map is updated in RefCollection::insertRecord()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base,
|
void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base,
|
||||||
std::map<ESM::RefNum, std::string>& cache, CSMDoc::Messages& messages)
|
std::map<unsigned int, unsigned int>& cache, CSMDoc::Messages& messages)
|
||||||
{
|
{
|
||||||
Record<Cell> cell = mCells.getRecord (cellIndex);
|
Record<Cell> cell = mCells.getRecord (cellIndex);
|
||||||
|
|
||||||
|
|
@ -35,7 +66,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
|
||||||
ref.mCell = "#" + std::to_string(index.first) + " " + std::to_string(index.second);
|
ref.mCell = "#" + std::to_string(index.first) + " " + std::to_string(index.second);
|
||||||
|
|
||||||
// Handle non-base moved references
|
// Handle non-base moved references
|
||||||
if (!base && !isMoved)
|
if (!base && isMoved)
|
||||||
{
|
{
|
||||||
// Moved references must have a link back to their original cell
|
// Moved references must have a link back to their original cell
|
||||||
// See discussion: https://forum.openmw.org/viewtopic.php?f=6&t=577&start=30
|
// See discussion: https://forum.openmw.org/viewtopic.php?f=6&t=577&start=30
|
||||||
|
|
@ -60,16 +91,12 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
|
||||||
else
|
else
|
||||||
ref.mCell = cell2.mId;
|
ref.mCell = cell2.mId;
|
||||||
|
|
||||||
// ignore content file number
|
unsigned int refNum = (ref.mRefNum.mIndex & 0x00ffffff) |
|
||||||
std::map<ESM::RefNum, std::string>::iterator iter = cache.begin();
|
(ref.mRefNum.hasContentFile() ? ref.mRefNum.mContentFile : 0xff) << 24;
|
||||||
unsigned int thisIndex = ref.mRefNum.mIndex & 0x00ffffff;
|
|
||||||
if (ref.mRefNum.mContentFile != -1 && !base) ref.mRefNum.mContentFile = ref.mRefNum.mIndex >> 24;
|
|
||||||
|
|
||||||
for (; iter != cache.end(); ++iter)
|
std::map<unsigned int, unsigned int>::iterator iter = cache.find(refNum);
|
||||||
{
|
|
||||||
if (thisIndex == iter->first.mIndex)
|
if (ref.mRefNum.mContentFile != -1 && !base) ref.mRefNum.mContentFile = ref.mRefNum.mIndex >> 24;
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDeleted)
|
if (isDeleted)
|
||||||
{
|
{
|
||||||
|
|
@ -78,13 +105,15 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
|
||||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Cell,
|
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Cell,
|
||||||
mCells.getId (cellIndex));
|
mCells.getId (cellIndex));
|
||||||
|
|
||||||
messages.add (id, "Attempt to delete a non-existent reference");
|
messages.add (id, "Attempt to delete a non-existent reference - RefNum index "
|
||||||
|
+ std::to_string(ref.mRefNum.mIndex) + ", refID " + ref.mRefID + ", content file index "
|
||||||
|
+ std::to_string(ref.mRefNum.mContentFile),
|
||||||
|
/*hint*/"",
|
||||||
|
CSMDoc::Message::Severity_Warning);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int index = getIndex (iter->second);
|
int index = getIntIndex (iter->second);
|
||||||
|
|
||||||
Record<CellRef> record = getRecord (index);
|
|
||||||
|
|
||||||
if (base)
|
if (base)
|
||||||
{
|
{
|
||||||
|
|
@ -93,8 +122,9 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
record.mState = RecordBase::State_Deleted;
|
std::unique_ptr<Record<CellRef> > record(new Record<CellRef>(getRecord(index)));
|
||||||
setRecord (index, record);
|
record->mState = RecordBase::State_Deleted;
|
||||||
|
setRecord(index, std::move(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -103,30 +133,47 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
|
||||||
if (iter==cache.end())
|
if (iter==cache.end())
|
||||||
{
|
{
|
||||||
// new reference
|
// new reference
|
||||||
|
ref.mIdNum = mNextId; // FIXME: fragile
|
||||||
ref.mId = getNewId();
|
ref.mId = getNewId();
|
||||||
|
|
||||||
Record<CellRef> record;
|
cache.emplace(refNum, ref.mIdNum);
|
||||||
record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
|
||||||
const ESM::RefNum refNum = ref.mRefNum;
|
|
||||||
std::string refId = ref.mId;
|
|
||||||
(base ? record.mBase : record.mModified) = std::move(ref);
|
|
||||||
|
|
||||||
appendRecord (record);
|
std::unique_ptr<Record<CellRef> > record(new Record<CellRef>);
|
||||||
|
record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
||||||
|
(base ? record->mBase : record->mModified) = std::move(ref);
|
||||||
|
|
||||||
cache.emplace(refNum, std::move(refId));
|
appendRecord(std::move(record));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// old reference -> merge
|
// old reference -> merge
|
||||||
ref.mId = iter->second;
|
int index = getIntIndex(iter->second);
|
||||||
|
#if 0
|
||||||
|
// ref.mRefNum.mIndex : the key
|
||||||
|
// iter->second : previously cached idNum for the key
|
||||||
|
// index : position of the record for that idNum
|
||||||
|
// getRecord(index).get() : record in the index position
|
||||||
|
assert(iter->second != getRecord(index).get().mIdNum); // sanity check
|
||||||
|
|
||||||
int index = getIndex (ref.mId);
|
// check if the plugin used the same RefNum index for a different record
|
||||||
|
if (ref.mRefID != getRecord(index).get().mRefID)
|
||||||
|
{
|
||||||
|
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Cell, mCells.getId(cellIndex));
|
||||||
|
messages.add(id,
|
||||||
|
"RefNum renamed from RefID \"" + getRecord(index).get().mRefID + "\" to \""
|
||||||
|
+ ref.mRefID + "\" (RefNum index " + std::to_string(ref.mRefNum.mIndex) + ")",
|
||||||
|
/*hint*/"",
|
||||||
|
CSMDoc::Message::Severity_Info);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
ref.mId = getRecord(index).get().mId;
|
||||||
|
ref.mIdNum = extractIdNum(ref.mId);
|
||||||
|
|
||||||
Record<CellRef> record = getRecord (index);
|
std::unique_ptr<Record<CellRef> > record(new Record<CellRef>(getRecord(index)));
|
||||||
record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_Modified;
|
record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_Modified;
|
||||||
(base ? record.mBase : record.mModified) = std::move(ref);
|
(base ? record->mBase : record->mModified) = std::move(ref);
|
||||||
|
|
||||||
setRecord (index, record);
|
setRecord(index, std::move(record));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -135,3 +182,122 @@ std::string CSMWorld::RefCollection::getNewId()
|
||||||
{
|
{
|
||||||
return "ref#" + std::to_string(mNextId++);
|
return "ref#" + std::to_string(mNextId++);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int CSMWorld::RefCollection::extractIdNum(std::string_view id) const
|
||||||
|
{
|
||||||
|
const auto separator = id.find_last_of('#');
|
||||||
|
|
||||||
|
if (separator == std::string_view::npos)
|
||||||
|
throw std::runtime_error("invalid ref ID: " + std::string(id));
|
||||||
|
|
||||||
|
const std::string_view number = id.substr(separator + 1);
|
||||||
|
unsigned int result;
|
||||||
|
if (std::from_chars(number.data(), number.data() + number.size(), result).ec != std::errc())
|
||||||
|
throw std::runtime_error("invalid ref ID number: " + std::string(number));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSMWorld::RefCollection::getIntIndex (unsigned int id) const
|
||||||
|
{
|
||||||
|
int index = searchId(id);
|
||||||
|
|
||||||
|
if (index == -1)
|
||||||
|
throw std::runtime_error("invalid RefNum: " + std::to_string(id));
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSMWorld::RefCollection::searchId (unsigned int id) const
|
||||||
|
{
|
||||||
|
std::map<unsigned int, int>::const_iterator iter = mRefIndex.find(id);
|
||||||
|
|
||||||
|
if (iter == mRefIndex.end())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::RefCollection::removeRows (int index, int count)
|
||||||
|
{
|
||||||
|
Collection<CellRef, IdAccessor<CellRef> >::removeRows(index, count); // erase records only
|
||||||
|
|
||||||
|
std::map<unsigned int, int>::iterator iter = mRefIndex.begin();
|
||||||
|
while (iter != mRefIndex.end())
|
||||||
|
{
|
||||||
|
if (iter->second>=index)
|
||||||
|
{
|
||||||
|
if (iter->second >= index+count)
|
||||||
|
{
|
||||||
|
iter->second -= count;
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mRefIndex.erase(iter++);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::RefCollection::appendBlankRecord (const std::string& id, UniversalId::Type type)
|
||||||
|
{
|
||||||
|
std::unique_ptr<Record<CellRef> > record(new Record<CellRef>);
|
||||||
|
|
||||||
|
record->mState = Record<CellRef>::State_ModifiedOnly;
|
||||||
|
record->mModified.blank();
|
||||||
|
|
||||||
|
record->get().mId = id;
|
||||||
|
record->get().mIdNum = extractIdNum(id);
|
||||||
|
|
||||||
|
Collection<CellRef, IdAccessor<CellRef> >::appendRecord(std::move(record));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::RefCollection::cloneRecord (const std::string& origin,
|
||||||
|
const std::string& destination,
|
||||||
|
const UniversalId::Type type)
|
||||||
|
{
|
||||||
|
std::unique_ptr<Record<CellRef> > copy(new Record<CellRef>);
|
||||||
|
|
||||||
|
copy->mModified = getRecord(origin).get();
|
||||||
|
copy->mState = RecordBase::State_ModifiedOnly;
|
||||||
|
|
||||||
|
copy->get().mId = destination;
|
||||||
|
copy->get().mIdNum = extractIdNum(destination);
|
||||||
|
|
||||||
|
insertRecord(std::move(copy), getAppendIndex(destination, type)); // call RefCollection::insertRecord()
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSMWorld::RefCollection::searchId (const std::string& id) const
|
||||||
|
{
|
||||||
|
return searchId(extractIdNum(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::RefCollection::appendRecord (std::unique_ptr<RecordBase> record, UniversalId::Type type)
|
||||||
|
{
|
||||||
|
int index = getAppendIndex(/*id*/"", type); // for CellRef records id is ignored
|
||||||
|
|
||||||
|
mRefIndex.insert(std::make_pair(static_cast<Record<CellRef>*>(record.get())->get().mIdNum, index));
|
||||||
|
|
||||||
|
Collection<CellRef, IdAccessor<CellRef> >::insertRecord(std::move(record), index, type); // add records only
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::RefCollection::insertRecord (std::unique_ptr<RecordBase> record, int index,
|
||||||
|
UniversalId::Type type)
|
||||||
|
{
|
||||||
|
int size = getAppendIndex(/*id*/"", type); // for CellRef records id is ignored
|
||||||
|
unsigned int idNum = static_cast<Record<CellRef>*>(record.get())->get().mIdNum;
|
||||||
|
|
||||||
|
Collection<CellRef, IdAccessor<CellRef> >::insertRecord(std::move(record), index, type); // add records only
|
||||||
|
|
||||||
|
if (index < size-1)
|
||||||
|
{
|
||||||
|
for (std::map<unsigned int, int>::iterator iter(mRefIndex.begin()); iter != mRefIndex.end(); ++iter)
|
||||||
|
{
|
||||||
|
if (iter->second >= index)
|
||||||
|
++(iter->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mRefIndex.insert(std::make_pair(idNum, index));
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,27 @@ namespace CSMWorld
|
||||||
struct Cell;
|
struct Cell;
|
||||||
class UniversalId;
|
class UniversalId;
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void Collection<CellRef, IdAccessor<CellRef> >::removeRows (int index, int count);
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void Collection<CellRef, IdAccessor<CellRef> >::insertRecord (std::unique_ptr<RecordBase> record, int index,
|
||||||
|
UniversalId::Type type);
|
||||||
|
|
||||||
/// \brief References in cells
|
/// \brief References in cells
|
||||||
class RefCollection : public Collection<CellRef>
|
class RefCollection : public Collection<CellRef>
|
||||||
{
|
{
|
||||||
Collection<Cell>& mCells;
|
Collection<Cell>& mCells;
|
||||||
|
std::map<unsigned int, int> mRefIndex;
|
||||||
|
|
||||||
int mNextId;
|
int mNextId;
|
||||||
|
|
||||||
|
unsigned int extractIdNum(std::string_view id) const;
|
||||||
|
|
||||||
|
int getIntIndex (unsigned int id) const;
|
||||||
|
|
||||||
|
int searchId (unsigned int id) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// MSVC needs the constructor for a class inheriting a template to be defined in header
|
// MSVC needs the constructor for a class inheriting a template to be defined in header
|
||||||
RefCollection (Collection<Cell>& cells)
|
RefCollection (Collection<Cell>& cells)
|
||||||
|
|
@ -27,10 +42,28 @@ namespace CSMWorld
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void load (ESM::ESMReader& reader, int cellIndex, bool base,
|
void load (ESM::ESMReader& reader, int cellIndex, bool base,
|
||||||
std::map<ESM::RefNum, std::string>& cache, CSMDoc::Messages& messages);
|
std::map<unsigned int, unsigned int>& cache, CSMDoc::Messages& messages);
|
||||||
///< Load a sequence of references.
|
///< Load a sequence of references.
|
||||||
|
|
||||||
std::string getNewId();
|
std::string getNewId();
|
||||||
|
|
||||||
|
virtual void removeRows (int index, int count);
|
||||||
|
|
||||||
|
virtual void appendBlankRecord (const std::string& id,
|
||||||
|
UniversalId::Type type = UniversalId::Type_None);
|
||||||
|
|
||||||
|
virtual void cloneRecord (const std::string& origin,
|
||||||
|
const std::string& destination,
|
||||||
|
const UniversalId::Type type);
|
||||||
|
|
||||||
|
virtual int searchId (const std::string& id) const;
|
||||||
|
|
||||||
|
virtual void appendRecord (std::unique_ptr<RecordBase> record,
|
||||||
|
UniversalId::Type type = UniversalId::Type_None);
|
||||||
|
|
||||||
|
virtual void insertRecord (std::unique_ptr<RecordBase> record,
|
||||||
|
int index,
|
||||||
|
UniversalId::Type type = UniversalId::Type_None);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -796,18 +796,19 @@ int CSMWorld::RefIdCollection::searchId (const std::string& id) const
|
||||||
return mData.localToGlobalIndex (localIndex);
|
return mData.localToGlobalIndex (localIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::RefIdCollection::replace (int index, const RecordBase& record)
|
void CSMWorld::RefIdCollection::replace (int index, std::unique_ptr<RecordBase> record)
|
||||||
{
|
{
|
||||||
mData.getRecord (mData.globalToLocalIndex (index)).assign (record);
|
mData.getRecord (mData.globalToLocalIndex (index)).assign (*record.release());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin,
|
void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin,
|
||||||
const std::string& destination,
|
const std::string& destination,
|
||||||
const CSMWorld::UniversalId::Type type)
|
const CSMWorld::UniversalId::Type type)
|
||||||
{
|
{
|
||||||
std::unique_ptr<RecordBase> newRecord(mData.getRecord(mData.searchId(origin)).modifiedCopy());
|
std::unique_ptr<RecordBase> newRecord =
|
||||||
|
std::move(mData.getRecord(mData.searchId(origin)).modifiedCopy());
|
||||||
mAdapters.find(type)->second->setId(*newRecord, destination);
|
mAdapters.find(type)->second->setId(*newRecord, destination);
|
||||||
mData.insertRecord(*newRecord, type, destination);
|
mData.insertRecord(std::move(newRecord), type, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSMWorld::RefIdCollection::touchRecord(const std::string& id)
|
bool CSMWorld::RefIdCollection::touchRecord(const std::string& id)
|
||||||
|
|
@ -816,16 +817,16 @@ bool CSMWorld::RefIdCollection::touchRecord(const std::string& id)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record,
|
void CSMWorld::RefIdCollection::appendRecord (std::unique_ptr<RecordBase> record,
|
||||||
UniversalId::Type type)
|
UniversalId::Type type)
|
||||||
{
|
{
|
||||||
std::string id = findAdapter (type).getId (record);
|
std::string id = findAdapter (type).getId (*record.get());
|
||||||
|
|
||||||
int index = mData.getAppendIndex (type);
|
int index = mData.getAppendIndex (type);
|
||||||
|
|
||||||
mData.appendRecord (type, id, false);
|
mData.appendRecord (type, id, false);
|
||||||
|
|
||||||
mData.getRecord (mData.globalToLocalIndex (index)).assign (record);
|
mData.getRecord (mData.globalToLocalIndex (index)).assign (*record.release());
|
||||||
}
|
}
|
||||||
|
|
||||||
const CSMWorld::RecordBase& CSMWorld::RefIdCollection::getRecord (const std::string& id) const
|
const CSMWorld::RecordBase& CSMWorld::RefIdCollection::getRecord (const std::string& id) const
|
||||||
|
|
|
||||||
|
|
@ -89,12 +89,12 @@ namespace CSMWorld
|
||||||
////< Search record with \a id.
|
////< Search record with \a id.
|
||||||
/// \return index of record (if found) or -1 (not found)
|
/// \return index of record (if found) or -1 (not found)
|
||||||
|
|
||||||
void replace (int index, const RecordBase& record) override;
|
void replace (int index, std::unique_ptr<RecordBase> record) override;
|
||||||
///< If the record type does not match, an exception is thrown.
|
///< If the record type does not match, an exception is thrown.
|
||||||
///
|
///
|
||||||
/// \attention \a record must not change the ID.
|
/// \attention \a record must not change the ID.
|
||||||
|
|
||||||
void appendRecord (const RecordBase& record, UniversalId::Type type) override;
|
void appendRecord (std::unique_ptr<RecordBase> record, UniversalId::Type type) override;
|
||||||
///< If the record type does not match, an exception is thrown.
|
///< If the record type does not match, an exception is thrown.
|
||||||
///
|
///
|
||||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||||
|
|
|
||||||
|
|
@ -400,7 +400,7 @@ const CSMWorld::RefIdDataContainer< ESM::Static >& CSMWorld::RefIdData::getStati
|
||||||
return mStatics;
|
return mStatics;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::RefIdData::insertRecord (CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, const std::string& id)
|
void CSMWorld::RefIdData::insertRecord (std::unique_ptr<CSMWorld::RecordBase> record, CSMWorld::UniversalId::Type type, const std::string& id)
|
||||||
{
|
{
|
||||||
std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =
|
std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =
|
||||||
mRecordContainers.find (type);
|
mRecordContainers.find (type);
|
||||||
|
|
@ -408,7 +408,7 @@ void CSMWorld::RefIdData::insertRecord (CSMWorld::RecordBase& record, CSMWorld::
|
||||||
if (iter==mRecordContainers.end())
|
if (iter==mRecordContainers.end())
|
||||||
throw std::logic_error ("invalid local index type");
|
throw std::logic_error ("invalid local index type");
|
||||||
|
|
||||||
iter->second->insertRecord(record);
|
iter->second->insertRecord(std::move(record));
|
||||||
|
|
||||||
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id),
|
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id),
|
||||||
LocalIndex (iter->second->getSize()-1, type)));
|
LocalIndex (iter->second->getSize()-1, type)));
|
||||||
|
|
@ -420,9 +420,7 @@ void CSMWorld::RefIdData::copyTo (int index, RefIdData& target) const
|
||||||
|
|
||||||
RefIdDataContainerBase *source = mRecordContainers.find (localIndex.second)->second;
|
RefIdDataContainerBase *source = mRecordContainers.find (localIndex.second)->second;
|
||||||
|
|
||||||
std::string id = source->getId (localIndex.first);
|
target.insertRecord(source->getRecord(localIndex.first).modifiedCopy(),
|
||||||
|
localIndex.second,
|
||||||
std::unique_ptr<CSMWorld::RecordBase> newRecord (source->getRecord (localIndex.first).modifiedCopy());
|
source->getId(localIndex.first));
|
||||||
|
|
||||||
target.insertRecord (*newRecord, localIndex.second, id);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
#include <components/esm/loadacti.hpp>
|
#include <components/esm/loadacti.hpp>
|
||||||
#include <components/esm/loadalch.hpp>
|
#include <components/esm/loadalch.hpp>
|
||||||
|
|
@ -51,7 +53,7 @@ namespace CSMWorld
|
||||||
|
|
||||||
virtual void appendRecord (const std::string& id, bool base) = 0;
|
virtual void appendRecord (const std::string& id, bool base) = 0;
|
||||||
|
|
||||||
virtual void insertRecord (RecordBase& record) = 0;
|
virtual void insertRecord (std::unique_ptr<RecordBase> record) = 0;
|
||||||
|
|
||||||
virtual int load (ESM::ESMReader& reader, bool base) = 0;
|
virtual int load (ESM::ESMReader& reader, bool base) = 0;
|
||||||
///< \return index of a loaded record or -1 if no record was loaded
|
///< \return index of a loaded record or -1 if no record was loaded
|
||||||
|
|
@ -66,7 +68,7 @@ namespace CSMWorld
|
||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
struct RefIdDataContainer : public RefIdDataContainerBase
|
struct RefIdDataContainer : public RefIdDataContainerBase
|
||||||
{
|
{
|
||||||
std::vector<Record<RecordT> > mContainer;
|
std::vector<std::unique_ptr<Record<RecordT> > > mContainer;
|
||||||
|
|
||||||
int getSize() const override;
|
int getSize() const override;
|
||||||
|
|
||||||
|
|
@ -78,7 +80,7 @@ namespace CSMWorld
|
||||||
|
|
||||||
void appendRecord (const std::string& id, bool base) override;
|
void appendRecord (const std::string& id, bool base) override;
|
||||||
|
|
||||||
void insertRecord (RecordBase& record) override;
|
void insertRecord (std::unique_ptr<RecordBase> record) override;
|
||||||
|
|
||||||
int load (ESM::ESMReader& reader, bool base) override;
|
int load (ESM::ESMReader& reader, bool base) override;
|
||||||
///< \return index of a loaded record or -1 if no record was loaded
|
///< \return index of a loaded record or -1 if no record was loaded
|
||||||
|
|
@ -91,10 +93,13 @@ namespace CSMWorld
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
void RefIdDataContainer<RecordT>::insertRecord(RecordBase& record)
|
void RefIdDataContainer<RecordT>::insertRecord(std::unique_ptr<RecordBase> record)
|
||||||
{
|
{
|
||||||
Record<RecordT>& newRecord = dynamic_cast<Record<RecordT>& >(record);
|
assert(record != nullptr);
|
||||||
mContainer.push_back(newRecord);
|
// convert base pointer to record type pointer
|
||||||
|
std::unique_ptr<Record<RecordT>> typedRecord(&dynamic_cast<Record<RecordT>&>(*record));
|
||||||
|
record.release();
|
||||||
|
mContainer.push_back(std::move(typedRecord));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
|
|
@ -106,33 +111,33 @@ namespace CSMWorld
|
||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
const RecordBase& RefIdDataContainer<RecordT>::getRecord (int index) const
|
const RecordBase& RefIdDataContainer<RecordT>::getRecord (int index) const
|
||||||
{
|
{
|
||||||
return mContainer.at (index);
|
return *mContainer.at (index);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
RecordBase& RefIdDataContainer<RecordT>::getRecord (int index)
|
RecordBase& RefIdDataContainer<RecordT>::getRecord (int index)
|
||||||
{
|
{
|
||||||
return mContainer.at (index);
|
return *mContainer.at (index);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
unsigned int RefIdDataContainer<RecordT>::getRecordFlags (int index) const
|
unsigned int RefIdDataContainer<RecordT>::getRecordFlags (int index) const
|
||||||
{
|
{
|
||||||
return mContainer.at (index).get().mRecordFlags;
|
return mContainer.at (index)->get().mRecordFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
void RefIdDataContainer<RecordT>::appendRecord (const std::string& id, bool base)
|
void RefIdDataContainer<RecordT>::appendRecord (const std::string& id, bool base)
|
||||||
{
|
{
|
||||||
Record<RecordT> record;
|
std::unique_ptr<Record<RecordT> > record(new Record<RecordT>);
|
||||||
|
|
||||||
record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
||||||
|
|
||||||
record.mBase.mId = id;
|
record->mBase.mId = id;
|
||||||
record.mModified.mId = id;
|
record->mModified.mId = id;
|
||||||
(base ? record.mBase : record.mModified).blank();
|
(base ? record->mBase : record->mModified).blank();
|
||||||
|
|
||||||
mContainer.push_back (record);
|
mContainer.push_back (std::move(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
|
|
@ -147,7 +152,7 @@ namespace CSMWorld
|
||||||
int numRecords = static_cast<int>(mContainer.size());
|
int numRecords = static_cast<int>(mContainer.size());
|
||||||
for (; index < numRecords; ++index)
|
for (; index < numRecords; ++index)
|
||||||
{
|
{
|
||||||
if (Misc::StringUtils::ciEqual(mContainer[index].get().mId, record.mId))
|
if (Misc::StringUtils::ciEqual(mContainer[index]->get().mId, record.mId))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -165,7 +170,7 @@ namespace CSMWorld
|
||||||
|
|
||||||
// Flag the record as Deleted even for a base content file.
|
// Flag the record as Deleted even for a base content file.
|
||||||
// RefIdData is responsible for its erasure.
|
// RefIdData is responsible for its erasure.
|
||||||
mContainer[index].mState = RecordBase::State_Deleted;
|
mContainer[index]->mState = RecordBase::State_Deleted;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -174,22 +179,22 @@ namespace CSMWorld
|
||||||
appendRecord(record.mId, base);
|
appendRecord(record.mId, base);
|
||||||
if (base)
|
if (base)
|
||||||
{
|
{
|
||||||
mContainer.back().mBase = record;
|
mContainer.back()->mBase = record;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mContainer.back().mModified = record;
|
mContainer.back()->mModified = record;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!base)
|
else if (!base)
|
||||||
{
|
{
|
||||||
mContainer[index].setModified(record);
|
mContainer[index]->setModified(record);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Overwrite
|
// Overwrite
|
||||||
mContainer[index].setModified(record);
|
mContainer[index]->setModified(record);
|
||||||
mContainer[index].merge();
|
mContainer[index]->merge();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -208,13 +213,13 @@ namespace CSMWorld
|
||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
std::string RefIdDataContainer<RecordT>::getId (int index) const
|
std::string RefIdDataContainer<RecordT>::getId (int index) const
|
||||||
{
|
{
|
||||||
return mContainer.at (index).get().mId;
|
return mContainer.at (index)->get().mId;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
void RefIdDataContainer<RecordT>::save (int index, ESM::ESMWriter& writer) const
|
void RefIdDataContainer<RecordT>::save (int index, ESM::ESMWriter& writer) const
|
||||||
{
|
{
|
||||||
Record<RecordT> record = mContainer.at(index);
|
const Record<RecordT>& record = *mContainer.at(index);
|
||||||
|
|
||||||
if (record.isModified() || record.mState == RecordBase::State_Deleted)
|
if (record.isModified() || record.mState == RecordBase::State_Deleted)
|
||||||
{
|
{
|
||||||
|
|
@ -276,7 +281,7 @@ namespace CSMWorld
|
||||||
|
|
||||||
void erase (int index, int count);
|
void erase (int index, int count);
|
||||||
|
|
||||||
void insertRecord (CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type,
|
void insertRecord (std::unique_ptr<RecordBase> record, CSMWorld::UniversalId::Type type,
|
||||||
const std::string& id);
|
const std::string& id);
|
||||||
|
|
||||||
const RecordBase& getRecord (const LocalIndex& index) const;
|
const RecordBase& getRecord (const LocalIndex& index) const;
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ void CSVDoc::LoadingDocument::closeEvent (QCloseEvent *event)
|
||||||
}
|
}
|
||||||
|
|
||||||
CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document)
|
CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document)
|
||||||
: mDocument (document), mAborted (false), mMessages (nullptr), mTotalRecords (0)
|
: mDocument (document), mAborted (false), mMessages (nullptr), mRecordsLabel (0), mTotalRecordsLabel (0)
|
||||||
{
|
{
|
||||||
setWindowTitle (QString::fromUtf8((std::string("Opening ") + document->getSavePath().filename().string()).c_str()));
|
setWindowTitle (QString::fromUtf8((std::string("Opening ") + document->getSavePath().filename().string()).c_str()));
|
||||||
|
|
||||||
|
|
@ -25,26 +25,25 @@ CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document)
|
||||||
|
|
||||||
mLayout = new QVBoxLayout (this);
|
mLayout = new QVBoxLayout (this);
|
||||||
|
|
||||||
// file progress
|
// total progress
|
||||||
mFile = new QLabel (this);
|
mTotalRecordsLabel = new QLabel (this);
|
||||||
|
|
||||||
mLayout->addWidget (mFile);
|
mLayout->addWidget (mTotalRecordsLabel);
|
||||||
|
|
||||||
mFileProgress = new QProgressBar (this);
|
mTotalProgress = new QProgressBar (this);
|
||||||
|
|
||||||
mLayout->addWidget (mFileProgress);
|
mLayout->addWidget (mTotalProgress);
|
||||||
|
|
||||||
int size = static_cast<int> (document->getContentFiles().size())+1;
|
mTotalProgress->setMinimum (0);
|
||||||
if (document->isNew())
|
mTotalProgress->setMaximum (document->getData().getTotalRecords(document->getContentFiles()));
|
||||||
--size;
|
mTotalProgress->setTextVisible (true);
|
||||||
|
mTotalProgress->setValue (0);
|
||||||
|
mTotalRecords = 0;
|
||||||
|
|
||||||
mFileProgress->setMinimum (0);
|
mFilesLoaded = 0;
|
||||||
mFileProgress->setMaximum (size);
|
|
||||||
mFileProgress->setTextVisible (true);
|
|
||||||
mFileProgress->setValue (0);
|
|
||||||
|
|
||||||
// record progress
|
// record progress
|
||||||
mLayout->addWidget (mRecords = new QLabel ("Records", this));
|
mLayout->addWidget (mRecordsLabel = new QLabel ("Records", this));
|
||||||
|
|
||||||
mRecordProgress = new QProgressBar (this);
|
mRecordProgress = new QProgressBar (this);
|
||||||
|
|
||||||
|
|
@ -74,29 +73,32 @@ CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document)
|
||||||
connect (mButtons, SIGNAL (rejected()), this, SLOT (cancel()));
|
connect (mButtons, SIGNAL (rejected()), this, SLOT (cancel()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVDoc::LoadingDocument::nextStage (const std::string& name, int totalRecords)
|
void CSVDoc::LoadingDocument::nextStage (const std::string& name, int fileRecords)
|
||||||
{
|
{
|
||||||
mFile->setText (QString::fromUtf8 (("Loading: " + name).c_str()));
|
++mFilesLoaded;
|
||||||
|
size_t numFiles = mDocument->getContentFiles().size();
|
||||||
|
|
||||||
mFileProgress->setValue (mFileProgress->value()+1);
|
mTotalRecordsLabel->setText (QString::fromUtf8 (("Loading: "+name
|
||||||
|
+" ("+std::to_string(mFilesLoaded)+" of "+std::to_string((numFiles))+")").c_str()));
|
||||||
|
|
||||||
|
mTotalRecords = mTotalProgress->value();
|
||||||
|
|
||||||
mRecordProgress->setValue (0);
|
mRecordProgress->setValue (0);
|
||||||
mRecordProgress->setMaximum (totalRecords>0 ? totalRecords : 1);
|
mRecordProgress->setMaximum (fileRecords>0 ? fileRecords : 1);
|
||||||
|
|
||||||
mTotalRecords = totalRecords;
|
mRecords = fileRecords;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVDoc::LoadingDocument::nextRecord (int records)
|
void CSVDoc::LoadingDocument::nextRecord (int records)
|
||||||
{
|
{
|
||||||
if (records<=mTotalRecords)
|
if (records <= mRecords)
|
||||||
{
|
{
|
||||||
mRecordProgress->setValue (records);
|
mTotalProgress->setValue (mTotalRecords+records);
|
||||||
|
|
||||||
std::ostringstream stream;
|
mRecordProgress->setValue(records);
|
||||||
|
|
||||||
stream << "Records: " << records << " of " << mTotalRecords;
|
mRecordsLabel->setText(QString::fromStdString(
|
||||||
|
"Records: "+std::to_string(records)+" of "+std::to_string(mRecords)));
|
||||||
mRecords->setText (QString::fromUtf8 (stream.str().c_str()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -176,12 +178,12 @@ void CSVDoc::Loader::loadingStopped (CSMDoc::Document *document, bool completed,
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVDoc::Loader::nextStage (CSMDoc::Document *document, const std::string& name,
|
void CSVDoc::Loader::nextStage (CSMDoc::Document *document, const std::string& name,
|
||||||
int totalRecords)
|
int fileRecords)
|
||||||
{
|
{
|
||||||
std::map<CSMDoc::Document *, LoadingDocument *>::iterator iter = mDocuments.find (document);
|
std::map<CSMDoc::Document *, LoadingDocument *>::iterator iter = mDocuments.find (document);
|
||||||
|
|
||||||
if (iter!=mDocuments.end())
|
if (iter!=mDocuments.end())
|
||||||
iter->second->nextStage (name, totalRecords);
|
iter->second->nextStage (name, fileRecords);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVDoc::Loader::nextRecord (CSMDoc::Document *document, int records)
|
void CSVDoc::Loader::nextRecord (CSMDoc::Document *document, int records)
|
||||||
|
|
|
||||||
|
|
@ -25,16 +25,18 @@ namespace CSVDoc
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
CSMDoc::Document *mDocument;
|
CSMDoc::Document *mDocument;
|
||||||
QLabel *mFile;
|
QLabel *mTotalRecordsLabel;
|
||||||
QLabel *mRecords;
|
QLabel *mRecordsLabel;
|
||||||
QProgressBar *mFileProgress;
|
QProgressBar *mTotalProgress;
|
||||||
QProgressBar *mRecordProgress;
|
QProgressBar *mRecordProgress;
|
||||||
bool mAborted;
|
bool mAborted;
|
||||||
QDialogButtonBox *mButtons;
|
QDialogButtonBox *mButtons;
|
||||||
QLabel *mError;
|
QLabel *mError;
|
||||||
QListWidget *mMessages;
|
QListWidget *mMessages;
|
||||||
QVBoxLayout *mLayout;
|
QVBoxLayout *mLayout;
|
||||||
|
int mRecords;
|
||||||
int mTotalRecords;
|
int mTotalRecords;
|
||||||
|
int mFilesLoaded;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -270,12 +270,6 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
|
||||||
setSelectionBehavior (QAbstractItemView::SelectRows);
|
setSelectionBehavior (QAbstractItemView::SelectRows);
|
||||||
setSelectionMode (QAbstractItemView::ExtendedSelection);
|
setSelectionMode (QAbstractItemView::ExtendedSelection);
|
||||||
|
|
||||||
setSortingEnabled (sorting);
|
|
||||||
if (sorting)
|
|
||||||
{
|
|
||||||
sortByColumn (mModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id), Qt::AscendingOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
int columns = mModel->columnCount();
|
int columns = mModel->columnCount();
|
||||||
for (int i=0; i<columns; ++i)
|
for (int i=0; i<columns; ++i)
|
||||||
{
|
{
|
||||||
|
|
@ -296,6 +290,13 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
|
||||||
hideColumn (i);
|
hideColumn (i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sorting)
|
||||||
|
{
|
||||||
|
// FIXME: some tables (e.g. CellRef) have this column hidden, which makes it confusing
|
||||||
|
sortByColumn (mModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id), Qt::AscendingOrder);
|
||||||
|
}
|
||||||
|
setSortingEnabled (sorting);
|
||||||
|
|
||||||
mEditAction = new QAction (tr ("Edit Record"), this);
|
mEditAction = new QAction (tr ("Edit Record"), this);
|
||||||
connect (mEditAction, SIGNAL (triggered()), this, SLOT (editRecord()));
|
connect (mEditAction, SIGNAL (triggered()), this, SLOT (editRecord()));
|
||||||
mEditAction->setIcon(QIcon(":edit-edit"));
|
mEditAction->setIcon(QIcon(":edit-edit"));
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue