Merge branch 'master' of https://github.com/zinnschlag/openmw into magic

Conflicts:
	apps/openmw/mwgui/inventoryitemmodel.cpp
actorid
scrawl 11 years ago
commit 956d8adb99

@ -339,6 +339,8 @@ int load(Arguments& info)
}
std::string id = esm.getHNOString("NAME");
if (id.empty())
id = esm.getHNOString("INAM");
if(!quiet && interested)
std::cout << "\nRecord: " << n.toString()

@ -740,8 +740,8 @@ void Record<ESM::DialInfo>::print()
if (mData.mClass != "")
std::cout << " Class: " << mData.mClass << std::endl;
std::cout << " Factionless: " << mData.mFactionLess << std::endl;
if (mData.mNpcFaction != "")
std::cout << " NPC Faction: " << mData.mNpcFaction << std::endl;
if (mData.mFaction != "")
std::cout << " NPC Faction: " << mData.mFaction << std::endl;
if (mData.mData.mRank != -1)
std::cout << " NPC Rank: " << (int)mData.mData.mRank << std::endl;
if (mData.mPcFaction != "")

@ -24,11 +24,11 @@ opencs_units (model/world
opencs_units_noqt (model/world
universalid record commands columnbase scriptcontext cell refidcollection
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection
)
opencs_hdrs_noqt (model/world
columnimp idcollection collection
columnimp idcollection collection info
)
@ -60,7 +60,7 @@ opencs_hdrs_noqt (view/doc
opencs_units (view/world
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
cellcreator referenceablecreator referencecreator scenesubview scenetoolbar scenetool
scenetoolmode
scenetoolmode infocreator
)
opencs_units (view/render

@ -58,12 +58,9 @@ CSMDoc::Saving::Saving (Document& document, const boost::filesystem::path& proje
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Spell> >
(mDocument.getData().getSpells(), mState));
/// \todo deal with info records for topcis and journals
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Dialogue> >
(mDocument.getData().getTopics(), mState));
appendStage (new WriteDialogueCollectionStage (mDocument, mState, false));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Dialogue> >
(mDocument.getData().getJournals(), mState));
appendStage (new WriteDialogueCollectionStage (mDocument, mState, true));
appendStage (new WriteRefIdCollectionStage (mDocument, mState));

@ -7,6 +7,10 @@
#include <QUndoStack>
#include <components/esm/loaddial.hpp>
#include "../world/infocollection.hpp"
#include "document.hpp"
#include "savingstate.hpp"
@ -80,6 +84,115 @@ void CSMDoc::WriteHeaderStage::perform (int stage, std::vector<std::string>& mes
}
CSMDoc::WriteDialogueCollectionStage::WriteDialogueCollectionStage (Document& document,
SavingState& state, bool journal)
: mDocument (document), mState (state),
mTopics (journal ? document.getData().getJournals() : document.getData().getTopics()),
mInfos (journal ? document.getData().getJournalInfos() : document.getData().getTopicInfos())
{}
int CSMDoc::WriteDialogueCollectionStage::setup()
{
return mTopics.getSize();
}
void CSMDoc::WriteDialogueCollectionStage::perform (int stage, std::vector<std::string>& messages)
{
const CSMWorld::Record<ESM::Dialogue>& topic = mTopics.getRecord (stage);
CSMWorld::RecordBase::State state = topic.mState;
if (state==CSMWorld::RecordBase::State_Deleted)
{
// if the topic is deleted, we do not need to bother with INFO records.
/// \todo wrote record with delete flag
return;
}
// Test, if we need to save anything associated info records.
bool infoModified = false;
CSMWorld::InfoCollection::Range range = mInfos.getTopicRange (topic.get().mId);
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)
{
CSMWorld::RecordBase::State state = iter->mState;
if (state==CSMWorld::RecordBase::State_Modified ||
state==CSMWorld::RecordBase::State_ModifiedOnly ||
state==CSMWorld::RecordBase::State_Deleted)
{
infoModified = true;
break;
}
}
if (state==CSMWorld::RecordBase::State_Modified ||
state==CSMWorld::RecordBase::State_ModifiedOnly ||
infoModified)
{
// always write the topic record
std::string type;
for (int i=0; i<4; ++i)
/// \todo make endianess agnostic (change ESMWriter interface?)
type += reinterpret_cast<const char *> (&topic.mModified.sRecordId)[i];
mState.getWriter().startRecord (type);
mState.getWriter().writeHNCString ("NAME", topic.mModified.mId);
topic.mModified.save (mState.getWriter());
mState.getWriter().endRecord (type);
// write modified selected info records
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second;
++iter)
{
CSMWorld::RecordBase::State state = iter->mState;
if (state==CSMWorld::RecordBase::State_Deleted)
{
/// \todo wrote record with delete flag
}
else if (state==CSMWorld::RecordBase::State_Modified ||
state==CSMWorld::RecordBase::State_ModifiedOnly)
{
ESM::DialInfo info = iter->get();
info.mId = info.mId.substr (info.mId.find_last_of ('#')+1);
if (iter!=range.first)
{
CSMWorld::InfoCollection::RecordConstIterator prev = iter;
--prev;
info.mPrev =
prev->mModified.mId.substr (prev->mModified.mId.find_last_of ('#')+1);
}
CSMWorld::InfoCollection::RecordConstIterator next = iter;
++next;
if (next!=range.second)
{
info.mNext =
next->mModified.mId.substr (next->mModified.mId.find_last_of ('#')+1);
}
std::string type;
for (int i=0; i<4; ++i)
/// \todo make endianess agnostic (change ESMWriter interface?)
type += reinterpret_cast<const char *> (&info.sRecordId)[i];
mState.getWriter().startRecord (type);
mState.getWriter().writeHNCString ("INAM", info.mId);
info.save (mState.getWriter());
mState.getWriter().endRecord (type);
}
}
}
}
CSMDoc::WriteRefIdCollectionStage::WriteRefIdCollectionStage (Document& document, SavingState& state)
: mDocument (document), mState (state)
{}

