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

Conflicts:
	apps/openmw/mwgui/inventoryitemmodel.cpp
This commit is contained in:
scrawl 2013-11-14 14:25:22 +01:00
commit 956d8adb99
50 changed files with 1461 additions and 97 deletions

View file

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

View file

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

View file

@ -24,11 +24,11 @@ opencs_units (model/world
opencs_units_noqt (model/world opencs_units_noqt (model/world
universalid record commands columnbase scriptcontext cell refidcollection 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 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 opencs_units (view/world
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
cellcreator referenceablecreator referencecreator scenesubview scenetoolbar scenetool cellcreator referenceablecreator referencecreator scenesubview scenetoolbar scenetool
scenetoolmode scenetoolmode infocreator
) )
opencs_units (view/render opencs_units (view/render

View file

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

View file

@ -7,6 +7,10 @@
#include <QUndoStack> #include <QUndoStack>
#include <components/esm/loaddial.hpp>
#include "../world/infocollection.hpp"
#include "document.hpp" #include "document.hpp"
#include "savingstate.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) CSMDoc::WriteRefIdCollectionStage::WriteRefIdCollectionStage (Document& document, SavingState& state)
: mDocument (document), mState (state) : mDocument (document), mState (state)
{} {}

View file

@ -3,13 +3,23 @@
#include "stage.hpp" #include "stage.hpp"
#include "savingstate.hpp"
#include "../world/record.hpp" #include "../world/record.hpp"
#include "../world/idcollection.hpp" #include "../world/idcollection.hpp"
#include "../filter/filter.hpp" #include "../filter/filter.hpp"
#include "savingstate.hpp"
namespace ESM
{
struct Dialogue;
}
namespace CSMWorld
{
class InfoCollection;
}
namespace CSMDoc namespace CSMDoc
{ {
class Document; 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 class WriteRefIdCollectionStage : public Stage
{ {
Document& mDocument; Document& mDocument;

View file

@ -51,6 +51,18 @@ namespace CSMWorld
Collection (const Collection&); Collection (const Collection&);
Collection& operator= (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: public:
Collection(); Collection();
@ -104,7 +116,8 @@ namespace CSMWorld
virtual const Record<ESXRecordT>& getRecord (int index) const; 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 ///< \param type Will be ignored, unless the collection supports multiple record types
virtual std::vector<std::string> getIds (bool listDeleted = true) const; virtual std::vector<std::string> getIds (bool listDeleted = true) const;
@ -112,12 +125,74 @@ 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,
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 addColumn (Column<ESXRecordT> *column);
void setRecord (int index, const Record<ESXRecordT>& record); void setRecord (int index, const Record<ESXRecordT>& record);
///< \attention This function must not change the ID. ///< \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> template<typename ESXRecordT, typename IdAccessorT>
Collection<ESXRecordT, IdAccessorT>::Collection() Collection<ESXRecordT, IdAccessorT>::Collection()
{} {}
@ -142,8 +217,7 @@ namespace CSMWorld
record2.mState = Record<ESXRecordT>::State_ModifiedOnly; record2.mState = Record<ESXRecordT>::State_ModifiedOnly;
record2.mModified = record; record2.mModified = record;
mRecords.push_back (record2); insertRecord (record2, getAppendIndex (id));
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), mRecords.size()-1));
} }
else else
{ {
@ -260,7 +334,12 @@ namespace CSMWorld
ESXRecordT record; ESXRecordT record;
IdAccessorT().getId (record) = id; IdAccessorT().getId (record) = id;
record.blank(); 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> template<typename ESXRecordT, typename IdAccessorT>
@ -286,14 +365,14 @@ namespace CSMWorld
void Collection<ESXRecordT, IdAccessorT>::appendRecord (const RecordBase& record, void Collection<ESXRecordT, IdAccessorT>::appendRecord (const RecordBase& record,
UniversalId::Type type) UniversalId::Type type)
{ {
mRecords.push_back (dynamic_cast<const Record<ESXRecordT>&> (record)); insertRecord (record,
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (IdAccessorT().getId ( getAppendIndex (IdAccessorT().getId (
dynamic_cast<const Record<ESXRecordT>&> (record).get())), dynamic_cast<const Record<ESXRecordT>&> (record).get()), type), type);
mRecords.size()-1));
} }
template<typename ESXRecordT, typename IdAccessorT> 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()); return static_cast<int> (mRecords.size());
} }
@ -326,6 +405,29 @@ namespace CSMWorld
return mRecords.at (index); 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> template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::setRecord (int index, const Record<ESXRecordT>& record) void Collection<ESXRecordT, IdAccessorT>::setRecord (int index, const Record<ESXRecordT>& record)
{ {
@ -334,6 +436,12 @@ namespace CSMWorld
mRecords.at (index) = record; mRecords.at (index) = record;
} }
template<typename ESXRecordT, typename IdAccessorT>
bool Collection<ESXRecordT, IdAccessorT>::reorderRows (int baseIndex, const std::vector<int>& newOrder)
{
return false;
}
} }
#endif #endif

