From 88e09159c427761508244e3786d055ba93213cd0 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 21 Oct 2013 18:04:40 +0200 Subject: [PATCH 01/26] splitting off characters menu from mechanics menu (was getting too big) --- apps/opencs/view/doc/view.cpp | 50 ++++++++++++++++++++--------------- apps/opencs/view/doc/view.hpp | 2 ++ 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 5713449f2..733f5b2bb 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -136,41 +136,46 @@ void CSVDoc::View::setupMechanicsMenu() connect (gmsts, SIGNAL (triggered()), this, SLOT (addGmstsSubView())); 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); connect (scripts, SIGNAL (triggered()), this, SLOT (addScriptsSubView())); 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); 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())); + 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); 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); } void CSVDoc::View::setupAssetsMenu() @@ -189,6 +194,7 @@ void CSVDoc::View::setupUi() setupViewMenu(); setupWorldMenu(); setupMechanicsMenu(); + setupCharacterMenu(); setupAssetsMenu(); } diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 2a31d9d80..7c13e374f 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -63,6 +63,8 @@ namespace CSVDoc void setupMechanicsMenu(); + void setupCharacterMenu(); + void setupAssetsMenu(); void setupUi(); From aa61948801067f4d96cf0000c0862095275f4b27 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 22 Oct 2013 11:08:37 +0200 Subject: [PATCH 02/26] relaxed rules for new IDs when ID is also a user visible text --- apps/opencs/view/world/dialoguecreator.cpp | 2 +- apps/opencs/view/world/genericcreator.cpp | 4 ++-- apps/opencs/view/world/genericcreator.hpp | 2 +- apps/opencs/view/world/idvalidator.cpp | 20 +++++++++++++++----- apps/opencs/view/world/idvalidator.hpp | 5 ++++- 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/apps/opencs/view/world/dialoguecreator.cpp b/apps/opencs/view/world/dialoguecreator.cpp index c16214283..3523d5e32 100644 --- a/apps/opencs/view/world/dialoguecreator.cpp +++ b/apps/opencs/view/world/dialoguecreator.cpp @@ -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, diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index df43d6c5f..ba2b3665a 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -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"); diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 6752d8591..8dd2ca911 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -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); diff --git a/apps/opencs/view/world/idvalidator.cpp b/apps/opencs/view/world/idvalidator.cpp index cf6e5d77b..c685e44ee 100644 --- a/apps/opencs/view/world/idvalidator.cpp +++ b/apps/opencs/view/world/idvalidator.cpp @@ -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) 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; } \ No newline at end of file diff --git a/apps/opencs/view/world/idvalidator.hpp b/apps/opencs/view/world/idvalidator.hpp index db0ecb27a..8ca162440 100644 --- a/apps/opencs/view/world/idvalidator.hpp +++ b/apps/opencs/view/world/idvalidator.hpp @@ -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; From dc473221e79775dde811359cdb155add62ea5a1b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 22 Oct 2013 11:21:12 +0200 Subject: [PATCH 03/26] added ID-argument to CollectionBase::getAppendIndex (required for info record collection) --- apps/opencs/model/world/collection.hpp | 6 ++++-- apps/opencs/model/world/collectionbase.hpp | 3 ++- apps/opencs/model/world/idtable.cpp | 4 ++-- apps/opencs/model/world/refidcollection.cpp | 2 +- apps/opencs/model/world/refidcollection.hpp | 2 +- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 84a00cef8..5ae53e710 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -104,7 +104,8 @@ namespace CSMWorld virtual const Record& 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 getIds (bool listDeleted = true) const; @@ -293,7 +294,8 @@ namespace CSMWorld } template - int Collection::getAppendIndex (UniversalId::Type type) const + int Collection::getAppendIndex (const std::string& id, + UniversalId::Type type) const { return static_cast (mRecords.size()); } diff --git a/apps/opencs/model/world/collectionbase.hpp b/apps/opencs/model/world/collectionbase.hpp index ff6dab247..8045ea543 100644 --- a/apps/opencs/model/world/collectionbase.hpp +++ b/apps/opencs/model/world/collectionbase.hpp @@ -76,7 +76,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 getIds (bool listDeleted = true) const = 0; diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index baaf75289..f37e3894e 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -118,7 +118,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 +138,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); diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index cda2711cc..b4352ef2e 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -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); } diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp index 22f83150d..e5e205955 100644 --- a/apps/opencs/model/world/refidcollection.hpp +++ b/apps/opencs/model/world/refidcollection.hpp @@ -87,7 +87,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 getIds (bool listDeleted) const; From 67bc0a0c70379ed703ffcfb513b78b090f51bc77 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 22 Oct 2013 11:32:10 +0200 Subject: [PATCH 04/26] additional modification to the IDValidator (restricting relaxed mode slightly more) --- apps/opencs/view/world/idvalidator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/world/idvalidator.cpp b/apps/opencs/view/world/idvalidator.cpp index c685e44ee..7c210daae 100644 --- a/apps/opencs/view/world/idvalidator.cpp +++ b/apps/opencs/view/world/idvalidator.cpp @@ -20,7 +20,7 @@ QValidator::State CSVWorld::IdValidator::validate (QString& input, int& pos) con { if (mRelaxed) { - if (input.indexOf ('"')!=-1 || input.indexOf ("::")!=-1) + if (input.indexOf ('"')!=-1 || input.indexOf ("::")!=-1 || input.indexOf ("#")!=-1) return QValidator::Invalid; } else From 525d6fadece204ce6b844b5b70488f6a6ca8e0f5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 27 Oct 2013 14:00:25 +0100 Subject: [PATCH 05/26] added Collection insert function for arbitrary position and reimplemented appendRecord in via this function --- apps/opencs/model/world/collection.hpp | 37 +++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 5ae53e710..4d93dc881 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -113,6 +113,15 @@ 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. + void addColumn (Column *column); void setRecord (int index, const Record& record); @@ -287,10 +296,7 @@ namespace CSMWorld void Collection::appendRecord (const RecordBase& record, UniversalId::Type type) { - mRecords.push_back (dynamic_cast&> (record)); - mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (IdAccessorT().getId ( - dynamic_cast&> (record).get())), - mRecords.size()-1)); + insertRecord (record, mRecords.size(), type); } template @@ -328,6 +334,29 @@ namespace CSMWorld return mRecords.at (index); } + template + void Collection::insertRecord (const RecordBase& record, int index, + UniversalId::Type type) + { + if (index<0 || index>static_cast (mRecords.size())) + throw std::runtime_error ("index out of range"); + + const Record& record2 = dynamic_cast&> (record); + + mRecords.insert (mRecords.begin()+index, record2); + + if (index (mRecords.size())-1) + { + for (std::map::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 void Collection::setRecord (int index, const Record& record) { From 9a80e111828457af1ed93f6c7294983c4ea301b0 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 27 Oct 2013 14:13:10 +0100 Subject: [PATCH 06/26] reimplemented add and appendBlankRecord via insertRecord --- apps/opencs/model/world/collection.hpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 4d93dc881..167210de9 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -152,8 +152,7 @@ namespace CSMWorld record2.mState = Record::State_ModifiedOnly; record2.mModified = record; - mRecords.push_back (record2); - mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), mRecords.size()-1)); + insertRecord (record2, mRecords.size()); } else { @@ -270,7 +269,12 @@ namespace CSMWorld ESXRecordT record; IdAccessorT().getId (record) = id; record.blank(); - add (record); + + Record record2; + record2.mState = Record::State_ModifiedOnly; + record2.mModified = record; + + insertRecord (record2, mRecords.size(), type); } template From 45f5a66bcc8ecb0912b65b8296380dbfed5c9826 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 29 Oct 2013 09:27:23 +0100 Subject: [PATCH 07/26] use result of getAppendIndex instead of always appending at the end --- apps/opencs/model/world/collection.hpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 167210de9..a4cdec4ea 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -152,7 +152,7 @@ namespace CSMWorld record2.mState = Record::State_ModifiedOnly; record2.mModified = record; - insertRecord (record2, mRecords.size()); + insertRecord (record2, getAppendIndex (id)); } else { @@ -274,7 +274,7 @@ namespace CSMWorld record2.mState = Record::State_ModifiedOnly; record2.mModified = record; - insertRecord (record2, mRecords.size(), type); + insertRecord (record2, getAppendIndex (id, type), type); } template @@ -300,7 +300,9 @@ namespace CSMWorld void Collection::appendRecord (const RecordBase& record, UniversalId::Type type) { - insertRecord (record, mRecords.size(), type); + insertRecord (record, + getAppendIndex (IdAccessorT().getId ( + dynamic_cast&> (record).get()), type), type); } template From ba88c94d585235d651a7a38fad8a53d8afd3e2c8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 29 Oct 2013 13:18:22 +0100 Subject: [PATCH 08/26] first attempt at an info record collection --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/model/world/data.cpp | 34 ++++++++++ apps/opencs/model/world/data.hpp | 11 ++++ apps/opencs/model/world/infocollection.cpp | 72 ++++++++++++++++++++++ apps/opencs/model/world/infocollection.hpp | 20 ++++++ apps/opencs/model/world/universalid.cpp | 4 ++ apps/opencs/model/world/universalid.hpp | 4 ++ apps/opencs/view/doc/view.cpp | 18 ++++++ apps/opencs/view/doc/view.hpp | 4 ++ apps/opencs/view/world/subviews.cpp | 2 + components/esm/loadinfo.cpp | 24 ++++++++ components/esm/loadinfo.hpp | 3 + 12 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 apps/opencs/model/world/infocollection.cpp create mode 100644 apps/opencs/model/world/infocollection.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 541b3b5a2..f4846675e 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -24,7 +24,7 @@ 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 diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 130ce334f..2947b2b8f 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -149,6 +149,12 @@ CSMWorld::Data::Data() : mRefs (mCells) mJournals.addColumn (new RecordStateColumn); mJournals.addColumn (new DialogueTypeColumn (true)); + mTopicInfos.addColumn (new StringIdColumn); + mTopicInfos.addColumn (new RecordStateColumn); + + mJournalInfos.addColumn (new StringIdColumn); + mJournalInfos.addColumn (new RecordStateColumn); + mCells.addColumn (new StringIdColumn); mCells.addColumn (new RecordStateColumn); mCells.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Cell)); @@ -206,6 +212,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); @@ -350,6 +358,25 @@ CSMWorld::IdCollection& 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::Data::getCells() const { @@ -514,6 +541,13 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) break; } + case ESM::REC_INFO: + { + /// \todo associate info record with last loaded dialogue record + mJournalInfos.load (reader, base); + break; + } + default: /// \todo throw an exception instead, once all records are implemented diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index cf31c9494..2b278fd6f 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -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 mSpells; IdCollection mTopics; IdCollection mJournals; + InfoCollection mTopicInfos; + InfoCollection mJournalInfos; IdCollection mCells; RefIdCollection mReferenceables; RefCollection mRefs; @@ -127,6 +130,14 @@ namespace CSMWorld IdCollection& getJournals(); + const InfoCollection& getTopicInfos() const; + + InfoCollection& getTopicInfos(); + + const InfoCollection& getJournalInfos() const; + + InfoCollection& getJournalInfos(); + const IdCollection& getCells() const; IdCollection& getCells(); diff --git a/apps/opencs/model/world/infocollection.cpp b/apps/opencs/model/world/infocollection.cpp new file mode 100644 index 000000000..858f788fa --- /dev/null +++ b/apps/opencs/model/world/infocollection.cpp @@ -0,0 +1,72 @@ + +#include "infocollection.hpp" + +#include + +void CSMWorld::InfoCollection::load (const ESM::DialInfo& record, bool base) +{ + int index = searchId (record.mId); + + if (index==-1) + { + // new record + Record record2; + record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; + (base ? record2.mBase : record2.mModified) = record; + + appendRecord (record2); + } + else + { + // old record + Record record2 = getRecord (index); + + if (base) + record2.mBase = record; + else + record2.setModified (record); + + setRecord (index, record2); + } +} + +void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base) +{ + /// \todo put records into proper order + /// \todo adjust ID + std::string id = reader.getHNOString ("NAME"); + + 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 record = getRecord (index); + record.mState = RecordBase::State_Deleted; + setRecord (index, record); + } + } + else + { + ESM::DialInfo record; + record.mId = id; + record.load (reader); + + load (record, base); + } +} diff --git a/apps/opencs/model/world/infocollection.hpp b/apps/opencs/model/world/infocollection.hpp new file mode 100644 index 000000000..8dca2b219 --- /dev/null +++ b/apps/opencs/model/world/infocollection.hpp @@ -0,0 +1,20 @@ +#ifndef CSM_WOLRD_INFOCOLLECTION_H +#define CSM_WOLRD_INFOCOLLECTION_H + +#include + +#include "collection.hpp" + +namespace CSMWorld +{ + class InfoCollection : public Collection > + { + void load (const ESM::DialInfo& record, bool base); + + public: + + void load (ESM::ESMReader& reader, bool base); + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 6201a3cda..e633f4f69 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -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" }, diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index ffd99e572..0c17da03b 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -91,6 +91,10 @@ namespace CSMWorld Type_Topic, Type_Journals, Type_Journal, + Type_TopicInfos, + Type_TopicInfo, + Type_JournalInfos, + Type_JournalInfo, Type_Scene }; diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 733f5b2bb..533ca7f57 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -176,6 +176,14 @@ void CSVDoc::View::setupCharacterMenu() QAction *journals = new QAction (tr ("Journals"), this); connect (journals, SIGNAL (triggered()), this, SLOT (addJournalsSubView())); 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() @@ -436,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); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 7c13e374f..13c15ec9b 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -172,6 +172,10 @@ namespace CSVDoc void addJournalsSubView(); + void addTopicInfosSubView(); + + void addJournalInfosSubView(); + void toggleShowStatusBar (bool show); }; } diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 3d98cf73c..66e5806ba 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -37,6 +37,8 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) CSMWorld::UniversalId::Type_Regions, CSMWorld::UniversalId::Type_Birthsigns, CSMWorld::UniversalId::Type_Spells, + CSMWorld::UniversalId::Type_TopicInfos, + CSMWorld::UniversalId::Type_JournalInfos, CSMWorld::UniversalId::Type_None // end marker }; diff --git a/components/esm/loadinfo.cpp b/components/esm/loadinfo.cpp index 90f8fcf35..f248e0784 100644 --- a/components/esm/loadinfo.cpp +++ b/components/esm/loadinfo.cpp @@ -153,4 +153,28 @@ void DialInfo::save(ESMWriter &esm) } } + 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(); + mNpcFaction.clear(); + mPcFaction.clear(); + mCell.clear(); + mSound.clear(); + mResponse.clear(); + mResultScript.clear(); + mFactionLess = false; + mQuestStatus = QS_None; + } } diff --git a/components/esm/loadinfo.hpp b/components/esm/loadinfo.hpp index 2361ed9eb..badb85c38 100644 --- a/components/esm/loadinfo.hpp +++ b/components/esm/loadinfo.hpp @@ -100,6 +100,9 @@ struct DialInfo void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } From 69f28ee4bebdb636b3623c7342f1c72d98532858 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 31 Oct 2013 12:16:45 +0100 Subject: [PATCH 09/26] split info records between journal and topic info tables --- apps/opencs/model/world/data.cpp | 24 ++++++++++++++++++++-- apps/opencs/model/world/infocollection.cpp | 2 +- apps/opencs/model/world/infocollection.hpp | 7 ++++++- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 2947b2b8f..0962c3856 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -456,6 +456,8 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) reader.open (path.string()); + const ESM::Dialogue *dialogue = 0; + // Note: We do not need to send update signals here, because at this point the model is not connected // to any view. while (reader.hasMoreRecs()) @@ -516,10 +518,15 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) if (record.mType==ESM::Dialogue::Journal) { + int index = mJournals.getAppendIndex (id); mJournals.load (record, base); + dialogue = &mJournals.getRecord (index).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 @@ -535,7 +542,9 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) } else { + int index = mTopics.getAppendIndex (id); mTopics.load (record, base); + dialogue = &mTopics.getRecord (index).get(); } break; @@ -543,14 +552,25 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) case ESM::REC_INFO: { - /// \todo associate info record with last loaded dialogue record - mJournalInfos.load (reader, base); + 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; } default: /// \todo throw an exception instead, once all records are implemented + /// or maybe report error and continue? reader.skipRecord(); } } diff --git a/apps/opencs/model/world/infocollection.cpp b/apps/opencs/model/world/infocollection.cpp index 858f788fa..42425ecf5 100644 --- a/apps/opencs/model/world/infocollection.cpp +++ b/apps/opencs/model/world/infocollection.cpp @@ -30,7 +30,7 @@ void CSMWorld::InfoCollection::load (const ESM::DialInfo& record, bool base) } } -void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base) +void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue) { /// \todo put records into proper order /// \todo adjust ID diff --git a/apps/opencs/model/world/infocollection.hpp b/apps/opencs/model/world/infocollection.hpp index 8dca2b219..8bb8e5309 100644 --- a/apps/opencs/model/world/infocollection.hpp +++ b/apps/opencs/model/world/infocollection.hpp @@ -5,6 +5,11 @@ #include "collection.hpp" +namespace ESM +{ + class Dialogue; +} + namespace CSMWorld { class InfoCollection : public Collection > @@ -13,7 +18,7 @@ namespace CSMWorld public: - void load (ESM::ESMReader& reader, bool base); + void load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue); }; } From 767cb54e7c1b1271518b4bba713d4817b3375d82 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 31 Oct 2013 12:54:55 +0100 Subject: [PATCH 10/26] added missing columns to journal info table --- apps/opencs/model/world/columnbase.hpp | 3 +- apps/opencs/model/world/columnimp.hpp | 77 ++++++++++++++++++++++++++ apps/opencs/model/world/columns.cpp | 9 +++ apps/opencs/model/world/columns.hpp | 3 + apps/opencs/model/world/data.cpp | 3 + apps/opencs/view/doc/viewmanager.cpp | 3 +- components/esm/loadinfo.hpp | 8 +-- 7 files changed, 100 insertions(+), 6 deletions(-) diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index 9b8d7dafb..2c3408a01 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -44,7 +44,8 @@ namespace CSMWorld Display_WeaponType, Display_RecordState, Display_RefRecordType, - Display_DialogueType + Display_DialogueType, + Display_QuestStatusType }; int mColumnId; diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 23c529b3e..cddfafb63 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -1317,6 +1317,83 @@ namespace CSMWorld return false; } }; + + template + struct QuestStatusTypeColumn : public Column + { + QuestStatusTypeColumn() + : Column (Columns::ColumnId_QuestStatusType, ColumnBase::Display_QuestStatusType) + {} + + virtual QVariant get (const Record& record) const + { + return static_cast (record.get().mQuestStatus); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mQuestStatus = static_cast (data.toInt()); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct QuestDescriptionColumn : public Column + { + QuestDescriptionColumn() : Column (Columns::ColumnId_QuestDescription, ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mResponse.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mResponse = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct QuestIndexColumn : public Column + { + QuestIndexColumn() + : Column (Columns::ColumnId_QuestIndex, ColumnBase::Display_Integer) + {} + + virtual QVariant get (const Record& record) const + { + return record.get().mData.mDisposition; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + record2.mData.mDisposition = data.toInt(); + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; } #endif diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index ae4136bd9..307862b1b 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -160,6 +160,9 @@ 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_UseValue1, "Use value 1" }, { ColumnId_UseValue2, "Use value 2" }, @@ -275,6 +278,11 @@ namespace "Topic", "Voice", "Greeting", "Persuasion", 0 }; + static const char *sQuestStatusTypes[] = + { + "None", "Name", "Finished", "Restart", 0 + }; + const char **getEnumNames (CSMWorld::Columns::ColumnId column) { switch (column) @@ -290,6 +298,7 @@ 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; default: return 0; } diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 111931fa8..d7b96cab6 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -153,6 +153,9 @@ namespace CSMWorld ColumnId_DoorPositionYRot = 140, ColumnId_DoorPositionZRot = 141, ColumnId_DialogueType = 142, + ColumnId_QuestIndex = 143, + ColumnId_QuestStatusType = 144, + ColumnId_QuestDescription = 145, // Allocated to a separate value range, so we don't get a collision should we ever need // to extend the number of use values. diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 0962c3856..fc81287fe 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -154,6 +154,9 @@ CSMWorld::Data::Data() : mRefs (mCells) mJournalInfos.addColumn (new StringIdColumn); mJournalInfos.addColumn (new RecordStateColumn); + mJournalInfos.addColumn (new QuestStatusTypeColumn); + mJournalInfos.addColumn (new QuestIndexColumn); + mJournalInfos.addColumn (new QuestDescriptionColumn); mCells.addColumn (new StringIdColumn); mCells.addColumn (new RecordStateColumn); diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index a4849795b..b4b55dea0 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -75,7 +75,8 @@ 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 } }; for (std::size_t i=0; i Date: Thu, 31 Oct 2013 13:11:15 +0100 Subject: [PATCH 11/26] fixed dialogue record loading with multiple content files --- apps/opencs/model/world/data.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index fc81287fe..d06840370 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -521,9 +521,8 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) if (record.mType==ESM::Dialogue::Journal) { - int index = mJournals.getAppendIndex (id); mJournals.load (record, base); - dialogue = &mJournals.getRecord (index).get(); + dialogue = &mJournals.getRecord (id).get(); } else if (record.mType==ESM::Dialogue::Deleted) { @@ -545,9 +544,8 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) } else { - int index = mTopics.getAppendIndex (id); mTopics.load (record, base); - dialogue = &mTopics.getRecord (index).get(); + dialogue = &mTopics.getRecord (id).get(); } break; From ea0e8be0d337111d25e78824d451c70c58419380 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 31 Oct 2013 13:40:14 +0100 Subject: [PATCH 12/26] disallow sorting (via column headers) in info tables --- apps/opencs/view/doc/subviewfactoryimp.hpp | 11 ++++++++++- apps/opencs/view/world/subviews.cpp | 8 ++++++-- apps/opencs/view/world/table.cpp | 4 ++-- apps/opencs/view/world/table.hpp | 3 ++- apps/opencs/view/world/tablesubview.cpp | 4 ++-- apps/opencs/view/world/tablesubview.hpp | 2 +- 6 files changed, 23 insertions(+), 9 deletions(-) diff --git a/apps/opencs/view/doc/subviewfactoryimp.hpp b/apps/opencs/view/doc/subviewfactoryimp.hpp index 2c4158f85..059b24fd0 100644 --- a/apps/opencs/view/doc/subviewfactoryimp.hpp +++ b/apps/opencs/view/doc/subviewfactoryimp.hpp @@ -26,16 +26,25 @@ namespace CSVDoc template class SubViewFactoryWithCreator : public SubViewFactoryBase { + bool mSorting; + public: + SubViewFactoryWithCreator (bool sorting = true); + virtual CSVDoc::SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); }; + template + SubViewFactoryWithCreator::SubViewFactoryWithCreator (bool sorting) + : mSorting (sorting) + {} + template CSVDoc::SubView *SubViewFactoryWithCreator::makeSubView ( const CSMWorld::UniversalId& id, CSMDoc::Document& document) { - return new SubViewT (id, document, CreatorFactoryT()); + return new SubViewT (id, document, CreatorFactoryT(), mSorting); } } diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 66e5806ba..85972e7f0 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -37,8 +37,6 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) CSMWorld::UniversalId::Type_Regions, CSMWorld::UniversalId::Type_Birthsigns, CSMWorld::UniversalId::Type_Spells, - CSMWorld::UniversalId::Type_TopicInfos, - CSMWorld::UniversalId::Type_JournalInfos, CSMWorld::UniversalId::Type_None // end marker }; @@ -62,6 +60,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add (CSMWorld::UniversalId::Type_Journal, new CSVDoc::SubViewFactoryWithCreator); + manager.add (CSMWorld::UniversalId::Type_TopicInfos, + new CSVDoc::SubViewFactoryWithCreator > (false)); + + manager.add (CSMWorld::UniversalId::Type_JournalInfos, + new CSVDoc::SubViewFactoryWithCreator > (false)); + // Subviews for editing/viewing individual records manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory); diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index a58eb873f..dfdfa6d13 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -121,7 +121,7 @@ std::vector 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 (*data.getTableModel (id)); @@ -132,7 +132,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); diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index d93109056..3e4214424 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -49,8 +49,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); diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 1e05fbf51..55ded09de 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -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); diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index d61c78935..56a441a4d 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -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); From 15b7d3263c2c70ee7c70dddb49769507b42f837d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 1 Nov 2013 17:43:45 +0100 Subject: [PATCH 13/26] subclass ESM::DialInfo to keep track of parent topic --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/model/world/columnimp.hpp | 3 ++- apps/opencs/model/world/data.cpp | 14 +++++++------- apps/opencs/model/world/info.hpp | 14 ++++++++++++++ apps/opencs/model/world/infocollection.cpp | 12 +++++++----- apps/opencs/model/world/infocollection.hpp | 7 +++---- 6 files changed, 34 insertions(+), 18 deletions(-) create mode 100644 apps/opencs/model/world/info.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index f4846675e..5e6b853e8 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -28,7 +28,7 @@ opencs_units_noqt (model/world ) opencs_hdrs_noqt (model/world - columnimp idcollection collection + columnimp idcollection collection info ) diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index cddfafb63..dc80f6eeb 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -9,6 +9,7 @@ #include "columnbase.hpp" #include "columns.hpp" +#include "info.hpp" namespace CSMWorld { @@ -1334,7 +1335,7 @@ namespace CSMWorld { ESXRecordT record2 = record.get(); - record2.mQuestStatus = static_cast (data.toInt()); + record2.mQuestStatus = static_cast (data.toInt()); record.setModified (record2); } diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index d06840370..bfec13f90 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -149,14 +149,14 @@ CSMWorld::Data::Data() : mRefs (mCells) mJournals.addColumn (new RecordStateColumn); mJournals.addColumn (new DialogueTypeColumn (true)); - mTopicInfos.addColumn (new StringIdColumn); - mTopicInfos.addColumn (new RecordStateColumn); + mTopicInfos.addColumn (new StringIdColumn); + mTopicInfos.addColumn (new RecordStateColumn); - mJournalInfos.addColumn (new StringIdColumn); - mJournalInfos.addColumn (new RecordStateColumn); - mJournalInfos.addColumn (new QuestStatusTypeColumn); - mJournalInfos.addColumn (new QuestIndexColumn); - mJournalInfos.addColumn (new QuestDescriptionColumn); + mJournalInfos.addColumn (new StringIdColumn); + mJournalInfos.addColumn (new RecordStateColumn); + mJournalInfos.addColumn (new QuestStatusTypeColumn); + mJournalInfos.addColumn (new QuestIndexColumn); + mJournalInfos.addColumn (new QuestDescriptionColumn); mCells.addColumn (new StringIdColumn); mCells.addColumn (new RecordStateColumn); diff --git a/apps/opencs/model/world/info.hpp b/apps/opencs/model/world/info.hpp new file mode 100644 index 000000000..1bcb2dc2d --- /dev/null +++ b/apps/opencs/model/world/info.hpp @@ -0,0 +1,14 @@ +#ifndef CSM_WOLRD_INFO_H +#define CSM_WOLRD_INFO_H + +#include + +namespace CSMWorld +{ + struct Info : public ESM::DialInfo + { + std::string mTopicId; + }; +} + +#endif diff --git a/apps/opencs/model/world/infocollection.cpp b/apps/opencs/model/world/infocollection.cpp index 42425ecf5..e21c3d477 100644 --- a/apps/opencs/model/world/infocollection.cpp +++ b/apps/opencs/model/world/infocollection.cpp @@ -2,15 +2,16 @@ #include "infocollection.hpp" #include +#include -void CSMWorld::InfoCollection::load (const ESM::DialInfo& record, bool base) +void CSMWorld::InfoCollection::load (const Info& record, bool base) { int index = searchId (record.mId); if (index==-1) { // new record - Record record2; + Record record2; record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; (base ? record2.mBase : record2.mModified) = record; @@ -19,7 +20,7 @@ void CSMWorld::InfoCollection::load (const ESM::DialInfo& record, bool base) else { // old record - Record record2 = getRecord (index); + Record record2 = getRecord (index); if (base) record2.mBase = record; @@ -56,14 +57,15 @@ void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ES } else { - Record record = getRecord (index); + Record record = getRecord (index); record.mState = RecordBase::State_Deleted; setRecord (index, record); } } else { - ESM::DialInfo record; + Info record; + record.mTopicId = dialogue.mId; record.mId = id; record.load (reader); diff --git a/apps/opencs/model/world/infocollection.hpp b/apps/opencs/model/world/infocollection.hpp index 8bb8e5309..5faf61a8c 100644 --- a/apps/opencs/model/world/infocollection.hpp +++ b/apps/opencs/model/world/infocollection.hpp @@ -1,9 +1,8 @@ #ifndef CSM_WOLRD_INFOCOLLECTION_H #define CSM_WOLRD_INFOCOLLECTION_H -#include - #include "collection.hpp" +#include "info.hpp" namespace ESM { @@ -12,9 +11,9 @@ namespace ESM namespace CSMWorld { - class InfoCollection : public Collection > + class InfoCollection : public Collection > { - void load (const ESM::DialInfo& record, bool base); + void load (const Info& record, bool base); public: From 4724df7e9bb0c0dfbdca1b86022651ec8adf60bf Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 3 Nov 2013 10:48:50 +0100 Subject: [PATCH 14/26] added topic/journal column to info tables --- apps/opencs/model/world/columnimp.hpp | 30 +++++++++++++++++++++++++++ apps/opencs/model/world/columns.cpp | 2 ++ apps/opencs/model/world/columns.hpp | 2 ++ apps/opencs/model/world/data.cpp | 2 ++ 4 files changed, 36 insertions(+) diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index dc80f6eeb..5d9fe9a1b 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -1395,6 +1395,36 @@ namespace CSMWorld return true; } }; + + template + struct TopicColumn : public Column + { + TopicColumn (bool journal) : Column (journal ? Columns::ColumnId_Journal : Columns::ColumnId_Topic, ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mTopicId.c_str()); + } + + virtual void set (Record& 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; + } + }; } #endif diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index 307862b1b..286afc64d 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -163,6 +163,8 @@ namespace CSMWorld { ColumnId_QuestIndex, "Quest Index" }, { ColumnId_QuestStatusType, "Quest Status" }, { ColumnId_QuestDescription, "Quest Description" }, + { ColumnId_Topic, "Topic" }, + { ColumnId_Journal, "Journal", }, { ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue2, "Use value 2" }, diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index d7b96cab6..6965d4c31 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -156,6 +156,8 @@ namespace CSMWorld ColumnId_QuestIndex = 143, ColumnId_QuestStatusType = 144, ColumnId_QuestDescription = 145, + ColumnId_Topic = 146, + ColumnId_Journal = 147, // Allocated to a separate value range, so we don't get a collision should we ever need // to extend the number of use values. diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index bfec13f90..88fb9fded 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -151,9 +151,11 @@ CSMWorld::Data::Data() : mRefs (mCells) mTopicInfos.addColumn (new StringIdColumn); mTopicInfos.addColumn (new RecordStateColumn); + mTopicInfos.addColumn (new TopicColumn (false)); mJournalInfos.addColumn (new StringIdColumn); mJournalInfos.addColumn (new RecordStateColumn); + mJournalInfos.addColumn (new TopicColumn (true)); mJournalInfos.addColumn (new QuestStatusTypeColumn); mJournalInfos.addColumn (new QuestIndexColumn); mJournalInfos.addColumn (new QuestDescriptionColumn); From 9d7695ea88cd260da9358cf928ec84459ddb2a07 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 5 Nov 2013 11:41:48 +0100 Subject: [PATCH 15/26] added missing columns to topic info table --- apps/esmtool/record.cpp | 4 +- apps/opencs/model/world/columnbase.hpp | 3 +- apps/opencs/model/world/columnimp.hpp | 231 ++++++++++++++++++++++++- apps/opencs/model/world/columns.cpp | 15 +- apps/opencs/model/world/columns.hpp | 7 + apps/opencs/model/world/data.cpp | 12 ++ apps/opencs/model/world/ref.cpp | 2 +- apps/opencs/model/world/ref.hpp | 2 +- apps/opencs/view/doc/viewmanager.cpp | 3 +- apps/openmw/mwdialogue/filter.cpp | 4 +- components/esm/loadinfo.cpp | 8 +- components/esm/loadinfo.hpp | 2 +- 12 files changed, 277 insertions(+), 16 deletions(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index 68e0dcc09..e9fb1c537 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -740,8 +740,8 @@ void Record::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 != "") diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index 2c3408a01..70f38c534 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -45,7 +45,8 @@ namespace CSMWorld Display_RecordState, Display_RefRecordType, Display_DialogueType, - Display_QuestStatusType + Display_QuestStatusType, + Display_Gender }; int mColumnId; diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 5d9fe9a1b..a2994ec64 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -815,14 +815,14 @@ namespace CSMWorld virtual QVariant get (const Record& record) const { - return QString::fromUtf8 (record.get().mCellId.c_str()); + return QString::fromUtf8 (record.get().mCell.c_str()); } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); - record2.mCellId = data.toString().toUtf8().constData(); + record2.mCell = data.toString().toUtf8().constData(); record.setModified (record2); } @@ -1425,6 +1425,233 @@ namespace CSMWorld return false; } }; + + template + struct ActorColumn : public Column + { + ActorColumn() : Column (Columns::ColumnId_Actor, ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mActor.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mActor = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct RaceColumn : public Column + { + RaceColumn() : Column (Columns::ColumnId_Race, ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mRace.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mRace = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct ClassColumn : public Column + { + ClassColumn() : Column (Columns::ColumnId_Class, ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mClass.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mClass = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct PcFactionColumn : public Column + { + PcFactionColumn() : Column (Columns::ColumnId_PcFaction, ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mPcFaction.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mPcFaction = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct ResponseColumn : public Column + { + ResponseColumn() : Column (Columns::ColumnId_Response, ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mResponse.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mResponse = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct DispositionColumn : public Column + { + DispositionColumn() + : Column (Columns::ColumnId_Disposition, ColumnBase::Display_Integer) + {} + + virtual QVariant get (const Record& record) const + { + return record.get().mData.mDisposition; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + record2.mData.mDisposition = data.toInt(); + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct RankColumn : public Column + { + RankColumn() + : Column (Columns::ColumnId_Rank, ColumnBase::Display_Integer) + {} + + virtual QVariant get (const Record& record) const + { + return static_cast (record.get().mData.mRank); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + record2.mData.mRank = static_cast (data.toInt()); + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct PcRankColumn : public Column + { + PcRankColumn() + : Column (Columns::ColumnId_PcRank, ColumnBase::Display_Integer) + {} + + virtual QVariant get (const Record& record) const + { + return static_cast (record.get().mData.mPCrank); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + record2.mData.mPCrank = static_cast (data.toInt()); + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct GenderColumn : public Column + { + GenderColumn() + : Column (Columns::ColumnId_Gender, ColumnBase::Display_Gender) + {} + + virtual QVariant get (const Record& record) const + { + return static_cast (record.get().mData.mGender); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mData.mGender = data.toInt(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; } #endif diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index 286afc64d..980d1f75d 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -164,7 +164,14 @@ namespace CSMWorld { ColumnId_QuestStatusType, "Quest Status" }, { ColumnId_QuestDescription, "Quest Description" }, { ColumnId_Topic, "Topic" }, - { ColumnId_Journal, "Journal", }, + { 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_UseValue1, "Use value 1" }, { ColumnId_UseValue2, "Use value 2" }, @@ -285,6 +292,11 @@ namespace "None", "Name", "Finished", "Restart", 0 }; + static const char *sGenderEnums[] = + { + "Male", "Female", 0 + }; + const char **getEnumNames (CSMWorld::Columns::ColumnId column) { switch (column) @@ -301,6 +313,7 @@ namespace 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; } diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 6965d4c31..056cc4fa1 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -158,6 +158,13 @@ namespace CSMWorld 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, // Allocated to a separate value range, so we don't get a collision should we ever need // to extend the number of use values. diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 88fb9fded..dc9feab51 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -152,6 +152,18 @@ CSMWorld::Data::Data() : mRefs (mCells) mTopicInfos.addColumn (new StringIdColumn); mTopicInfos.addColumn (new RecordStateColumn); mTopicInfos.addColumn (new TopicColumn (false)); + mTopicInfos.addColumn (new ActorColumn); + mTopicInfos.addColumn (new RaceColumn); + mTopicInfos.addColumn (new ClassColumn); + mTopicInfos.addColumn (new FactionColumn); + mTopicInfos.addColumn (new CellColumn); + mTopicInfos.addColumn (new DispositionColumn); + mTopicInfos.addColumn (new RankColumn); + mTopicInfos.addColumn (new GenderColumn); + mTopicInfos.addColumn (new PcFactionColumn); + mTopicInfos.addColumn (new PcRankColumn); + mTopicInfos.addColumn (new SoundFileColumn); + mTopicInfos.addColumn (new ResponseColumn); mJournalInfos.addColumn (new StringIdColumn); mJournalInfos.addColumn (new RecordStateColumn); diff --git a/apps/opencs/model/world/ref.cpp b/apps/opencs/model/world/ref.cpp index af363bafb..74f60419b 100644 --- a/apps/opencs/model/world/ref.cpp +++ b/apps/opencs/model/world/ref.cpp @@ -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); diff --git a/apps/opencs/model/world/ref.hpp b/apps/opencs/model/world/ref.hpp index 3d107d675..fcf016ee2 100644 --- a/apps/opencs/model/world/ref.hpp +++ b/apps/opencs/model/world/ref.hpp @@ -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. diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index b4b55dea0..4a4dbc124 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -76,7 +76,8 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) { 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_QuestStatusType, CSMWorld::Columns::ColumnId_QuestStatusType, 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::iterator iter = stats.getFactionRanks().find ( Misc::StringUtils::lowerCase (info.mNpcFaction)); + std::map::iterator iter = stats.getFactionRanks().find ( Misc::StringUtils::lowerCase (info.mFaction)); if (iter==stats.getFactionRanks().end()) return false; diff --git a/components/esm/loadinfo.cpp b/components/esm/loadinfo.cpp index f248e0784..be07e5128 100644 --- a/components/esm/loadinfo.cpp +++ b/components/esm/loadinfo.cpp @@ -48,8 +48,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; @@ -129,7 +129,7 @@ void DialInfo::save(ESMWriter &esm) 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); @@ -168,7 +168,7 @@ void DialInfo::save(ESMWriter &esm) mActor.clear(); mRace.clear(); mClass.clear(); - mNpcFaction.clear(); + mFaction.clear(); mPcFaction.clear(); mCell.clear(); mSound.clear(); diff --git a/components/esm/loadinfo.hpp b/components/esm/loadinfo.hpp index 7ec3e84bb..469d5805e 100644 --- a/components/esm/loadinfo.hpp +++ b/components/esm/loadinfo.hpp @@ -63,7 +63,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; From 2d4a6c0edfcb355e50a89fe54d21b213f9c754d1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 5 Nov 2013 12:56:20 +0100 Subject: [PATCH 16/26] cleaned up ID handling in INFO record (including a ESMTool bug fix) --- apps/esmtool/esmtool.cpp | 2 ++ apps/opencs/model/world/infocollection.cpp | 2 +- apps/openmw/mwworld/esmstore.cpp | 2 ++ apps/openmw/mwworld/store.hpp | 2 +- components/esm/loadinfo.cpp | 1 - 5 files changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index a60e9f0e2..1be326438 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -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() diff --git a/apps/opencs/model/world/infocollection.cpp b/apps/opencs/model/world/infocollection.cpp index e21c3d477..96d2f7417 100644 --- a/apps/opencs/model/world/infocollection.cpp +++ b/apps/opencs/model/world/infocollection.cpp @@ -35,7 +35,7 @@ void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ES { /// \todo put records into proper order /// \todo adjust ID - std::string id = reader.getHNOString ("NAME"); + std::string id = reader.getHNOString ("INAM"); if (reader.isNextSub ("DELE")) { diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 7703f2d23..616ecc514 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -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; diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 90452f661..c25197319 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -258,7 +258,7 @@ namespace MWWorld typename std::vector::iterator sharedIter = mShared.begin(); typename std::vector::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; diff --git a/components/esm/loadinfo.cpp b/components/esm/loadinfo.cpp index be07e5128..5dc35db88 100644 --- a/components/esm/loadinfo.cpp +++ b/components/esm/loadinfo.cpp @@ -8,7 +8,6 @@ namespace ESM void DialInfo::load(ESMReader &esm) { - mId = esm.getHNString("INAM"); mPrev = esm.getHNString("PNAM"); mNext = esm.getHNString("NNAM"); From c545b3682ac27f5c70e02ed66fc673c0a842df00 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 5 Nov 2013 12:57:55 +0100 Subject: [PATCH 17/26] compose info record IDs from actual record ID and parent topic ID (to make sure IDs are unique) --- apps/opencs/model/world/infocollection.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/opencs/model/world/infocollection.cpp b/apps/opencs/model/world/infocollection.cpp index 96d2f7417..1be186f76 100644 --- a/apps/opencs/model/world/infocollection.cpp +++ b/apps/opencs/model/world/infocollection.cpp @@ -34,8 +34,7 @@ void CSMWorld::InfoCollection::load (const Info& record, bool base) void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue) { /// \todo put records into proper order - /// \todo adjust ID - std::string id = reader.getHNOString ("INAM"); + std::string id = dialogue.mId + "#" + reader.getHNOString ("INAM"); if (reader.isNextSub ("DELE")) { From 0745a86039596ce29289c6fe88236df50c370ea2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 8 Nov 2013 11:51:59 +0100 Subject: [PATCH 18/26] added InfoCreator --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/world/infocreator.cpp | 79 ++++++++++++++++++++++++++ apps/opencs/view/world/infocreator.hpp | 42 ++++++++++++++ apps/opencs/view/world/subviews.cpp | 5 +- 4 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 apps/opencs/view/world/infocreator.cpp create mode 100644 apps/opencs/view/world/infocreator.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 5e6b853e8..f6f1be02b 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -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_noqt (view/world diff --git a/apps/opencs/view/world/infocreator.cpp b/apps/opencs/view/world/infocreator.cpp new file mode 100644 index 000000000..dcc943d54 --- /dev/null +++ b/apps/opencs/view/world/infocreator.cpp @@ -0,0 +1,79 @@ + +#include "infocreator.hpp" + +#include + +#include +#include +#include + +#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 = 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 (*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(); +} \ No newline at end of file diff --git a/apps/opencs/view/world/infocreator.hpp b/apps/opencs/view/world/infocreator.hpp new file mode 100644 index 000000000..e9cb7e596 --- /dev/null +++ b/apps/opencs/view/world/infocreator.hpp @@ -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 diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 85972e7f0..48c32e171 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -15,6 +15,7 @@ #include "referencecreator.hpp" #include "scenesubview.hpp" #include "dialoguecreator.hpp" +#include "infocreator.hpp" void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) { @@ -61,10 +62,10 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) new CSVDoc::SubViewFactoryWithCreator); manager.add (CSMWorld::UniversalId::Type_TopicInfos, - new CSVDoc::SubViewFactoryWithCreator > (false)); + new CSVDoc::SubViewFactoryWithCreator > (false)); manager.add (CSMWorld::UniversalId::Type_JournalInfos, - new CSVDoc::SubViewFactoryWithCreator > (false)); + new CSVDoc::SubViewFactoryWithCreator > (false)); // Subviews for editing/viewing individual records manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory); From 982024a328183c96a985d9eb445c118dafc415b1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 8 Nov 2013 11:52:30 +0100 Subject: [PATCH 19/26] Topic range access in InfoCollection --- apps/opencs/model/world/collection.hpp | 10 ++++++++ apps/opencs/model/world/infocollection.cpp | 28 +++++++++++++++++++++- apps/opencs/model/world/infocollection.hpp | 11 +++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index a4cdec4ea..1eee36ae9 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -51,6 +51,10 @@ namespace CSMWorld Collection (const Collection&); Collection& operator= (const Collection&); + protected: + + const std::map& getIdMap() const; + public: Collection(); @@ -128,6 +132,12 @@ namespace CSMWorld ///< \attention This function must not change the ID. }; + template + const std::map& Collection::getIdMap() const + { + return mIndex; + } + template Collection::Collection() {} diff --git a/apps/opencs/model/world/infocollection.cpp b/apps/opencs/model/world/infocollection.cpp index 1be186f76..215354f59 100644 --- a/apps/opencs/model/world/infocollection.cpp +++ b/apps/opencs/model/world/infocollection.cpp @@ -4,6 +4,8 @@ #include #include +#include + void CSMWorld::InfoCollection::load (const Info& record, bool base) { int index = searchId (record.mId); @@ -34,7 +36,8 @@ void CSMWorld::InfoCollection::load (const Info& record, bool base) void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue) { /// \todo put records into proper order - std::string id = dialogue.mId + "#" + reader.getHNOString ("INAM"); + std::string id = Misc::StringUtils::lowerCase (dialogue.mId) + "#" + + reader.getHNOString ("INAM"); if (reader.isNextSub ("DELE")) { @@ -71,3 +74,26 @@ void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ES load (record, base); } } + +std::pair + CSMWorld::InfoCollection::getTopicRange (const std::string& topic) const +{ + std::string topic2 = Misc::StringUtils::lowerCase (topic); + + MapConstIterator begin = getIdMap().lower_bound (topic2); + + // Skip invalid records: The beginning of a topic string could be identical to another topic + // string. + for (; begin!=getIdMap().end(); ++begin) + if (getRecord (begin->second).get().mTopicId==topic) + break; + + // Find end + MapConstIterator end = begin; + + for (; end!=getIdMap().end(); ++end) + if (getRecord (end->second).get().mTopicId!=topic) + break; + + return std::make_pair (begin, end); +} \ No newline at end of file diff --git a/apps/opencs/model/world/infocollection.hpp b/apps/opencs/model/world/infocollection.hpp index 5faf61a8c..f92e63e81 100644 --- a/apps/opencs/model/world/infocollection.hpp +++ b/apps/opencs/model/world/infocollection.hpp @@ -13,11 +13,22 @@ namespace CSMWorld { class InfoCollection : public Collection > { + public: + + typedef std::map::const_iterator MapConstIterator; + + private: + void load (const Info& record, bool base); public: void load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue); + + std::pair getTopicRange (const std::string& topic) + const; + ///< Return iterators that point to the beginning and past the end of the range for + /// the given topic. }; } From a06aa881cb9518a9770f9d172aaf495b29f0e5d6 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 8 Nov 2013 12:03:03 +0100 Subject: [PATCH 20/26] make sure case handling in info IDs is consistent --- apps/opencs/view/world/infocreator.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/world/infocreator.cpp b/apps/opencs/view/world/infocreator.cpp index dcc943d54..f09222930 100644 --- a/apps/opencs/view/world/infocreator.cpp +++ b/apps/opencs/view/world/infocreator.cpp @@ -7,6 +7,8 @@ #include #include +#include + #include "../../model/world/data.hpp" #include "../../model/world/commands.hpp" #include "../../model/world/columns.hpp" @@ -14,7 +16,7 @@ std::string CSVWorld::InfoCreator::getId() const { - std::string id = mTopic->text().toUtf8().constData(); + std::string id = Misc::StringUtils::lowerCase (mTopic->text().toUtf8().constData()); std::string unique = QUuid::createUuid().toByteArray().data(); From 3d8da2b9e0dee4d18c3eaf3534cdaf584ceef1a6 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 8 Nov 2013 12:16:41 +0100 Subject: [PATCH 21/26] proper sorting for newly created records and some case smashing fixes --- apps/opencs/model/world/infocollection.cpp | 29 +++++++++++++++++++--- apps/opencs/model/world/infocollection.hpp | 4 +++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/infocollection.cpp b/apps/opencs/model/world/infocollection.cpp index 215354f59..a63d3bce4 100644 --- a/apps/opencs/model/world/infocollection.cpp +++ b/apps/opencs/model/world/infocollection.cpp @@ -1,6 +1,8 @@ #include "infocollection.hpp" +#include + #include #include @@ -17,7 +19,7 @@ void CSMWorld::InfoCollection::load (const Info& record, bool base) record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; (base ? record2.mBase : record2.mModified) = record; - appendRecord (record2); + insertRecord (record2, getIdMap().size()); } else { @@ -33,6 +35,27 @@ void CSMWorld::InfoCollection::load (const Info& record, bool base) } } +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 range = getTopicRange (id.substr (0, separator)); + + if (range.first==range.second) + return Collection >::getAppendIndex (id, type); + + int index = 0; + + for (; range.first!=range.second; ++range.first) + if (range.first->second>index) + index = range.first->second; + + return index+1; +} + void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue) { /// \todo put records into proper order @@ -85,14 +108,14 @@ std::pairsecond).get().mTopicId==topic) + if (Misc::StringUtils::lowerCase (getRecord (begin->second).get().mTopicId)==topic2) break; // Find end MapConstIterator end = begin; for (; end!=getIdMap().end(); ++end) - if (getRecord (end->second).get().mTopicId!=topic) + if (Misc::StringUtils::lowerCase (getRecord (end->second).get().mTopicId)!=topic2) break; return std::make_pair (begin, end); diff --git a/apps/opencs/model/world/infocollection.hpp b/apps/opencs/model/world/infocollection.hpp index f92e63e81..8ca5f3b45 100644 --- a/apps/opencs/model/world/infocollection.hpp +++ b/apps/opencs/model/world/infocollection.hpp @@ -23,6 +23,10 @@ namespace CSMWorld 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 + void load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue); std::pair getTopicRange (const std::string& topic) From 935d9241d8ee0b74f9e414cbfde93b13369148b1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 9 Nov 2013 11:42:19 +0100 Subject: [PATCH 22/26] first attempt at proper info record loading: partially incorrect and way too slow --- apps/opencs/model/world/infocollection.cpp | 37 ++++++++++++++++++++-- apps/opencs/model/world/infocollection.hpp | 5 +++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/infocollection.cpp b/apps/opencs/model/world/infocollection.cpp index a63d3bce4..d326543fb 100644 --- a/apps/opencs/model/world/infocollection.cpp +++ b/apps/opencs/model/world/infocollection.cpp @@ -2,6 +2,7 @@ #include "infocollection.hpp" #include +#include #include #include @@ -19,7 +20,27 @@ void CSMWorld::InfoCollection::load (const Info& record, bool base) record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; (base ? record2.mBase : record2.mModified) = record; - insertRecord (record2, getIdMap().size()); + 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) + index = getIdMap().size(); + + insertRecord (record2, index); } else { @@ -35,6 +56,19 @@ void CSMWorld::InfoCollection::load (const Info& record, bool base) } } +int CSMWorld::InfoCollection::getIndex (const std::string& id, const std::string& topic) const +{ + std::string fullId = Misc::StringUtils::lowerCase (topic) + "#" + id; + + std::pair range = getTopicRange (topic); + + for (; range.first!=range.second; ++range.first) + if (range.first->first==fullId) + return std::distance (getIdMap().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 ('#'); @@ -58,7 +92,6 @@ int CSMWorld::InfoCollection::getAppendIndex (const std::string& id, UniversalId void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue) { - /// \todo put records into proper order std::string id = Misc::StringUtils::lowerCase (dialogue.mId) + "#" + reader.getHNOString ("INAM"); diff --git a/apps/opencs/model/world/infocollection.hpp b/apps/opencs/model/world/infocollection.hpp index 8ca5f3b45..398f9becc 100644 --- a/apps/opencs/model/world/infocollection.hpp +++ b/apps/opencs/model/world/infocollection.hpp @@ -21,6 +21,11 @@ namespace CSMWorld 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, From ec6018928c32771cc59ee1b511cc41df0e45a8ba Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 10 Nov 2013 12:09:49 +0100 Subject: [PATCH 23/26] some fixes to info record sorting (doesn't address the main problem) --- apps/opencs/model/world/infocollection.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/world/infocollection.cpp b/apps/opencs/model/world/infocollection.cpp index d326543fb..8e7b6cf01 100644 --- a/apps/opencs/model/world/infocollection.cpp +++ b/apps/opencs/model/world/infocollection.cpp @@ -38,7 +38,22 @@ void CSMWorld::InfoCollection::load (const Info& record, bool base) } if (index==-1) - index = getIdMap().size(); + { + std::pair range = getTopicRange (topic); + + if (range.first==range.second) + index = getIdMap().size(); + else + { + for (; range.first!=range.second; ++range.first) + { + if (range.first->second>index) + index = range.first->second; + } + + ++index; + } + } insertRecord (record2, index); } From 583f1ae9c285f67646691402fcd07f09573c0ad1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 10 Nov 2013 13:00:46 +0100 Subject: [PATCH 24/26] fixed info record ordering and performance problems by determining topic ranges in the record collection instead of in the index collection --- apps/opencs/model/world/collection.hpp | 8 +++ apps/opencs/model/world/infocollection.cpp | 64 +++++++++++----------- apps/opencs/model/world/infocollection.hpp | 6 +- 3 files changed, 42 insertions(+), 36 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 1eee36ae9..316e76c5f 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -55,6 +55,8 @@ namespace CSMWorld const std::map& getIdMap() const; + const std::vector >& getRecords() const; + public: Collection(); @@ -138,6 +140,12 @@ namespace CSMWorld return mIndex; } + template + const std::vector >& Collection::getRecords() const + { + return mRecords; + } + template Collection::Collection() {} diff --git a/apps/opencs/model/world/infocollection.cpp b/apps/opencs/model/world/infocollection.cpp index 8e7b6cf01..b5c1872a1 100644 --- a/apps/opencs/model/world/infocollection.cpp +++ b/apps/opencs/model/world/infocollection.cpp @@ -39,20 +39,9 @@ void CSMWorld::InfoCollection::load (const Info& record, bool base) if (index==-1) { - std::pair range = getTopicRange (topic); + Range range = getTopicRange (topic); - if (range.first==range.second) - index = getIdMap().size(); - else - { - for (; range.first!=range.second; ++range.first) - { - if (range.first->second>index) - index = range.first->second; - } - - ++index; - } + index = std::distance (getRecords().begin(), range.second); } insertRecord (record2, index); @@ -75,11 +64,11 @@ int CSMWorld::InfoCollection::getIndex (const std::string& id, const std::string { std::string fullId = Misc::StringUtils::lowerCase (topic) + "#" + id; - std::pair range = getTopicRange (topic); + std::pair range = getTopicRange (topic); for (; range.first!=range.second; ++range.first) - if (range.first->first==fullId) - return std::distance (getIdMap().begin(), range.first); + if (Misc::StringUtils::lowerCase (range.first->get().mId)==fullId) + return std::distance (getRecords().begin(), range.first); return -1; } @@ -91,18 +80,12 @@ int CSMWorld::InfoCollection::getAppendIndex (const std::string& id, UniversalId if (separator==std::string::npos) throw std::runtime_error ("invalid info ID: " + id); - std::pair range = getTopicRange (id.substr (0, separator)); + std::pair range = getTopicRange (id.substr (0, separator)); if (range.first==range.second) return Collection >::getAppendIndex (id, type); - int index = 0; - - for (; range.first!=range.second; ++range.first) - if (range.first->second>index) - index = range.first->second; - - return index+1; + return std::distance (getRecords().begin(), range.second); } void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue) @@ -146,25 +129,40 @@ void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ES } } -std::pair - CSMWorld::InfoCollection::getTopicRange (const std::string& topic) const +CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const std::string& topic) + const { std::string topic2 = Misc::StringUtils::lowerCase (topic); - MapConstIterator begin = getIdMap().lower_bound (topic2); + std::map::const_iterator iter = getIdMap().lower_bound (topic2); // Skip invalid records: The beginning of a topic string could be identical to another topic // string. - for (; begin!=getIdMap().end(); ++begin) - if (Misc::StringUtils::lowerCase (getRecord (begin->second).get().mTopicId)==topic2) + 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()second; + // Find end - MapConstIterator end = begin; + RecordConstIterator end = begin; - for (; end!=getIdMap().end(); ++end) - if (Misc::StringUtils::lowerCase (getRecord (end->second).get().mTopicId)!=topic2) + for (; end!=getRecords().end(); ++end) + if (Misc::StringUtils::lowerCase (end->get().mTopicId)!=topic2) break; - return std::make_pair (begin, end); + return Range (begin, end); } \ No newline at end of file diff --git a/apps/opencs/model/world/infocollection.hpp b/apps/opencs/model/world/infocollection.hpp index 398f9becc..1bccbb4ae 100644 --- a/apps/opencs/model/world/infocollection.hpp +++ b/apps/opencs/model/world/infocollection.hpp @@ -15,7 +15,8 @@ namespace CSMWorld { public: - typedef std::map::const_iterator MapConstIterator; + typedef std::vector >::const_iterator RecordConstIterator; + typedef std::pair Range; private: @@ -34,8 +35,7 @@ namespace CSMWorld void load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue); - std::pair getTopicRange (const std::string& topic) - const; + 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. }; From 2fff7fc8436eafb12813c1be705956f940f6fffe Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 11 Nov 2013 12:21:26 +0100 Subject: [PATCH 25/26] save info records --- apps/opencs/model/doc/saving.cpp | 7 +- apps/opencs/model/doc/savingstages.cpp | 113 +++++++++++++++++++++++++ apps/opencs/model/doc/savingstages.hpp | 33 +++++++- components/esm/loadinfo.cpp | 1 - 4 files changed, 146 insertions(+), 8 deletions(-) diff --git a/apps/opencs/model/doc/saving.cpp b/apps/opencs/model/doc/saving.cpp index b756a9dc1..73278ba97 100644 --- a/apps/opencs/model/doc/saving.cpp +++ b/apps/opencs/model/doc/saving.cpp @@ -58,12 +58,9 @@ CSMDoc::Saving::Saving (Document& document, const boost::filesystem::path& proje appendStage (new WriteCollectionStage > (mDocument.getData().getSpells(), mState)); - /// \todo deal with info records for topcis and journals - appendStage (new WriteCollectionStage > - (mDocument.getData().getTopics(), mState)); + appendStage (new WriteDialogueCollectionStage (mDocument, mState, false)); - appendStage (new WriteCollectionStage > - (mDocument.getData().getJournals(), mState)); + appendStage (new WriteDialogueCollectionStage (mDocument, mState, true)); appendStage (new WriteRefIdCollectionStage (mDocument, mState)); diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index d68c72317..8e9bcfc0d 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -7,6 +7,10 @@ #include +#include + +#include "../world/infocollection.hpp" + #include "document.hpp" #include "savingstate.hpp" @@ -80,6 +84,115 @@ void CSMDoc::WriteHeaderStage::perform (int stage, std::vector& 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& messages) +{ + const CSMWorld::Record& 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 (&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 (&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) {} diff --git a/apps/opencs/model/doc/savingstages.hpp b/apps/opencs/model/doc/savingstages.hpp index ff94116fd..ca5586511 100644 --- a/apps/opencs/model/doc/savingstages.hpp +++ b/apps/opencs/model/doc/savingstages.hpp @@ -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& 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& messages); + ///< Messages resulting from this stage will be appended to \a messages. + }; + + class WriteRefIdCollectionStage : public Stage { Document& mDocument; diff --git a/components/esm/loadinfo.cpp b/components/esm/loadinfo.cpp index 5120a0043..f86ad3b51 100644 --- a/components/esm/loadinfo.cpp +++ b/components/esm/loadinfo.cpp @@ -123,7 +123,6 @@ 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); From 780ea3a41f25577e46e4f27a5c0b25cdd12192a2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 14 Nov 2013 11:39:14 +0100 Subject: [PATCH 26/26] added support for record reordering to model (only implemented in info collection) --- apps/opencs/model/world/collection.hpp | 50 +++++++++++ apps/opencs/model/world/collectionbase.hpp | 7 ++ apps/opencs/model/world/commands.cpp | 22 +++++ apps/opencs/model/world/commands.hpp | 16 ++++ apps/opencs/model/world/idtable.cpp | 24 ++++-- apps/opencs/model/world/idtable.hpp | 21 ++++- apps/opencs/model/world/infocollection.cpp | 16 ++++ apps/opencs/model/world/infocollection.hpp | 6 ++ apps/opencs/model/world/refidcollection.cpp | 5 ++ apps/opencs/model/world/refidcollection.hpp | 6 ++ apps/opencs/view/world/table.cpp | 96 +++++++++++++++++++++ apps/opencs/view/world/table.hpp | 6 ++ 12 files changed, 267 insertions(+), 8 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 316e76c5f..3f410e397 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -57,6 +57,12 @@ namespace CSMWorld const std::vector >& getRecords() const; + bool reorderRowsImp (int baseIndex, const std::vector& 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(); @@ -128,6 +134,12 @@ namespace CSMWorld /// 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& 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 *column); void setRecord (int index, const Record& record); @@ -146,6 +158,38 @@ namespace CSMWorld return mRecords; } + template + bool Collection::reorderRowsImp (int baseIndex, + const std::vector& newOrder) + { + if (!newOrder.empty()) + { + int size = static_cast (newOrder.size()); + + // check that all indices are present + std::vector test (newOrder); + std::sort (test.begin(), test.end()); + if (*test.begin()!=0 || *--test.end()!=size-1) + return false; + + // reorder records + std::vector > buffer (size); + + for (int i=0; i::iterator iter (mIndex.begin()); iter!=mIndex.end(); + ++iter) + if (iter->second>=baseIndex && iter->secondsecond = newOrder.at (iter->second-baseIndex)+baseIndex; + } + + return true; + } + template Collection::Collection() {} @@ -389,6 +433,12 @@ namespace CSMWorld mRecords.at (index) = record; } + + template + bool Collection::reorderRows (int baseIndex, const std::vector& newOrder) + { + return false; + } } #endif diff --git a/apps/opencs/model/world/collectionbase.hpp b/apps/opencs/model/world/collectionbase.hpp index 2d1b5318c..ab7a9ebec 100644 --- a/apps/opencs/model/world/collectionbase.hpp +++ b/apps/opencs/model/world/collectionbase.hpp @@ -2,6 +2,7 @@ #define CSM_WOLRD_COLLECTIONBASE_H #include +#include #include "universalid.hpp" #include "columns.hpp" @@ -86,6 +87,12 @@ namespace CSMWorld /// /// \param listDeleted include deleted record in the list + virtual bool reorderRows (int baseIndex, const std::vector& 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. diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index f6f421c6a..21feb14be 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -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& newOrder) +: mModel (model), mBaseIndex (baseIndex), mNewOrder (newOrder) +{} + +void CSMWorld::ReorderRowsCommand::redo() +{ + mModel.reorderRows (mBaseIndex, mNewOrder); +} + +void CSMWorld::ReorderRowsCommand::undo() +{ + int size = static_cast (mNewOrder.size()); + std::vector reverse (size); + + for (int i=0; i #include +#include #include #include @@ -99,6 +100,21 @@ namespace CSMWorld virtual void undo(); }; + + class ReorderRowsCommand : public QUndoCommand + { + IdTable& mModel; + int mBaseIndex; + std::vector mNewOrder; + + public: + + ReorderRowsCommand (IdTable& model, int baseIndex, const std::vector& newOrder); + + virtual void redo(); + + virtual void undo(); + }; } #endif \ No newline at end of file diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index f5cd14efc..809d64339 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -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 { @@ -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& 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; } \ No newline at end of file diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index 00c577236..e4ae58fd0 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -1,6 +1,8 @@ #ifndef CSM_WOLRD_IDTABLE_H #define CSM_WOLRD_IDTABLE_H +#include + #include #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& 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; }; } diff --git a/apps/opencs/model/world/infocollection.cpp b/apps/opencs/model/world/infocollection.cpp index b5c1872a1..87bb925c2 100644 --- a/apps/opencs/model/world/infocollection.cpp +++ b/apps/opencs/model/world/infocollection.cpp @@ -88,6 +88,22 @@ int CSMWorld::InfoCollection::getAppendIndex (const std::string& id, UniversalId return std::distance (getRecords().begin(), range.second); } +bool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector& 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) + "#" + diff --git a/apps/opencs/model/world/infocollection.hpp b/apps/opencs/model/world/infocollection.hpp index 1bccbb4ae..ae61f5d39 100644 --- a/apps/opencs/model/world/infocollection.hpp +++ b/apps/opencs/model/world/infocollection.hpp @@ -33,6 +33,12 @@ namespace CSMWorld 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& 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; diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 0218642e0..86a542c5c 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -540,6 +540,11 @@ std::vector CSMWorld::RefIdCollection::getIds (bool listDeleted) co return mData.getIds (listDeleted); } +bool CSMWorld::RefIdCollection::reorderRows (int baseIndex, const std::vector& newOrder) +{ + return false; +} + void CSMWorld::RefIdCollection::save (int index, ESM::ESMWriter& writer) const { mData.save (index, writer); diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp index 0788b6023..5ff4a70bf 100644 --- a/apps/opencs/model/world/refidcollection.hpp +++ b/apps/opencs/model/world/refidcollection.hpp @@ -100,6 +100,12 @@ namespace CSMWorld /// /// \param listDeleted include deleted record in the list + virtual bool reorderRows (int baseIndex, const std::vector& 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; }; } diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index dfdfa6d13..dade9c8ea 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -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" @@ -37,6 +38,35 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) 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 (rowrowCount()-1 && mProxyModel->data (mProxyModel->index (row, column))== + mProxyModel->data (mProxyModel->index (row+1, column))) + { + menu.addAction (mMoveDownAction); + } + } + } + } } menu.exec (event->globalPos()); @@ -176,6 +206,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 +292,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 newOrder (row2-row+1); + newOrder[0] = row2-row; + newOrder[row2-row] = 0; + for (int i=1; iselectedRows(); + + if (selectedRows.size()==1) + { + int row =selectedRows.begin()->row(); + + if (rowrowCount()-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 newOrder (row2-row+1); + newOrder[0] = row2-row; + newOrder[row2-row] = 0; + for (int i=1; icolumnCount(); diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 3e4214424..889e2847a 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -34,6 +34,8 @@ namespace CSVWorld QAction *mCreateAction; QAction *mRevertAction; QAction *mDeleteAction; + QAction *mMoveUpAction; + QAction *mMoveDownAction; CSMWorld::IdTableProxyModel *mProxyModel; CSMWorld::IdTable *mModel; bool mEditLock; @@ -80,6 +82,10 @@ namespace CSVWorld void editRecord(); + void moveUpRecord(); + + void moveDownRecord(); + public slots: void tableSizeUpdate();