@ -3,13 +3,23 @@
#include "stage.hpp"
#include "savingstate.hpp"
#include "../world/record.hpp"
#include "../world/idcollection.hpp"
#include "../filter/filter.hpp"
#include "savingstate.hpp"
namespace ESM
{
struct Dialogue;
}
namespace CSMWorld
{
class InfoCollection;
}
namespace CSMDoc
{
class Document;
@ -106,6 +116,25 @@ namespace CSMDoc
}
class WriteDialogueCollectionStage : public Stage
{
Document& mDocument;
SavingState& mState;
const CSMWorld::IdCollection<ESM::Dialogue>& mTopics;
CSMWorld::InfoCollection& mInfos;
public:
WriteDialogueCollectionStage (Document& document, SavingState& state, bool journal);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
class WriteRefIdCollectionStage : public Stage
{
Document& mDocument;

@ -51,6 +51,18 @@ namespace CSMWorld
Collection (const Collection&);
Collection& operator= (const Collection&);
protected:
const std::map<std::string, int>& getIdMap() const;
const std::vector<Record<ESXRecordT> >& getRecords() const;
bool reorderRowsImp (int baseIndex, const std::vector<int>& newOrder);
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
/// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex).
///
/// \return Success?
public:
Collection();
@ -104,7 +116,8 @@ namespace CSMWorld
virtual const Record<ESXRecordT>& getRecord (int index) const;
virtual int getAppendIndex (UniversalId::Type type = UniversalId::Type_None) const;
virtual int getAppendIndex (const std::string& id,
UniversalId::Type type = UniversalId::Type_None) const;
///< \param type Will be ignored, unless the collection supports multiple record types
virtual std::vector<std::string> getIds (bool listDeleted = true) const;
@ -112,12 +125,74 @@ namespace CSMWorld
///
/// \param listDeleted include deleted record in the list
virtual void insertRecord (const RecordBase& record, int index,
UniversalId::Type type = UniversalId::Type_None);
///< Insert record before index.
///
/// If the record type does not match, an exception is thrown.
///
/// If the index is invalid either generally (by being out of range) or for the particular
/// record, an exception is thrown.
virtual bool reorderRows (int baseIndex, const std::vector<int>& newOrder);
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
/// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex).
///
/// \return Success?
void addColumn (Column<ESXRecordT> *column);
void setRecord (int index, const Record<ESXRecordT>& record);
///< \attention This function must not change the ID.
};
template<typename ESXRecordT, typename IdAccessorT>
const std::map<std::string, int>& Collection<ESXRecordT, IdAccessorT>::getIdMap() const
{
return mIndex;
}
template<typename ESXRecordT, typename IdAccessorT>
const std::vector<Record<ESXRecordT> >& Collection<ESXRecordT, IdAccessorT>::getRecords() const
{
return mRecords;
}
template<typename ESXRecordT, typename IdAccessorT>
bool Collection<ESXRecordT, IdAccessorT>::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<Record<ESXRecordT> > buffer (size);
for (int i=0; i<size; ++i)
{
buffer[newOrder[i]] = mRecords [baseIndex+i];
buffer[newOrder[i]].setModified (buffer[newOrder[i]].get());
}
std::copy (buffer.begin(), buffer.end(), mRecords.begin()+baseIndex);
// adjust index
for (std::map<std::string, int>::iterator iter (mIndex.begin()); iter!=mIndex.end();
++iter)
if (iter->second>=baseIndex && iter->second<baseIndex+size)
iter->second = newOrder.at (iter->second-baseIndex)+baseIndex;
}
return true;
}
template<typename ESXRecordT, typename IdAccessorT>
Collection<ESXRecordT, IdAccessorT>::Collection()
{}
@ -142,8 +217,7 @@ namespace CSMWorld
record2.mState = Record<ESXRecordT>::State_ModifiedOnly;
record2.mModified = record;
mRecords.push_back (record2);
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), mRecords.size()-1));
insertRecord (record2, getAppendIndex (id));
}
else
{
@ -260,7 +334,12 @@ namespace CSMWorld
ESXRecordT record;
IdAccessorT().getId (record) = id;
record.blank();
add (record);
Record<ESXRecordT> record2;
record2.mState = Record<ESXRecordT>::State_ModifiedOnly;
record2.mModified = record;
insertRecord (record2, getAppendIndex (id, type), type);
}
template<typename ESXRecordT, typename IdAccessorT>
@ -286,14 +365,14 @@ namespace CSMWorld
void Collection<ESXRecordT, IdAccessorT>::appendRecord (const RecordBase& record,
UniversalId::Type type)
{
mRecords.push_back (dynamic_cast<const Record<ESXRecordT>&> (record));
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (IdAccessorT().getId (
dynamic_cast<const Record<ESXRecordT>&> (record).get())),
mRecords.size()-1));
insertRecord (record,
getAppendIndex (IdAccessorT().getId (
dynamic_cast<const Record<ESXRecordT>&> (record).get()), type), type);
}
template<typename ESXRecordT, typename IdAccessorT>
int Collection<ESXRecordT, IdAccessorT>::getAppendIndex (UniversalId::Type type) const
int Collection<ESXRecordT, IdAccessorT>::getAppendIndex (const std::string& id,
UniversalId::Type type) const
{
return static_cast<int> (mRecords.size());
}
@ -326,6 +405,29 @@ namespace CSMWorld
return mRecords.at (index);
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::insertRecord (const RecordBase& record, int index,
UniversalId::Type type)
{
if (index<0 || index>static_cast<int> (mRecords.size()))
throw std::runtime_error ("index out of range");
const Record<ESXRecordT>& record2 = dynamic_cast<const Record<ESXRecordT>&> (record);
mRecords.insert (mRecords.begin()+index, record2);
if (index<static_cast<int> (mRecords.size())-1)
{
for (std::map<std::string, int>::iterator iter (mIndex.begin()); iter!=mIndex.end();
++iter)
if (iter->second>=index)
++(iter->second);
}
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (IdAccessorT().getId (
record2.get())), index));
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::setRecord (int index, const Record<ESXRecordT>& record)
{
@ -334,6 +436,12 @@ namespace CSMWorld
mRecords.at (index) = record;
}
template<typename ESXRecordT, typename IdAccessorT>
bool Collection<ESXRecordT, IdAccessorT>::reorderRows (int baseIndex, const std::vector<int>& newOrder)
{
return false;
}
}
#endif

@ -2,6 +2,7 @@
#define CSM_WOLRD_COLLECTIONBASE_H
#include <string>
#include <vector>
#include "universalid.hpp"
#include "columns.hpp"
@ -77,7 +78,8 @@ namespace CSMWorld
virtual const RecordBase& getRecord (int index) const = 0;
virtual int getAppendIndex (UniversalId::Type type = UniversalId::Type_None) const = 0;
virtual int getAppendIndex (const std::string& id,
UniversalId::Type type = UniversalId::Type_None) const = 0;
///< \param type Will be ignored, unless the collection supports multiple record types
virtual std::vector<std::string> getIds (bool listDeleted = true) const = 0;
@ -85,6 +87,12 @@ namespace CSMWorld
///
/// \param listDeleted include deleted record in the list
virtual bool reorderRows (int baseIndex, const std::vector<int>& newOrder) = 0;
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
/// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex).
///
/// \return Success?
int searchColumnIndex (Columns::ColumnId id) const;
///< Return index of column with the given \a id. If no such column exists, -1 is returned.

@ -44,7 +44,9 @@ namespace CSMWorld
Display_WeaponType,
Display_RecordState,
Display_RefRecordType,
Display_DialogueType
Display_DialogueType,
Display_QuestStatusType,
Display_Gender
};
int mColumnId;