View file

@ -2,6 +2,7 @@
#define CSM_WOLRD_COLLECTIONBASE_H #define CSM_WOLRD_COLLECTIONBASE_H
#include <string> #include <string>
#include <vector>
#include "universalid.hpp" #include "universalid.hpp"
#include "columns.hpp" #include "columns.hpp"
@ -77,7 +78,8 @@ namespace CSMWorld
virtual const RecordBase& getRecord (int index) const = 0; 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 ///< \param type Will be ignored, unless the collection supports multiple record types
virtual std::vector<std::string> getIds (bool listDeleted = true) const = 0; 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 /// \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; 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.

View file

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

View file

@ -9,6 +9,7 @@
#include "columnbase.hpp" #include "columnbase.hpp"
#include "columns.hpp" #include "columns.hpp"
#include "info.hpp"
namespace CSMWorld namespace CSMWorld
{ {
@ -814,14 +815,14 @@ namespace CSMWorld
virtual QVariant get (const Record<ESXRecordT>& record) const 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) virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{ {
ESXRecordT record2 = record.get(); ESXRecordT record2 = record.get();
record2.mCellId = data.toString().toUtf8().constData(); record2.mCell = data.toString().toUtf8().constData();
record.setModified (record2); record.setModified (record2);
} }
@ -1348,6 +1349,340 @@ namespace CSMWorld
return false; 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 #endif

View file

@ -160,6 +160,18 @@ namespace CSMWorld
{ ColumnId_DoorPositionYRot, "Teleport Rot Y" }, { ColumnId_DoorPositionYRot, "Teleport Rot Y" },
{ ColumnId_DoorPositionZRot, "Teleport Rot Z" }, { ColumnId_DoorPositionZRot, "Teleport Rot Z" },
{ ColumnId_DialogueType, "Dialogue Type" }, { 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_Scope, "Scope", },
{ ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue1, "Use value 1" },
@ -276,6 +288,16 @@ namespace
"Topic", "Voice", "Greeting", "Persuasion", 0 "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) const char **getEnumNames (CSMWorld::Columns::ColumnId column)
{ {
switch (column) switch (column)
@ -291,6 +313,8 @@ namespace
case CSMWorld::Columns::ColumnId_Modification: return sModificationEnums; case CSMWorld::Columns::ColumnId_Modification: return sModificationEnums;
case CSMWorld::Columns::ColumnId_ValueType: return sVarTypeEnums; case CSMWorld::Columns::ColumnId_ValueType: return sVarTypeEnums;
case CSMWorld::Columns::ColumnId_DialogueType: return sDialogueTypeEnums; case CSMWorld::Columns::ColumnId_DialogueType: return sDialogueTypeEnums;
case CSMWorld::Columns::ColumnId_QuestStatusType: return sQuestStatusTypes;
case CSMWorld::Columns::ColumnId_Gender: return sGenderEnums;
default: return 0; default: return 0;
} }

View file

@ -153,7 +153,19 @@ namespace CSMWorld
ColumnId_DoorPositionYRot = 140, ColumnId_DoorPositionYRot = 140,
ColumnId_DoorPositionZRot = 141, ColumnId_DoorPositionZRot = 141,
ColumnId_DialogueType = 142, 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 // Allocated to a separate value range, so we don't get a collision should we ever need
// to extend the number of use values. // to extend the number of use values.

View file

@ -123,3 +123,25 @@ void CSMWorld::DeleteCommand::undo()
{ {
mModel.setRecord (mId, *mOld); 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);
}

View file

@ -5,6 +5,7 @@
#include <string> #include <string>
#include <map> #include <map>
#include <vector>
#include <QVariant> #include <QVariant>
#include <QUndoCommand> #include <QUndoCommand>
@ -99,6 +100,21 @@ namespace CSMWorld
virtual void undo(); 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 #endif

View file

@ -160,6 +160,29 @@ CSMWorld::Data::Data() : mRefs (mCells)
mJournals.addColumn (new RecordStateColumn<ESM::Dialogue>); mJournals.addColumn (new RecordStateColumn<ESM::Dialogue>);
mJournals.addColumn (new DialogueTypeColumn<ESM::Dialogue> (true)); 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 StringIdColumn<Cell>);
mCells.addColumn (new RecordStateColumn<Cell>); mCells.addColumn (new RecordStateColumn<Cell>);
mCells.addColumn (new FixedRecordTypeColumn<Cell> (UniversalId::Type_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 (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell);
addModel (new IdTable (&mTopics), UniversalId::Type_Topics, UniversalId::Type_Topic); addModel (new IdTable (&mTopics), UniversalId::Type_Topics, UniversalId::Type_Topic);
addModel (new IdTable (&mJournals), UniversalId::Type_Journals, UniversalId::Type_Journal); 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 (&mCells), UniversalId::Type_Cells, UniversalId::Type_Cell);
addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables, addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables,
UniversalId::Type_Referenceable); UniversalId::Type_Referenceable);
@ -362,6 +387,25 @@ CSMWorld::IdCollection<ESM::Dialogue>& CSMWorld::Data::getJournals()
return mJournals; 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 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()); reader.open (path.string());
const ESM::Dialogue *dialogue = 0;
mAuthor = reader.getAuthor(); mAuthor = reader.getAuthor();
mDescription = reader.getDesc(); 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) if (record.mType==ESM::Dialogue::Journal)
{ {
mJournals.load (record, base); mJournals.load (record, base);
dialogue = &mJournals.getRecord (id).get();
} }
else if (record.mType==ESM::Dialogue::Deleted) 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)) if (mJournals.tryDelete (id))
{ {
/// \todo handle info records /// \todo handle info records
@ -524,11 +574,29 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base, b
else else
{ {
mTopics.load (record, base); mTopics.load (record, base);
dialogue = &mTopics.getRecord (id).get();
} }
break; 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;
}
case ESM::REC_FILT: case ESM::REC_FILT:
if (project) if (project)
@ -545,6 +613,7 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base, b
default: default:
/// \todo throw an exception instead, once all records are implemented /// \todo throw an exception instead, once all records are implemented
/// or maybe report error and continue?
reader.skipRecord(); reader.skipRecord();
} }
} }