@ -9,6 +9,7 @@
#include "columnbase.hpp"
#include "columns.hpp"
#include "info.hpp"
namespace CSMWorld
{
@ -814,14 +815,14 @@ namespace CSMWorld
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return QString::fromUtf8 (record.get().mCellId.c_str());
return QString::fromUtf8 (record.get().mCell.c_str());
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mCellId = data.toString().toUtf8().constData();
record2.mCell = data.toString().toUtf8().constData();
record.setModified (record2);
}
@ -1348,6 +1349,340 @@ namespace CSMWorld
return false;
}
};
template<typename ESXRecordT>
struct QuestStatusTypeColumn : public Column<ESXRecordT>
{
QuestStatusTypeColumn()
: Column<ESXRecordT> (Columns::ColumnId_QuestStatusType, ColumnBase::Display_QuestStatusType)
{}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return static_cast<int> (record.get().mQuestStatus);
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mQuestStatus = static_cast<Info::QuestStatus> (data.toInt());
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
};
template<typename ESXRecordT>
struct QuestDescriptionColumn : public Column<ESXRecordT>
{
QuestDescriptionColumn() : Column<ESXRecordT> (Columns::ColumnId_QuestDescription, ColumnBase::Display_String) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return QString::fromUtf8 (record.get().mResponse.c_str());
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mResponse = data.toString().toUtf8().constData();
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
};
template<typename ESXRecordT>
struct QuestIndexColumn : public Column<ESXRecordT>
{
QuestIndexColumn()
: Column<ESXRecordT> (Columns::ColumnId_QuestIndex, ColumnBase::Display_Integer)
{}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return record.get().mData.mDisposition;
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mData.mDisposition = data.toInt();
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
};
template<typename ESXRecordT>
struct TopicColumn : public Column<ESXRecordT>
{
TopicColumn (bool journal) : Column<ESXRecordT> (journal ? Columns::ColumnId_Journal : Columns::ColumnId_Topic, ColumnBase::Display_String) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return QString::fromUtf8 (record.get().mTopicId.c_str());
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mTopicId = data.toString().toUtf8().constData();
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
virtual bool isUserEditable() const
{
return false;
}
};
template<typename ESXRecordT>
struct ActorColumn : public Column<ESXRecordT>
{
ActorColumn() : Column<ESXRecordT> (Columns::ColumnId_Actor, ColumnBase::Display_String) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return QString::fromUtf8 (record.get().mActor.c_str());
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mActor = data.toString().toUtf8().constData();
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
};
template<typename ESXRecordT>
struct RaceColumn : public Column<ESXRecordT>
{
RaceColumn() : Column<ESXRecordT> (Columns::ColumnId_Race, ColumnBase::Display_String) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return QString::fromUtf8 (record.get().mRace.c_str());
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mRace = data.toString().toUtf8().constData();
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
};
template<typename ESXRecordT>
struct ClassColumn : public Column<ESXRecordT>
{
ClassColumn() : Column<ESXRecordT> (Columns::ColumnId_Class, ColumnBase::Display_String) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return QString::fromUtf8 (record.get().mClass.c_str());
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mClass = data.toString().toUtf8().constData();
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
};
template<typename ESXRecordT>
struct PcFactionColumn : public Column<ESXRecordT>
{
PcFactionColumn() : Column<ESXRecordT> (Columns::ColumnId_PcFaction, ColumnBase::Display_String) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return QString::fromUtf8 (record.get().mPcFaction.c_str());
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mPcFaction = data.toString().toUtf8().constData();
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
};
template<typename ESXRecordT>
struct ResponseColumn : public Column<ESXRecordT>
{
ResponseColumn() : Column<ESXRecordT> (Columns::ColumnId_Response, ColumnBase::Display_String) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return QString::fromUtf8 (record.get().mResponse.c_str());
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mResponse = data.toString().toUtf8().constData();
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
};
template<typename ESXRecordT>
struct DispositionColumn : public Column<ESXRecordT>
{
DispositionColumn()
: Column<ESXRecordT> (Columns::ColumnId_Disposition, ColumnBase::Display_Integer)
{}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return record.get().mData.mDisposition;
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mData.mDisposition = data.toInt();
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
};
template<typename ESXRecordT>
struct RankColumn : public Column<ESXRecordT>
{
RankColumn()
: Column<ESXRecordT> (Columns::ColumnId_Rank, ColumnBase::Display_Integer)
{}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return static_cast<int> (record.get().mData.mRank);
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mData.mRank = static_cast<signed char> (data.toInt());
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
};
template<typename ESXRecordT>
struct PcRankColumn : public Column<ESXRecordT>
{
PcRankColumn()
: Column<ESXRecordT> (Columns::ColumnId_PcRank, ColumnBase::Display_Integer)
{}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return static_cast<int> (record.get().mData.mPCrank);
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mData.mPCrank = static_cast<signed char> (data.toInt());
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
};
template<typename ESXRecordT>
struct GenderColumn : public Column<ESXRecordT>
{
GenderColumn()
: Column<ESXRecordT> (Columns::ColumnId_Gender, ColumnBase::Display_Gender)
{}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return static_cast<int> (record.get().mData.mGender);
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mData.mGender = data.toInt();
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
};
}
#endif

@ -160,6 +160,18 @@ namespace CSMWorld
{ ColumnId_DoorPositionYRot, "Teleport Rot Y" },
{ ColumnId_DoorPositionZRot, "Teleport Rot Z" },
{ ColumnId_DialogueType, "Dialogue Type" },
{ ColumnId_QuestIndex, "Quest Index" },
{ ColumnId_QuestStatusType, "Quest Status" },
{ ColumnId_QuestDescription, "Quest Description" },
{ ColumnId_Topic, "Topic" },
{ ColumnId_Journal, "Journal" },
{ ColumnId_Actor, "Actor" },
{ ColumnId_PcFaction, "PC Faction" },
{ ColumnId_Response, "Response" },
{ ColumnId_Disposition, "Disposition" },
{ ColumnId_Rank, "Rank" },
{ ColumnId_Gender, "Gender" },
{ ColumnId_PcRank, "PC Rank" },
{ ColumnId_Scope, "Scope", },
{ ColumnId_UseValue1, "Use value 1" },
@ -276,6 +288,16 @@ namespace
"Topic", "Voice", "Greeting", "Persuasion", 0
};
static const char *sQuestStatusTypes[] =
{
"None", "Name", "Finished", "Restart", 0
};
static const char *sGenderEnums[] =
{
"Male", "Female", 0
};
const char **getEnumNames (CSMWorld::Columns::ColumnId column)
{
switch (column)
@ -291,6 +313,8 @@ namespace
case CSMWorld::Columns::ColumnId_Modification: return sModificationEnums;
case CSMWorld::Columns::ColumnId_ValueType: return sVarTypeEnums;
case CSMWorld::Columns::ColumnId_DialogueType: return sDialogueTypeEnums;
case CSMWorld::Columns::ColumnId_QuestStatusType: return sQuestStatusTypes;
case CSMWorld::Columns::ColumnId_Gender: return sGenderEnums;
default: return 0;
}

@ -153,7 +153,19 @@ namespace CSMWorld
ColumnId_DoorPositionYRot = 140,
ColumnId_DoorPositionZRot = 141,
ColumnId_DialogueType = 142,
ColumnId_Scope = 143,
ColumnId_QuestIndex = 143,
ColumnId_QuestStatusType = 144,
ColumnId_QuestDescription = 145,
ColumnId_Topic = 146,
ColumnId_Journal = 147,
ColumnId_Actor = 148,
ColumnId_PcFaction = 149,
ColumnId_Response = 150,
ColumnId_Disposition = 151,
ColumnId_Rank = 152,
ColumnId_Gender = 153,
ColumnId_PcRank = 154,
ColumnId_Scope = 155,
// Allocated to a separate value range, so we don't get a collision should we ever need
// to extend the number of use values.

@ -122,4 +122,26 @@ void CSMWorld::DeleteCommand::redo()
void CSMWorld::DeleteCommand::undo()
{
mModel.setRecord (mId, *mOld);
}
CSMWorld::ReorderRowsCommand::ReorderRowsCommand (IdTable& model, int baseIndex,
const std::vector<int>& newOrder)
: mModel (model), mBaseIndex (baseIndex), mNewOrder (newOrder)
{}
void CSMWorld::ReorderRowsCommand::redo()
{
mModel.reorderRows (mBaseIndex, mNewOrder);
}
void CSMWorld::ReorderRowsCommand::undo()
{
int size = static_cast<int> (mNewOrder.size());
std::vector<int> reverse (size);
for (int i=0; i<size; ++i)
reverse.at (mNewOrder[i]) = i;
mModel.reorderRows (mBaseIndex, reverse);
}

@ -5,6 +5,7 @@
#include <string>
#include <map>
#include <vector>
#include <QVariant>
#include <QUndoCommand>
@ -99,6 +100,21 @@ namespace CSMWorld
virtual void undo();
};
class ReorderRowsCommand : public QUndoCommand
{
IdTable& mModel;
int mBaseIndex;
std::vector<int> mNewOrder;
public:
ReorderRowsCommand (IdTable& model, int baseIndex, const std::vector<int>& newOrder);
virtual void redo();
virtual void undo();
};
}
#endif