View file

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

View file

@ -4,15 +4,12 @@
#include "collectionbase.hpp" #include "collectionbase.hpp"
#include "columnbase.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() CSMWorld::IdTable::~IdTable()
{ {}
}
int CSMWorld::IdTable::rowCount (const QModelIndex & parent) const 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) 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); beginInsertRows (QModelIndex(), index, index);
@ -138,7 +135,7 @@ void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& reco
if (index==-1) if (index==-1)
{ {
int index = mIdCollection->getAppendIndex(); int index = mIdCollection->getAppendIndex (id);
beginInsertRows (QModelIndex(), index, index); beginInsertRows (QModelIndex(), index, index);
@ -168,3 +165,16 @@ int CSMWorld::IdTable::findColumnIndex (Columns::ColumnId id) const
{ {
return mIdCollection->findColumnIndex (id); 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;
}

View file

@ -1,6 +1,8 @@
#ifndef CSM_WOLRD_IDTABLE_H #ifndef CSM_WOLRD_IDTABLE_H
#define CSM_WOLRD_IDTABLE_H #define CSM_WOLRD_IDTABLE_H
#include <vector>
#include <QAbstractItemModel> #include <QAbstractItemModel>
#include "universalid.hpp" #include "universalid.hpp"
@ -15,7 +17,18 @@ namespace CSMWorld
{ {
Q_OBJECT Q_OBJECT
public:
enum Reordering
{
Reordering_None,
Reordering_WithinTopic
};
private:
CollectionBase *mIdCollection; CollectionBase *mIdCollection;
Reordering mReordering;
// not implemented // not implemented
IdTable (const IdTable&); IdTable (const IdTable&);
@ -23,7 +36,7 @@ namespace CSMWorld
public: public:
IdTable (CollectionBase *idCollection); IdTable (CollectionBase *idCollection, Reordering reordering = Reordering_WithinTopic);
///< The ownership of \a idCollection is not transferred. ///< The ownership of \a idCollection is not transferred.
virtual ~IdTable(); virtual ~IdTable();
@ -63,6 +76,12 @@ namespace CSMWorld
int findColumnIndex (Columns::ColumnId id) const; int findColumnIndex (Columns::ColumnId id) const;
///< Return index of column with the given \a id. If no such column exists, an exception is ///< Return index of column with the given \a id. If no such column exists, an exception is
/// thrown. /// 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;
}; };
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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); return mData.getAppendIndex (type);
} }
@ -540,6 +540,11 @@ std::vector<std::string> CSMWorld::RefIdCollection::getIds (bool listDeleted) co
return mData.getIds (listDeleted); 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 void CSMWorld::RefIdCollection::save (int index, ESM::ESMWriter& writer) const
{ {
mData.save (index, writer); mData.save (index, writer);

View file

@ -92,7 +92,7 @@ namespace CSMWorld
void load (ESM::ESMReader& reader, bool base, UniversalId::Type type); 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 ///< \param type Will be ignored, unless the collection supports multiple record types
virtual std::vector<std::string> getIds (bool listDeleted) const; virtual std::vector<std::string> getIds (bool listDeleted) const;
@ -100,6 +100,12 @@ namespace CSMWorld
/// ///
/// \param listDeleted include deleted record in the list /// \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; void save (int index, ESM::ESMWriter& writer) const;
}; };
} }