@ -160,6 +160,29 @@ CSMWorld::Data::Data() : mRefs (mCells)
mJournals.addColumn (new RecordStateColumn<ESM::Dialogue>);
mJournals.addColumn (new DialogueTypeColumn<ESM::Dialogue> (true));
mTopicInfos.addColumn (new StringIdColumn<Info> (true));
mTopicInfos.addColumn (new RecordStateColumn<Info>);
mTopicInfos.addColumn (new TopicColumn<Info> (false));
mTopicInfos.addColumn (new ActorColumn<Info>);
mTopicInfos.addColumn (new RaceColumn<Info>);
mTopicInfos.addColumn (new ClassColumn<Info>);
mTopicInfos.addColumn (new FactionColumn<Info>);
mTopicInfos.addColumn (new CellColumn<Info>);
mTopicInfos.addColumn (new DispositionColumn<Info>);
mTopicInfos.addColumn (new RankColumn<Info>);
mTopicInfos.addColumn (new GenderColumn<Info>);
mTopicInfos.addColumn (new PcFactionColumn<Info>);
mTopicInfos.addColumn (new PcRankColumn<Info>);
mTopicInfos.addColumn (new SoundFileColumn<Info>);
mTopicInfos.addColumn (new ResponseColumn<Info>);
mJournalInfos.addColumn (new StringIdColumn<Info> (true));
mJournalInfos.addColumn (new RecordStateColumn<Info>);
mJournalInfos.addColumn (new TopicColumn<Info> (true));
mJournalInfos.addColumn (new QuestStatusTypeColumn<Info>);
mJournalInfos.addColumn (new QuestIndexColumn<Info>);
mJournalInfos.addColumn (new QuestDescriptionColumn<Info>);
mCells.addColumn (new StringIdColumn<Cell>);
mCells.addColumn (new RecordStateColumn<Cell>);
mCells.addColumn (new FixedRecordTypeColumn<Cell> (UniversalId::Type_Cell));
@ -218,6 +241,8 @@ CSMWorld::Data::Data() : mRefs (mCells)
addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell);
addModel (new IdTable (&mTopics), UniversalId::Type_Topics, UniversalId::Type_Topic);
addModel (new IdTable (&mJournals), UniversalId::Type_Journals, UniversalId::Type_Journal);
addModel (new IdTable (&mTopicInfos), UniversalId::Type_TopicInfos, UniversalId::Type_TopicInfo);
addModel (new IdTable (&mJournalInfos), UniversalId::Type_JournalInfos, UniversalId::Type_JournalInfo);
addModel (new IdTable (&mCells), UniversalId::Type_Cells, UniversalId::Type_Cell);
addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables,
UniversalId::Type_Referenceable);
@ -362,6 +387,25 @@ CSMWorld::IdCollection<ESM::Dialogue>& CSMWorld::Data::getJournals()
return mJournals;
}
const CSMWorld::InfoCollection& CSMWorld::Data::getTopicInfos() const
{
return mTopicInfos;
}
CSMWorld::InfoCollection& CSMWorld::Data::getTopicInfos()
{
return mTopicInfos;
}
const CSMWorld::InfoCollection& CSMWorld::Data::getJournalInfos() const
{
return mJournalInfos;
}
CSMWorld::InfoCollection& CSMWorld::Data::getJournalInfos()
{
return mJournalInfos;
}
const CSMWorld::IdCollection<CSMWorld::Cell>& CSMWorld::Data::getCells() const
{
@ -441,6 +485,8 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base, b
reader.open (path.string());
const ESM::Dialogue *dialogue = 0;
mAuthor = reader.getAuthor();
mDescription = reader.getDesc();
@ -505,9 +551,13 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base, b
if (record.mType==ESM::Dialogue::Journal)
{
mJournals.load (record, base);
dialogue = &mJournals.getRecord (id).get();
}
else if (record.mType==ESM::Dialogue::Deleted)
{
dialogue = 0; // record vector can be shuffled around which would make pointer
// to record invalid
if (mJournals.tryDelete (id))
{
/// \todo handle info records
@ -524,8 +574,26 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base, b
else
{
mTopics.load (record, base);
dialogue = &mTopics.getRecord (id).get();
}
break;
}
case ESM::REC_INFO:
{
if (!dialogue)
{
/// \todo INFO record without matching DIAL record -> report to user
reader.skipRecord();
break;
}
if (dialogue->mType==ESM::Dialogue::Journal)
mJournalInfos.load (reader, base, *dialogue);
else
mTopicInfos.load (reader, base, *dialogue);
break;
}
@ -545,6 +613,7 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base, b
default:
/// \todo throw an exception instead, once all records are implemented
/// or maybe report error and continue?
reader.skipRecord();
}
}

@ -29,6 +29,7 @@
#include "cell.hpp"
#include "refidcollection.hpp"
#include "refcollection.hpp"
#include "infocollection.hpp"
class QAbstractItemModel;
@ -51,6 +52,8 @@ namespace CSMWorld
IdCollection<ESM::Spell> mSpells;
IdCollection<ESM::Dialogue> mTopics;
IdCollection<ESM::Dialogue> mJournals;
InfoCollection mTopicInfos;
InfoCollection mJournalInfos;
IdCollection<Cell> mCells;
RefIdCollection mReferenceables;
RefCollection mRefs;
@ -131,6 +134,14 @@ namespace CSMWorld
IdCollection<ESM::Dialogue>& getJournals();
const InfoCollection& getTopicInfos() const;
InfoCollection& getTopicInfos();
const InfoCollection& getJournalInfos() const;
InfoCollection& getJournalInfos();
const IdCollection<Cell>& getCells() const;
IdCollection<Cell>& getCells();