View file

@ -31,6 +31,8 @@ namespace
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 }, { 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_Topics, "Topics", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", 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_Cells, "Cells", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables,
"Referenceables", 0 }, "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_Spell, "Spell", ":./spell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Topic, "Topic", 0 }, { 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_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_Cell, "Cell", ":./cell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" },

View file

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

View file

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

View file

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

View file

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

View file

@ -75,7 +75,9 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager)
{ CSMWorld::ColumnBase::Display_ClothingType, CSMWorld::Columns::ColumnId_ClothingType, false }, { CSMWorld::ColumnBase::Display_ClothingType, CSMWorld::Columns::ColumnId_ClothingType, false },
{ CSMWorld::ColumnBase::Display_CreatureType, CSMWorld::Columns::ColumnId_CreatureType, false }, { CSMWorld::ColumnBase::Display_CreatureType, CSMWorld::Columns::ColumnId_CreatureType, false },
{ CSMWorld::ColumnBase::Display_WeaponType, CSMWorld::Columns::ColumnId_WeaponType, 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) for (std::size_t i=0; i<sizeof (sMapping)/sizeof (Mapping); ++i)

View file

@ -19,7 +19,7 @@ void CSVWorld::DialogueCreator::configureCreateCommand (CSMWorld::CreateCommand&
CSVWorld::DialogueCreator::DialogueCreator (CSMWorld::Data& data, QUndoStack& undoStack, CSVWorld::DialogueCreator::DialogueCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id, int type) 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, CSVWorld::Creator *CSVWorld::TopicCreatorFactory::makeCreator (CSMWorld::Data& data,

View file

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

View file

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

View file

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

View file

@ -7,13 +7,16 @@ namespace CSVWorld
{ {
class IdValidator : public QValidator class IdValidator : public QValidator
{ {
bool mRelaxed;
private: private:
bool isValid (const QChar& c, bool first) const; bool isValid (const QChar& c, bool first) const;
public: 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; virtual State validate (QString& input, int& pos) const;

View file

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

View file

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

View file

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

View file

@ -12,6 +12,7 @@
#include "../../model/world/idtableproxymodel.hpp" #include "../../model/world/idtableproxymodel.hpp"
#include "../../model/world/idtable.hpp" #include "../../model/world/idtable.hpp"
#include "../../model/world/record.hpp" #include "../../model/world/record.hpp"
#include "../../model/world/columns.hpp"
#include "recordstatusdelegate.hpp" #include "recordstatusdelegate.hpp"
#include "util.hpp" #include "util.hpp"
@ -32,11 +33,43 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
if (mCreateAction) if (mCreateAction)
menu.addAction (mCreateAction); menu.addAction (mCreateAction);
if (listRevertableSelectedIds().size()>0) /// \todo Reverting temporarily disabled on tables that support reordering, because
menu.addAction (mRevertAction); /// 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) if (listDeletableSelectedIds().size()>0)
menu.addAction (mDeleteAction); 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()); 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, 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) : mUndoStack (undoStack), mCreateAction (0), mEditLock (false), mRecordStatusDisplay (0)
{ {
mModel = &dynamic_cast<CSMWorld::IdTable&> (*data.getTableModel (id)); 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); setModel (mProxyModel);
horizontalHeader()->setResizeMode (QHeaderView::Interactive); horizontalHeader()->setResizeMode (QHeaderView::Interactive);
verticalHeader()->hide(); verticalHeader()->hide();
setSortingEnabled (true); setSortingEnabled (sorting);
setSelectionBehavior (QAbstractItemView::SelectRows); setSelectionBehavior (QAbstractItemView::SelectRows);
setSelectionMode (QAbstractItemView::ExtendedSelection); 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())); connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord()));
addAction (mDeleteAction); 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)), connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (tableSizeUpdate())); 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) void CSVWorld::Table::updateEditorSetting (const QString &settingName, const QString &settingValue)
{ {
int columns = mModel->columnCount(); int columns = mModel->columnCount();

View file

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

View file

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

View file

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

View file

@ -58,13 +58,13 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
} }
// NPC faction // NPC faction
if (!info.mNpcFaction.empty()) if (!info.mFaction.empty())
{ {
if (isCreature) if (isCreature)
return false; return false;
MWMechanics::NpcStats& stats = MWWorld::Class::get (mActor).getNpcStats (mActor); 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()) if (iter==stats.getFactionRanks().end())
return false; return false;

View file

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

View file

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

View file

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

View file

@ -10,7 +10,6 @@ namespace ESM
void DialInfo::load(ESMReader &esm) void DialInfo::load(ESMReader &esm)
{ {
mId = esm.getHNString("INAM");
mPrev = esm.getHNString("PNAM"); mPrev = esm.getHNString("PNAM");
mNext = esm.getHNString("NNAM"); mNext = esm.getHNString("NNAM");
@ -50,8 +49,8 @@ void DialInfo::load(ESMReader &esm)
mFactionLess = false; mFactionLess = false;
if (subName.val == REC_FNAM) if (subName.val == REC_FNAM)
{ {
mNpcFaction = esm.getHString(); mFaction = esm.getHString();
if (mNpcFaction == "FFFF") if (mFaction == "FFFF")
mFactionLess = true; mFactionLess = true;
if (esm.isEmptyOrGetName()) if (esm.isEmptyOrGetName())
return; return;
@ -124,14 +123,13 @@ void DialInfo::load(ESMReader &esm)
void DialInfo::save(ESMWriter &esm) const void DialInfo::save(ESMWriter &esm) const
{ {
esm.writeHNCString("INAM", mId);
esm.writeHNCString("PNAM", mPrev); esm.writeHNCString("PNAM", mPrev);
esm.writeHNCString("NNAM", mNext); esm.writeHNCString("NNAM", mNext);
esm.writeHNT("DATA", mData, 12); esm.writeHNT("DATA", mData, 12);
esm.writeHNOCString("ONAM", mActor); esm.writeHNOCString("ONAM", mActor);
esm.writeHNOCString("RNAM", mRace); esm.writeHNOCString("RNAM", mRace);
esm.writeHNOCString("CNAM", mClass); esm.writeHNOCString("CNAM", mClass);
esm.writeHNOCString("FNAM", mNpcFaction); esm.writeHNOCString("FNAM", mFaction);
esm.writeHNOCString("ANAM", mCell); esm.writeHNOCString("ANAM", mCell);
esm.writeHNOCString("DNAM", mPcFaction); esm.writeHNOCString("DNAM", mPcFaction);
esm.writeHNOCString("SNAM", mSound); 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;
}
} }

View file

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