@ -4,15 +4,12 @@
#include "collectionbase.hpp"
#include "columnbase.hpp"
CSMWorld::IdTable::IdTable (CollectionBase *idCollection) : mIdCollection (idCollection)
{
}
CSMWorld::IdTable::IdTable (CollectionBase *idCollection, Reordering reordering)
: mIdCollection (idCollection), mReordering (reordering)
{}
CSMWorld::IdTable::~IdTable()
{
}
{}
int CSMWorld::IdTable::rowCount (const QModelIndex & parent) const
{
@ -118,7 +115,7 @@ QModelIndex CSMWorld::IdTable::parent (const QModelIndex& index) const
void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type)
{
int index = mIdCollection->getAppendIndex();
int index = mIdCollection->getAppendIndex (id, type);
beginInsertRows (QModelIndex(), index, index);
@ -138,7 +135,7 @@ void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& reco
if (index==-1)
{
int index = mIdCollection->getAppendIndex();
int index = mIdCollection->getAppendIndex (id);
beginInsertRows (QModelIndex(), index, index);
@ -167,4 +164,17 @@ int CSMWorld::IdTable::searchColumnIndex (Columns::ColumnId id) const
int CSMWorld::IdTable::findColumnIndex (Columns::ColumnId id) const
{
return mIdCollection->findColumnIndex (id);
}
void CSMWorld::IdTable::reorderRows (int baseIndex, const std::vector<int>& newOrder)
{
if (!newOrder.empty())
if (mIdCollection->reorderRows (baseIndex, newOrder))
emit dataChanged (index (baseIndex, 0),
index (baseIndex+newOrder.size()-1, mIdCollection->getColumns()-1));
}
CSMWorld::IdTable::Reordering CSMWorld::IdTable::getReordering() const
{
return mReordering;
}

@ -1,6 +1,8 @@
#ifndef CSM_WOLRD_IDTABLE_H
#define CSM_WOLRD_IDTABLE_H
#include <vector>
#include <QAbstractItemModel>
#include "universalid.hpp"
@ -15,7 +17,18 @@ namespace CSMWorld
{
Q_OBJECT
public:
enum Reordering
{
Reordering_None,
Reordering_WithinTopic
};
private:
CollectionBase *mIdCollection;
Reordering mReordering;
// not implemented
IdTable (const IdTable&);
@ -23,7 +36,7 @@ namespace CSMWorld
public:
IdTable (CollectionBase *idCollection);
IdTable (CollectionBase *idCollection, Reordering reordering = Reordering_WithinTopic);
///< The ownership of \a idCollection is not transferred.
virtual ~IdTable();
@ -63,6 +76,12 @@ namespace CSMWorld
int findColumnIndex (Columns::ColumnId id) const;
///< Return index of column with the given \a id. If no such column exists, an exception is
/// thrown.
void reorderRows (int baseIndex, const std::vector<int>& newOrder);
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
/// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex).
Reordering getReordering() const;
};
}

@ -0,0 +1,14 @@
#ifndef CSM_WOLRD_INFO_H
#define CSM_WOLRD_INFO_H
#include <components/esm/loadinfo.hpp>
namespace CSMWorld
{
struct Info : public ESM::DialInfo
{
std::string mTopicId;
};
}
#endif

@ -0,0 +1,184 @@
#include "infocollection.hpp"
#include <stdexcept>
#include <iterator>
#include <components/esm/esmreader.hpp>
#include <components/esm/loaddial.hpp>
#include <components/misc/stringops.hpp>
void CSMWorld::InfoCollection::load (const Info& record, bool base)
{
int index = searchId (record.mId);
if (index==-1)
{
// new record
Record<Info> record2;
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
(base ? record2.mBase : record2.mModified) = record;
int index = -1;
std::string topic = Misc::StringUtils::lowerCase (record2.get().mTopicId);
if (!record2.get().mPrev.empty())
{
index = getIndex (record2.get().mPrev, topic);
if (index!=-1)
++index;
}
if (index==-1 && !record2.get().mNext.empty())
{
index = getIndex (record2.get().mNext, topic);
}
if (index==-1)
{
Range range = getTopicRange (topic);
index = std::distance (getRecords().begin(), range.second);
}
insertRecord (record2, index);
}
else
{
// old record
Record<Info> record2 = getRecord (index);
if (base)
record2.mBase = record;
else
record2.setModified (record);
setRecord (index, record2);
}
}
int CSMWorld::InfoCollection::getIndex (const std::string& id, const std::string& topic) const
{
std::string fullId = Misc::StringUtils::lowerCase (topic) + "#" + id;
std::pair<RecordConstIterator, RecordConstIterator> range = getTopicRange (topic);
for (; range.first!=range.second; ++range.first)
if (Misc::StringUtils::lowerCase (range.first->get().mId)==fullId)
return std::distance (getRecords().begin(), range.first);
return -1;
}
int CSMWorld::InfoCollection::getAppendIndex (const std::string& id, UniversalId::Type type) const
{
std::string::size_type separator = id.find_last_of ('#');
if (separator==std::string::npos)
throw std::runtime_error ("invalid info ID: " + id);
std::pair<RecordConstIterator, RecordConstIterator> range = getTopicRange (id.substr (0, separator));
if (range.first==range.second)
return Collection<Info, IdAccessor<Info> >::getAppendIndex (id, type);
return std::distance (getRecords().begin(), range.second);
}
bool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector<int>& newOrder)
{
// check if the range is valid
int lastIndex = baseIndex + newOrder.size() -1;
if (lastIndex>=getSize())
return false;
// Check that topics match
if (getRecord (baseIndex).get().mTopicId!=getRecord (lastIndex).get().mTopicId)
return false;
// reorder
return reorderRowsImp (baseIndex, newOrder);
}
void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue)
{
std::string id = Misc::StringUtils::lowerCase (dialogue.mId) + "#" +
reader.getHNOString ("INAM");
if (reader.isNextSub ("DELE"))
{
int index = searchId (id);
reader.skipRecord();
if (index==-1)
{
// deleting a record that does not exist
// ignore it for now
/// \todo report the problem to the user
}
else if (base)
{
removeRows (index, 1);
}
else
{
Record<Info> record = getRecord (index);
record.mState = RecordBase::State_Deleted;
setRecord (index, record);
}
}
else
{
Info record;
record.mTopicId = dialogue.mId;
record.mId = id;
record.load (reader);
load (record, base);
}
}
CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const std::string& topic)
const
{
std::string topic2 = Misc::StringUtils::lowerCase (topic);
std::map<std::string, int>::const_iterator iter = getIdMap().lower_bound (topic2);
// Skip invalid records: The beginning of a topic string could be identical to another topic
// 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());
RecordConstIterator begin = getRecords().begin()+iter->second;
// Find end
RecordConstIterator end = begin;
for (; end!=getRecords().end(); ++end)
if (Misc::StringUtils::lowerCase (end->get().mTopicId)!=topic2)
break;
return Range (begin, end);
}

@ -0,0 +1,50 @@
#ifndef CSM_WOLRD_INFOCOLLECTION_H
#define CSM_WOLRD_INFOCOLLECTION_H
#include "collection.hpp"
#include "info.hpp"
namespace ESM
{
class Dialogue;
}
namespace CSMWorld
{
class InfoCollection : public Collection<Info, IdAccessor<Info> >
{
public:
typedef std::vector<Record<Info> >::const_iterator RecordConstIterator;
typedef std::pair<RecordConstIterator, RecordConstIterator> Range;
private:
void load (const Info& record, bool base);
int getIndex (const std::string& id, const std::string& topic) const;
///< Return index for record \a id or -1 (if not present; deleted records are considered)
///
/// \param id info ID without topic prefix
public:
virtual int getAppendIndex (const std::string& id,
UniversalId::Type type = UniversalId::Type_None) const;
///< \param type Will be ignored, unless the collection supports multiple record types
virtual bool reorderRows (int baseIndex, const std::vector<int>& newOrder);
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
/// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex).
///
/// \return Success?
void load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue);
Range getTopicRange (const std::string& topic) const;
///< Return iterators that point to the beginning and past the end of the range for
/// the given topic.
};
}
#endif

@ -6,7 +6,7 @@
void CSMWorld::CellRef::load (ESM::ESMReader &esm, Cell& cell, const std::string& id)
{
mId = id;
mCellId = cell.mId;
mCell = cell.mId;
if (!mDeleted)
cell.addRef (mId);

@ -16,7 +16,7 @@ namespace CSMWorld
struct CellRef : public ESM::CellRef
{
std::string mId;
std::string mCellId;
std::string mCell;
void load (ESM::ESMReader &esm, Cell& cell, const std::string& id);
///< Load cell ref and register it with \a cell.

@ -530,7 +530,7 @@ void CSMWorld::RefIdCollection::load (ESM::ESMReader& reader, bool base, Univers
}
}
int CSMWorld::RefIdCollection::getAppendIndex (UniversalId::Type type) const
int CSMWorld::RefIdCollection::getAppendIndex (const std::string& id, UniversalId::Type type) const
{
return mData.getAppendIndex (type);
}
@ -540,7 +540,12 @@ std::vector<std::string> CSMWorld::RefIdCollection::getIds (bool listDeleted) co
return mData.getIds (listDeleted);
}
bool CSMWorld::RefIdCollection::reorderRows (int baseIndex, const std::vector<int>& newOrder)
{
return false;
}
void CSMWorld::RefIdCollection::save (int index, ESM::ESMWriter& writer) const
{
mData.save (index, writer);
}
}

@ -92,7 +92,7 @@ namespace CSMWorld
void load (ESM::ESMReader& reader, bool base, UniversalId::Type type);
virtual int getAppendIndex (UniversalId::Type type) const;
virtual int getAppendIndex (const std::string& id, UniversalId::Type type) const;
///< \param type Will be ignored, unless the collection supports multiple record types
virtual std::vector<std::string> getIds (bool listDeleted) const;
@ -100,6 +100,12 @@ namespace CSMWorld
///
/// \param listDeleted include deleted record in the list
virtual bool reorderRows (int baseIndex, const std::vector<int>& newOrder);
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
/// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex).
///
/// \return Success?
void save (int index, ESM::ESMWriter& writer) const;
};
}

@ -31,6 +31,8 @@ namespace
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_TopicInfos, "Topic Infos", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_JournalInfos, "Journal Infos", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables,
"Referenceables", 0 },
@ -58,6 +60,8 @@ namespace
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", ":./spell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Topic, "Topic", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Journal, "Journal", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_TopicInfo, "TopicInfo", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_JournalInfo, "JournalInfo", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" },

@ -91,6 +91,10 @@ namespace CSMWorld
Type_Topic,
Type_Journals,
Type_Journal,
Type_TopicInfos,
Type_TopicInfo,
Type_JournalInfos,
Type_JournalInfo,
Type_Scene
};

@ -26,16 +26,25 @@ namespace CSVDoc
template<class SubViewT, class CreatorFactoryT>
class SubViewFactoryWithCreator : public SubViewFactoryBase
{
bool mSorting;
public:
SubViewFactoryWithCreator (bool sorting = true);
virtual CSVDoc::SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document);
};
template<class SubViewT, class CreatorFactoryT>
SubViewFactoryWithCreator<SubViewT, CreatorFactoryT>::SubViewFactoryWithCreator (bool sorting)
: mSorting (sorting)
{}
template<class SubViewT, class CreatorFactoryT>
CSVDoc::SubView *SubViewFactoryWithCreator<SubViewT, CreatorFactoryT>::makeSubView (
const CSMWorld::UniversalId& id, CSMDoc::Document& document)
{
return new SubViewT (id, document, CreatorFactoryT());
return new SubViewT (id, document, CreatorFactoryT(), mSorting);
}
}

@ -136,41 +136,54 @@ void CSVDoc::View::setupMechanicsMenu()
connect (gmsts, SIGNAL (triggered()), this, SLOT (addGmstsSubView()));
mechanics->addAction (gmsts);
QAction *scripts = new QAction (tr ("Scripts"), this);
connect (scripts, SIGNAL (triggered()), this, SLOT (addScriptsSubView()));
mechanics->addAction (scripts);
QAction *spells = new QAction (tr ("Spells"), this);
connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView()));
mechanics->addAction (spells);
}
void CSVDoc::View::setupCharacterMenu()
{
QMenu *characters = menuBar()->addMenu (tr ("Characters"));
QAction *skills = new QAction (tr ("Skills"), this);
connect (skills, SIGNAL (triggered()), this, SLOT (addSkillsSubView()));
mechanics->addAction (skills);
characters->addAction (skills);
QAction *classes = new QAction (tr ("Classes"), this);
connect (classes, SIGNAL (triggered()), this, SLOT (addClassesSubView()));
mechanics->addAction (classes);
characters->addAction (classes);
QAction *factions = new QAction (tr ("Factions"), this);
connect (factions, SIGNAL (triggered()), this, SLOT (addFactionsSubView()));
mechanics->addAction (factions);
characters->addAction (factions);
QAction *races = new QAction (tr ("Races"), this);
connect (races, SIGNAL (triggered()), this, SLOT (addRacesSubView()));
mechanics->addAction (races);
QAction *scripts = new QAction (tr ("Scripts"), this);
connect (scripts, SIGNAL (triggered()), this, SLOT (addScriptsSubView()));
mechanics->addAction (scripts);
characters->addAction (races);
QAction *birthsigns = new QAction (tr ("Birthsigns"), this);
connect (birthsigns, SIGNAL (triggered()), this, SLOT (addBirthsignsSubView()));
mechanics->addAction (birthsigns);
QAction *spells = new QAction (tr ("Spells"), this);
connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView()));
mechanics->addAction (spells);
characters->addAction (birthsigns);
QAction *topics = new QAction (tr ("Topics"), this);
connect (topics, SIGNAL (triggered()), this, SLOT (addTopicsSubView()));
mechanics->addAction (topics);
characters->addAction (topics);
QAction *journals = new QAction (tr ("Journals"), this);
connect (journals, SIGNAL (triggered()), this, SLOT (addJournalsSubView()));
mechanics->addAction (journals);
characters->addAction (journals);
QAction *topicInfos = new QAction (tr ("Topic Infos"), this);
connect (topicInfos, SIGNAL (triggered()), this, SLOT (addTopicInfosSubView()));
characters->addAction (topicInfos);
QAction *journalInfos = new QAction (tr ("Journal Infos"), this);
connect (journalInfos, SIGNAL (triggered()), this, SLOT (addJournalInfosSubView()));
characters->addAction (journalInfos);
}
void CSVDoc::View::setupAssetsMenu()
@ -189,6 +202,7 @@ void CSVDoc::View::setupUi()
setupViewMenu();
setupWorldMenu();
setupMechanicsMenu();
setupCharacterMenu();
setupAssetsMenu();
}
@ -430,6 +444,16 @@ void CSVDoc::View::addJournalsSubView()
addSubView (CSMWorld::UniversalId::Type_Journals);
}
void CSVDoc::View::addTopicInfosSubView()
{
addSubView (CSMWorld::UniversalId::Type_TopicInfos);
}
void CSVDoc::View::addJournalInfosSubView()
{
addSubView (CSMWorld::UniversalId::Type_JournalInfos);
}
void CSVDoc::View::abortOperation (int type)
{
mDocument->abortOperation (type);

@ -63,6 +63,8 @@ namespace CSVDoc
void setupMechanicsMenu();
void setupCharacterMenu();
void setupAssetsMenu();
void setupUi();
@ -170,6 +172,10 @@ namespace CSVDoc
void addJournalsSubView();
void addTopicInfosSubView();
void addJournalInfosSubView();
void toggleShowStatusBar (bool show);
};
}

@ -75,7 +75,9 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager)
{ CSMWorld::ColumnBase::Display_ClothingType, CSMWorld::Columns::ColumnId_ClothingType, false },
{ CSMWorld::ColumnBase::Display_CreatureType, CSMWorld::Columns::ColumnId_CreatureType, false },
{ CSMWorld::ColumnBase::Display_WeaponType, CSMWorld::Columns::ColumnId_WeaponType, false },
{ CSMWorld::ColumnBase::Display_DialogueType, CSMWorld::Columns::ColumnId_DialogueType, false }
{ CSMWorld::ColumnBase::Display_DialogueType, CSMWorld::Columns::ColumnId_DialogueType, false },
{ CSMWorld::ColumnBase::Display_QuestStatusType, CSMWorld::Columns::ColumnId_QuestStatusType, false },
{ CSMWorld::ColumnBase::Display_Gender, CSMWorld::Columns::ColumnId_Gender, true }
};
for (std::size_t i=0; i<sizeof (sMapping)/sizeof (Mapping); ++i)

@ -19,7 +19,7 @@ void CSVWorld::DialogueCreator::configureCreateCommand (CSMWorld::CreateCommand&
CSVWorld::DialogueCreator::DialogueCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id, int type)
: GenericCreator (data, undoStack, id), mType (type)
: GenericCreator (data, undoStack, id, true), mType (type)
{}
CSVWorld::Creator *CSVWorld::TopicCreatorFactory::makeCreator (CSMWorld::Data& data,

@ -57,14 +57,14 @@ const CSMWorld::UniversalId& CSVWorld::GenericCreator::getCollectionId() const
}
CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id)
const CSMWorld::UniversalId& id, bool relaxedIdRules)
: mData (data), mUndoStack (undoStack), mListId (id), mLocked (false)
{
mLayout = new QHBoxLayout;
mLayout->setContentsMargins (0, 0, 0, 0);
mId = new QLineEdit;
mId->setValidator (new IdValidator (this));
mId->setValidator (new IdValidator (relaxedIdRules, this));
mLayout->addWidget (mId, 1);
mCreate = new QPushButton ("Create");

@ -51,7 +51,7 @@ namespace CSVWorld
public:
GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id);
const CSMWorld::UniversalId& id, bool relaxedIdRules = false);
virtual void setEditLock (bool locked);

@ -12,15 +12,25 @@ bool CSVWorld::IdValidator::isValid (const QChar& c, bool first) const
return false;
}
CSVWorld::IdValidator::IdValidator (QObject *parent) : QValidator (parent) {}
CSVWorld::IdValidator::IdValidator (bool relaxed, QObject *parent)
: QValidator (parent), mRelaxed (relaxed)
{}
QValidator::State CSVWorld::IdValidator::validate (QString& input, int& pos) const
{
bool first = true;
for (QString::const_iterator iter (input.begin()); iter!=input.end(); ++iter, first = false)
if (!isValid (*iter, first))
if (mRelaxed)
{
if (input.indexOf ('"')!=-1 || input.indexOf ("::")!=-1 || input.indexOf ("#")!=-1)
return QValidator::Invalid;
}
else
{
bool first = true;
for (QString::const_iterator iter (input.begin()); iter!=input.end(); ++iter, first = false)
if (!isValid (*iter, first))
return QValidator::Invalid;
}
return QValidator::Acceptable;
}

@ -7,13 +7,16 @@ namespace CSVWorld
{
class IdValidator : public QValidator
{
bool mRelaxed;
private:
bool isValid (const QChar& c, bool first) const;
public:
IdValidator (QObject *parent = 0);
IdValidator (bool relaxed = false, QObject *parent = 0);
///< \param relaxed Relaxed rules for IDs that also functino as user visible text
virtual State validate (QString& input, int& pos) const;

@ -0,0 +1,81 @@
#include "infocreator.hpp"
#include <algorithm>
#include <QLabel>
#include <QLineEdit>
#include <QUuid>
#include <components/misc/stringops.hpp>
#include "../../model/world/data.hpp"
#include "../../model/world/commands.hpp"
#include "../../model/world/columns.hpp"
#include "../../model/world/idtable.hpp"
std::string CSVWorld::InfoCreator::getId() const
{
std::string id = Misc::StringUtils::lowerCase (mTopic->text().toUtf8().constData());
std::string unique = QUuid::createUuid().toByteArray().data();
unique.erase (std::remove (unique.begin(), unique.end(), '-'), unique.end());
unique = unique.substr (1, unique.size()-2);
return id + '#' + unique;
}
void CSVWorld::InfoCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const
{
int index =
dynamic_cast<CSMWorld::IdTable&> (*getData().getTableModel (getCollectionId())).
findColumnIndex (
getCollectionId().getType()==CSMWorld::UniversalId::Type_TopicInfos ?
CSMWorld::Columns::ColumnId_Topic : CSMWorld::Columns::ColumnId_Journal);
command.addValue (index, mTopic->text());
}
CSVWorld::InfoCreator::InfoCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id)
: GenericCreator (data, undoStack, id)
{
QLabel *label = new QLabel ("Topic", this);
insertBeforeButtons (label, false);
mTopic = new QLineEdit (this);
insertBeforeButtons (mTopic, true);
setManualEditing (false);
connect (mTopic, SIGNAL (textChanged (const QString&)), this, SLOT (topicChanged()));
}
void CSVWorld::InfoCreator::reset()
{
mTopic->setText ("");
GenericCreator::reset();
}
std::string CSVWorld::InfoCreator::getErrors() const
{
// We ignore errors from GenericCreator here, because they can never happen in an InfoCreator.
std::string errors;
std::string topic = mTopic->text().toUtf8().constData();
if ((getCollectionId().getType()==CSMWorld::UniversalId::Type_TopicInfos ?
getData().getTopics() : getData().getJournals()).searchId (topic)==-1)
{
errors += "Invalid Topic ID";
}
return errors;
}
void CSVWorld::InfoCreator::topicChanged()
{
update();
}

@ -0,0 +1,42 @@
#ifndef CSV_WORLD_INFOCREATOR_H
#define CSV_WORLD_INFOCREATOR_H
#include "genericcreator.hpp"
class QLineEdit;
namespace CSMWorld
{
class InfoCollection;
}
namespace CSVWorld
{
class InfoCreator : public GenericCreator
{
Q_OBJECT
QLineEdit *mTopic;
virtual std::string getId() const;
virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const;
public:
InfoCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id);
virtual void reset();
virtual std::string getErrors() const;
///< Return formatted error descriptions for the current state of the creator. if an empty
/// string is returned, there is no error.
private slots:
void topicChanged();
};
}
#endif

@ -15,6 +15,7 @@
#include "referencecreator.hpp"
#include "scenesubview.hpp"
#include "dialoguecreator.hpp"
#include "infocreator.hpp"
void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
{
@ -60,6 +61,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
manager.add (CSMWorld::UniversalId::Type_Journal,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, JournalCreatorFactory>);
manager.add (CSMWorld::UniversalId::Type_TopicInfos,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<InfoCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_JournalInfos,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<InfoCreator> > (false));
// Subviews for editing/viewing individual records
manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory<ScriptSubView>);

@ -12,6 +12,7 @@
#include "../../model/world/idtableproxymodel.hpp"
#include "../../model/world/idtable.hpp"
#include "../../model/world/record.hpp"
#include "../../model/world/columns.hpp"
#include "recordstatusdelegate.hpp"
#include "util.hpp"
@ -32,11 +33,43 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
if (mCreateAction)
menu.addAction (mCreateAction);
if (listRevertableSelectedIds().size()>0)
menu.addAction (mRevertAction);
/// \todo Reverting temporarily disabled on tables that support reordering, because
/// revert logic currently can not handle reordering.
if (mModel->getReordering()==CSMWorld::IdTable::Reordering_None)
if (listRevertableSelectedIds().size()>0)
menu.addAction (mRevertAction);
if (listDeletableSelectedIds().size()>0)
menu.addAction (mDeleteAction);
if (mModel->getReordering()==CSMWorld::IdTable::Reordering_WithinTopic)
{
/// \todo allow reordering of multiple rows
if (selectedRows.size()==1)
{
int row =selectedRows.begin()->row();
int column = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Topic);
if (column==-1)
column = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Journal);
if (column!=-1)
{
if (row>0 && mProxyModel->data (mProxyModel->index (row, column))==
mProxyModel->data (mProxyModel->index (row-1, column)))
{
menu.addAction (mMoveUpAction);
}
if (row<mProxyModel->rowCount()-1 && mProxyModel->data (mProxyModel->index (row, column))==
mProxyModel->data (mProxyModel->index (row+1, column)))
{
menu.addAction (mMoveDownAction);
}
}
}
}
}
menu.exec (event->globalPos());
@ -121,7 +154,7 @@ std::vector<std::string> CSVWorld::Table::listDeletableSelectedIds() const
}
CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack,
bool createAndDelete)
bool createAndDelete, bool sorting)
: mUndoStack (undoStack), mCreateAction (0), mEditLock (false), mRecordStatusDisplay (0)
{
mModel = &dynamic_cast<CSMWorld::IdTable&> (*data.getTableModel (id));
@ -132,7 +165,7 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q
setModel (mProxyModel);
horizontalHeader()->setResizeMode (QHeaderView::Interactive);
verticalHeader()->hide();
setSortingEnabled (true);
setSortingEnabled (sorting);
setSelectionBehavior (QAbstractItemView::SelectRows);
setSelectionMode (QAbstractItemView::ExtendedSelection);
@ -176,6 +209,14 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q
connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord()));
addAction (mDeleteAction);
mMoveUpAction = new QAction (tr ("Move Up"), this);
connect (mMoveUpAction, SIGNAL (triggered()), this, SLOT (moveUpRecord()));
addAction (mMoveUpAction);
mMoveDownAction = new QAction (tr ("Move Down"), this);
connect (mMoveDownAction, SIGNAL (triggered()), this, SLOT (moveDownRecord()));
addAction (mMoveDownAction);
connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (tableSizeUpdate()));
@ -254,6 +295,64 @@ void CSVWorld::Table::editRecord()
}
}
void CSVWorld::Table::moveUpRecord()
{
QModelIndexList selectedRows = selectionModel()->selectedRows();
if (selectedRows.size()==1)
{
int row2 =selectedRows.begin()->row();
if (row2>0)
{
int row = row2-1;
row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row();
row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row();
if (row2<=row)
throw std::runtime_error ("Inconsistent row order");
std::vector<int> newOrder (row2-row+1);
newOrder[0] = row2-row;
newOrder[row2-row] = 0;
for (int i=1; i<row2-row; ++i)
newOrder[i] = i;
mUndoStack.push (new CSMWorld::ReorderRowsCommand (*mModel, row, newOrder));
}
}
}
void CSVWorld::Table::moveDownRecord()
{
QModelIndexList selectedRows = selectionModel()->selectedRows();
if (selectedRows.size()==1)
{
int row =selectedRows.begin()->row();
if (row<mProxyModel->rowCount()-1)
{
int row2 = row+1;
row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row();
row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row();
if (row2<=row)
throw std::runtime_error ("Inconsistent row order");
std::vector<int> newOrder (row2-row+1);
newOrder[0] = row2-row;
newOrder[row2-row] = 0;
for (int i=1; i<row2-row; ++i)
newOrder[i] = i;
mUndoStack.push (new CSMWorld::ReorderRowsCommand (*mModel, row, newOrder));
}
}
}
void CSVWorld::Table::updateEditorSetting (const QString &settingName, const QString &settingValue)
{
int columns = mModel->columnCount();

@ -34,6 +34,8 @@ namespace CSVWorld
QAction *mCreateAction;
QAction *mRevertAction;
QAction *mDeleteAction;
QAction *mMoveUpAction;
QAction *mMoveDownAction;
CSMWorld::IdTableProxyModel *mProxyModel;
CSMWorld::IdTable *mModel;
bool mEditLock;
@ -49,8 +51,9 @@ namespace CSVWorld
public:
Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete);
Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, bool sorting);
///< \param createAndDelete Allow creation and deletion of records.
/// \param sorting Allow changing order of rows in the view via column headers.
void setEditLock (bool locked);
@ -79,6 +82,10 @@ namespace CSVWorld
void editRecord();
void moveUpRecord();
void moveDownRecord();
public slots:
void tableSizeUpdate();

@ -12,7 +12,7 @@
#include "creator.hpp"
CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document,
const CreatorFactoryBase& creatorFactory)
const CreatorFactoryBase& creatorFactory, bool sorting)
: SubView (id)
{
QVBoxLayout *layout = new QVBoxLayout;
@ -23,7 +23,7 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D
new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this), 0);
layout->insertWidget (0, mTable =
new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete()), 2);
new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete(), sorting), 2);
CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this);

@ -26,7 +26,7 @@ namespace CSVWorld
public:
TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document,
const CreatorFactoryBase& creatorFactory);
const CreatorFactoryBase& creatorFactory, bool sorting);
virtual void setEditLock (bool locked);

@ -58,13 +58,13 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
}
// NPC faction
if (!info.mNpcFaction.empty())
if (!info.mFaction.empty())
{
if (isCreature)
return false;
MWMechanics::NpcStats& stats = MWWorld::Class::get (mActor).getNpcStats (mActor);
std::map<std::string, int>::iterator iter = stats.getFactionRanks().find ( Misc::StringUtils::lowerCase (info.mNpcFaction));
std::map<std::string, int>::iterator iter = stats.getFactionRanks().find ( Misc::StringUtils::lowerCase (info.mFaction));
if (iter==stats.getFactionRanks().end())
return false;

@ -253,6 +253,8 @@ namespace MWGui
void InventoryWindow::open()
{
mPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
updateEncumbranceBar();
mItemView->update();

@ -70,8 +70,10 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener)
if (it == mStores.end()) {
if (n.val == ESM::REC_INFO) {
std::string id = esm.getHNOString("INAM");
if (dialogue) {
dialogue->mInfo.push_back(ESM::DialInfo());
dialogue->mInfo.back().mId = id;
dialogue->mInfo.back().load(esm);
} else {
std::cerr << "error: info record without dialog" << std::endl;

@ -258,7 +258,7 @@ namespace MWWorld
typename std::vector<T *>::iterator sharedIter = mShared.begin();
typename std::vector<T *>::iterator end = sharedIter + mStatic.size();
while (sharedIter != mShared.end() && sharedIter != end) {
while (sharedIter != mShared.end() && sharedIter != end) {
if((*sharedIter)->mId == item.mId) {
mShared.erase(sharedIter);
break;

@ -2239,6 +2239,7 @@ namespace MWWorld
void World::updateAnimParts(const Ptr& actor)
{
mRendering->updateAnimParts(actor);
if (actor.mCell && actor.mCell == mWorldScene->getCurrentCell())
mRendering->updateAnimParts(actor);
}
}

@ -10,7 +10,6 @@ namespace ESM
void DialInfo::load(ESMReader &esm)
{
mId = esm.getHNString("INAM");
mPrev = esm.getHNString("PNAM");
mNext = esm.getHNString("NNAM");
@ -50,8 +49,8 @@ void DialInfo::load(ESMReader &esm)
mFactionLess = false;
if (subName.val == REC_FNAM)
{
mNpcFaction = esm.getHString();
if (mNpcFaction == "FFFF")
mFaction = esm.getHString();
if (mFaction == "FFFF")
mFactionLess = true;
if (esm.isEmptyOrGetName())
return;
@ -124,14 +123,13 @@ void DialInfo::load(ESMReader &esm)
void DialInfo::save(ESMWriter &esm) const
{
esm.writeHNCString("INAM", mId);
esm.writeHNCString("PNAM", mPrev);
esm.writeHNCString("NNAM", mNext);
esm.writeHNT("DATA", mData, 12);
esm.writeHNOCString("ONAM", mActor);
esm.writeHNOCString("RNAM", mRace);
esm.writeHNOCString("CNAM", mClass);
esm.writeHNOCString("FNAM", mNpcFaction);
esm.writeHNOCString("FNAM", mFaction);
esm.writeHNOCString("ANAM", mCell);
esm.writeHNOCString("DNAM", mPcFaction);
esm.writeHNOCString("SNAM", mSound);
@ -155,4 +153,28 @@ void DialInfo::save(ESMWriter &esm) const
}
}
void DialInfo::blank()
{
mData.mUnknown1 = 0;
mData.mDisposition = 0;
mData.mRank = 0;
mData.mGender = 0;
mData.mPCrank = 0;
mData.mUnknown2 = 0;
mSelects.clear();
mPrev.clear();
mNext.clear();
mActor.clear();
mRace.clear();
mClass.clear();
mFaction.clear();
mPcFaction.clear();
mCell.clear();
mSound.clear();
mResponse.clear();
mResultScript.clear();
mFactionLess = false;
mQuestStatus = QS_None;
}
}

@ -50,10 +50,10 @@ struct DialInfo
// Journal quest indices (introduced with the quest system in Tribunal)
enum QuestStatus
{
QS_None,
QS_Name,
QS_Finished,
QS_Restart,
QS_None = 0,
QS_Name = 1,
QS_Finished = 2,
QS_Restart = 3,
QS_Deleted
};
@ -65,7 +65,7 @@ struct DialInfo
std::string mId, mPrev, mNext;
// Various references used in determining when to select this item.
std::string mActor, mRace, mClass, mNpcFaction, mPcFaction, mCell;
std::string mActor, mRace, mClass, mFaction, mPcFaction, mCell;
// Sound and text associated with this item
std::string mSound, mResponse;
@ -102,6 +102,9 @@ struct DialInfo
void load(ESMReader &esm);
void save(ESMWriter &esm) const;
void blank();
///< Set record to default state (does not touch the ID).
};
}

Loading…
Cancel
Save