diff --git a/README.md b/README.md index aa5af0e6d..811eb8763 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ OpenMW ====== -[![Build Status](https://img.shields.io/travis/OpenMW/openmw.svg?style=plastic)](https://travis-ci.org/OpenMW/openmw) [![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740) +[![Build Status](https://img.shields.io/travis/OpenMW/openmw.svg)](https://travis-ci.org/OpenMW/openmw) [![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740) OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 7861894b0..ec2e1246e 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -24,10 +24,10 @@ const char *Launcher::DataFilesPage::mDefaultContentListName = "Default"; Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings, Config::LauncherSettings &launcherSettings, QWidget *parent) - : mCfgMgr(cfg) + : QWidget(parent) + , mCfgMgr(cfg) , mGameSettings(gameSettings) , mLauncherSettings(launcherSettings) - , QWidget(parent) { ui.setupUi (this); setObjectName ("DataFilesPage"); diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 4c142231d..00e6a9aa2 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -25,7 +25,7 @@ using namespace Process; Launcher::MainDialog::MainDialog(QWidget *parent) - : mGameSettings(mCfgMgr), QMainWindow (parent) + : QMainWindow(parent), mGameSettings (mCfgMgr) { setupUi(this); diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index bc3571aec..8347af6ee 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -18,14 +18,14 @@ opencs_hdrs_noqt (model/doc opencs_units (model/world - idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable + idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree ) opencs_units_noqt (model/world universalid record commands columnbase scriptcontext cell refidcollection refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope - pathgrid landtexture land + pathgrid landtexture land nestedtablewrapper nestedcollection nestedcoladapterimp nestedinfocollection ) opencs_hdrs_noqt (model/world @@ -62,7 +62,7 @@ opencs_hdrs_noqt (view/doc opencs_units (view/world table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator cellcreator referenceablecreator referencecreator scenesubview - infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable + infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable ) opencs_units_noqt (view/world diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 27fa4b3a8..bf065a181 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2386,7 +2386,8 @@ CSMWorld::UniversalId CSMDoc::Document::newSearch() void CSMDoc::Document::runSearch (const CSMWorld::UniversalId& searchId, const CSMTools::Search& search) { - return mTools.runSearch (searchId, search); + mTools.runSearch (searchId, search); + emit stateChanged (getState(), this); } void CSMDoc::Document::abortOperation (int type) diff --git a/apps/opencs/model/doc/documentmanager.cpp b/apps/opencs/model/doc/documentmanager.cpp index edce6e613..f617cbe22 100644 --- a/apps/opencs/model/doc/documentmanager.cpp +++ b/apps/opencs/model/doc/documentmanager.cpp @@ -69,7 +69,7 @@ void CSMDoc::DocumentManager::removeDocument (CSMDoc::Document *document) throw std::runtime_error ("removing invalid document"); mDocuments.erase (iter); - delete document; + document->deleteLater(); if (mDocuments.empty()) emit lastDocumentDeleted(); diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index ef7d1d3af..d6258da6a 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -231,8 +231,29 @@ void CSMDoc::CollectionReferencesStage::perform (int stage, Messages& messages) record.mState==CSMWorld::RecordBase::State_Modified || record.mState==CSMWorld::RecordBase::State_ModifiedOnly) { - mState.getSubRecords()[Misc::StringUtils::lowerCase (record.get().mCell)] - .push_back (i); + std::string cellId = record.get().mOriginalCell.empty() ? + record.get().mCell : record.get().mOriginalCell; + + std::deque& indices = + mState.getSubRecords()[Misc::StringUtils::lowerCase (cellId)]; + + // collect moved references at the end of the container + bool interior = cellId.substr (0, 1)!="#"; + std::ostringstream stream; + if (!interior) + { + // recalculate the ref's cell location + std::pair index = record.get().getCellIndex(); + stream << "#" << index.first << " " << index.second; + } + + // An empty mOriginalCell is meant to indicate that it is the same as + // the current cell. It is possible that a moved ref is moved again. + if ((record.get().mOriginalCell.empty() ? + record.get().mCell : record.get().mOriginalCell) != stream.str() && !interior) + indices.push_back (i); + else + indices.push_front (i); } } } @@ -253,7 +274,7 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages) const CSMWorld::Record& cell = mDocument.getData().getCells().getRecord (stage); - std::map >::const_iterator references = + std::map >::const_iterator references = mState.getSubRecords().find (Misc::StringUtils::lowerCase (cell.get().mId)); if (cell.mState==CSMWorld::RecordBase::State_Modified || @@ -284,7 +305,7 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages) // write references if (references!=mState.getSubRecords().end()) { - for (std::vector::const_iterator iter (references->second.begin()); + for (std::deque::const_iterator iter (references->second.begin()); iter!=references->second.end(); ++iter) { const CSMWorld::Record& ref = @@ -293,6 +314,32 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages) if (ref.mState==CSMWorld::RecordBase::State_Modified || ref.mState==CSMWorld::RecordBase::State_ModifiedOnly) { + // recalculate the ref's cell location + std::ostringstream stream; + if (!interior) + { + std::pair index = ref.get().getCellIndex(); + stream << "#" << index.first << " " << index.second; + } + + // An empty mOriginalCell is meant to indicate that it is the same as + // the current cell. It is possible that a moved ref is moved again. + if ((ref.get().mOriginalCell.empty() ? ref.get().mCell : ref.get().mOriginalCell) + != stream.str() && !interior) + { + ESM::MovedCellRef moved; + moved.mRefNum = ref.get().mRefNum; + + // Need to fill mTarget with the ref's new position. + std::istringstream istream (stream.str().c_str()); + + char ignore; + istream >> ignore >> moved.mTarget[0] >> moved.mTarget[1]; + + ref.get().mRefNum.save (mState.getWriter(), false, "MVRF"); + mState.getWriter().writeHNT ("CNDT", moved.mTarget, 8); + } + ref.get().save (mState.getWriter()); } else if (ref.mState==CSMWorld::RecordBase::State_Deleted) diff --git a/apps/opencs/model/doc/savingstate.cpp b/apps/opencs/model/doc/savingstate.cpp index 84bca1e95..e7ad551b2 100644 --- a/apps/opencs/model/doc/savingstate.cpp +++ b/apps/opencs/model/doc/savingstate.cpp @@ -64,7 +64,7 @@ bool CSMDoc::SavingState::isProjectFile() const return mProjectFile; } -std::map >& CSMDoc::SavingState::getSubRecords() +std::map >& CSMDoc::SavingState::getSubRecords() { return mSubRecords; } diff --git a/apps/opencs/model/doc/savingstate.hpp b/apps/opencs/model/doc/savingstate.hpp index 577fc734d..e6c8c545a 100644 --- a/apps/opencs/model/doc/savingstate.hpp +++ b/apps/opencs/model/doc/savingstate.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -26,7 +27,7 @@ namespace CSMDoc ESM::ESMWriter mWriter; boost::filesystem::path mProjectPath; bool mProjectFile; - std::map > mSubRecords; // record ID, list of subrecords + std::map > mSubRecords; // record ID, list of subrecords public: @@ -49,7 +50,7 @@ namespace CSMDoc bool isProjectFile() const; ///< Currently saving project file? (instead of content file) - std::map >& getSubRecords(); + std::map >& getSubRecords(); }; diff --git a/apps/opencs/model/settings/usersettings.cpp b/apps/opencs/model/settings/usersettings.cpp index 076eed1cd..8d8119d25 100644 --- a/apps/opencs/model/settings/usersettings.cpp +++ b/apps/opencs/model/settings/usersettings.cpp @@ -203,9 +203,24 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() "Characters after search string"); after->setDefaultValue (10); after->setRange (0, 1000); - after->setToolTip ("Maximum number of character to display in search result after the searched text"); + after->setToolTip ("Maximum number of character to display in search result after the searched text"); + + Setting *autoDelete = createSetting (Type_CheckBox, "auto-delete", "Delete row from result table after a successful replace"); + autoDelete->setDefaultValue ("true"); } - + + declareSection ("script-editor", "Script Editor"); + { + Setting *lineNum = createSetting (Type_CheckBox, "show-linenum", "Show Line Numbers"); + lineNum->setDefaultValue ("true"); + lineNum->setToolTip ("Show line numbers to the left of the script editor window." + "The current row and column numbers of the text cursor are shown at the bottom."); + + Setting *monoFont = createSetting (Type_CheckBox, "mono-font", "Use monospace font"); + monoFont->setDefaultValue ("true"); + monoFont->setToolTip ("Whether to use monospaced fonts on script edit subview."); + } + { /****************************************************************** * There are three types of values: diff --git a/apps/opencs/model/tools/referencecheck.cpp b/apps/opencs/model/tools/referencecheck.cpp index 198c3627f..be9663e7a 100644 --- a/apps/opencs/model/tools/referencecheck.cpp +++ b/apps/opencs/model/tools/referencecheck.cpp @@ -28,7 +28,7 @@ void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages &message // Check for empty reference id if (cellRef.mRefID.empty()) { - messages.push_back(std::make_pair(id, " is an empty reference")); + messages.push_back(std::make_pair(id, " is an empty instance (not based on an object)")); } else { // Check for non existing referenced object if (mReferencables.searchId(cellRef.mRefID) == -1) { diff --git a/apps/opencs/model/tools/reportmodel.cpp b/apps/opencs/model/tools/reportmodel.cpp index 5648ace54..1248e202b 100644 --- a/apps/opencs/model/tools/reportmodel.cpp +++ b/apps/opencs/model/tools/reportmodel.cpp @@ -120,7 +120,14 @@ bool CSMTools::ReportModel::removeRows (int row, int count, const QModelIndex& p if (parent.isValid()) return false; - mRows.erase (mRows.begin()+row, mRows.begin()+row+count); + if (count>0) + { + beginRemoveRows (parent, row, row+count-1); + + mRows.erase (mRows.begin()+row, mRows.begin()+row+count); + + endRemoveRows(); + } return true; } diff --git a/apps/opencs/model/tools/search.cpp b/apps/opencs/model/tools/search.cpp index cb8850754..7eb531161 100644 --- a/apps/opencs/model/tools/search.cpp +++ b/apps/opencs/model/tools/search.cpp @@ -276,4 +276,21 @@ void CSMTools::Search::replace (CSMDoc::Document& document, CSMWorld::IdTableBas new CSMWorld::ModifyCommand (*model, index, QString::fromUtf8 (newText.c_str()))); } } + +bool CSMTools::Search::verify (CSMDoc::Document& document, CSMWorld::IdTableBase *model, + const CSMWorld::UniversalId& id, const std::string& messageHint) const +{ + CSMDoc::Messages messages; + + int row = model->getModelIndex (id.getId(), + model->findColumnIndex (CSMWorld::Columns::ColumnId_Id)).row(); + + searchRow (model, row, messages); + + for (CSMDoc::Messages::Iterator iter (messages.begin()); iter!=messages.end(); ++iter) + if (iter->mHint==messageHint) + return true; + + return false; +} diff --git a/apps/opencs/model/tools/search.hpp b/apps/opencs/model/tools/search.hpp index c9dfc4c44..69b98bbdb 100644 --- a/apps/opencs/model/tools/search.hpp +++ b/apps/opencs/model/tools/search.hpp @@ -88,6 +88,10 @@ namespace CSMTools void replace (CSMDoc::Document& document, CSMWorld::IdTableBase *model, const CSMWorld::UniversalId& id, const std::string& messageHint, const std::string& replaceText) const; + + // Check if model still matches search results. + bool verify (CSMDoc::Document& document, CSMWorld::IdTableBase *model, + const CSMWorld::UniversalId& id, const std::string& messageHint) const; }; } diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 1fb3e1f1d..b0571bbed 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -148,6 +148,8 @@ namespace CSMWorld void setRecord (int index, const Record& record); ///< \attention This function must not change the ID. + + NestableColumn *getNestableColumn (int column) const; }; template @@ -289,6 +291,15 @@ namespace CSMWorld return *mColumns.at (column); } + template + NestableColumn *Collection::getNestableColumn (int column) const + { + if (column < 0 || column >= static_cast(mColumns.size())) + throw std::runtime_error("column index out of range"); + + return mColumns.at (column); + } + template void Collection::addColumn (Column *column) { diff --git a/apps/opencs/model/world/columnbase.cpp b/apps/opencs/model/world/columnbase.cpp index 17e4def2f..659954f48 100644 --- a/apps/opencs/model/world/columnbase.cpp +++ b/apps/opencs/model/world/columnbase.cpp @@ -1,10 +1,9 @@ - #include "columnbase.hpp" #include "columns.hpp" CSMWorld::ColumnBase::ColumnBase (int columnId, Display displayType, int flags) -: mColumnId (columnId), mDisplayType (displayType), mFlags (flags) + : mColumnId (columnId), mDisplayType (displayType), mFlags (flags) {} CSMWorld::ColumnBase::~ColumnBase() {} @@ -19,7 +18,7 @@ std::string CSMWorld::ColumnBase::getTitle() const return Columns::getName (static_cast (mColumnId)); } -int CSMWorld::ColumnBase::getId() const +int CSMWorld::ColumnBase::getId() const { return mColumnId; } @@ -76,6 +75,12 @@ bool CSMWorld::ColumnBase::isId (Display display) Display_Video, Display_Id, + Display_SkillImpact, + Display_EffectRange, + Display_EffectId, + Display_PartRefType, + Display_AiPackageType, + Display_YesNo, Display_None }; @@ -84,7 +89,7 @@ bool CSMWorld::ColumnBase::isId (Display display) if (ids[i]==display) return true; - return false; + return false; } bool CSMWorld::ColumnBase::isText (Display display) @@ -96,3 +101,44 @@ bool CSMWorld::ColumnBase::isScript (Display display) { return display==Display_ScriptFile || display==Display_ScriptLines; } + +void CSMWorld::NestableColumn::addColumn(CSMWorld::NestableColumn *column) +{ + mNestedColumns.push_back(column); +} + +const CSMWorld::ColumnBase& CSMWorld::NestableColumn::nestedColumn(int subColumn) const +{ + if (mNestedColumns.empty()) + throw std::logic_error("Tried to access nested column of the non-nest column"); + + return *mNestedColumns.at(subColumn); +} + +CSMWorld::NestableColumn::NestableColumn(int columnId, CSMWorld::ColumnBase::Display displayType, + int flag) + : CSMWorld::ColumnBase(columnId, displayType, flag) +{} + +CSMWorld::NestableColumn::~NestableColumn() +{ + for (unsigned int i = 0; i < mNestedColumns.size(); ++i) + { + delete mNestedColumns[i]; + } +} + +bool CSMWorld::NestableColumn::hasChildren() const +{ + return !mNestedColumns.empty(); +} + +CSMWorld::NestedChildColumn::NestedChildColumn (int id, + CSMWorld::ColumnBase::Display display, bool isEditable) + : NestableColumn (id, display, CSMWorld::ColumnBase::Flag_Dialogue) , mIsEditable(isEditable) +{} + +bool CSMWorld::NestedChildColumn::isEditable () const +{ + return mIsEditable; +} diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index 6f67898f6..71c22a9f0 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -2,6 +2,8 @@ #define CSM_WOLRD_COLUMNBASE_H #include +#include +#include #include #include @@ -15,13 +17,15 @@ namespace CSMWorld enum Roles { Role_Flags = Qt::UserRole, - Role_Display = Qt::UserRole+1 + Role_Display = Qt::UserRole+1, + Role_ColumnId = Qt::UserRole+2 }; enum Flags { Flag_Table = 1, // column should be displayed in table view - Flag_Dialogue = 2 // column should be displayed in dialogue view + Flag_Dialogue = 2, // column should be displayed in dialogue view + Flag_Dialogue_List = 4 // column should be diaplyed in dialogue view }; enum Display @@ -30,7 +34,7 @@ namespace CSMWorld Display_String, Display_LongString, - //CONCRETE TYPES STARTS HERE + //CONCRETE TYPES STARTS HERE (for drag and drop) Display_Skill, Display_Class, Display_Faction, @@ -105,7 +109,16 @@ namespace CSMWorld Display_ScriptLines, // console context Display_SoundGeneratorType, Display_School, - Display_Id + Display_Id, + Display_SkillImpact, + Display_EffectRange, + Display_EffectId, + Display_PartRefType, + Display_AiPackageType, + Display_YesNo, + + //top level columns that nest other columns + Display_NestedHeader }; int mColumnId; @@ -132,11 +145,28 @@ namespace CSMWorld static bool isScript (Display display); }; + class NestableColumn : public ColumnBase + { + std::vector mNestedColumns; + + public: + + NestableColumn(int columnId, Display displayType, int flag); + + ~NestableColumn(); + + void addColumn(CSMWorld::NestableColumn *column); + + const ColumnBase& nestedColumn(int subColumn) const; + + bool hasChildren() const; + }; + template - struct Column : public ColumnBase + struct Column : public NestableColumn { Column (int columnId, Display displayType, int flags = Flag_Table | Flag_Dialogue) - : ColumnBase (columnId, displayType, flags) {} + : NestableColumn (columnId, displayType, flags) {} virtual QVariant get (const Record& record) const = 0; @@ -145,6 +175,34 @@ namespace CSMWorld throw std::logic_error ("Column " + getTitle() + " is not editable"); } }; + + template + struct NestedParentColumn : public Column + { + NestedParentColumn (int id, int flags = ColumnBase::Flag_Dialogue) : Column (id, + ColumnBase::Display_NestedHeader, flags) + {} + + virtual QVariant get (const Record& record) const + { + return true; // required by IdTree::hasChildren() + } + + virtual bool isEditable() const + { + return true; + } + }; + + struct NestedChildColumn : public NestableColumn + { + NestedChildColumn (int id, Display display, bool isEditable = true); + + virtual bool isEditable() const; + + private: + bool mIsEditable; + }; } #endif diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index e67fdd59c..cbe0d74c4 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -870,7 +870,13 @@ namespace CSMWorld template struct CellColumn : public Column { - CellColumn() : Column (Columns::ColumnId_Cell, ColumnBase::Display_Cell) {} + bool mBlocked; + + /// \param blocked Do not allow user-modification + CellColumn (bool blocked = false) + : Column (Columns::ColumnId_Cell, ColumnBase::Display_Cell), + mBlocked (blocked) + {} virtual QVariant get (const Record& record) const { @@ -892,9 +898,41 @@ namespace CSMWorld } virtual bool isUserEditable() const + { + return !mBlocked; + } + }; + + template + struct OriginalCellColumn : public Column + { + OriginalCellColumn() + : Column (Columns::ColumnId_OriginalCell, ColumnBase::Display_Cell) + {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mOriginalCell.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mOriginalCell = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const { return true; } + + virtual bool isUserEditable() const + { + return false; + } }; template @@ -1277,7 +1315,6 @@ namespace CSMWorld } }; - template struct PosColumn : public Column { diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index 8349eb515..3172e72e4 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -173,6 +173,11 @@ namespace CSMWorld { ColumnId_Gender, "Gender" }, { ColumnId_PcRank, "PC Rank" }, { ColumnId_ReferenceableId, "Referenceable ID" }, + + { ColumnId_ContainerContent, "Content" }, + { ColumnId_ItemCount, "Count" }, + { ColumnId_InventoryItemId, "ID"}, + { ColumnId_CombatState, "Combat" }, { ColumnId_MagicState, "Magic" }, { ColumnId_StealthState, "Stealth" }, @@ -180,6 +185,22 @@ namespace CSMWorld { ColumnId_Vampire, "Vampire" }, { ColumnId_BodyPartType, "Bodypart Type" }, { ColumnId_MeshType, "Mesh Type" }, + + { ColumnId_ActorInventory, "Inventory" }, + { ColumnId_SpellList, "Spells" }, + { ColumnId_SpellId, "ID"}, + + { ColumnId_NpcDestinations, "Destinations" }, + { ColumnId_DestinationCell, "Cell"}, + { ColumnId_PosX, "Dest X"}, + { ColumnId_PosY, "Dest Y"}, + { ColumnId_PosZ, "Dest Z"}, + { ColumnId_RotX, "Rotation X"}, + { ColumnId_RotY, "Rotation Y"}, + { ColumnId_RotZ, "Rotation Z"}, + + { ColumnId_Skill, "Skill" }, + { ColumnId_OwnerGlobal, "Owner Global" }, { ColumnId_DefaultProfile, "Default Profile" }, { ColumnId_BypassNewGame, "Bypass New Game" }, @@ -202,6 +223,59 @@ namespace CSMWorld { ColumnId_AreaSound, "Area Sound" }, { ColumnId_BoltSound, "Bolt Sound" }, + { ColumnId_PathgridPoints, "Points" }, + { ColumnId_PathgridIndex, "Index" }, + { ColumnId_PathgridPosX, "X" }, + { ColumnId_PathgridPosY, "Y" }, + { ColumnId_PathgridPosZ, "Z" }, + { ColumnId_PathgridEdges, "Edges" }, + { ColumnId_PathgridEdgeIndex, "Index" }, + { ColumnId_PathgridEdge0, "Point 0" }, + { ColumnId_PathgridEdge1, "Point 1" }, + + { ColumnId_RegionSounds, "Sounds" }, + { ColumnId_SoundName, "Name" }, + { ColumnId_SoundChance, "Chance" }, + + { ColumnId_FactionReactions, "Reactions" }, + //{ ColumnId_FactionID, "Faction ID" }, + { ColumnId_FactionReaction, "Reaction" }, + + { ColumnId_EffectList, "Effects" }, + { ColumnId_EffectId, "Effect" }, + //{ ColumnId_EffectAttribute, "Attrib" }, + { ColumnId_EffectRange, "Range" }, + { ColumnId_EffectArea, "Area" }, + + { ColumnId_AiPackageList, "Ai Packages" }, + { ColumnId_AiPackageType, "Package" }, + { ColumnId_AiWanderDist, "Wander Dist" }, + { ColumnId_AiDuration, "Duration" }, + { ColumnId_AiWanderToD, "Wander ToD" }, + { ColumnId_AiWanderIdle, "Wander Idle" }, + { ColumnId_AiWanderRepeat, "Wander Repeat" }, + { ColumnId_AiActivateName, "Activate" }, + { ColumnId_AiTargetId, "Target ID" }, + { ColumnId_AiTargetCell, "Target Cell" }, + + { ColumnId_PartRefList, "Part Reference" }, + { ColumnId_PartRefType, "Type" }, + { ColumnId_PartRefMale, "Male" }, + { ColumnId_PartRefFemale, "Female" }, + + { ColumnId_LevelledList,"Levelled List" }, + { ColumnId_LevelledItemId,"Item ID" }, + { ColumnId_LevelledItemLevel,"Level" }, + { ColumnId_LevelledItemType, "Calculate all levels <= player" }, + { ColumnId_LevelledItemTypeEach, "Select a new item each instance" }, + { ColumnId_LevelledItemChanceNone, "Chance None" }, + + { ColumnId_PowerList, "Powers" }, + { ColumnId_SkillImpact, "Skills" }, + + { ColumnId_InfoList, "Info List" }, + { ColumnId_OriginalCell, "Original Cell" }, + { ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue2, "Use value 2" }, { ColumnId_UseValue3, "Use value 3" }, @@ -228,6 +302,7 @@ namespace CSMWorld { ColumnId_Skill4, "Skill 4" }, { ColumnId_Skill5, "Skill 5" }, { ColumnId_Skill6, "Skill 6" }, + { ColumnId_Skill7, "Skill 7" }, { -1, 0 } // end marker }; @@ -261,6 +336,7 @@ namespace "Combat", "Magic", "Stealth", 0 }; + // see ESM::Attribute::AttributeID in static const char *sAttributes[] = { "Strength", "Intelligence", "Willpower", "Agility", "Speed", "Endurance", "Personality", @@ -353,6 +429,79 @@ namespace "Alteration", "Conjuration", "Destruction", "Illusion", "Mysticism", "Restoration", 0 }; + // impact from magic effects, see ESM::Skill::SkillEnum in + static const char *sSkills[] = + { + "Block", "Armorer", "MediumArmor", "HeavyArmor", "BluntWeapon", + "LongBlade", "Axe", "Spear", "Athletics", "Enchant", + "Destruction", "Alteration", "Illusion", "Conjuration", "Mysticism", + "Restoration", "Alchemy", "Unarmored", "Security", "Sneak", + "Acrobatics", "LightArmor", "ShortBlade", "Marksman", "Mercantile", + "Speechcraft", "HandToHand", 0 + }; + + // range of magic effects, see ESM::RangeType in + static const char *sEffectRange[] = + { + "Self", "Touch", "Target", 0 + }; + + // magic effect names, see ESM::MagicEffect::Effects in + static const char *sEffectId[] = + { + "WaterBreathing", "SwiftSwim", "WaterWalking", "Shield", "FireShield", + "LightningShield", "FrostShield", "Burden", "Feather", "Jump", + "Levitate", "SlowFall", "Lock", "Open", "FireDamage", + "ShockDamage", "FrostDamage", "DrainAttribute", "DrainHealth", "DrainMagicka", + "DrainFatigue", "DrainSkill", "DamageAttribute", "DamageHealth", "DamageMagicka", + "DamageFatigue", "DamageSkill", "Poison", "WeaknessToFire", "WeaknessToFrost", + "WeaknessToShock", "WeaknessToMagicka", "WeaknessToCommonDisease", "WeaknessToBlightDisease", "WeaknessToCorprusDisease", + "WeaknessToPoison", "WeaknessToNormalWeapons", "DisintegrateWeapon", "DisintegrateArmor", "Invisibility", + "Chameleon", "Light", "Sanctuary", "NightEye", "Charm", + "Paralyze", "Silence", "Blind", "Sound", "CalmHumanoid", + "CalmCreature", "FrenzyHumanoid", "FrenzyCreature", "DemoralizeHumanoid", "DemoralizeCreature", + "RallyHumanoid", "RallyCreature", "Dispel", "Soultrap", "Telekinesis", + "Mark", "Recall", "DivineIntervention", "AlmsiviIntervention", "DetectAnimal", + "DetectEnchantment", "DetectKey", "SpellAbsorption", "Reflect", "CureCommonDisease", + "CureBlightDisease", "CureCorprusDisease", "CurePoison", "CureParalyzation", "RestoreAttribute", + "RestoreHealth", "RestoreMagicka", "RestoreFatigue", "RestoreSkill", "FortifyAttribute", + "FortifyHealth", "FortifyMagicka", "FortifyFatigue", "FortifySkill", "FortifyMaximumMagicka", + "AbsorbAttribute", "AbsorbHealth", "AbsorbMagicka", "AbsorbFatigue", "AbsorbSkill", + "ResistFire", "ResistFrost", "ResistShock", "ResistMagicka", "ResistCommonDisease", + "ResistBlightDisease", "ResistCorprusDisease", "ResistPoison", "ResistNormalWeapons", "ResistParalysis", + "RemoveCurse", "TurnUndead", "SummonScamp", "SummonClannfear", "SummonDaedroth", + "SummonDremora", "SummonAncestralGhost", "SummonSkeletalMinion", "SummonBonewalker", "SummonGreaterBonewalker", + "SummonBonelord", "SummonWingedTwilight", "SummonHunger", "SummonGoldenSaint", "SummonFlameAtronach", + "SummonFrostAtronach", "SummonStormAtronach", "FortifyAttack", "CommandCreature", "CommandHumanoid", + "BoundDagger", "BoundLongsword", "BoundMace", "BoundBattleAxe", "BoundSpear", + "BoundLongbow", "ExtraSpell", "BoundCuirass", "BoundHelm", "BoundBoots", + "BoundShield", "BoundGloves", "Corprus", "Vampirism", "SummonCenturionSphere", + "SunDamage", "StuntedMagicka", "SummonFabricant", "SummonWolf", "SummonBear", + "SummonBonewolf", "SummonCreature04", "SummonCreature05", 0 + }; + + // see ESM::PartReferenceType in + static const char *sPartRefType[] = + { + "Head", "Hair", "Neck", "Cuirass", "Groin", + "Skirt", "Right Hand", "Left Hand", "Right Wrist", "Left Wrist", + "Shield", "Right Forearm", "Left Forearm", "Right Upperarm", "Left Upperarm", + "Right Foot", "Left Foot", "Right Ankle", "Left Ankle", "Right Knee", + "Left Knee", "Right Leg", "Left Leg", "Right Pauldron", "Left Pauldron", + "Weapon", "Tail", 0 + }; + + // see the enums in + static const char *sAiPackageType[] = + { + "AI Wander", "AI Travel", "AI Follow", "AI Escort", "AI Activate", 0 + }; + + static const char *sAiWanderRepeat[] = + { + "No", "Yes", 0 + }; + const char **getEnumNames (CSMWorld::Columns::ColumnId column) { switch (column) @@ -375,6 +524,12 @@ namespace case CSMWorld::Columns::ColumnId_MeshType: return sMeshTypes; case CSMWorld::Columns::ColumnId_SoundGeneratorType: return sSoundGeneratorType; case CSMWorld::Columns::ColumnId_School: return sSchools; + case CSMWorld::Columns::ColumnId_SkillImpact: return sSkills; + case CSMWorld::Columns::ColumnId_EffectRange: return sEffectRange; + case CSMWorld::Columns::ColumnId_EffectId: return sEffectId; + case CSMWorld::Columns::ColumnId_PartRefType: return sPartRefType; + case CSMWorld::Columns::ColumnId_AiPackageType: return sAiPackageType; + case CSMWorld::Columns::ColumnId_AiWanderRepeat: return sAiWanderRepeat; default: return 0; } diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index ca0326655..b87f6c53d 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -4,6 +4,8 @@ #include #include +#include "columnbase.hpp" + namespace CSMWorld { namespace Columns @@ -165,35 +167,106 @@ namespace CSMWorld ColumnId_Rank = 152, ColumnId_Gender = 153, ColumnId_PcRank = 154, - ColumnId_ReferenceableId = 156, - ColumnId_CombatState = 157, - ColumnId_MagicState = 158, - ColumnId_StealthState = 159, - ColumnId_EnchantmentType = 160, - ColumnId_Vampire = 161, - ColumnId_BodyPartType = 162, - ColumnId_MeshType = 163, - ColumnId_OwnerGlobal = 164, - ColumnId_DefaultProfile = 165, - ColumnId_BypassNewGame = 166, - ColumnId_GlobalProfile = 167, - ColumnId_RefNumCounter = 168, - ColumnId_RefNum = 169, - ColumnId_Creature = 170, - ColumnId_SoundGeneratorType = 171, - ColumnId_AllowSpellmaking = 172, - ColumnId_AllowEnchanting = 173, - ColumnId_BaseCost = 174, - ColumnId_School = 175, - ColumnId_Particle = 176, - ColumnId_CastingObject = 177, - ColumnId_HitObject = 178, - ColumnId_AreaObject = 179, - ColumnId_BoltObject = 180, - ColumnId_CastingSound = 177, - ColumnId_HitSound = 178, - ColumnId_AreaSound = 179, - ColumnId_BoltSound = 180, + ColumnId_ReferenceableId = 155, + ColumnId_ContainerContent = 156, + ColumnId_ItemCount = 157, + ColumnId_InventoryItemId = 158, + ColumnId_CombatState = 159, + ColumnId_MagicState = 160, + ColumnId_StealthState = 161, + ColumnId_EnchantmentType = 162, + ColumnId_Vampire = 163, + ColumnId_BodyPartType = 164, + ColumnId_MeshType = 165, + ColumnId_ActorInventory = 166, + ColumnId_SpellList = 167, + ColumnId_SpellId = 168, + ColumnId_NpcDestinations = 169, + ColumnId_DestinationCell = 170, + ColumnId_PosX = 171, // these are float + ColumnId_PosY = 172, // these are float + ColumnId_PosZ = 173, // these are float + ColumnId_RotX = 174, + ColumnId_RotY = 175, + ColumnId_RotZ = 176, + ColumnId_Skill = 177, + ColumnId_OwnerGlobal = 178, + ColumnId_DefaultProfile = 179, + ColumnId_BypassNewGame = 180, + ColumnId_GlobalProfile = 181, + ColumnId_RefNumCounter = 182, + ColumnId_RefNum = 183, + ColumnId_Creature = 184, + ColumnId_SoundGeneratorType = 185, + ColumnId_AllowSpellmaking = 186, + ColumnId_AllowEnchanting = 187, + ColumnId_BaseCost = 188, + ColumnId_School = 189, + ColumnId_Particle = 190, + ColumnId_CastingObject = 191, + ColumnId_HitObject = 192, + ColumnId_AreaObject = 193, + ColumnId_BoltObject = 194, + ColumnId_CastingSound = 195, + ColumnId_HitSound = 196, + ColumnId_AreaSound = 197, + ColumnId_BoltSound = 198, + + ColumnId_PathgridPoints = 199, + ColumnId_PathgridIndex = 200, + ColumnId_PathgridPosX = 201, // these are int + ColumnId_PathgridPosY = 202, // these are int + ColumnId_PathgridPosZ = 203, // these are int + ColumnId_PathgridEdges = 204, + ColumnId_PathgridEdgeIndex = 205, + ColumnId_PathgridEdge0 = 206, + ColumnId_PathgridEdge1 = 207, + + ColumnId_RegionSounds = 208, + ColumnId_SoundName = 209, + ColumnId_SoundChance = 210, + + ColumnId_FactionReactions = 211, + //ColumnId_FactionID = 212, + ColumnId_FactionReaction = 213, + + ColumnId_EffectList = 214, + ColumnId_EffectId = 215, + //ColumnId_EffectAttribute = 216, + ColumnId_EffectRange = 217, + ColumnId_EffectArea = 218, + + ColumnId_AiPackageList = 219, + ColumnId_AiPackageType = 220, + ColumnId_AiWanderDist = 221, + ColumnId_AiDuration = 222, + ColumnId_AiWanderToD = 223, + ColumnId_AiWanderIdle = 224, + ColumnId_AiWanderRepeat = 225, + ColumnId_AiActivateName = 226, + // use ColumnId_PosX, etc for AI destinations + ColumnId_AiTargetId = 227, + ColumnId_AiTargetCell = 228, + + ColumnId_PartRefList = 229, + ColumnId_PartRefType = 230, + ColumnId_PartRefMale = 231, + ColumnId_PartRefFemale = 232, + + ColumnId_LevelledList = 233, + ColumnId_LevelledItemId = 234, + ColumnId_LevelledItemLevel = 235, + ColumnId_LevelledItemType = 236, + ColumnId_LevelledItemTypeEach = 237, + ColumnId_LevelledItemChanceNone = 238, + + ColumnId_PowerList = 239, + ColumnId_SkillImpact = 240, // impact from magic effects + + ColumnId_InfoList = 241, + + ColumnId_OriginalCell = 242, + // Allocated to a separate value range, so we don't get a collision should we ever need // to extend the number of use values. ColumnId_UseValue1 = 0x10000, @@ -227,7 +300,8 @@ namespace CSMWorld ColumnId_Skill3 = 0x50002, ColumnId_Skill4 = 0x50003, ColumnId_Skill5 = 0x50004, - ColumnId_Skill6 = 0x50005 + ColumnId_Skill6 = 0x50005, + ColumnId_Skill7 = 0x50006 }; std::string getName (ColumnId column); diff --git a/apps/opencs/model/world/commanddispatcher.cpp b/apps/opencs/model/world/commanddispatcher.cpp index ca6faafbc..2d3262487 100644 --- a/apps/opencs/model/world/commanddispatcher.cpp +++ b/apps/opencs/model/world/commanddispatcher.cpp @@ -2,6 +2,7 @@ #include "commanddispatcher.hpp" #include +#include #include @@ -10,6 +11,7 @@ #include "idtable.hpp" #include "record.hpp" #include "commands.hpp" +#include "idtableproxymodel.hpp" std::vector CSMWorld::CommandDispatcher::getDeletableRecords() const { @@ -131,6 +133,54 @@ std::vector CSMWorld::CommandDispatcher::getExtendedTypes return tables; } +void CSMWorld::CommandDispatcher::executeModify (QAbstractItemModel *model, const QModelIndex& index, const QVariant& new_) +{ + if (mLocked) + return; + + std::auto_ptr modifyCell; + + int columnId = model->data (index, ColumnBase::Role_ColumnId).toInt(); + + if (columnId==Columns::ColumnId_PositionXPos || columnId==Columns::ColumnId_PositionYPos) + { + IdTableProxyModel *proxy = dynamic_cast (model); + + int row = proxy ? proxy->mapToSource (index).row() : index.row(); + + // This is not guaranteed to be the same as \a model, since a proxy could be used. + IdTable& model2 = dynamic_cast (*mDocument.getData().getTableModel (mId)); + + int cellColumn = model2.searchColumnIndex (Columns::ColumnId_Cell); + + if (cellColumn!=-1) + { + QModelIndex cellIndex = model2.index (row, cellColumn); + + std::string cellId = model2.data (cellIndex).toString().toUtf8().data(); + + if (cellId.find ('#')!=std::string::npos) + { + // Need to recalculate the cell + modifyCell.reset (new UpdateCellCommand (model2, row)); + } + } + } + + std::auto_ptr modifyData ( + new CSMWorld::ModifyCommand (*model, index, new_)); + + if (modifyCell.get()) + { + mDocument.getUndoStack().beginMacro (modifyData->text()); + mDocument.getUndoStack().push (modifyData.release()); + mDocument.getUndoStack().push (modifyCell.release()); + mDocument.getUndoStack().endMacro(); + } + else + mDocument.getUndoStack().push (modifyData.release()); +} + void CSMWorld::CommandDispatcher::executeDelete() { if (mLocked) @@ -153,7 +203,15 @@ void CSMWorld::CommandDispatcher::executeDelete() std::string id = model.data (model.getModelIndex (*iter, columnIndex)). toString().toUtf8().constData(); - mDocument.getUndoStack().push (new CSMWorld::DeleteCommand (model, id)); + if (mId.getType() == UniversalId::Type_Referenceables) + { + mDocument.getUndoStack().push ( new CSMWorld::DeleteCommand (model, id, + static_cast(model.data (model.index ( + model.getModelIndex (id, columnIndex).row(), + model.findColumnIndex (CSMWorld::Columns::ColumnId_RecordType))).toInt()))); + } + else + mDocument.getUndoStack().push (new CSMWorld::DeleteCommand (model, id)); } if (rows.size()>1) diff --git a/apps/opencs/model/world/commanddispatcher.hpp b/apps/opencs/model/world/commanddispatcher.hpp index 50085b1a1..1d29e48c1 100644 --- a/apps/opencs/model/world/commanddispatcher.hpp +++ b/apps/opencs/model/world/commanddispatcher.hpp @@ -7,6 +7,9 @@ #include "universalid.hpp" +class QModelIndex; +class QAbstractItemModel; + namespace CSMDoc { class Document; @@ -53,6 +56,12 @@ namespace CSMWorld /// the extended mode, the returned vector will be empty instead. std::vector getExtendedTypes() const; + /// Add a modify command to the undo stack. + /// + /// \attention model must either be a model for the table operated on by this + /// dispatcher or a proxy of it. + void executeModify (QAbstractItemModel *model, const QModelIndex& index, const QVariant& new_); + public slots: void executeDelete(); diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 1d86ba080..9a0401081 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -1,27 +1,43 @@ - #include "commands.hpp" +#include +#include + +#include + #include +#include #include "idtable.hpp" -#include +#include "idtree.hpp" +#include "nestedtablewrapper.hpp" CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_, QUndoCommand* parent) -: QUndoCommand (parent), mModel (model), mIndex (index), mNew (new_) +: QUndoCommand (parent), mModel (&model), mIndex (index), mNew (new_) { - setText ("Modify " + mModel.headerData (mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString()); + if (QAbstractProxyModel *proxy = dynamic_cast (&model)) + { + // Replace proxy with actual model + mIndex = proxy->mapToSource (index); + mModel = proxy->sourceModel(); + + setText ("Modify " + dynamic_cast(mModel)->nestedHeaderData ( + mIndex.parent().column(), mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString()); + } + else + setText ("Modify " + mModel->headerData (mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString()); } void CSMWorld::ModifyCommand::redo() { - mOld = mModel.data (mIndex, Qt::EditRole); - mModel.setData (mIndex, mNew); + mOld = mModel->data (mIndex, Qt::EditRole); + mModel->setData (mIndex, mNew); } void CSMWorld::ModifyCommand::undo() { - mModel.setData (mIndex, mOld); + mModel->setData (mIndex, mOld); } @@ -93,8 +109,9 @@ void CSMWorld::RevertCommand::undo() mModel.setRecord (mId, *mOld); } -CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand* parent) -: QUndoCommand (parent), mModel (model), mId (id), mOld (0) +CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, + const std::string& id, CSMWorld::UniversalId::Type type, QUndoCommand* parent) +: QUndoCommand (parent), mModel (model), mId (id), mOld (0), mType(type) { setText (("Delete record " + id).c_str()); @@ -125,7 +142,7 @@ void CSMWorld::DeleteCommand::redo() void CSMWorld::DeleteCommand::undo() { - mModel.setRecord (mId, *mOld); + mModel.setRecord (mId, *mOld, mType); } @@ -171,3 +188,116 @@ void CSMWorld::CloneCommand::undo() { mModel.removeRow (mModel.getModelIndex (mId, 0).row()); } + + +CSMWorld::UpdateCellCommand::UpdateCellCommand (IdTable& model, int row, QUndoCommand *parent) +: QUndoCommand (parent), mModel (model), mRow (row) +{ + setText ("Update cell ID"); +} + +void CSMWorld::UpdateCellCommand::redo() +{ + if (!mNew.isValid()) + { + int cellColumn = mModel.searchColumnIndex (Columns::ColumnId_Cell); + mIndex = mModel.index (mRow, cellColumn); + + const int cellSize = 8192; + + QModelIndex xIndex = mModel.index ( + mRow, mModel.findColumnIndex (Columns::ColumnId_PositionXPos)); + + QModelIndex yIndex = mModel.index ( + mRow, mModel.findColumnIndex (Columns::ColumnId_PositionYPos)); + + int x = std::floor (mModel.data (xIndex).toFloat() / cellSize); + int y = std::floor (mModel.data (yIndex).toFloat() / cellSize); + + std::ostringstream stream; + + stream << "#" << x << " " << y; + + mNew = QString::fromUtf8 (stream.str().c_str()); + } + + mModel.setData (mIndex, mNew); +} + +void CSMWorld::UpdateCellCommand::undo() +{ + mModel.setData (mIndex, mOld); +} + + +CSMWorld::DeleteNestedCommand::DeleteNestedCommand (IdTree& model, + const std::string& id, + int nestedRow, + int parentColumn, + QUndoCommand* parent) : + mId(id), + mModel(model), + mParentColumn(parentColumn), + QUndoCommand(parent), + mNestedRow(nestedRow), + NestedTableStoring(model, id, parentColumn) +{ + std::string title = + model.headerData(parentColumn, Qt::Horizontal, Qt::DisplayRole).toString().toUtf8().constData(); + setText (("Delete row in " + title + " sub-table of " + mId).c_str()); +} + +void CSMWorld::DeleteNestedCommand::redo() +{ + const QModelIndex& parentIndex = mModel.getModelIndex(mId, mParentColumn); + + mModel.removeRows (mNestedRow, 1, parentIndex); +} + + +void CSMWorld::DeleteNestedCommand::undo() +{ + const QModelIndex& parentIndex = mModel.getModelIndex(mId, mParentColumn); + + mModel.setNestedTable(parentIndex, getOld()); +} + +CSMWorld::AddNestedCommand::AddNestedCommand(IdTree& model, const std::string& id, int nestedRow, int parentColumn, QUndoCommand* parent) + : mModel(model), + mId(id), + mNewRow(nestedRow), + mParentColumn(parentColumn), + QUndoCommand(parent), + NestedTableStoring(model, id, parentColumn) +{ + std::string title = + model.headerData(parentColumn, Qt::Horizontal, Qt::DisplayRole).toString().toUtf8().constData(); + setText (("Add row in " + title + " sub-table of " + mId).c_str()); +} + +void CSMWorld::AddNestedCommand::redo() +{ + const QModelIndex& parentIndex = mModel.getModelIndex(mId, mParentColumn); + + mModel.addNestedRow (parentIndex, mNewRow); +} + +void CSMWorld::AddNestedCommand::undo() +{ + const QModelIndex& parentIndex = mModel.getModelIndex(mId, mParentColumn); + + mModel.setNestedTable(parentIndex, getOld()); +} + +CSMWorld::NestedTableStoring::NestedTableStoring(const IdTree& model, const std::string& id, int parentColumn) + : mOld(model.nestedTable(model.getModelIndex(id, parentColumn))) {} + +CSMWorld::NestedTableStoring::~NestedTableStoring() +{ + delete mOld; +} + +const CSMWorld::NestedTableWrapperBase& CSMWorld::NestedTableStoring::getOld() const +{ + return *mOld; +} diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index 3c24b64a5..f13376145 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -12,6 +12,7 @@ #include #include "universalid.hpp" +#include "nestedtablewrapper.hpp" class QModelIndex; class QAbstractItemModel; @@ -19,12 +20,13 @@ class QAbstractItemModel; namespace CSMWorld { class IdTable; - class IdTable; + class IdTree; struct RecordBase; + struct NestedTableWrapperBase; class ModifyCommand : public QUndoCommand { - QAbstractItemModel& mModel; + QAbstractItemModel *mModel; QModelIndex mIndex; QVariant mNew; QVariant mOld; @@ -109,6 +111,7 @@ namespace CSMWorld IdTable& mModel; std::string mId; RecordBase *mOld; + UniversalId::Type mType; // not implemented DeleteCommand (const DeleteCommand&); @@ -116,7 +119,8 @@ namespace CSMWorld public: - DeleteCommand (IdTable& model, const std::string& id, QUndoCommand *parent = 0); + DeleteCommand (IdTable& model, const std::string& id, + UniversalId::Type type = UniversalId::Type_None, QUndoCommand *parent = 0); virtual ~DeleteCommand(); @@ -139,6 +143,81 @@ namespace CSMWorld virtual void undo(); }; + + /// \brief Update cell ID according to x/y-coordinates + /// + /// \note The new value will be calculated in the first call to redo instead of the + /// constructor to accommodate multiple coordinate-affecting commands being executed + /// in a macro. + class UpdateCellCommand : public QUndoCommand + { + IdTable& mModel; + int mRow; + QModelIndex mIndex; + QVariant mNew; // invalid, if new cell ID has not been calculated yet + QVariant mOld; + + public: + + UpdateCellCommand (IdTable& model, int row, QUndoCommand *parent = 0); + + virtual void redo(); + + virtual void undo(); + }; + + + class NestedTableStoring + { + NestedTableWrapperBase* mOld; + + public: + NestedTableStoring(const IdTree& model, const std::string& id, int parentColumn); + + ~NestedTableStoring(); + + protected: + + const NestedTableWrapperBase& getOld() const; + }; + + class DeleteNestedCommand : public QUndoCommand, private NestedTableStoring + { + IdTree& mModel; + + std::string mId; + + int mParentColumn; + + int mNestedRow; + + public: + + DeleteNestedCommand (IdTree& model, const std::string& id, int nestedRow, int parentColumn, QUndoCommand* parent = 0); + + virtual void redo(); + + virtual void undo(); + }; + + class AddNestedCommand : public QUndoCommand, private NestedTableStoring + { + IdTree& mModel; + + std::string mId; + + int mNewRow; + + int mParentColumn; + + public: + + AddNestedCommand(IdTree& model, const std::string& id, int nestedRow, int parentColumn, QUndoCommand* parent = 0); + + virtual void redo(); + + virtual void undo(); + }; } #endif diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index dcbea56df..a6f664898 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -12,11 +12,13 @@ #include #include "idtable.hpp" +#include "idtree.hpp" #include "columnimp.hpp" #include "regionmap.hpp" #include "columns.hpp" #include "resourcesmanager.hpp" #include "resourcetable.hpp" +#include "nestedcoladapterimp.hpp" void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type, bool update) { @@ -62,6 +64,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc : mEncoder (encoding), mPathgrids (mCells), mRefs (mCells), mResourcesManager (resourcesManager), mReader (0), mDialogue (0), mReaderIndex(0), mResourceSystem(resourcesManager.getVFS()) { + int index = 0; + mGlobals.addColumn (new StringIdColumn); mGlobals.addColumn (new RecordStateColumn); mGlobals.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Global)); @@ -106,6 +110,14 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc mFactions.addColumn (new HiddenColumn); for (int i=0; i<7; ++i) mFactions.addColumn (new SkillsColumn (i)); + // Faction Reactions + mFactions.addColumn (new NestedParentColumn (Columns::ColumnId_FactionReactions)); + index = mFactions.getColumns()-1; + mFactions.addAdapter (std::make_pair(&mFactions.getColumn(index), new FactionReactionsAdapter ())); + mFactions.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_Faction, ColumnBase::Display_String)); + mFactions.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_FactionReaction, ColumnBase::Display_Integer)); mRaces.addColumn (new StringIdColumn); mRaces.addColumn (new RecordStateColumn); @@ -118,6 +130,12 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc mRaces.addColumn (new WeightHeightColumn (true, false)); mRaces.addColumn (new WeightHeightColumn (false, true)); mRaces.addColumn (new WeightHeightColumn (false, false)); + // Race spells + mRaces.addColumn (new NestedParentColumn (Columns::ColumnId_PowerList)); + index = mRaces.getColumns()-1; + mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new SpellListAdapter ())); + mRaces.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_SpellId, ColumnBase::Display_String)); mSounds.addColumn (new StringIdColumn); mSounds.addColumn (new RecordStateColumn); @@ -138,6 +156,14 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc mRegions.addColumn (new NameColumn); mRegions.addColumn (new MapColourColumn); mRegions.addColumn (new SleepListColumn); + // Region Sounds + mRegions.addColumn (new NestedParentColumn (Columns::ColumnId_RegionSounds)); + index = mRegions.getColumns()-1; + mRegions.addAdapter (std::make_pair(&mRegions.getColumn(index), new RegionSoundListAdapter ())); + mRegions.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_SoundName, ColumnBase::Display_String)); + mRegions.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_SoundChance, ColumnBase::Display_Integer)); mBirthsigns.addColumn (new StringIdColumn); mBirthsigns.addColumn (new RecordStateColumn); @@ -145,6 +171,13 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc mBirthsigns.addColumn (new NameColumn); mBirthsigns.addColumn (new TextureColumn); mBirthsigns.addColumn (new DescriptionColumn); + // Birthsign spells + mBirthsigns.addColumn (new NestedParentColumn (Columns::ColumnId_PowerList)); + index = mBirthsigns.getColumns()-1; + mBirthsigns.addAdapter (std::make_pair(&mBirthsigns.getColumn(index), + new SpellListAdapter ())); + mBirthsigns.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_SpellId, ColumnBase::Display_String)); mSpells.addColumn (new StringIdColumn); mSpells.addColumn (new RecordStateColumn); @@ -155,6 +188,26 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc mSpells.addColumn (new FlagColumn (Columns::ColumnId_AutoCalc, 0x1)); mSpells.addColumn (new FlagColumn (Columns::ColumnId_StarterSpell, 0x2)); mSpells.addColumn (new FlagColumn (Columns::ColumnId_AlwaysSucceeds, 0x4)); + // Spell effects + mSpells.addColumn (new NestedParentColumn (Columns::ColumnId_EffectList)); + index = mSpells.getColumns()-1; + mSpells.addAdapter (std::make_pair(&mSpells.getColumn(index), new EffectsListAdapter ())); + mSpells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_EffectId, ColumnBase::Display_EffectId)); + mSpells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_SkillImpact, ColumnBase::Display_SkillImpact)); + mSpells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_Attribute)); + mSpells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_EffectRange, ColumnBase::Display_EffectRange)); + mSpells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_EffectArea, ColumnBase::Display_String)); + mSpells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_Duration, ColumnBase::Display_Integer)); // reuse from light + mSpells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_MinRange, ColumnBase::Display_Integer)); // reuse from sound + mSpells.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_MaxRange, ColumnBase::Display_Integer)); // reuse from sound mTopics.addColumn (new StringIdColumn); mTopics.addColumn (new RecordStateColumn); @@ -182,6 +235,13 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc mTopicInfos.addColumn (new PcRankColumn); mTopicInfos.addColumn (new SoundFileColumn); mTopicInfos.addColumn (new ResponseColumn); + // Result script + mTopicInfos.addColumn (new NestedParentColumn (Columns::ColumnId_InfoList, + ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List)); + index = mTopicInfos.getColumns()-1; + mTopicInfos.addAdapter (std::make_pair(&mTopicInfos.getColumn(index), new InfoListAdapter ())); + mTopicInfos.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_ScriptText, ColumnBase::Display_ScriptLines)); mJournalInfos.addColumn (new StringIdColumn (true)); mJournalInfos.addColumn (new RecordStateColumn); @@ -208,6 +268,27 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc mEnchantments.addColumn (new CostColumn); mEnchantments.addColumn (new ChargesColumn2); mEnchantments.addColumn (new AutoCalcColumn); + // Enchantment effects + mEnchantments.addColumn (new NestedParentColumn (Columns::ColumnId_EffectList)); + index = mEnchantments.getColumns()-1; + mEnchantments.addAdapter (std::make_pair(&mEnchantments.getColumn(index), + new EffectsListAdapter ())); + mEnchantments.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_EffectId, ColumnBase::Display_EffectId)); + mEnchantments.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_SkillImpact, ColumnBase::Display_SkillImpact)); + mEnchantments.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_Attribute)); + mEnchantments.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_EffectRange, ColumnBase::Display_EffectRange)); + mEnchantments.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_EffectArea, ColumnBase::Display_String)); + mEnchantments.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_Duration, ColumnBase::Display_Integer)); // reuse from light + mEnchantments.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_MinRange, ColumnBase::Display_Integer)); // reuse from sound + mEnchantments.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_MaxRange, ColumnBase::Display_Integer)); // reuse from sound mBodyParts.addColumn (new StringIdColumn); mBodyParts.addColumn (new RecordStateColumn); @@ -254,6 +335,32 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc mPathgrids.addColumn (new RecordStateColumn); mPathgrids.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Pathgrid)); + // new object deleted in dtor of Collection + mPathgrids.addColumn (new NestedParentColumn (Columns::ColumnId_PathgridPoints)); + index = mPathgrids.getColumns()-1; + // new object deleted in dtor of NestedCollection + mPathgrids.addAdapter (std::make_pair(&mPathgrids.getColumn(index), new PathgridPointListAdapter ())); + // new objects deleted in dtor of NestableColumn + // WARNING: The order of the columns below are assumed in PathgridPointListAdapter + mPathgrids.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_PathgridIndex, ColumnBase::Display_Integer, false)); + mPathgrids.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_PathgridPosX, ColumnBase::Display_Integer)); + mPathgrids.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_PathgridPosY, ColumnBase::Display_Integer)); + mPathgrids.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_PathgridPosZ, ColumnBase::Display_Integer)); + + mPathgrids.addColumn (new NestedParentColumn (Columns::ColumnId_PathgridEdges)); + index = mPathgrids.getColumns()-1; + mPathgrids.addAdapter (std::make_pair(&mPathgrids.getColumn(index), new PathgridEdgeListAdapter ())); + mPathgrids.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_PathgridEdgeIndex, ColumnBase::Display_Integer, false)); + mPathgrids.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_PathgridEdge0, ColumnBase::Display_Integer)); + mPathgrids.getNestableColumn(index)->addColumn( + new NestedChildColumn (Columns::ColumnId_PathgridEdge1, ColumnBase::Display_Integer)); + mStartScripts.addColumn (new StringIdColumn); mStartScripts.addColumn (new RecordStateColumn); mStartScripts.addColumn (new FixedRecordTypeColumn (UniversalId::Type_StartScript)); @@ -261,7 +368,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc mRefs.addColumn (new StringIdColumn (true)); mRefs.addColumn (new RecordStateColumn); mRefs.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Reference)); - mRefs.addColumn (new CellColumn); + mRefs.addColumn (new CellColumn (true)); + mRefs.addColumn (new OriginalCellColumn); mRefs.addColumn (new IdColumn); mRefs.addColumn (new PosColumn (&CellRef::mPos, 0, false)); mRefs.addColumn (new PosColumn (&CellRef::mPos, 1, false)); @@ -314,25 +422,26 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc addModel (new IdTable (&mGmsts), UniversalId::Type_Gmst); addModel (new IdTable (&mSkills), UniversalId::Type_Skill); addModel (new IdTable (&mClasses), UniversalId::Type_Class); - addModel (new IdTable (&mFactions), UniversalId::Type_Faction); - addModel (new IdTable (&mRaces), UniversalId::Type_Race); + addModel (new IdTree (&mFactions, &mFactions), UniversalId::Type_Faction); + addModel (new IdTree (&mRaces, &mRaces), UniversalId::Type_Race); addModel (new IdTable (&mSounds), UniversalId::Type_Sound); addModel (new IdTable (&mScripts), UniversalId::Type_Script); - addModel (new IdTable (&mRegions), UniversalId::Type_Region); - addModel (new IdTable (&mBirthsigns), UniversalId::Type_Birthsign); - addModel (new IdTable (&mSpells), UniversalId::Type_Spell); + addModel (new IdTree (&mRegions, &mRegions), UniversalId::Type_Region); + addModel (new IdTree (&mBirthsigns, &mBirthsigns), UniversalId::Type_Birthsign); + addModel (new IdTree (&mSpells, &mSpells), UniversalId::Type_Spell); addModel (new IdTable (&mTopics), UniversalId::Type_Topic); addModel (new IdTable (&mJournals), UniversalId::Type_Journal); - addModel (new IdTable (&mTopicInfos, IdTable::Feature_ReorderWithinTopic), UniversalId::Type_TopicInfo); + addModel (new IdTree (&mTopicInfos, &mTopicInfos, IdTable::Feature_ReorderWithinTopic), + UniversalId::Type_TopicInfo); addModel (new IdTable (&mJournalInfos, IdTable::Feature_ReorderWithinTopic), UniversalId::Type_JournalInfo); addModel (new IdTable (&mCells, IdTable::Feature_ViewId), UniversalId::Type_Cell); - addModel (new IdTable (&mEnchantments), UniversalId::Type_Enchantment); + addModel (new IdTree (&mEnchantments, &mEnchantments), UniversalId::Type_Enchantment); addModel (new IdTable (&mBodyParts), UniversalId::Type_BodyPart); addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen); addModel (new IdTable (&mMagicEffects), UniversalId::Type_MagicEffect); - addModel (new IdTable (&mPathgrids), UniversalId::Type_Pathgrid); + addModel (new IdTree (&mPathgrids, &mPathgrids), UniversalId::Type_Pathgrid); addModel (new IdTable (&mStartScripts), UniversalId::Type_StartScript); - addModel (new IdTable (&mReferenceables, IdTable::Feature_Preview), + addModel (new IdTree (&mReferenceables, &mReferenceables, IdTable::Feature_Preview), UniversalId::Type_Referenceable); addModel (new IdTable (&mRefs, IdTable::Feature_ViewCell | IdTable::Feature_Preview), UniversalId::Type_Reference); addModel (new IdTable (&mFilters), UniversalId::Type_Filter); @@ -349,6 +458,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc UniversalId::Type_Texture); addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Videos)), UniversalId::Type_Video); + + mRefLoadCache.clear(); // clear here rather than startLoading() and continueLoading() for multiple content files } CSMWorld::Data::~Data() @@ -675,7 +786,6 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base mReader = 0; mDialogue = 0; - mRefLoadCache.clear(); mReader = new ESM::ESMReader; mReader->setEncoder (&mEncoder); @@ -712,7 +822,6 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages) mReader = 0; mDialogue = 0; - mRefLoadCache.clear(); return true; } @@ -757,9 +866,16 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages) case ESM::REC_CELL: { - mCells.load (*mReader, mBase); - std::string cellId = Misc::StringUtils::lowerCase (mCells.getId (mCells.getSize()-1)); - mRefs.load (*mReader, mCells.getSize()-1, mBase, mRefLoadCache[cellId], messages); + int index = mCells.load (*mReader, mBase); + if (index < 0 || index >= mCells.getSize()) + { + // log an error and continue loading the refs to the last loaded cell + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_None); + messages.add (id, "Logic error: cell index out of bounds"); + index = mCells.getSize()-1; + } + std::string cellId = Misc::StringUtils::lowerCase (mCells.getId (index)); + mRefs.load (*mReader, index, mBase, mRefLoadCache[cellId], messages); break; } diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 5bb117aa0..93cd46d86 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -36,6 +36,7 @@ #include "../doc/stage.hpp" #include "idcollection.hpp" +#include "nestedidcollection.hpp" #include "universalid.hpp" #include "cell.hpp" #include "land.hpp" @@ -43,8 +44,11 @@ #include "refidcollection.hpp" #include "refcollection.hpp" #include "infocollection.hpp" +#include "nestedinfocollection.hpp" #include "pathgrid.hpp" +#ifndef Q_MOC_RUN #include "subcellcollection.hpp" +#endif class QAbstractItemModel; @@ -73,23 +77,23 @@ namespace CSMWorld IdCollection mGmsts; IdCollection mSkills; IdCollection mClasses; - IdCollection mFactions; - IdCollection mRaces; + NestedIdCollection mFactions; + NestedIdCollection mRaces; IdCollection mSounds; IdCollection mScripts; - IdCollection mRegions; - IdCollection mBirthsigns; - IdCollection mSpells; + NestedIdCollection mRegions; + NestedIdCollection mBirthsigns; + NestedIdCollection mSpells; IdCollection mTopics; IdCollection mJournals; - IdCollection mEnchantments; + NestedIdCollection mEnchantments; IdCollection mBodyParts; IdCollection mMagicEffects; SubCellCollection mPathgrids; IdCollection mDebugProfiles; IdCollection mSoundGens; IdCollection mStartScripts; - InfoCollection mTopicInfos; + NestedInfoCollection mTopicInfos; InfoCollection mJournalInfos; IdCollection mCells; IdCollection mLandTextures; diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index f00ea447a..4eafc59bd 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -74,6 +74,15 @@ namespace CSMWorld { ESXRecordT record; + // Sometimes id (i.e. NAME of the cell) may be different to the id we stored + // earlier. e.g. NAME == "Vivec, Arena" but id == "#-4 11". Sometime NAME is + // missing altogether for scripts or cells. + // + // In such cases the returned index will be -1. We then try updating the + // IdAccessor's id manually (e.g. set mId of the record to "Vivec, Arena") + // and try getting the index once more after loading the record. The mId of the + // record would have changed to "#-4 11" after the load, and searchId() should find + // it (if this is a modify) int index = this->searchId (id); if (index==-1) diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index c68b71789..a0dd0b47b 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -1,6 +1,7 @@ - #include "idtable.hpp" +#include + #include "collectionbase.hpp" #include "columnbase.hpp" @@ -27,9 +28,15 @@ int CSMWorld::IdTable::columnCount (const QModelIndex & parent) const return mIdCollection->getColumns(); } -QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const +QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const { - if ((role!=Qt::DisplayRole && role!=Qt::EditRole) || index.row() < 0 || index.column() < 0) + if (index.row() < 0 || index.column() < 0) + return QVariant(); + + if (role==ColumnBase::Role_ColumnId) + return QVariant (getColumnId (index.column())); + + if ((role!=Qt::DisplayRole && role!=Qt::EditRole)) return QVariant(); if (role==Qt::EditRole && !mIdCollection->getColumn (index.column()).isEditable()) @@ -43,6 +50,9 @@ QVariant CSMWorld::IdTable::headerData (int section, Qt::Orientation orientation if (orientation==Qt::Vertical) return QVariant(); + if (orientation != Qt::Horizontal) + throw std::logic_error("Unknown header orientation specified"); + if (role==Qt::DisplayRole) return tr (mIdCollection->getColumn (section).getTitle().c_str()); @@ -52,10 +62,13 @@ QVariant CSMWorld::IdTable::headerData (int section, Qt::Orientation orientation if (role==ColumnBase::Role_Display) return mIdCollection->getColumn (section).mDisplayType; + if (role==ColumnBase::Role_ColumnId) + return getColumnId (section); + return QVariant(); } -bool CSMWorld::IdTable::setData ( const QModelIndex &index, const QVariant &value, int role) +bool CSMWorld::IdTable::setData (const QModelIndex &index, const QVariant &value, int role) { if (mIdCollection->getColumn (index.column()).isEditable() && role==Qt::EditRole) { @@ -129,18 +142,19 @@ void CSMWorld::IdTable::cloneRecord(const std::string& origin, CSMWorld::UniversalId::Type type) { int index = mIdCollection->getAppendIndex (destination); + beginInsertRows (QModelIndex(), index, index); mIdCollection->cloneRecord(origin, destination, type); endInsertRows(); } - +///This method can return only indexes to the top level table cells QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const { - return index (mIdCollection->getIndex (id), column); + return index(mIdCollection->getIndex (id), column); } -void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& record) +void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& record, CSMWorld::UniversalId::Type type) { int index = mIdCollection->searchId (id); @@ -150,7 +164,7 @@ void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& reco beginInsertRows (QModelIndex(), index, index); - mIdCollection->appendRecord (record); + mIdCollection->appendRecord (record, type); endInsertRows(); } @@ -221,6 +235,7 @@ std::pair CSMWorld::IdTable::view (int row) return std::make_pair (UniversalId (UniversalId::Type_Scene, id), hint); } +///For top level data/columns bool CSMWorld::IdTable::isDeleted (const std::string& id) const { return getRecord (id).isDeleted(); @@ -230,3 +245,8 @@ int CSMWorld::IdTable::getColumnId(int column) const { return mIdCollection->getColumn(column).getId(); } + +CSMWorld::CollectionBase *CSMWorld::IdTable::idCollection() const +{ + return mIdCollection; +} diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index 6f4be7178..9ecba0214 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -59,7 +59,8 @@ namespace CSMWorld virtual QModelIndex getModelIndex (const std::string& id, int column) const; - void setRecord (const std::string& id, const RecordBase& record); + void setRecord (const std::string& id, const RecordBase& record, + UniversalId::Type type = UniversalId::Type_None); ///< Add record or overwrite existing recrod. const RecordBase& getRecord (const std::string& id) const; @@ -83,6 +84,10 @@ namespace CSMWorld virtual bool isDeleted (const std::string& id) const; virtual int getColumnId(int column) const; + + protected: + + virtual CollectionBase *idCollection() const; }; } diff --git a/apps/opencs/model/world/idtree.cpp b/apps/opencs/model/world/idtree.cpp new file mode 100644 index 000000000..06db09a0f --- /dev/null +++ b/apps/opencs/model/world/idtree.cpp @@ -0,0 +1,259 @@ +#include "idtree.hpp" + +#include "nestedtablewrapper.hpp" + +#include "collectionbase.hpp" +#include "nestedcollection.hpp" +#include "columnbase.hpp" + +// NOTE: parent class still needs idCollection +CSMWorld::IdTree::IdTree (NestedCollection *nestedCollection, CollectionBase *idCollection, unsigned int features) +: IdTable (idCollection, features), mNestedCollection (nestedCollection) +{} + +CSMWorld::IdTree::~IdTree() +{} + +int CSMWorld::IdTree::rowCount (const QModelIndex & parent) const +{ + if (hasChildren(parent)) + return mNestedCollection->getNestedRowsCount(parent.row(), parent.column()); + + return IdTable::rowCount(parent); +} + +int CSMWorld::IdTree::columnCount (const QModelIndex & parent) const +{ + if (hasChildren(parent)) + return mNestedCollection->getNestedColumnsCount(parent.row(), parent.column()); + + return IdTable::columnCount(parent); +} + +QVariant CSMWorld::IdTree::data (const QModelIndex & index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if ((role!=Qt::DisplayRole && role!=Qt::EditRole) || index.row() < 0 || index.column() < 0) + return QVariant(); + + if (index.internalId() != 0) + { + std::pair parentAddress(unfoldIndexAddress(index.internalId())); + + if (role == Qt::EditRole && + !mNestedCollection->getNestableColumn(parentAddress.second)->nestedColumn(index.column()).isEditable()) + { + return QVariant(); + } + + return mNestedCollection->getNestedData(parentAddress.first, + parentAddress.second, index.row(), index.column()); + } + else + { + if (role==Qt::EditRole && !idCollection()->getColumn (index.column()).isEditable()) + return QVariant(); + + return idCollection()->getData (index.row(), index.column()); + } +} + +QVariant CSMWorld::IdTree::nestedHeaderData(int section, int subSection, Qt::Orientation orientation, int role) const +{ + if (section < 0 || section >= idCollection()->getColumns()) + return QVariant(); + + const NestableColumn *parentColumn = mNestedCollection->getNestableColumn(section); + + if (orientation==Qt::Vertical) + return QVariant(); + + if (role==Qt::DisplayRole) + return tr(parentColumn->nestedColumn(subSection).getTitle().c_str()); + + if (role==ColumnBase::Role_Flags) + return idCollection()->getColumn (section).mFlags; + + if (role==ColumnBase::Role_Display) + return parentColumn->nestedColumn(subSection).mDisplayType; + + return QVariant(); +} + +bool CSMWorld::IdTree::setData (const QModelIndex &index, const QVariant &value, int role) +{ + if (index.internalId() != 0) + { + if (idCollection()->getColumn(parent(index).column()).isEditable() && role==Qt::EditRole) + { + const std::pair& parentAddress(unfoldIndexAddress(index.internalId())); + + mNestedCollection->setNestedData(parentAddress.first, parentAddress.second, value, index.row(), index.column()); + + emit dataChanged (CSMWorld::IdTree::index (parentAddress.first, 0), + CSMWorld::IdTree::index (parentAddress.first, idCollection()->getColumns()-1)); + return true; + } + else + return false; + } + return IdTable::setData(index, value, role); +} + +Qt::ItemFlags CSMWorld::IdTree::flags (const QModelIndex & index) const +{ + if (!index.isValid()) + return 0; + + if (index.internalId() != 0) + { + std::pair parentAddress(unfoldIndexAddress(index.internalId())); + + Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; + + if (mNestedCollection->getNestableColumn(parentAddress.second)->nestedColumn(index.column()).isEditable()) + flags |= Qt::ItemIsEditable; + + return flags; + } + else + return IdTable::flags(index); +} + +bool CSMWorld::IdTree::removeRows (int row, int count, const QModelIndex& parent) +{ + if (parent.isValid()) + { + beginRemoveRows (parent, row, row+count-1); + + for (int i = 0; i < count; ++i) + { + mNestedCollection->removeNestedRows(parent.row(), parent.column(), row+i); + } + + endRemoveRows(); + + emit dataChanged (CSMWorld::IdTree::index (parent.row(), 0), + CSMWorld::IdTree::index (parent.row(), idCollection()->getColumns()-1)); + + return true; + } + else + return IdTable::removeRows(row, count, parent); +} + +void CSMWorld::IdTree::addNestedRow(const QModelIndex& parent, int position) +{ + if (!hasChildren(parent)) + throw std::logic_error("Tried to set nested table, but index has no children"); + + int row = parent.row(); + + beginInsertRows(parent, position, position); + mNestedCollection->addNestedRow(row, parent.column(), position); + endInsertRows(); + + emit dataChanged (CSMWorld::IdTree::index (row, 0), + CSMWorld::IdTree::index (row, idCollection()->getColumns()-1)); +} + +QModelIndex CSMWorld::IdTree::index (int row, int column, const QModelIndex& parent) const +{ + unsigned int encodedId = 0; + if (parent.isValid()) + { + encodedId = this->foldIndexAddress(parent); + } + + if (row<0 || row>=idCollection()->getSize()) + return QModelIndex(); + + if (column<0 || column>=idCollection()->getColumns()) + return QModelIndex(); + + return createIndex(row, column, encodedId); // store internal id +} + +QModelIndex CSMWorld::IdTree::getNestedModelIndex (const std::string& id, int column) const +{ + return CSMWorld::IdTable::index(idCollection()->getIndex (id), column); +} + +QModelIndex CSMWorld::IdTree::parent (const QModelIndex& index) const +{ + if (index.internalId() == 0) // 0 is used for indexs with invalid parent (top level data) + return QModelIndex(); + + unsigned int id = index.internalId(); + const std::pair& adress(unfoldIndexAddress(id)); + + if (adress.first >= this->rowCount() || adress.second >= this->columnCount()) + throw "Parent index is not present in the model"; + + return createIndex(adress.first, adress.second); +} + +unsigned int CSMWorld::IdTree::foldIndexAddress (const QModelIndex& index) const +{ + unsigned int out = index.row() * this->columnCount(); + out += index.column(); + return ++out; +} + +std::pair< int, int > CSMWorld::IdTree::unfoldIndexAddress (unsigned int id) const +{ + if (id == 0) + throw "Attempt to unfold index id of the top level data cell"; + + --id; + int row = id / this->columnCount(); + int column = id - row * this->columnCount(); + return std::make_pair (row, column); +} + +// FIXME: Not sure why this check is also needed? +// +// index.data().isValid() requires RefIdAdapter::getData() to return a valid QVariant for +// nested columns (refidadapterimp.hpp) +// +// Also see comments in refidadapter.hpp and refidadapterimp.hpp. +bool CSMWorld::IdTree::hasChildren(const QModelIndex& index) const +{ + return (index.isValid() && + index.internalId() == 0 && + mNestedCollection->getNestableColumn(index.column())->hasChildren() && + index.data().isValid()); +} + +void CSMWorld::IdTree::setNestedTable(const QModelIndex& index, const CSMWorld::NestedTableWrapperBase& nestedTable) +{ + if (!hasChildren(index)) + throw std::logic_error("Tried to set nested table, but index has no children"); + + bool removeRowsMode = false; + if (nestedTable.size() != this->nestedTable(index)->size()) + { + emit resetStart(this->index(index.row(), 0).data().toString()); + removeRowsMode = true; + } + + mNestedCollection->setNestedTable(index.row(), index.column(), nestedTable); + + emit dataChanged (CSMWorld::IdTree::index (index.row(), 0), + CSMWorld::IdTree::index (index.row(), idCollection()->getColumns()-1)); + + if (removeRowsMode) + { + emit resetEnd(this->index(index.row(), 0).data().toString()); + } +} + +CSMWorld::NestedTableWrapperBase* CSMWorld::IdTree::nestedTable(const QModelIndex& index) const +{ + if (!hasChildren(index)) + throw std::logic_error("Tried to retrive nested table, but index has no children"); + + return mNestedCollection->nestedTable(index.row(), index.column()); +} diff --git a/apps/opencs/model/world/idtree.hpp b/apps/opencs/model/world/idtree.hpp new file mode 100644 index 000000000..5337ed82b --- /dev/null +++ b/apps/opencs/model/world/idtree.hpp @@ -0,0 +1,84 @@ +#ifndef CSM_WOLRD_IDTREE_H +#define CSM_WOLRD_IDTREE_H + +#include "idtable.hpp" +#include "universalid.hpp" +#include "columns.hpp" + +/*! \brief + * Class for holding the model. Uses typical qt table abstraction/interface for granting access + * to the individiual fields of the records, Some records are holding nested data (for instance + * inventory list of the npc). In casses like this, table model offers interface to access + * nested data in the qt way - that is specify parent. Since some of those nested data require + * multiple columns to represent informations, single int (default way to index model in the + * qmodelindex) is not sufficiant. Therefore tablemodelindex class can hold two ints for the + * sake of indexing two dimensions of the table. This model does not support multiple levels of + * the nested data. Vast majority of methods makes sense only for the top level data. + */ + +namespace CSMWorld +{ + class NestedCollection; + struct RecordBase; + struct NestedTableWrapperBase; + + class IdTree : public IdTable + { + Q_OBJECT + + private: + + NestedCollection *mNestedCollection; + + // not implemented + IdTree (const IdTree&); + IdTree& operator= (const IdTree&); + + unsigned int foldIndexAddress(const QModelIndex& index) const; + std::pair unfoldIndexAddress(unsigned int id) const; + + public: + + IdTree (NestedCollection *nestedCollection, CollectionBase *idCollection, unsigned int features = 0); + ///< The ownerships of \a nestedCollecton and \a idCollection are not transferred. + + virtual ~IdTree(); + + virtual int rowCount (const QModelIndex & parent = QModelIndex()) const; + + virtual int columnCount (const QModelIndex & parent = QModelIndex()) const; + + virtual QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const; + + virtual bool setData ( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + + virtual Qt::ItemFlags flags (const QModelIndex & index) const; + + virtual bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex()); + + virtual QModelIndex index (int row, int column, const QModelIndex& parent = QModelIndex()) + const; + + virtual QModelIndex parent (const QModelIndex& index) const; + + QModelIndex getNestedModelIndex (const std::string& id, int column) const; + + QVariant nestedHeaderData(int section, int subSection, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + NestedTableWrapperBase* nestedTable(const QModelIndex &index) const; + + void setNestedTable(const QModelIndex &index, const NestedTableWrapperBase& nestedTable); + + void addNestedRow (const QModelIndex& parent, int position); + + virtual bool hasChildren (const QModelIndex& index) const; + + signals: + + void resetStart(const QString& id); + + void resetEnd(const QString& id); + }; +} + +#endif diff --git a/apps/opencs/model/world/nestedcoladapterimp.cpp b/apps/opencs/model/world/nestedcoladapterimp.cpp new file mode 100644 index 000000000..d29155a47 --- /dev/null +++ b/apps/opencs/model/world/nestedcoladapterimp.cpp @@ -0,0 +1,531 @@ +#include "nestedcoladapterimp.hpp" + +#include +#include + +#include "idcollection.hpp" +#include "pathgrid.hpp" +#include "info.hpp" + +namespace CSMWorld +{ + PathgridPointListAdapter::PathgridPointListAdapter () {} + + void PathgridPointListAdapter::addRow(Record& record, int position) const + { + Pathgrid pathgrid = record.get(); + + ESM::Pathgrid::PointList& points = pathgrid.mPoints; + + // blank row + ESM::Pathgrid::Point point; + point.mX = 0; + point.mY = 0; + point.mZ = 0; + point.mAutogenerated = 0; + point.mConnectionNum = 0; + point.mUnknown = 0; + + // inserting a point should trigger re-indexing of the edges + // + // FIXME: does not auto refresh edges table view + std::vector::iterator iter = pathgrid.mEdges.begin(); + for (;iter != pathgrid.mEdges.end(); ++iter) + { + if ((*iter).mV0 >= position) + (*iter).mV0++; + if ((*iter).mV1 >= position) + (*iter).mV1++; + } + + points.insert(points.begin()+position, point); + pathgrid.mData.mS2 += 1; // increment the number of points + + record.setModified (pathgrid); + } + + void PathgridPointListAdapter::removeRow(Record& record, int rowToRemove) const + { + Pathgrid pathgrid = record.get(); + + ESM::Pathgrid::PointList& points = pathgrid.mPoints; + + if (rowToRemove < 0 || rowToRemove >= static_cast (points.size())) + throw std::runtime_error ("index out of range"); + + // deleting a point should trigger re-indexing of the edges + // dangling edges are not allowed and hence removed + // + // FIXME: does not auto refresh edges table view + std::vector::iterator iter = pathgrid.mEdges.begin(); + for (; iter != pathgrid.mEdges.end();) + { + if (((*iter).mV0 == rowToRemove) || ((*iter).mV1 == rowToRemove)) + iter = pathgrid.mEdges.erase(iter); + else + { + if ((*iter).mV0 > rowToRemove) + (*iter).mV0--; + + if ((*iter).mV1 > rowToRemove) + (*iter).mV1--; + + ++iter; + } + } + points.erase(points.begin()+rowToRemove); + pathgrid.mData.mS2 -= 1; // decrement the number of points + + record.setModified (pathgrid); + } + + void PathgridPointListAdapter::setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const + { + Pathgrid pathgrid = record.get(); + + pathgrid.mPoints = + static_cast(nestedTable).mRecord.mPoints; + pathgrid.mData.mS2 = + static_cast(nestedTable).mRecord.mData.mS2; + // also update edges in case points were added/removed + pathgrid.mEdges = + static_cast(nestedTable).mRecord.mEdges; + + record.setModified (pathgrid); + } + + NestedTableWrapperBase* PathgridPointListAdapter::table(const Record& record) const + { + // deleted by dtor of NestedTableStoring + return new PathgridPointsWrap(record.get()); + } + + QVariant PathgridPointListAdapter::getData(const Record& record, + int subRowIndex, int subColIndex) const + { + ESM::Pathgrid::Point point = record.get().mPoints[subRowIndex]; + switch (subColIndex) + { + case 0: return subRowIndex; + case 1: return point.mX; + case 2: return point.mY; + case 3: return point.mZ; + default: throw std::runtime_error("Pathgrid point subcolumn index out of range"); + } + } + + void PathgridPointListAdapter::setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const + { + Pathgrid pathgrid = record.get(); + ESM::Pathgrid::Point point = pathgrid.mPoints[subRowIndex]; + switch (subColIndex) + { + case 0: return; // return without saving + case 1: point.mX = value.toInt(); break; + case 2: point.mY = value.toInt(); break; + case 3: point.mZ = value.toInt(); break; + default: throw std::runtime_error("Pathgrid point subcolumn index out of range"); + } + + pathgrid.mPoints[subRowIndex] = point; + + record.setModified (pathgrid); + } + + int PathgridPointListAdapter::getColumnsCount(const Record& record) const + { + return 4; + } + + int PathgridPointListAdapter::getRowsCount(const Record& record) const + { + return static_cast(record.get().mPoints.size()); + } + + PathgridEdgeListAdapter::PathgridEdgeListAdapter () {} + + // ToDo: seems to be auto-sorted in the dialog table display after insertion + void PathgridEdgeListAdapter::addRow(Record& record, int position) const + { + Pathgrid pathgrid = record.get(); + + ESM::Pathgrid::EdgeList& edges = pathgrid.mEdges; + + // blank row + ESM::Pathgrid::Edge edge; + edge.mV0 = 0; + edge.mV1 = 0; + + // NOTE: inserting a blank edge does not really make sense, perhaps this should be a + // logic_error exception + // + // Currently the code assumes that the end user to know what he/she is doing. + // e.g. Edges come in pairs, from points a->b and b->a + edges.insert(edges.begin()+position, edge); + + record.setModified (pathgrid); + } + + void PathgridEdgeListAdapter::removeRow(Record& record, int rowToRemove) const + { + Pathgrid pathgrid = record.get(); + + ESM::Pathgrid::EdgeList& edges = pathgrid.mEdges; + + if (rowToRemove < 0 || rowToRemove >= static_cast (edges.size())) + throw std::runtime_error ("index out of range"); + + edges.erase(edges.begin()+rowToRemove); + + record.setModified (pathgrid); + } + + void PathgridEdgeListAdapter::setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const + { + Pathgrid pathgrid = record.get(); + + pathgrid.mEdges = + static_cast &>(nestedTable).mNestedTable; + + record.setModified (pathgrid); + } + + NestedTableWrapperBase* PathgridEdgeListAdapter::table(const Record& record) const + { + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper(record.get().mEdges); + } + + QVariant PathgridEdgeListAdapter::getData(const Record& record, + int subRowIndex, int subColIndex) const + { + Pathgrid pathgrid = record.get(); + + if (subRowIndex < 0 || subRowIndex >= static_cast (pathgrid.mEdges.size())) + throw std::runtime_error ("index out of range"); + + ESM::Pathgrid::Edge edge = pathgrid.mEdges[subRowIndex]; + switch (subColIndex) + { + case 0: return subRowIndex; + case 1: return edge.mV0; + case 2: return edge.mV1; + default: throw std::runtime_error("Pathgrid edge subcolumn index out of range"); + } + } + + // ToDo: detect duplicates in mEdges + void PathgridEdgeListAdapter::setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const + { + Pathgrid pathgrid = record.get(); + + if (subRowIndex < 0 || subRowIndex >= static_cast (pathgrid.mEdges.size())) + throw std::runtime_error ("index out of range"); + + ESM::Pathgrid::Edge edge = pathgrid.mEdges[subRowIndex]; + switch (subColIndex) + { + case 0: return; // return without saving + case 1: edge.mV0 = value.toInt(); break; + case 2: edge.mV1 = value.toInt(); break; + default: throw std::runtime_error("Pathgrid edge subcolumn index out of range"); + } + + pathgrid.mEdges[subRowIndex] = edge; + + record.setModified (pathgrid); + } + + int PathgridEdgeListAdapter::getColumnsCount(const Record& record) const + { + return 3; + } + + int PathgridEdgeListAdapter::getRowsCount(const Record& record) const + { + return static_cast(record.get().mEdges.size()); + } + + FactionReactionsAdapter::FactionReactionsAdapter () {} + + void FactionReactionsAdapter::addRow(Record& record, int position) const + { + ESM::Faction faction = record.get(); + + std::map& reactions = faction.mReactions; + + // blank row + reactions.insert(std::make_pair("", 0)); + + record.setModified (faction); + } + + void FactionReactionsAdapter::removeRow(Record& record, int rowToRemove) const + { + ESM::Faction faction = record.get(); + + std::map& reactions = faction.mReactions; + + if (rowToRemove < 0 || rowToRemove >= static_cast (reactions.size())) + throw std::runtime_error ("index out of range"); + + // FIXME: how to ensure that the map entries correspond to table indicies? + // WARNING: Assumed that the table view has the same order as std::map + std::map::iterator iter = reactions.begin(); + for(int i = 0; i < rowToRemove; ++i) + iter++; + reactions.erase(iter); + + record.setModified (faction); + } + + void FactionReactionsAdapter::setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const + { + ESM::Faction faction = record.get(); + + faction.mReactions = + static_cast >&>(nestedTable).mNestedTable; + + record.setModified (faction); + } + + NestedTableWrapperBase* FactionReactionsAdapter::table(const Record& record) const + { + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper >(record.get().mReactions); + } + + QVariant FactionReactionsAdapter::getData(const Record& record, + int subRowIndex, int subColIndex) const + { + ESM::Faction faction = record.get(); + + std::map& reactions = faction.mReactions; + + if (subRowIndex < 0 || subRowIndex >= static_cast (reactions.size())) + throw std::runtime_error ("index out of range"); + + // FIXME: how to ensure that the map entries correspond to table indicies? + // WARNING: Assumed that the table view has the same order as std::map + std::map::const_iterator iter = reactions.begin(); + for(int i = 0; i < subRowIndex; ++i) + iter++; + switch (subColIndex) + { + case 0: return QString((*iter).first.c_str()); + case 1: return (*iter).second; + default: throw std::runtime_error("Faction reactions subcolumn index out of range"); + } + } + + void FactionReactionsAdapter::setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const + { + ESM::Faction faction = record.get(); + + std::map& reactions = faction.mReactions; + + if (subRowIndex < 0 || subRowIndex >= static_cast (reactions.size())) + throw std::runtime_error ("index out of range"); + + // FIXME: how to ensure that the map entries correspond to table indicies? + // WARNING: Assumed that the table view has the same order as std::map + std::map::iterator iter = reactions.begin(); + for(int i = 0; i < subRowIndex; ++i) + iter++; + + std::string factionId = (*iter).first; + int reaction = (*iter).second; + + switch (subColIndex) + { + case 0: + { + reactions.erase(iter); + reactions.insert(std::make_pair(value.toString().toUtf8().constData(), reaction)); + break; + } + case 1: + { + reactions[factionId] = value.toInt(); + break; + } + default: throw std::runtime_error("Faction reactions subcolumn index out of range"); + } + + record.setModified (faction); + } + + int FactionReactionsAdapter::getColumnsCount(const Record& record) const + { + return 2; + } + + int FactionReactionsAdapter::getRowsCount(const Record& record) const + { + return static_cast(record.get().mReactions.size()); + } + + RegionSoundListAdapter::RegionSoundListAdapter () {} + + void RegionSoundListAdapter::addRow(Record& record, int position) const + { + ESM::Region region = record.get(); + + std::vector& soundList = region.mSoundList; + + // blank row + ESM::Region::SoundRef soundRef; + soundRef.mSound.assign(""); + soundRef.mChance = 0; + + soundList.insert(soundList.begin()+position, soundRef); + + record.setModified (region); + } + + void RegionSoundListAdapter::removeRow(Record& record, int rowToRemove) const + { + ESM::Region region = record.get(); + + std::vector& soundList = region.mSoundList; + + if (rowToRemove < 0 || rowToRemove >= static_cast (soundList.size())) + throw std::runtime_error ("index out of range"); + + soundList.erase(soundList.begin()+rowToRemove); + + record.setModified (region); + } + + void RegionSoundListAdapter::setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const + { + ESM::Region region = record.get(); + + region.mSoundList = + static_cast >&>(nestedTable).mNestedTable; + + record.setModified (region); + } + + NestedTableWrapperBase* RegionSoundListAdapter::table(const Record& record) const + { + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper >(record.get().mSoundList); + } + + QVariant RegionSoundListAdapter::getData(const Record& record, + int subRowIndex, int subColIndex) const + { + ESM::Region region = record.get(); + + std::vector& soundList = region.mSoundList; + + if (subRowIndex < 0 || subRowIndex >= static_cast (soundList.size())) + throw std::runtime_error ("index out of range"); + + ESM::Region::SoundRef soundRef = soundList[subRowIndex]; + switch (subColIndex) + { + case 0: return QString(soundRef.mSound.toString().c_str()); + case 1: return soundRef.mChance; + default: throw std::runtime_error("Region sounds subcolumn index out of range"); + } + } + + void RegionSoundListAdapter::setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const + { + ESM::Region region = record.get(); + + std::vector& soundList = region.mSoundList; + + if (subRowIndex < 0 || subRowIndex >= static_cast (soundList.size())) + throw std::runtime_error ("index out of range"); + + ESM::Region::SoundRef soundRef = soundList[subRowIndex]; + switch (subColIndex) + { + case 0: soundRef.mSound.assign(value.toString().toUtf8().constData()); break; + case 1: soundRef.mChance = static_cast(value.toInt()); break; + default: throw std::runtime_error("Region sounds subcolumn index out of range"); + } + + region.mSoundList[subRowIndex] = soundRef; + + record.setModified (region); + } + + int RegionSoundListAdapter::getColumnsCount(const Record& record) const + { + return 2; + } + + int RegionSoundListAdapter::getRowsCount(const Record& record) const + { + return static_cast(record.get().mSoundList.size()); + } + + InfoListAdapter::InfoListAdapter () {} + + void InfoListAdapter::addRow(Record& record, int position) const + { + throw std::logic_error ("cannot add a row to a fixed table"); + } + + void InfoListAdapter::removeRow(Record& record, int rowToRemove) const + { + throw std::logic_error ("cannot add a row to a fixed table"); + } + + void InfoListAdapter::setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const + { + throw std::logic_error ("table operation not supported"); + } + + NestedTableWrapperBase* InfoListAdapter::table(const Record& record) const + { + throw std::logic_error ("table operation not supported"); + } + + QVariant InfoListAdapter::getData(const Record& record, + int subRowIndex, int subColIndex) const + { + Info info = record.get(); + + if (subColIndex == 0) + return QString(info.mResultScript.c_str()); + else + throw std::runtime_error("Trying to access non-existing column in the nested table!"); + } + + void InfoListAdapter::setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const + { + Info info = record.get(); + + if (subColIndex == 0) + info.mResultScript = value.toString().toStdString(); + else + throw std::runtime_error("Trying to access non-existing column in the nested table!"); + + record.setModified (info); + } + + int InfoListAdapter::getColumnsCount(const Record& record) const + { + return 1; + } + + int InfoListAdapter::getRowsCount(const Record& record) const + { + return 1; // fixed at size 1 + } +} diff --git a/apps/opencs/model/world/nestedcoladapterimp.hpp b/apps/opencs/model/world/nestedcoladapterimp.hpp new file mode 100644 index 000000000..96dcd943d --- /dev/null +++ b/apps/opencs/model/world/nestedcoladapterimp.hpp @@ -0,0 +1,417 @@ +#ifndef CSM_WOLRD_NESTEDCOLADAPTERIMP_H +#define CSM_WOLRD_NESTEDCOLADAPTERIMP_H + +#include + +#include +#include +#include // for converting magic effect id to string & back +#include // for converting skill names +#include // for converting attributes + +#include "nestedcolumnadapter.hpp" +#include "nestedtablewrapper.hpp" + +namespace ESM +{ + struct Faction; + struct Region; +} + +namespace CSMWorld +{ + struct Pathgrid; + struct Info; + + struct PathgridPointsWrap : public NestedTableWrapperBase + { + ESM::Pathgrid mRecord; + + PathgridPointsWrap(ESM::Pathgrid pathgrid) + : mRecord(pathgrid) {} + + virtual ~PathgridPointsWrap() {} + + virtual int size() const + { + return mRecord.mPoints.size(); // used in IdTree::setNestedTable() + } + }; + + class PathgridPointListAdapter : public NestedColumnAdapter + { + public: + PathgridPointListAdapter (); + + virtual void addRow(Record& record, int position) const; + + virtual void removeRow(Record& record, int rowToRemove) const; + + virtual void setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const; + + virtual NestedTableWrapperBase* table(const Record& record) const; + + virtual QVariant getData(const Record& record, + int subRowIndex, int subColIndex) const; + + virtual void setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const; + + virtual int getColumnsCount(const Record& record) const; + + virtual int getRowsCount(const Record& record) const; + }; + + class PathgridEdgeListAdapter : public NestedColumnAdapter + { + public: + PathgridEdgeListAdapter (); + + virtual void addRow(Record& record, int position) const; + + virtual void removeRow(Record& record, int rowToRemove) const; + + virtual void setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const; + + virtual NestedTableWrapperBase* table(const Record& record) const; + + virtual QVariant getData(const Record& record, + int subRowIndex, int subColIndex) const; + + virtual void setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const; + + virtual int getColumnsCount(const Record& record) const; + + virtual int getRowsCount(const Record& record) const; + }; + + class FactionReactionsAdapter : public NestedColumnAdapter + { + public: + FactionReactionsAdapter (); + + virtual void addRow(Record& record, int position) const; + + virtual void removeRow(Record& record, int rowToRemove) const; + + virtual void setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const; + + virtual NestedTableWrapperBase* table(const Record& record) const; + + virtual QVariant getData(const Record& record, + int subRowIndex, int subColIndex) const; + + virtual void setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const; + + virtual int getColumnsCount(const Record& record) const; + + virtual int getRowsCount(const Record& record) const; + }; + + class RegionSoundListAdapter : public NestedColumnAdapter + { + public: + RegionSoundListAdapter (); + + virtual void addRow(Record& record, int position) const; + + virtual void removeRow(Record& record, int rowToRemove) const; + + virtual void setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const; + + virtual NestedTableWrapperBase* table(const Record& record) const; + + virtual QVariant getData(const Record& record, + int subRowIndex, int subColIndex) const; + + virtual void setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const; + + virtual int getColumnsCount(const Record& record) const; + + virtual int getRowsCount(const Record& record) const; + }; + + template + class SpellListAdapter : public NestedColumnAdapter + { + public: + SpellListAdapter () {} + + virtual void addRow(Record& record, int position) const + { + ESXRecordT raceOrBthSgn = record.get(); + + std::vector& spells = raceOrBthSgn.mPowers.mList; + + // blank row + std::string spell = ""; + + spells.insert(spells.begin()+position, spell); + + record.setModified (raceOrBthSgn); + } + + virtual void removeRow(Record& record, int rowToRemove) const + { + ESXRecordT raceOrBthSgn = record.get(); + + std::vector& spells = raceOrBthSgn.mPowers.mList; + + if (rowToRemove < 0 || rowToRemove >= static_cast (spells.size())) + throw std::runtime_error ("index out of range"); + + spells.erase(spells.begin()+rowToRemove); + + record.setModified (raceOrBthSgn); + } + + virtual void setTable(Record& record, const NestedTableWrapperBase& nestedTable) const + { + ESXRecordT raceOrBthSgn = record.get(); + + raceOrBthSgn.mPowers.mList = + static_cast >&>(nestedTable).mNestedTable; + + record.setModified (raceOrBthSgn); + } + + virtual NestedTableWrapperBase* table(const Record& record) const + { + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper >(record.get().mPowers.mList); + } + + virtual QVariant getData(const Record& record, int subRowIndex, int subColIndex) const + { + ESXRecordT raceOrBthSgn = record.get(); + + std::vector& spells = raceOrBthSgn.mPowers.mList; + + if (subRowIndex < 0 || subRowIndex >= static_cast (spells.size())) + throw std::runtime_error ("index out of range"); + + std::string spell = spells[subRowIndex]; + switch (subColIndex) + { + case 0: return QString(spell.c_str()); + default: throw std::runtime_error("Spells subcolumn index out of range"); + } + } + + virtual void setData(Record& record, const QVariant& value, + int subRowIndex, int subColIndex) const + { + ESXRecordT raceOrBthSgn = record.get(); + + std::vector& spells = raceOrBthSgn.mPowers.mList; + + if (subRowIndex < 0 || subRowIndex >= static_cast (spells.size())) + throw std::runtime_error ("index out of range"); + + std::string spell = spells[subRowIndex]; + switch (subColIndex) + { + case 0: spell = value.toString().toUtf8().constData(); break; + default: throw std::runtime_error("Spells subcolumn index out of range"); + } + + raceOrBthSgn.mPowers.mList[subRowIndex] = spell; + + record.setModified (raceOrBthSgn); + } + + virtual int getColumnsCount(const Record& record) const + { + return 1; + } + + virtual int getRowsCount(const Record& record) const + { + return static_cast(record.get().mPowers.mList.size()); + } + }; + + template + class EffectsListAdapter : public NestedColumnAdapter + { + public: + EffectsListAdapter () {} + + virtual void addRow(Record& record, int position) const + { + ESXRecordT magic = record.get(); + + std::vector& effectsList = magic.mEffects.mList; + + // blank row + ESM::ENAMstruct effect; + effect.mEffectID = 0; + effect.mSkill = -1; + effect.mAttribute = -1; + effect.mRange = 0; + effect.mArea = 0; + effect.mDuration = 0; + effect.mMagnMin = 0; + effect.mMagnMax = 0; + + effectsList.insert(effectsList.begin()+position, effect); + + record.setModified (magic); + } + + virtual void removeRow(Record& record, int rowToRemove) const + { + ESXRecordT magic = record.get(); + + std::vector& effectsList = magic.mEffects.mList; + + if (rowToRemove < 0 || rowToRemove >= static_cast (effectsList.size())) + throw std::runtime_error ("index out of range"); + + effectsList.erase(effectsList.begin()+rowToRemove); + + record.setModified (magic); + } + + virtual void setTable(Record& record, const NestedTableWrapperBase& nestedTable) const + { + ESXRecordT magic = record.get(); + + magic.mEffects.mList = + static_cast >&>(nestedTable).mNestedTable; + + record.setModified (magic); + } + + virtual NestedTableWrapperBase* table(const Record& record) const + { + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper >(record.get().mEffects.mList); + } + + virtual QVariant getData(const Record& record, int subRowIndex, int subColIndex) const + { + ESXRecordT magic = record.get(); + + std::vector& effectsList = magic.mEffects.mList; + + if (subRowIndex < 0 || subRowIndex >= static_cast (effectsList.size())) + throw std::runtime_error ("index out of range"); + + ESM::ENAMstruct effect = effectsList[subRowIndex]; + switch (subColIndex) + { + case 0: + { + if (effect.mEffectID >=0 && effect.mEffectID < ESM::MagicEffect::Length) + return effect.mRange; + else + throw std::runtime_error("Magic effects ID unexpected value"); + } + case 1: return effect.mSkill; + case 2: return effect.mAttribute; + case 3: + { + if (effect.mRange >=0 && effect.mRange <=2) + return effect.mRange; + else + throw std::runtime_error("Magic effects range unexpected value"); + } + case 4: return effect.mArea; + case 5: return effect.mDuration; + case 6: return effect.mMagnMin; + case 7: return effect.mMagnMax; + default: throw std::runtime_error("Magic Effects subcolumn index out of range"); + } + } + + virtual void setData(Record& record, const QVariant& value, + int subRowIndex, int subColIndex) const + { + ESXRecordT magic = record.get(); + + std::vector& effectsList = magic.mEffects.mList; + + if (subRowIndex < 0 || subRowIndex >= static_cast (effectsList.size())) + throw std::runtime_error ("index out of range"); + + ESM::ENAMstruct effect = effectsList[subRowIndex]; + switch (subColIndex) + { + case 0: + { + effect.mEffectID = static_cast(value.toInt()); + break; + } + case 1: + { + effect.mSkill = static_cast(value.toInt()); + break; + } + case 2: + { + effect.mAttribute = static_cast(value.toInt()); + break; + } + case 3: + { + effect.mRange = value.toInt(); + break; + } + case 4: effect.mArea = value.toInt(); break; + case 5: effect.mDuration = value.toInt(); break; + case 6: effect.mMagnMin = value.toInt(); break; + case 7: effect.mMagnMax = value.toInt(); break; + default: throw std::runtime_error("Magic Effects subcolumn index out of range"); + } + + magic.mEffects.mList[subRowIndex] = effect; + + record.setModified (magic); + } + + virtual int getColumnsCount(const Record& record) const + { + return 8; + } + + virtual int getRowsCount(const Record& record) const + { + return static_cast(record.get().mEffects.mList.size()); + } + }; + + class InfoListAdapter : public NestedColumnAdapter + { + public: + InfoListAdapter (); + + virtual void addRow(Record& record, int position) const; + + virtual void removeRow(Record& record, int rowToRemove) const; + + virtual void setTable(Record& record, + const NestedTableWrapperBase& nestedTable) const; + + virtual NestedTableWrapperBase* table(const Record& record) const; + + virtual QVariant getData(const Record& record, + int subRowIndex, int subColIndex) const; + + virtual void setData(Record& record, + const QVariant& value, int subRowIndex, int subColIndex) const; + + virtual int getColumnsCount(const Record& record) const; + + virtual int getRowsCount(const Record& record) const; + }; +} + +#endif // CSM_WOLRD_NESTEDCOLADAPTERIMP_H diff --git a/apps/opencs/model/world/nestedcollection.cpp b/apps/opencs/model/world/nestedcollection.cpp new file mode 100644 index 000000000..937ad6ad6 --- /dev/null +++ b/apps/opencs/model/world/nestedcollection.cpp @@ -0,0 +1,17 @@ +#include "nestedcollection.hpp" + +CSMWorld::NestedCollection::NestedCollection() +{} + +CSMWorld::NestedCollection::~NestedCollection() +{} + +int CSMWorld::NestedCollection::getNestedRowsCount(int row, int column) const +{ + return 0; +} + +int CSMWorld::NestedCollection::getNestedColumnsCount(int row, int column) const +{ + return 0; +} diff --git a/apps/opencs/model/world/nestedcollection.hpp b/apps/opencs/model/world/nestedcollection.hpp new file mode 100644 index 000000000..b075f53c4 --- /dev/null +++ b/apps/opencs/model/world/nestedcollection.hpp @@ -0,0 +1,39 @@ +#ifndef CSM_WOLRD_NESTEDCOLLECTION_H +#define CSM_WOLRD_NESTEDCOLLECTION_H + +class QVariant; + +namespace CSMWorld +{ + class NestableColumn; + struct NestedTableWrapperBase; + + class NestedCollection + { + + public: + + NestedCollection(); + virtual ~NestedCollection(); + + virtual void addNestedRow(int row, int col, int position) = 0; + + virtual void removeNestedRows(int row, int column, int subRow) = 0; + + virtual QVariant getNestedData(int row, int column, int subRow, int subColumn) const = 0; + + virtual void setNestedData(int row, int column, const QVariant& data, int subRow, int subColumn) = 0; + + virtual NestedTableWrapperBase* nestedTable(int row, int column) const = 0; + + virtual void setNestedTable(int row, int column, const NestedTableWrapperBase& nestedTable) = 0; + + virtual int getNestedRowsCount(int row, int column) const; + + virtual int getNestedColumnsCount(int row, int column) const; + + virtual NestableColumn *getNestableColumn(int column) = 0; + }; +} + +#endif // CSM_WOLRD_NESTEDCOLLECTION_H diff --git a/apps/opencs/model/world/nestedcolumnadapter.hpp b/apps/opencs/model/world/nestedcolumnadapter.hpp new file mode 100644 index 000000000..462b5a5ab --- /dev/null +++ b/apps/opencs/model/world/nestedcolumnadapter.hpp @@ -0,0 +1,40 @@ +#ifndef CSM_WOLRD_NESTEDCOLUMNADAPTER_H +#define CSM_WOLRD_NESTEDCOLUMNADAPTER_H + +class QVariant; + +namespace CSMWorld +{ + struct NestedTableWrapperBase; + + template + struct Record; + + template + class NestedColumnAdapter + { + public: + + NestedColumnAdapter() {} + + virtual ~NestedColumnAdapter() {} + + virtual void addRow(Record& record, int position) const = 0; + + virtual void removeRow(Record& record, int rowToRemove) const = 0; + + virtual void setTable(Record& record, const NestedTableWrapperBase& nestedTable) const = 0; + + virtual NestedTableWrapperBase* table(const Record& record) const = 0; + + virtual QVariant getData(const Record& record, int subRowIndex, int subColIndex) const = 0; + + virtual void setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const = 0; + + virtual int getColumnsCount(const Record& record) const = 0; + + virtual int getRowsCount(const Record& record) const = 0; + }; +} + +#endif // CSM_WOLRD_NESTEDCOLUMNADAPTER_H diff --git a/apps/opencs/model/world/nestedidcollection.hpp b/apps/opencs/model/world/nestedidcollection.hpp new file mode 100644 index 000000000..792a13b7d --- /dev/null +++ b/apps/opencs/model/world/nestedidcollection.hpp @@ -0,0 +1,175 @@ +#ifndef CSM_WOLRD_NESTEDIDCOLLECTION_H +#define CSM_WOLRD_NESTEDIDCOLLECTION_H + +#include +#include + +#include "nestedcollection.hpp" +#include "nestedcoladapterimp.hpp" + +namespace ESM +{ + class ESMReader; +} + +namespace CSMWorld +{ + struct NestedTableWrapperBase; + struct Cell; + + template + class IdCollection; + + template > + class NestedIdCollection : public IdCollection, public NestedCollection + { + std::map* > mAdapters; + + const NestedColumnAdapter& getAdapter(const ColumnBase &column) const; + + public: + + NestedIdCollection (); + ~NestedIdCollection(); + + virtual void addNestedRow(int row, int column, int position); + + virtual void removeNestedRows(int row, int column, int subRow); + + virtual QVariant getNestedData(int row, int column, int subRow, int subColumn) const; + + virtual void setNestedData(int row, int column, const QVariant& data, int subRow, int subColumn); + + virtual NestedTableWrapperBase* nestedTable(int row, int column) const; + + virtual void setNestedTable(int row, int column, const NestedTableWrapperBase& nestedTable); + + virtual int getNestedRowsCount(int row, int column) const; + + virtual int getNestedColumnsCount(int row, int column) const; + + // this method is inherited from NestedCollection, not from Collection + virtual NestableColumn *getNestableColumn(int column); + + void addAdapter(std::pair* > adapter); + }; + + template + NestedIdCollection::NestedIdCollection () + {} + + template + NestedIdCollection::~NestedIdCollection() + { + for (typename std::map* >::iterator + iter (mAdapters.begin()); iter!=mAdapters.end(); ++iter) + { + delete (*iter).second; + } + } + + template + void NestedIdCollection::addAdapter(std::pair* > adapter) + { + mAdapters.insert(adapter); + } + + template + const NestedColumnAdapter& NestedIdCollection::getAdapter(const ColumnBase &column) const + { + typename std::map* >::const_iterator iter = + mAdapters.find (&column); + + if (iter==mAdapters.end()) + throw std::logic_error("No such column in the nestedidadapter"); + + return *iter->second; + } + + template + void NestedIdCollection::addNestedRow(int row, int column, int position) + { + Record record; + record.assign(Collection::getRecord(row)); + + getAdapter(Collection::getColumn(column)).addRow(record, position); + + Collection::setRecord(row, record); + } + + template + void NestedIdCollection::removeNestedRows(int row, int column, int subRow) + { + Record record; + record.assign(Collection::getRecord(row)); + + getAdapter(Collection::getColumn(column)).removeRow(record, subRow); + + Collection::setRecord(row, record); + } + + template + QVariant NestedIdCollection::getNestedData (int row, + int column, int subRow, int subColumn) const + { + return getAdapter(Collection::getColumn(column)).getData( + Collection::getRecord(row), subRow, subColumn); + } + + template + void NestedIdCollection::setNestedData(int row, + int column, const QVariant& data, int subRow, int subColumn) + { + Record record; + record.assign(Collection::getRecord(row)); + + getAdapter(Collection::getColumn(column)).setData( + record, data, subRow, subColumn); + + Collection::setRecord(row, record); + } + + template + CSMWorld::NestedTableWrapperBase* NestedIdCollection::nestedTable(int row, + int column) const + { + return getAdapter(Collection::getColumn(column)).table( + Collection::getRecord(row)); + } + + template + void NestedIdCollection::setNestedTable(int row, + int column, const CSMWorld::NestedTableWrapperBase& nestedTable) + { + Record record; + record.assign(Collection::getRecord(row)); + + getAdapter(Collection::getColumn(column)).setTable( + record, nestedTable); + + Collection::setRecord(row, record); + } + + template + int NestedIdCollection::getNestedRowsCount(int row, int column) const + { + return getAdapter(Collection::getColumn(column)).getRowsCount( + Collection::getRecord(row)); + } + + template + int NestedIdCollection::getNestedColumnsCount(int row, int column) const + { + return getAdapter(Collection::getColumn(column)).getColumnsCount( + Collection::getRecord(row)); + } + + template + CSMWorld::NestableColumn *NestedIdCollection::getNestableColumn(int column) + { + return Collection::getNestableColumn(column); + } +} + +#endif // CSM_WOLRD_NESTEDIDCOLLECTION_H diff --git a/apps/opencs/model/world/nestedinfocollection.cpp b/apps/opencs/model/world/nestedinfocollection.cpp new file mode 100644 index 000000000..4abaaf9c0 --- /dev/null +++ b/apps/opencs/model/world/nestedinfocollection.cpp @@ -0,0 +1,110 @@ +#include "nestedinfocollection.hpp" + +#include "nestedcoladapterimp.hpp" + +namespace CSMWorld +{ + NestedInfoCollection::NestedInfoCollection () + {} + + NestedInfoCollection::~NestedInfoCollection() + { + for (std::map* >::iterator + iter (mAdapters.begin()); iter!=mAdapters.end(); ++iter) + { + delete (*iter).second; + } + } + + void NestedInfoCollection::addAdapter(std::pair* > adapter) + { + mAdapters.insert(adapter); + } + + const NestedColumnAdapter& NestedInfoCollection::getAdapter(const ColumnBase &column) const + { + std::map* >::const_iterator iter = + mAdapters.find (&column); + + if (iter==mAdapters.end()) + throw std::logic_error("No such column in the nestedidadapter"); + + return *iter->second; + } + + void NestedInfoCollection::addNestedRow(int row, int column, int position) + { + Record record; + record.assign(Collection >::getRecord(row)); + + getAdapter(Collection >::getColumn(column)).addRow(record, position); + + Collection >::setRecord(row, record); + } + + void NestedInfoCollection::removeNestedRows(int row, int column, int subRow) + { + Record record; + record.assign(Collection >::getRecord(row)); + + getAdapter(Collection >::getColumn(column)).removeRow(record, subRow); + + Collection >::setRecord(row, record); + } + + QVariant NestedInfoCollection::getNestedData (int row, + int column, int subRow, int subColumn) const + { + return getAdapter(Collection >::getColumn(column)).getData( + Collection >::getRecord(row), subRow, subColumn); + } + + void NestedInfoCollection::setNestedData(int row, + int column, const QVariant& data, int subRow, int subColumn) + { + Record record; + record.assign(Collection >::getRecord(row)); + + getAdapter(Collection >::getColumn(column)).setData( + record, data, subRow, subColumn); + + Collection >::setRecord(row, record); + } + + CSMWorld::NestedTableWrapperBase* NestedInfoCollection::nestedTable(int row, + int column) const + { + return getAdapter(Collection >::getColumn(column)).table( + Collection >::getRecord(row)); + } + + void NestedInfoCollection::setNestedTable(int row, + int column, const CSMWorld::NestedTableWrapperBase& nestedTable) + { + Record record; + record.assign(Collection >::getRecord(row)); + + getAdapter(Collection >::getColumn(column)).setTable( + record, nestedTable); + + Collection >::setRecord(row, record); + } + + int NestedInfoCollection::getNestedRowsCount(int row, int column) const + { + return getAdapter(Collection >::getColumn(column)).getRowsCount( + Collection >::getRecord(row)); + } + + int NestedInfoCollection::getNestedColumnsCount(int row, int column) const + { + return getAdapter(Collection >::getColumn(column)).getColumnsCount( + Collection >::getRecord(row)); + } + + CSMWorld::NestableColumn *NestedInfoCollection::getNestableColumn(int column) + { + return Collection >::getNestableColumn(column); + } +} diff --git a/apps/opencs/model/world/nestedinfocollection.hpp b/apps/opencs/model/world/nestedinfocollection.hpp new file mode 100644 index 000000000..03c0c2349 --- /dev/null +++ b/apps/opencs/model/world/nestedinfocollection.hpp @@ -0,0 +1,50 @@ +#ifndef CSM_WOLRD_NESTEDINFOCOLLECTION_H +#define CSM_WOLRD_NESTEDINFOCOLLECTION_H + +#include + +#include "infocollection.hpp" +#include "nestedcollection.hpp" + +namespace CSMWorld +{ + struct NestedTableWrapperBase; + + template + class NestedColumnAdapter; + + class NestedInfoCollection : public InfoCollection, public NestedCollection + { + std::map* > mAdapters; + + const NestedColumnAdapter& getAdapter(const ColumnBase &column) const; + + public: + + NestedInfoCollection (); + ~NestedInfoCollection(); + + virtual void addNestedRow(int row, int column, int position); + + virtual void removeNestedRows(int row, int column, int subRow); + + virtual QVariant getNestedData(int row, int column, int subRow, int subColumn) const; + + virtual void setNestedData(int row, int column, const QVariant& data, int subRow, int subColumn); + + virtual NestedTableWrapperBase* nestedTable(int row, int column) const; + + virtual void setNestedTable(int row, int column, const NestedTableWrapperBase& nestedTable); + + virtual int getNestedRowsCount(int row, int column) const; + + virtual int getNestedColumnsCount(int row, int column) const; + + // this method is inherited from NestedCollection, not from Collection > + virtual NestableColumn *getNestableColumn(int column); + + void addAdapter(std::pair* > adapter); + }; +} + +#endif // CSM_WOLRD_NESTEDINFOCOLLECTION_H diff --git a/apps/opencs/model/world/nestedtableproxymodel.cpp b/apps/opencs/model/world/nestedtableproxymodel.cpp new file mode 100644 index 000000000..acf197716 --- /dev/null +++ b/apps/opencs/model/world/nestedtableproxymodel.cpp @@ -0,0 +1,195 @@ +#include "nestedtableproxymodel.hpp" + +#include +#include "idtree.hpp" + +CSMWorld::NestedTableProxyModel::NestedTableProxyModel(const QModelIndex& parent, + ColumnBase::Display columnId, + CSMWorld::IdTree* parentModel) + : mParentColumn(parent.column()), + mMainModel(parentModel) +{ + const int parentRow = parent.row(); + + mId = std::string(parentModel->index(parentRow, 0).data().toString().toUtf8()); + + QAbstractProxyModel::setSourceModel(parentModel); + + connect(mMainModel, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(forwardRowsAboutToInserted(const QModelIndex &, int, int))); + + connect(mMainModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(forwardRowsInserted(const QModelIndex &, int, int))); + + connect(mMainModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(forwardRowsAboutToRemoved(const QModelIndex &, int, int))); + + connect(mMainModel, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(forwardRowsRemoved(const QModelIndex &, int, int))); + + connect(mMainModel, SIGNAL(resetStart(const QString&)), + this, SLOT(forwardResetStart(const QString&))); + + connect(mMainModel, SIGNAL(resetEnd(const QString&)), + this, SLOT(forwardResetEnd(const QString&))); + + connect(mMainModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(forwardDataChanged(const QModelIndex &, const QModelIndex &))); +} + +QModelIndex CSMWorld::NestedTableProxyModel::mapFromSource(const QModelIndex& sourceIndex) const +{ + const QModelIndex& testedParent = mMainModel->parent(sourceIndex); + const QModelIndex& parent = mMainModel->getNestedModelIndex (mId, mParentColumn); + if (testedParent == parent) + { + return createIndex(sourceIndex.row(), sourceIndex.column()); + } + else + { + return QModelIndex(); + } +} + +QModelIndex CSMWorld::NestedTableProxyModel::mapToSource(const QModelIndex& proxyIndex) const +{ + const QModelIndex& parent = mMainModel->getNestedModelIndex (mId, mParentColumn); + return mMainModel->index(proxyIndex.row(), proxyIndex.column(), parent); +} + +int CSMWorld::NestedTableProxyModel::rowCount(const QModelIndex& index) const +{ + assert (!index.isValid()); + + return mMainModel->rowCount(mMainModel->getModelIndex(mId, mParentColumn)); +} + +int CSMWorld::NestedTableProxyModel::columnCount(const QModelIndex& parent) const +{ + assert (!parent.isValid()); + + return mMainModel->columnCount(mMainModel->getModelIndex(mId, mParentColumn)); +} + +QModelIndex CSMWorld::NestedTableProxyModel::index(int row, int column, const QModelIndex& parent) const +{ + assert (!parent.isValid()); + + int rows = mMainModel->rowCount(parent); + int columns = mMainModel->columnCount(parent); + + if (row < 0 || row >= rows || column < 0 || column >= columns) + return QModelIndex(); + + return createIndex(row, column); +} + +QModelIndex CSMWorld::NestedTableProxyModel::parent(const QModelIndex& index) const +{ + return QModelIndex(); +} + +QVariant CSMWorld::NestedTableProxyModel::headerData(int section, + Qt::Orientation orientation, + int role) const +{ + return mMainModel->nestedHeaderData(mParentColumn, section, orientation, role); +} + +QVariant CSMWorld::NestedTableProxyModel::data(const QModelIndex& index, int role) const +{ + return mMainModel->data(mapToSource(index), role); +} + +// NOTE: Due to mapToSouce(index) the dataChanged() signal resulting from setData() will have the +// source model's index values. The indicies need to be converted to the proxy space values. +// See forwardDataChanged() +bool CSMWorld::NestedTableProxyModel::setData (const QModelIndex & index, const QVariant & value, int role) +{ + return mMainModel->setData(mapToSource(index), value, role); +} + +Qt::ItemFlags CSMWorld::NestedTableProxyModel::flags(const QModelIndex& index) const +{ + return mMainModel->flags(mapToSource(index)); +} + +std::string CSMWorld::NestedTableProxyModel::getParentId() const +{ + return mId; +} + +int CSMWorld::NestedTableProxyModel::getParentColumn() const +{ + return mParentColumn; +} + +CSMWorld::IdTree* CSMWorld::NestedTableProxyModel::model() const +{ + return mMainModel; +} + +void CSMWorld::NestedTableProxyModel::forwardRowsAboutToInserted(const QModelIndex& parent, + int first, int last) +{ + if (indexIsParent(parent)) + { + beginInsertRows(QModelIndex(), first, last); + } +} + +void CSMWorld::NestedTableProxyModel::forwardRowsInserted(const QModelIndex& parent, int first, int last) +{ + if (indexIsParent(parent)) + { + endInsertRows(); + } +} + +bool CSMWorld::NestedTableProxyModel::indexIsParent(const QModelIndex& index) +{ + return (index.isValid() && + index.column() == mParentColumn && + mMainModel->data(mMainModel->index(index.row(), 0)).toString().toUtf8().constData() == mId); +} + +void CSMWorld::NestedTableProxyModel::forwardRowsAboutToRemoved(const QModelIndex& parent, + int first, int last) +{ + if (indexIsParent(parent)) + { + beginRemoveRows(QModelIndex(), first, last); + } +} + +void CSMWorld::NestedTableProxyModel::forwardRowsRemoved(const QModelIndex& parent, int first, int last) +{ + if (indexIsParent(parent)) + { + endRemoveRows(); + } +} + +void CSMWorld::NestedTableProxyModel::forwardResetStart(const QString& id) +{ + if (id.toUtf8() == mId.c_str()) + beginResetModel(); +} + +void CSMWorld::NestedTableProxyModel::forwardResetEnd(const QString& id) +{ + if (id.toUtf8() == mId.c_str()) + endResetModel(); +} + +void CSMWorld::NestedTableProxyModel::forwardDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + const QModelIndex& parent = mMainModel->getNestedModelIndex (mId, mParentColumn); + + if (topLeft.column() <= parent.column() && bottomRight.column() >= parent.column()) + { + emit dataChanged(index(0,0), + index(mMainModel->rowCount(parent)-1, mMainModel->columnCount(parent)-1)); + } +} diff --git a/apps/opencs/model/world/nestedtableproxymodel.hpp b/apps/opencs/model/world/nestedtableproxymodel.hpp new file mode 100644 index 000000000..2d5a46c48 --- /dev/null +++ b/apps/opencs/model/world/nestedtableproxymodel.hpp @@ -0,0 +1,84 @@ +#ifndef CSM_WOLRD_NESTEDTABLEPROXYMODEL_H +#define CSM_WOLRD_NESTEDTABLEPROXYMODEL_H + +#include + +#include + +#include "universalid.hpp" +#include "columns.hpp" +#include "columnbase.hpp" + +/*! \brief + * Proxy model used to connect view in the dialogue into the nested columns of the main model. + */ + +namespace CSMWorld +{ + class CollectionBase; + struct RecordBase; + class IdTree; + + class NestedTableProxyModel : public QAbstractProxyModel + { + Q_OBJECT + + const int mParentColumn; + IdTree* mMainModel; + std::string mId; + + public: + NestedTableProxyModel(const QModelIndex& parent, + ColumnBase::Display displayType, + IdTree* parentModel); + //parent is the parent of columns to work with. Columnid provides information about the column + + std::string getParentId() const; + + int getParentColumn() const; + + CSMWorld::IdTree* model() const; + + virtual QModelIndex mapFromSource(const QModelIndex& sourceIndex) const; + + virtual QModelIndex mapToSource(const QModelIndex& proxyIndex) const; + + virtual int rowCount(const QModelIndex& parent) const; + + virtual int columnCount(const QModelIndex& parent) const; + + virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; + + virtual QModelIndex parent(const QModelIndex& index) const; + + virtual QVariant headerData (int section, Qt::Orientation orientation, int role) const; + + virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; + + virtual bool setData (const QModelIndex & index, const QVariant & value, int role = Qt::EditRole); + + virtual Qt::ItemFlags flags(const QModelIndex& index) const; + + private: + void setupHeaderVectors(ColumnBase::Display columnId); + + bool indexIsParent(const QModelIndex& index); + + private slots: + void forwardRowsAboutToInserted(const QModelIndex & parent, int first, int last); + + void forwardRowsInserted(const QModelIndex & parent, int first, int last); + + void forwardRowsAboutToRemoved(const QModelIndex & parent, int first, int last); + + void forwardRowsRemoved(const QModelIndex & parent, int first, int last); + + void forwardResetStart(const QString& id); + + void forwardResetEnd(const QString& id); + + void forwardDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + }; +} + +#endif diff --git a/apps/opencs/model/world/nestedtablewrapper.cpp b/apps/opencs/model/world/nestedtablewrapper.cpp new file mode 100644 index 000000000..3966dbc57 --- /dev/null +++ b/apps/opencs/model/world/nestedtablewrapper.cpp @@ -0,0 +1,12 @@ +#include "nestedtablewrapper.hpp" + +CSMWorld::NestedTableWrapperBase::NestedTableWrapperBase() +{} + +CSMWorld::NestedTableWrapperBase::~NestedTableWrapperBase() +{} + +int CSMWorld::NestedTableWrapperBase::size() const +{ + return -5; +} diff --git a/apps/opencs/model/world/nestedtablewrapper.hpp b/apps/opencs/model/world/nestedtablewrapper.hpp new file mode 100644 index 000000000..f0ca00dbe --- /dev/null +++ b/apps/opencs/model/world/nestedtablewrapper.hpp @@ -0,0 +1,31 @@ +#ifndef CSM_WOLRD_NESTEDTABLEWRAPPER_H +#define CSM_WOLRD_NESTEDTABLEWRAPPER_H + +namespace CSMWorld +{ + struct NestedTableWrapperBase + { + virtual ~NestedTableWrapperBase(); + + virtual int size() const; + + NestedTableWrapperBase(); + }; + + template + struct NestedTableWrapper : public NestedTableWrapperBase + { + NestedTable mNestedTable; + + NestedTableWrapper(const NestedTable& nestedTable) + : mNestedTable(nestedTable) {} + + virtual ~NestedTableWrapper() {} + + virtual int size() const + { + return mNestedTable.size(); //i hope that this will be enough + } + }; +} +#endif diff --git a/apps/opencs/model/world/ref.cpp b/apps/opencs/model/world/ref.cpp index eee454a16..13706c950 100644 --- a/apps/opencs/model/world/ref.cpp +++ b/apps/opencs/model/world/ref.cpp @@ -1,8 +1,18 @@ #include "ref.hpp" +#include + CSMWorld::CellRef::CellRef() { mRefNum.mIndex = 0; mRefNum.mContentFile = 0; } + +std::pair CSMWorld::CellRef::getCellIndex() const +{ + const int cellSize = 8192; + + return std::make_pair ( + std::floor (mPos.pos[0]/cellSize), std::floor (mPos.pos[1]/cellSize)); +} diff --git a/apps/opencs/model/world/ref.hpp b/apps/opencs/model/world/ref.hpp index 8ab901a6f..c60392221 100644 --- a/apps/opencs/model/world/ref.hpp +++ b/apps/opencs/model/world/ref.hpp @@ -1,6 +1,8 @@ #ifndef CSM_WOLRD_REF_H #define CSM_WOLRD_REF_H +#include + #include namespace CSMWorld @@ -10,8 +12,12 @@ namespace CSMWorld { std::string mId; std::string mCell; + std::string mOriginalCell; CellRef(); + + /// Calculate cell index based on coordinates (x and y) + std::pair getCellIndex() const; }; } diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 47f0276c6..ff30dafae 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -2,8 +2,10 @@ #include "refcollection.hpp" #include +#include #include +#include #include "ref.hpp" #include "cell.hpp" @@ -20,14 +22,75 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool CellRef ref; bool deleted = false; + ESM::MovedCellRef mref; - while (ESM::Cell::getNextRef (reader, ref, deleted)) + // hack to initialise mindex + while (!(mref.mRefNum.mIndex = 0) && ESM::Cell::getNextRef(reader, ref, deleted, true, &mref)) { - ref.mCell = cell2.mId; + // Keep mOriginalCell empty when in modified (as an indicator that the + // original cell will always be equal the current cell). + ref.mOriginalCell = base ? cell2.mId : ""; - /// \todo handle moved references + if (cell.get().isExterior()) + { + // ignoring moved references sub-record; instead calculate cell from coordinates + std::pair index = ref.getCellIndex(); + + std::ostringstream stream; + stream << "#" << index.first << " " << index.second; + + ref.mCell = stream.str(); + + if (!base && // don't try to update base records + mref.mRefNum.mIndex != 0) // MVRF tag found + { + // there is a requirement for a placeholder where the original object was + // + // see the forum discussions here for more details: + // https://forum.openmw.org/viewtopic.php?f=6&t=577&start=30 + ref.mOriginalCell = cell2.mId; + + if (deleted) + { + // FIXME: how to mark the record deleted? + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Cell, + mCells.getId (cellIndex)); + + messages.add (id, "Moved reference "+ref.mRefID+" is in DELE state"); + + continue; + } + + // It is not always possibe to ignore moved references sub-record and + // calculate from coordinates. Some mods may place the ref in positions + // outside normal bounds, resulting in non sensical cell id's. This often + // happens if the moved ref was deleted. + // + // Use the target cell from the MVRF tag but if different output an error + // message + if (index.first != mref.mTarget[0] || index.second != mref.mTarget[1]) + { + std::cerr << "The Position of moved ref " + << ref.mRefID << " does not match the target cell" << std::endl; + std::cerr << "Position: #" << index.first << " " << index.second + <<", Target #"<< mref.mTarget[0] << " " << mref.mTarget[1] << std::endl; + + std::ostringstream stream; + stream << "#" << mref.mTarget[0] << " " << mref.mTarget[1]; + ref.mCell = stream.str(); // overwrite + } + } + } + else + ref.mCell = cell2.mId; - std::map::iterator iter = cache.find (ref.mRefNum); + // ignore content file number + std::map::iterator iter = cache.begin(); + for (; iter != cache.end(); ++iter) + { + if (ref.mRefNum.mIndex == iter->first.mIndex) + break; + } if (deleted) { diff --git a/apps/opencs/model/world/refcollection.hpp b/apps/opencs/model/world/refcollection.hpp index 46572752e..d031398d3 100644 --- a/apps/opencs/model/world/refcollection.hpp +++ b/apps/opencs/model/world/refcollection.hpp @@ -27,8 +27,7 @@ namespace CSMWorld {} void load (ESM::ESMReader& reader, int cellIndex, bool base, - std::map& cache, - CSMDoc::Messages& messages); + std::map& cache, CSMDoc::Messages& messages); ///< Load a sequence of references. std::string getNewId(); diff --git a/apps/opencs/model/world/refidadapter.cpp b/apps/opencs/model/world/refidadapter.cpp index 8f74e6134..37cb67bca 100644 --- a/apps/opencs/model/world/refidadapter.cpp +++ b/apps/opencs/model/world/refidadapter.cpp @@ -1,6 +1,9 @@ - #include "refidadapter.hpp" CSMWorld::RefIdAdapter::RefIdAdapter() {} CSMWorld::RefIdAdapter::~RefIdAdapter() {} + +CSMWorld::NestedRefIdAdapterBase::NestedRefIdAdapterBase() {} + +CSMWorld::NestedRefIdAdapterBase::~NestedRefIdAdapterBase() {} diff --git a/apps/opencs/model/world/refidadapter.hpp b/apps/opencs/model/world/refidadapter.hpp index 74c5dfe58..ba9da577d 100644 --- a/apps/opencs/model/world/refidadapter.hpp +++ b/apps/opencs/model/world/refidadapter.hpp @@ -2,6 +2,14 @@ #define CSM_WOLRD_REFIDADAPTER_H #include +#include + +/*! \brief + * Adapters acts as indirection layer, abstracting details of the record types (in the wrappers) from the higher levels of model. + * Please notice that nested adaptor uses helper classes for actually performing any actions. Different record types require different helpers (needs to be created in the subclass and then fetched via member function). + * + * Important point: don't forget to make sure that getData on the nestedColumn returns true (otherwise code will not treat the index pointing to the column as having childs! + */ class QVariant; @@ -10,6 +18,8 @@ namespace CSMWorld class RefIdColumn; class RefIdData; struct RecordBase; + struct NestedTableWrapperBase; + class HelperBase; class RefIdAdapter { @@ -25,13 +35,45 @@ namespace CSMWorld virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int idnex) const = 0; + ///< If called on the nest column, should return QVariant(true). virtual void setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const = 0; ///< If the data type does not match an exception is thrown. virtual std::string getId (const RecordBase& record) const = 0; - virtual void setId(RecordBase& record, const std::string& id) = 0; + + virtual void setId(RecordBase& record, const std::string& id) = 0; // used by RefIdCollection::cloneRecord() + }; + + class NestedRefIdAdapterBase + { + public: + NestedRefIdAdapterBase(); + + virtual ~NestedRefIdAdapterBase(); + + virtual void setNestedData (const RefIdColumn *column, + RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const = 0; + + virtual QVariant getNestedData (const RefIdColumn *column, + const RefIdData& data, int index, int subRowIndex, int subColIndex) const = 0; + + virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const = 0; + + virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const = 0; + + virtual void removeNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int rowToRemove) const = 0; + + virtual void addNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int position) const = 0; + + virtual void setNestedTable (const RefIdColumn* column, + RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const = 0; + + virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, + const RefIdData& data, int index) const = 0; }; } diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp index 47caa8d71..98c1b6f0f 100644 --- a/apps/opencs/model/world/refidadapterimp.cpp +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -1,10 +1,19 @@ - #include "refidadapterimp.hpp" -CSMWorld::PotionRefIdAdapter::PotionRefIdAdapter (const InventoryColumns& columns, +#include +#include +#include + +#include +#include "nestedtablewrapper.hpp" + +CSMWorld::PotionColumns::PotionColumns (const InventoryColumns& columns) +: InventoryColumns (columns) {} + +CSMWorld::PotionRefIdAdapter::PotionRefIdAdapter (const PotionColumns& columns, const RefIdColumn *autoCalc) : InventoryRefIdAdapter (UniversalId::Type_Potion, columns), - mAutoCalc (autoCalc) + mAutoCalc (autoCalc), mColumns(columns) {} QVariant CSMWorld::PotionRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, @@ -16,6 +25,9 @@ QVariant CSMWorld::PotionRefIdAdapter::getData (const RefIdColumn *column, const if (column==mAutoCalc) return record.get().mData.mAutoCalc!=0; + if (column==mColumns.mEffects) + return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() + return InventoryRefIdAdapter::getData (column, data, index); } @@ -69,9 +81,10 @@ void CSMWorld::ApparatusRefIdAdapter::setData (const RefIdColumn *column, RefIdD CSMWorld::ArmorRefIdAdapter::ArmorRefIdAdapter (const EnchantableColumns& columns, - const RefIdColumn *type, const RefIdColumn *health, const RefIdColumn *armor) + const RefIdColumn *type, const RefIdColumn *health, const RefIdColumn *armor, + const RefIdColumn *partRef) : EnchantableRefIdAdapter (UniversalId::Type_Armor, columns), - mType (type), mHealth (health), mArmor (armor) + mType (type), mHealth (health), mArmor (armor), mPartRef(partRef) {} QVariant CSMWorld::ArmorRefIdAdapter::getData (const RefIdColumn *column, @@ -89,6 +102,9 @@ QVariant CSMWorld::ArmorRefIdAdapter::getData (const RefIdColumn *column, if (column==mArmor) return record.get().mData.mArmor; + if (column==mPartRef) + return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() + return EnchantableRefIdAdapter::getData (column, data, index); } @@ -144,8 +160,9 @@ void CSMWorld::BookRefIdAdapter::setData (const RefIdColumn *column, RefIdData& } CSMWorld::ClothingRefIdAdapter::ClothingRefIdAdapter (const EnchantableColumns& columns, - const RefIdColumn *type) -: EnchantableRefIdAdapter (UniversalId::Type_Clothing, columns), mType (type) + const RefIdColumn *type, const RefIdColumn *partRef) +: EnchantableRefIdAdapter (UniversalId::Type_Clothing, columns), mType (type), + mPartRef(partRef) {} QVariant CSMWorld::ClothingRefIdAdapter::getData (const RefIdColumn *column, @@ -157,6 +174,9 @@ QVariant CSMWorld::ClothingRefIdAdapter::getData (const RefIdColumn *column, if (column==mType) return record.get().mData.mType; + if (column==mPartRef) + return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() + return EnchantableRefIdAdapter::getData (column, data, index); } @@ -173,13 +193,14 @@ void CSMWorld::ClothingRefIdAdapter::setData (const RefIdColumn *column, RefIdDa } CSMWorld::ContainerRefIdAdapter::ContainerRefIdAdapter (const NameColumns& columns, - const RefIdColumn *weight, const RefIdColumn *organic, const RefIdColumn *respawn) + const RefIdColumn *weight, const RefIdColumn *organic, const RefIdColumn *respawn, const RefIdColumn *content) : NameRefIdAdapter (UniversalId::Type_Container, columns), mWeight (weight), - mOrganic (organic), mRespawn (respawn) + mOrganic (organic), mRespawn (respawn), mContent(content) {} -QVariant CSMWorld::ContainerRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, - int index) const +QVariant CSMWorld::ContainerRefIdAdapter::getData (const RefIdColumn *column, + const RefIdData& data, + int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Container))); @@ -193,6 +214,9 @@ QVariant CSMWorld::ContainerRefIdAdapter::getData (const RefIdColumn *column, co if (column==mRespawn) return (record.get().mFlags & ESM::Container::Respawn)!=0; + if (column==mContent) + return true; // Required to show nested tables in dialogue subview + return NameRefIdAdapter::getData (column, data, index); } diff --git a/apps/opencs/model/world/refidadapterimp.hpp b/apps/opencs/model/world/refidadapterimp.hpp index 202b7781c..61e8115c0 100644 --- a/apps/opencs/model/world/refidadapterimp.hpp +++ b/apps/opencs/model/world/refidadapterimp.hpp @@ -2,16 +2,21 @@ #define CSM_WOLRD_REFIDADAPTERIMP_H #include +#include #include #include +#include #include +#include +#include #include "record.hpp" #include "refiddata.hpp" #include "universalid.hpp" #include "refidadapter.hpp" +#include "nestedtablewrapper.hpp" namespace CSMWorld { @@ -23,6 +28,7 @@ namespace CSMWorld }; /// \brief Base adapter for all refereceable record types + /// Adapters that can handle nested tables, needs to return valid qvariant for parent columns template class BaseRefIdAdapter : public RefIdAdapter { @@ -311,13 +317,21 @@ namespace CSMWorld record.setModified(record2); } + struct PotionColumns : public InventoryColumns + { + const RefIdColumn *mEffects; + + PotionColumns (const InventoryColumns& columns); + }; + class PotionRefIdAdapter : public InventoryRefIdAdapter { + PotionColumns mColumns; const RefIdColumn *mAutoCalc; public: - PotionRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *autoCalc); + PotionRefIdAdapter (const PotionColumns& columns, const RefIdColumn *autoCalc); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; @@ -471,6 +485,10 @@ namespace CSMWorld const RefIdColumn *mFlee; const RefIdColumn *mFight; const RefIdColumn *mAlarm; + const RefIdColumn *mInventory; + const RefIdColumn *mSpells; + const RefIdColumn *mDestinations; + const RefIdColumn *mAiPackages; std::map mServices; ActorColumns (const NameColumns& base) : NameColumns (base) {} @@ -522,6 +540,18 @@ namespace CSMWorld if (column==mActors.mAlarm) return record.get().mAiData.mAlarm; + if (column==mActors.mInventory) + return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() + + if (column==mActors.mSpells) + return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() + + if (column==mActors.mDestinations) + return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() + + if (column==mActors.mAiPackages) + return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() + std::map::const_iterator iter = mActors.mServices.find (column); @@ -593,11 +623,12 @@ namespace CSMWorld const RefIdColumn *mType; const RefIdColumn *mHealth; const RefIdColumn *mArmor; + const RefIdColumn *mPartRef; public: ArmorRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *type, - const RefIdColumn *health, const RefIdColumn *armor); + const RefIdColumn *health, const RefIdColumn *armor, const RefIdColumn *partRef); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; @@ -628,10 +659,12 @@ namespace CSMWorld class ClothingRefIdAdapter : public EnchantableRefIdAdapter { const RefIdColumn *mType; + const RefIdColumn *mPartRef; public: - ClothingRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *type); + ClothingRefIdAdapter (const EnchantableColumns& columns, + const RefIdColumn *type, const RefIdColumn *partRef); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; @@ -646,17 +679,17 @@ namespace CSMWorld const RefIdColumn *mWeight; const RefIdColumn *mOrganic; const RefIdColumn *mRespawn; + const RefIdColumn *mContent; public: ContainerRefIdAdapter (const NameColumns& columns, const RefIdColumn *weight, - const RefIdColumn *organic, const RefIdColumn *respawn); + const RefIdColumn *organic, const RefIdColumn *respawn, const RefIdColumn *content); - virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) - const; + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; virtual void setData (const RefIdColumn *column, RefIdData& data, int index, - const QVariant& value) const; + const QVariant& value) const; ///< If the data type does not match an exception is thrown. }; @@ -808,6 +841,1280 @@ namespace CSMWorld const QVariant& value) const; ///< If the data type does not match an exception is thrown. }; + + class NestedRefIdAdapterBase; + + template + class EffectsListAdapter; + + template + class EffectsRefIdAdapter : public EffectsListAdapter, public NestedRefIdAdapterBase + { + UniversalId::Type mType; + + // not implemented + EffectsRefIdAdapter (const EffectsRefIdAdapter&); + EffectsRefIdAdapter& operator= (const EffectsRefIdAdapter&); + + public: + + EffectsRefIdAdapter(UniversalId::Type type) :mType(type) {} + + virtual ~EffectsRefIdAdapter() {} + + virtual void addNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int position) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + EffectsListAdapter::addRow(record, position); + } + + virtual void removeNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int rowToRemove) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + EffectsListAdapter::removeRow(record, rowToRemove); + } + + virtual void setNestedTable (const RefIdColumn* column, + RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + EffectsListAdapter::setTable(record, nestedTable); + } + + virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, + const RefIdData& data, int index) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + return EffectsListAdapter::table(record); + } + + virtual QVariant getNestedData (const RefIdColumn *column, + const RefIdData& data, int index, int subRowIndex, int subColIndex) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + return EffectsListAdapter::getData(record, subRowIndex, subColIndex); + } + + virtual void setNestedData (const RefIdColumn *column, + RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (row, mType))); + EffectsListAdapter::setData(record, value, subRowIndex, subColIndex); + } + + virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const + { + const Record record; // not used, just a dummy + return EffectsListAdapter::getColumnsCount(record); + } + + virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + return EffectsListAdapter::getRowsCount(record); + } + }; + + template + class NestedInventoryRefIdAdapter : public NestedRefIdAdapterBase + { + UniversalId::Type mType; + + // not implemented + NestedInventoryRefIdAdapter (const NestedInventoryRefIdAdapter&); + NestedInventoryRefIdAdapter& operator= (const NestedInventoryRefIdAdapter&); + + public: + + NestedInventoryRefIdAdapter(UniversalId::Type type) :mType(type) {} + + virtual ~NestedInventoryRefIdAdapter() {} + + virtual void addNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int position) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + ESXRecordT container = record.get(); + + std::vector& list = container.mInventory.mList; + + ESM::ContItem newRow = {0, {""}}; + + if (position >= (int)list.size()) + list.push_back(newRow); + else + list.insert(list.begin()+position, newRow); + + record.setModified (container); + } + + virtual void removeNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int rowToRemove) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + ESXRecordT container = record.get(); + + std::vector& list = container.mInventory.mList; + + if (rowToRemove < 0 || rowToRemove >= static_cast (list.size())) + throw std::runtime_error ("index out of range"); + + list.erase (list.begin () + rowToRemove); + + record.setModified (container); + } + + virtual void setNestedTable (const RefIdColumn* column, + RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + ESXRecordT container = record.get(); + + container.mInventory.mList = + static_cast >&>(nestedTable).mNestedTable; + + record.setModified (container); + } + + virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, + const RefIdData& data, int index) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper >(record.get().mInventory.mList); + } + + virtual QVariant getNestedData (const RefIdColumn *column, + const RefIdData& data, int index, int subRowIndex, int subColIndex) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + + const std::vector& list = record.get().mInventory.mList; + + if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) + throw std::runtime_error ("index out of range"); + + const ESM::ContItem& content = list.at(subRowIndex); + + switch (subColIndex) + { + case 0: return QString::fromUtf8(content.mItem.toString().c_str()); + case 1: return content.mCount; + default: + throw std::runtime_error("Trying to access non-existing column in the nested table!"); + } + } + + virtual void setNestedData (const RefIdColumn *column, + RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (row, mType))); + ESXRecordT container = record.get(); + std::vector& list = container.mInventory.mList; + + if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) + throw std::runtime_error ("index out of range"); + + switch(subColIndex) + { + case 0: + list.at(subRowIndex).mItem.assign(std::string(value.toString().toUtf8().constData())); + break; + + case 1: + list.at(subRowIndex).mCount = value.toInt(); + break; + + default: + throw std::runtime_error("Trying to access non-existing column in the nested table!"); + } + + record.setModified (container); + } + + virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const + { + return 2; + } + + virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + + return static_cast(record.get().mInventory.mList.size()); + } + }; + + template + class NestedSpellRefIdAdapter : public NestedRefIdAdapterBase + { + UniversalId::Type mType; + + // not implemented + NestedSpellRefIdAdapter (const NestedSpellRefIdAdapter&); + NestedSpellRefIdAdapter& operator= (const NestedSpellRefIdAdapter&); + + public: + + NestedSpellRefIdAdapter(UniversalId::Type type) :mType(type) {} + + virtual ~NestedSpellRefIdAdapter() {} + + virtual void addNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int position) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + ESXRecordT caster = record.get(); + + std::vector& list = caster.mSpells.mList; + + std::string newString; + + if (position >= (int)list.size()) + list.push_back(newString); + else + list.insert(list.begin()+position, newString); + + record.setModified (caster); + } + + virtual void removeNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int rowToRemove) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + ESXRecordT caster = record.get(); + + std::vector& list = caster.mSpells.mList; + + if (rowToRemove < 0 || rowToRemove >= static_cast (list.size())) + throw std::runtime_error ("index out of range"); + + list.erase (list.begin () + rowToRemove); + + record.setModified (caster); + } + + virtual void setNestedTable (const RefIdColumn* column, + RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + ESXRecordT caster = record.get(); + + caster.mSpells.mList = + static_cast >&>(nestedTable).mNestedTable; + + record.setModified (caster); + } + + virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, + const RefIdData& data, int index) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper >(record.get().mSpells.mList); + } + + virtual QVariant getNestedData (const RefIdColumn *column, + const RefIdData& data, int index, int subRowIndex, int subColIndex) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + + const std::vector& list = record.get().mSpells.mList; + + if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) + throw std::runtime_error ("index out of range"); + + const std::string& content = list.at(subRowIndex); + + if (subColIndex == 0) + return QString::fromUtf8(content.c_str()); + else + throw std::runtime_error("Trying to access non-existing column in the nested table!"); + } + + virtual void setNestedData (const RefIdColumn *column, + RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (row, mType))); + ESXRecordT caster = record.get(); + std::vector& list = caster.mSpells.mList; + + if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) + throw std::runtime_error ("index out of range"); + + if (subColIndex == 0) + list.at(subRowIndex) = std::string(value.toString().toUtf8()); + else + throw std::runtime_error("Trying to access non-existing column in the nested table!"); + + record.setModified (caster); + } + + virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const + { + return 1; + } + + virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + + return static_cast(record.get().mSpells.mList.size()); + } + }; + + template + class NestedTravelRefIdAdapter : public NestedRefIdAdapterBase + { + UniversalId::Type mType; + + // not implemented + NestedTravelRefIdAdapter (const NestedTravelRefIdAdapter&); + NestedTravelRefIdAdapter& operator= (const NestedTravelRefIdAdapter&); + + public: + + NestedTravelRefIdAdapter(UniversalId::Type type) :mType(type) {} + + virtual ~NestedTravelRefIdAdapter() {} + + virtual void addNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int position) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + ESXRecordT traveller = record.get(); + + std::vector& list = traveller.mTransport.mList; + + ESM::Position newPos; + for (unsigned i = 0; i < 3; ++i) + { + newPos.pos[i] = 0; + newPos.rot[i] = 0; + } + + ESM::Transport::Dest newRow; + newRow.mPos = newPos; + newRow.mCellName = ""; + + if (position >= (int)list.size()) + list.push_back(newRow); + else + list.insert(list.begin()+position, newRow); + + record.setModified (traveller); + } + + virtual void removeNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int rowToRemove) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + ESXRecordT traveller = record.get(); + + std::vector& list = traveller.mTransport.mList; + + if (rowToRemove < 0 || rowToRemove >= static_cast (list.size())) + throw std::runtime_error ("index out of range"); + + list.erase (list.begin () + rowToRemove); + + record.setModified (traveller); + } + + virtual void setNestedTable (const RefIdColumn* column, + RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + ESXRecordT traveller = record.get(); + + traveller.mTransport.mList = + static_cast >&>(nestedTable).mNestedTable; + + record.setModified (traveller); + } + + virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, + const RefIdData& data, int index) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper >(record.get().mTransport.mList); + } + + virtual QVariant getNestedData (const RefIdColumn *column, + const RefIdData& data, int index, int subRowIndex, int subColIndex) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + + const std::vector& list = record.get().mTransport.mList; + + if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) + throw std::runtime_error ("index out of range"); + + const ESM::Transport::Dest& content = list.at(subRowIndex); + + switch (subColIndex) + { + case 0: return QString::fromUtf8(content.mCellName.c_str()); + case 1: return content.mPos.pos[0]; + case 2: return content.mPos.pos[1]; + case 3: return content.mPos.pos[2]; + case 4: return content.mPos.rot[0]; + case 5: return content.mPos.rot[1]; + case 6: return content.mPos.rot[2]; + default: + throw std::runtime_error("Trying to access non-existing column in the nested table!"); + } + } + + virtual void setNestedData (const RefIdColumn *column, + RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (row, mType))); + ESXRecordT traveller = record.get(); + std::vector& list = traveller.mTransport.mList; + + if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) + throw std::runtime_error ("index out of range"); + + switch(subColIndex) + { + case 0: list.at(subRowIndex).mCellName = std::string(value.toString().toUtf8().constData()); break; + case 1: list.at(subRowIndex).mPos.pos[0] = value.toFloat(); break; + case 2: list.at(subRowIndex).mPos.pos[1] = value.toFloat(); break; + case 3: list.at(subRowIndex).mPos.pos[2] = value.toFloat(); break; + case 4: list.at(subRowIndex).mPos.rot[0] = value.toFloat(); break; + case 5: list.at(subRowIndex).mPos.rot[1] = value.toFloat(); break; + case 6: list.at(subRowIndex).mPos.rot[2] = value.toFloat(); break; + default: + throw std::runtime_error("Trying to access non-existing column in the nested table!"); + } + + record.setModified (traveller); + } + + virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const + { + return 7; + } + + virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + + return static_cast(record.get().mTransport.mList.size()); + } + }; + + template + class ActorAiRefIdAdapter : public NestedRefIdAdapterBase + { + UniversalId::Type mType; + + // not implemented + ActorAiRefIdAdapter (const ActorAiRefIdAdapter&); + ActorAiRefIdAdapter& operator= (const ActorAiRefIdAdapter&); + + public: + + ActorAiRefIdAdapter(UniversalId::Type type) :mType(type) {} + + virtual ~ActorAiRefIdAdapter() {} + + virtual void addNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int position) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + ESXRecordT actor = record.get(); + + std::vector& list = actor.mAiPackage.mList; + + ESM::AIPackage newRow; + newRow.mType = ESM::AI_Wander; + newRow.mWander.mDistance = 0; + newRow.mWander.mDuration = 0; + newRow.mWander.mTimeOfDay = 0; + for (int i = 0; i < 8; ++i) + newRow.mWander.mIdle[i] = 0; + newRow.mWander.mShouldRepeat = 0; + newRow.mCellName = ""; + + if (position >= (int)list.size()) + list.push_back(newRow); + else + list.insert(list.begin()+position, newRow); + + record.setModified (actor); + } + + virtual void removeNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int rowToRemove) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + ESXRecordT actor = record.get(); + + std::vector& list = actor.mAiPackage.mList; + + if (rowToRemove < 0 || rowToRemove >= static_cast (list.size())) + throw std::runtime_error ("index out of range"); + + list.erase (list.begin () + rowToRemove); + + record.setModified (actor); + } + + virtual void setNestedTable (const RefIdColumn* column, + RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + ESXRecordT actor = record.get(); + + actor.mAiPackage.mList = + static_cast >&>(nestedTable).mNestedTable; + + record.setModified (actor); + } + + virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, + const RefIdData& data, int index) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper >(record.get().mAiPackage.mList); + } + + virtual QVariant getNestedData (const RefIdColumn *column, + const RefIdData& data, int index, int subRowIndex, int subColIndex) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + + const std::vector& list = record.get().mAiPackage.mList; + + if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) + throw std::runtime_error ("index out of range"); + + const ESM::AIPackage& content = list.at(subRowIndex); + + switch (subColIndex) + { + case 0: + switch (content.mType) + { + case ESM::AI_Wander: return 0; + case ESM::AI_Travel: return 1; + case ESM::AI_Follow: return 2; + case ESM::AI_Escort: return 3; + case ESM::AI_Activate: return 4; + case ESM::AI_CNDT: + default: return QVariant(); + } + case 1: // wander dist + if (content.mType == ESM::AI_Wander) + return content.mWander.mDistance; + else + return QVariant(); + case 2: // wander dur + if (content.mType == ESM::AI_Wander || + content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + return content.mWander.mDuration; + else + return QVariant(); + case 3: // wander ToD + if (content.mType == ESM::AI_Wander) + return content.mWander.mTimeOfDay; // FIXME: not sure of the format + else + return QVariant(); + case 4: // wander idle + if (content.mType == ESM::AI_Wander) + { + return static_cast(content.mWander.mIdle[0]); // FIXME: + } + else + return QVariant(); + case 5: // wander repeat + if (content.mType == ESM::AI_Wander) + return content.mWander.mShouldRepeat; + else + return QVariant(); + case 6: // activate name + if (content.mType == ESM::AI_Activate) + return QString(content.mActivate.mName.toString().c_str()); + else + return QVariant(); + case 7: // target id + if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + return QString(content.mTarget.mId.toString().c_str()); + else + return QVariant(); + case 8: // target cell + if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + return QString::fromUtf8(content.mCellName.c_str()); + else + return QVariant(); + case 9: + if (content.mType == ESM::AI_Travel) + return content.mTravel.mX; + else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + return content.mTarget.mX; + else + return QVariant(); + case 10: + if (content.mType == ESM::AI_Travel) + return content.mTravel.mY; + else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + return content.mTarget.mY; + else + return QVariant(); + case 11: + if (content.mType == ESM::AI_Travel) + return content.mTravel.mZ; + else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + return content.mTarget.mZ; + else + return QVariant(); + default: + throw std::runtime_error("Trying to access non-existing column in the nested table!"); + } + } + + virtual void setNestedData (const RefIdColumn *column, + RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (row, mType))); + ESXRecordT actor = record.get(); + std::vector& list = actor.mAiPackage.mList; + + if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) + throw std::runtime_error ("index out of range"); + + ESM::AIPackage& content = list.at(subRowIndex); + + switch(subColIndex) + { + case 0: // ai package type + switch (value.toInt()) + { + case 0: content.mType = ESM::AI_Wander; + case 1: content.mType = ESM::AI_Travel; + case 2: content.mType = ESM::AI_Follow; + case 3: content.mType = ESM::AI_Escort; + case 4: content.mType = ESM::AI_Activate; + } + break; // always save + + case 1: + if (content.mType == ESM::AI_Wander) + content.mWander.mDistance = static_cast(value.toInt()); + else + return; // return without saving + case 2: + if (content.mType == ESM::AI_Wander || + content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + content.mWander.mDuration = static_cast(value.toInt()); + else + return; // return without saving + case 3: + if (content.mType == ESM::AI_Wander) + content.mWander.mTimeOfDay = static_cast(value.toInt()); + else + return; // return without saving + case 4: + if (content.mType == ESM::AI_Wander) + break; // FIXME: idle + else + return; // return without saving + case 5: + if (content.mType == ESM::AI_Wander) + { + content.mWander.mShouldRepeat = static_cast(value.toInt()); + break; + } + case 6: // NAME32 + if (content.mType == ESM::AI_Activate) + { + content.mActivate.mName.assign(value.toString().toUtf8().constData()); + break; + } + else + return; // return without saving + case 7: // NAME32 + if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + { + content.mTarget.mId.assign(value.toString().toUtf8().constData()); + break; + } + else + return; // return without saving + case 8: + if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + { + content.mCellName = std::string(value.toString().toUtf8().constData()); + break; + } + else + return; // return without saving + case 9: + if (content.mType == ESM::AI_Travel) + content.mTravel.mZ = value.toFloat(); + else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + content.mTarget.mZ = value.toFloat(); + else + return; // return without saving + case 10: + if (content.mType == ESM::AI_Travel) + content.mTravel.mZ = value.toFloat(); + else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + content.mTarget.mZ = value.toFloat(); + else + return; // return without saving + case 11: + if (content.mType == ESM::AI_Travel) + content.mTravel.mZ = value.toFloat(); + else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort) + content.mTarget.mZ = value.toFloat(); + else + return; // return without saving + default: + throw std::runtime_error("Trying to access non-existing column in the nested table!"); + } + + record.setModified (actor); + } + + virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const + { + return 12; + } + + virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + + return static_cast(record.get().mAiPackage.mList.size()); + } + }; + + + template + class BodyPartRefIdAdapter : public NestedRefIdAdapterBase + { + UniversalId::Type mType; + + // not implemented + BodyPartRefIdAdapter (const BodyPartRefIdAdapter&); + BodyPartRefIdAdapter& operator= (const BodyPartRefIdAdapter&); + + public: + + BodyPartRefIdAdapter(UniversalId::Type type) :mType(type) {} + + virtual ~BodyPartRefIdAdapter() {} + + virtual void addNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int position) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + ESXRecordT apparel = record.get(); + + std::vector& list = apparel.mParts.mParts; + + ESM::PartReference newPart; + newPart.mPart = 0; // 0 == head + newPart.mMale = ""; + newPart.mFemale = ""; + + if (position >= (int)list.size()) + list.push_back(newPart); + else + list.insert(list.begin()+position, newPart); + + record.setModified (apparel); + } + + virtual void removeNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int rowToRemove) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + ESXRecordT apparel = record.get(); + + std::vector& list = apparel.mParts.mParts; + + if (rowToRemove < 0 || rowToRemove >= static_cast (list.size())) + throw std::runtime_error ("index out of range"); + + list.erase (list.begin () + rowToRemove); + + record.setModified (apparel); + } + + virtual void setNestedTable (const RefIdColumn* column, + RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + ESXRecordT apparel = record.get(); + + apparel.mParts.mParts = + static_cast >&>(nestedTable).mNestedTable; + + record.setModified (apparel); + } + + virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, + const RefIdData& data, int index) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper >(record.get().mParts.mParts); + } + + virtual QVariant getNestedData (const RefIdColumn *column, + const RefIdData& data, int index, int subRowIndex, int subColIndex) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + + const std::vector& list = record.get().mParts.mParts; + + if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) + throw std::runtime_error ("index out of range"); + + const ESM::PartReference& content = list.at(subRowIndex); + + switch (subColIndex) + { + case 0: + { + if (content.mPart < ESM::PRT_Count) + return content.mPart; + else + throw std::runtime_error("Part Reference Type unexpected value"); + } + case 1: return QString(content.mMale.c_str()); + case 2: return QString(content.mFemale.c_str()); + default: + throw std::runtime_error("Trying to access non-existing column in the nested table!"); + } + } + + virtual void setNestedData (const RefIdColumn *column, + RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (row, mType))); + ESXRecordT apparel = record.get(); + std::vector& list = apparel.mParts.mParts; + + if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) + throw std::runtime_error ("index out of range"); + + switch(subColIndex) + { + case 0: list.at(subRowIndex).mPart = static_cast(value.toInt()); break; + case 1: list.at(subRowIndex).mMale = value.toString().toStdString(); break; + case 2: list.at(subRowIndex).mFemale = value.toString().toStdString(); break; + default: + throw std::runtime_error("Trying to access non-existing column in the nested table!"); + } + + record.setModified (apparel); + } + + virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const + { + return 3; + } + + virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + + return static_cast(record.get().mParts.mParts.size()); + } + }; + + + struct LevListColumns : public BaseColumns + { + const RefIdColumn *mLevList; + const RefIdColumn *mNestedListLevList; + + LevListColumns (const BaseColumns& base) : BaseColumns (base) {} + }; + + template + class LevelledListRefIdAdapter : public BaseRefIdAdapter + { + LevListColumns mLevList; + + public: + + LevelledListRefIdAdapter (UniversalId::Type type, const LevListColumns &columns); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + template + LevelledListRefIdAdapter::LevelledListRefIdAdapter (UniversalId::Type type, + const LevListColumns &columns) + : BaseRefIdAdapter (type, columns), mLevList (columns) + {} + + template + QVariant LevelledListRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const + { + if (column==mLevList.mLevList || column == mLevList.mNestedListLevList) + return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() + + return BaseRefIdAdapter::getData (column, data, index); + } + + template + void LevelledListRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const + { + BaseRefIdAdapter::setData (column, data, index, value); + return; + } + + + // for non-tables + template + class NestedListLevListRefIdAdapter : public NestedRefIdAdapterBase + { + UniversalId::Type mType; + + // not implemented + NestedListLevListRefIdAdapter (const NestedListLevListRefIdAdapter&); + NestedListLevListRefIdAdapter& operator= (const NestedListLevListRefIdAdapter&); + + public: + + NestedListLevListRefIdAdapter(UniversalId::Type type) + :mType(type) {} + + virtual ~NestedListLevListRefIdAdapter() {} + + virtual void addNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int position) const + { + throw std::logic_error ("cannot add a row to a fixed table"); + } + + virtual void removeNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int rowToRemove) const + { + throw std::logic_error ("cannot remove a row to a fixed table"); + } + + virtual void setNestedTable (const RefIdColumn* column, + RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const + { + throw std::logic_error ("table operation not supported"); + } + + virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, + const RefIdData& data, int index) const + { + throw std::logic_error ("table operation not supported"); + } + + virtual QVariant getNestedData (const RefIdColumn *column, + const RefIdData& data, int index, int subRowIndex, int subColIndex) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + + if (mType == UniversalId::Type_CreatureLevelledList) + { + switch (subColIndex) + { + case 0: return QVariant(); // don't allow checkbox editor to be created + case 1: return record.get().mFlags & ESM::CreatureLevList::AllLevels; + case 2: return static_cast (record.get().mChanceNone); + default: + throw std::runtime_error("Trying to access non-existing column in levelled creatues!"); + } + } + else + { + switch (subColIndex) + { + case 0: return record.get().mFlags & ESM::ItemLevList::Each; + case 1: return record.get().mFlags & ESM::ItemLevList::AllLevels; + case 2: return static_cast (record.get().mChanceNone); + default: + throw std::runtime_error("Trying to access non-existing column in levelled items!"); + } + } + } + + virtual void setNestedData (const RefIdColumn *column, + RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (row, mType))); + ESXRecordT leveled = record.get(); + + if (mType == UniversalId::Type_CreatureLevelledList) + { + switch(subColIndex) + { + case 0: return; // return without saving + case 1: + { + if(value.toBool()) + { + leveled.mFlags |= ESM::CreatureLevList::AllLevels; + break; + } + else + { + leveled.mFlags &= ~ESM::CreatureLevList::AllLevels; + break; + } + } + case 2: leveled.mChanceNone = static_cast(value.toInt()); break; + default: + throw std::runtime_error("Trying to set non-existing column in levelled creatures!"); + } + } + else + { + switch(subColIndex) + { + case 0: + { + if(value.toBool()) + { + leveled.mFlags |= ESM::ItemLevList::Each; + break; + } + else + { + leveled.mFlags &= ~ESM::ItemLevList::Each; + break; + } + } + case 1: + { + if(value.toBool()) + { + leveled.mFlags |= ESM::ItemLevList::AllLevels; + break; + } + else + { + leveled.mFlags &= ~ESM::ItemLevList::AllLevels; + break; + } + } + case 2: leveled.mChanceNone = static_cast(value.toInt()); break; + default: + throw std::runtime_error("Trying to set non-existing column in levelled items!"); + } + } + + record.setModified (leveled); + } + + virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const + { + return 3; + } + + virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const + { + return 1; // fixed at size 1 + } + }; + + // for tables + template + class NestedLevListRefIdAdapter : public NestedRefIdAdapterBase + { + UniversalId::Type mType; + + // not implemented + NestedLevListRefIdAdapter (const NestedLevListRefIdAdapter&); + NestedLevListRefIdAdapter& operator= (const NestedLevListRefIdAdapter&); + + public: + + NestedLevListRefIdAdapter(UniversalId::Type type) :mType(type) {} + + virtual ~NestedLevListRefIdAdapter() {} + + virtual void addNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int position) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + ESXRecordT leveled = record.get(); + + std::vector& list = leveled.mList; + + ESM::LevelledListBase::LevelItem newItem; + newItem.mId = ""; + newItem.mLevel = 0; + + if (position >= (int)list.size()) + list.push_back(newItem); + else + list.insert(list.begin()+position, newItem); + + record.setModified (leveled); + } + + virtual void removeNestedRow (const RefIdColumn *column, + RefIdData& data, int index, int rowToRemove) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + ESXRecordT leveled = record.get(); + + std::vector& list = leveled.mList; + + if (rowToRemove < 0 || rowToRemove >= static_cast (list.size())) + throw std::runtime_error ("index out of range"); + + list.erase (list.begin () + rowToRemove); + + record.setModified (leveled); + } + + virtual void setNestedTable (const RefIdColumn* column, + RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + ESXRecordT leveled = record.get(); + + leveled.mList = + static_cast >&>(nestedTable).mNestedTable; + + record.setModified (leveled); + } + + virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column, + const RefIdData& data, int index) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper >(record.get().mList); + } + + virtual QVariant getNestedData (const RefIdColumn *column, + const RefIdData& data, int index, int subRowIndex, int subColIndex) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + + const std::vector& list = record.get().mList; + + if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) + throw std::runtime_error ("index out of range"); + + const ESM::LevelledListBase::LevelItem& content = list.at(subRowIndex); + + switch (subColIndex) + { + case 0: return QString(content.mId.c_str()); + case 1: return content.mLevel; + default: + throw std::runtime_error("Trying to access non-existing column in the nested table!"); + } + } + + virtual void setNestedData (const RefIdColumn *column, + RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const + { + Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (row, mType))); + ESXRecordT leveled = record.get(); + std::vector& list = leveled.mList; + + if (subRowIndex < 0 || subRowIndex >= static_cast (list.size())) + throw std::runtime_error ("index out of range"); + + switch(subColIndex) + { + case 0: list.at(subRowIndex).mId = value.toString().toStdString(); break; + case 1: list.at(subRowIndex).mLevel = static_cast(value.toInt()); break; + default: + throw std::runtime_error("Trying to access non-existing column in the nested table!"); + } + + record.setModified (leveled); + } + + virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const + { + return 2; + } + + virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const + { + const Record& record = + static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); + + return static_cast(record.get().mList.size()); + } + }; } #endif diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 6963c1331..1941c505a 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -1,4 +1,3 @@ - #include "refidcollection.hpp" #include @@ -9,10 +8,12 @@ #include "refidadapter.hpp" #include "refidadapterimp.hpp" #include "columns.hpp" +#include "nestedtablewrapper.hpp" +#include "nestedcoladapterimp.hpp" CSMWorld::RefIdColumn::RefIdColumn (int columnId, Display displayType, int flag, bool editable, bool userEditable) -: ColumnBase (columnId, displayType, flag), mEditable (editable), mUserEditable (userEditable) + : NestableColumn (columnId, displayType, flag), mEditable (editable), mUserEditable (userEditable) {} bool CSMWorld::RefIdColumn::isEditable() const @@ -25,8 +26,7 @@ bool CSMWorld::RefIdColumn::isUserEditable() const return mUserEditable; } - -const CSMWorld::RefIdAdapter& CSMWorld::RefIdCollection::findAdaptor (UniversalId::Type type) const +const CSMWorld::RefIdAdapter& CSMWorld::RefIdCollection::findAdapter (UniversalId::Type type) const { std::map::const_iterator iter = mAdapters.find (type); @@ -71,6 +71,32 @@ CSMWorld::RefIdCollection::RefIdCollection() mColumns.push_back (RefIdColumn (Columns::ColumnId_CoinValue, ColumnBase::Display_Integer)); inventoryColumns.mValue = &mColumns.back(); + // nested table + PotionColumns potionColumns (inventoryColumns); + mColumns.push_back (RefIdColumn (Columns::ColumnId_EffectList, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + potionColumns.mEffects = &mColumns.back(); // see refidadapterimp.hpp + std::map effectsMap; + effectsMap.insert(std::make_pair(UniversalId::Type_Potion, + new EffectsRefIdAdapter (UniversalId::Type_Potion))); + mNestedAdapters.push_back (std::make_pair(&mColumns.back(), effectsMap)); + mColumns.back().addColumn( + new NestedChildColumn (Columns::ColumnId_EffectId, ColumnBase::Display_EffectId)); + mColumns.back().addColumn( + new NestedChildColumn (Columns::ColumnId_SkillImpact, ColumnBase::Display_SkillImpact)); + mColumns.back().addColumn( + new NestedChildColumn (Columns::ColumnId_Attribute, ColumnBase::Display_Attribute)); + mColumns.back().addColumn( + new NestedChildColumn (Columns::ColumnId_EffectRange, ColumnBase::Display_EffectRange)); + mColumns.back().addColumn( + new NestedChildColumn (Columns::ColumnId_EffectArea, ColumnBase::Display_String)); + mColumns.back().addColumn( + new NestedChildColumn (Columns::ColumnId_Duration, ColumnBase::Display_Integer)); // reuse from light + mColumns.back().addColumn( + new NestedChildColumn (Columns::ColumnId_MinRange, ColumnBase::Display_Integer)); // reuse from sound + mColumns.back().addColumn( + new NestedChildColumn (Columns::ColumnId_MaxRange, ColumnBase::Display_Integer)); // reuse from sound + EnchantableColumns enchantableColumns (inventoryColumns); mColumns.push_back (RefIdColumn (Columns::ColumnId_Enchantment, ColumnBase::Display_String)); @@ -98,6 +124,94 @@ CSMWorld::RefIdCollection::RefIdCollection() mColumns.push_back (RefIdColumn (Columns::ColumnId_AiAlarm, ColumnBase::Display_Integer)); actorsColumns.mAlarm = &mColumns.back(); + // Nested table + mColumns.push_back(RefIdColumn (Columns::ColumnId_ActorInventory, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + actorsColumns.mInventory = &mColumns.back(); + std::map inventoryMap; + inventoryMap.insert(std::make_pair(UniversalId::Type_Npc, + new NestedInventoryRefIdAdapter (UniversalId::Type_Npc))); + inventoryMap.insert(std::make_pair(UniversalId::Type_Creature, + new NestedInventoryRefIdAdapter (UniversalId::Type_Creature))); + mNestedAdapters.push_back (std::make_pair(&mColumns.back(), inventoryMap)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_InventoryItemId, CSMWorld::ColumnBase::Display_String)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_ItemCount, CSMWorld::ColumnBase::Display_Integer)); + + // Nested table + mColumns.push_back(RefIdColumn (Columns::ColumnId_SpellList, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + actorsColumns.mSpells = &mColumns.back(); + std::map spellsMap; + spellsMap.insert(std::make_pair(UniversalId::Type_Npc, + new NestedSpellRefIdAdapter (UniversalId::Type_Npc))); + spellsMap.insert(std::make_pair(UniversalId::Type_Creature, + new NestedSpellRefIdAdapter (UniversalId::Type_Creature))); + mNestedAdapters.push_back (std::make_pair(&mColumns.back(), spellsMap)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_SpellId, CSMWorld::ColumnBase::Display_String)); + + // Nested table + mColumns.push_back(RefIdColumn (Columns::ColumnId_NpcDestinations, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + actorsColumns.mDestinations = &mColumns.back(); + std::map destMap; + destMap.insert(std::make_pair(UniversalId::Type_Npc, + new NestedTravelRefIdAdapter (UniversalId::Type_Npc))); + destMap.insert(std::make_pair(UniversalId::Type_Creature, + new NestedTravelRefIdAdapter (UniversalId::Type_Creature))); + mNestedAdapters.push_back (std::make_pair(&mColumns.back(), destMap)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_DestinationCell, CSMWorld::ColumnBase::Display_String)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_PosX, CSMWorld::ColumnBase::Display_Float)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_PosY, CSMWorld::ColumnBase::Display_Float)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_PosZ, CSMWorld::ColumnBase::Display_Float)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_RotX, CSMWorld::ColumnBase::Display_Float)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_RotY, CSMWorld::ColumnBase::Display_Float)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_RotZ, CSMWorld::ColumnBase::Display_Float)); + + // Nested table + mColumns.push_back(RefIdColumn (Columns::ColumnId_AiPackageList, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + actorsColumns.mAiPackages = &mColumns.back(); + std::map aiMap; + aiMap.insert(std::make_pair(UniversalId::Type_Npc, + new ActorAiRefIdAdapter (UniversalId::Type_Npc))); + aiMap.insert(std::make_pair(UniversalId::Type_Creature, + new ActorAiRefIdAdapter (UniversalId::Type_Creature))); + mNestedAdapters.push_back (std::make_pair(&mColumns.back(), aiMap)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_AiPackageType, CSMWorld::ColumnBase::Display_AiPackageType)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_AiWanderDist, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_AiDuration, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_AiWanderToD, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_AiWanderIdle, CSMWorld::ColumnBase::Display_Integer)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_AiWanderRepeat, CSMWorld::ColumnBase::Display_YesNo)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_AiActivateName, CSMWorld::ColumnBase::Display_String)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_AiTargetId, CSMWorld::ColumnBase::Display_String)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_AiTargetCell, CSMWorld::ColumnBase::Display_String)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_PosX, CSMWorld::ColumnBase::Display_Float)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_PosY, CSMWorld::ColumnBase::Display_Float)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_PosZ, CSMWorld::ColumnBase::Display_Float)); + static const struct { int mName; @@ -165,6 +279,19 @@ CSMWorld::RefIdCollection::RefIdCollection() mColumns.push_back (RefIdColumn (Columns::ColumnId_Respawn, ColumnBase::Display_Boolean)); const RefIdColumn *respawn = &mColumns.back(); + // Nested table + mColumns.push_back(RefIdColumn (Columns::ColumnId_ContainerContent, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + const RefIdColumn *content = &mColumns.back(); + std::map contMap; + contMap.insert(std::make_pair(UniversalId::Type_Container, + new NestedInventoryRefIdAdapter (UniversalId::Type_Container))); + mNestedAdapters.push_back (std::make_pair(&mColumns.back(), contMap)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_InventoryItemId, CSMWorld::ColumnBase::Display_String)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_ItemCount, CSMWorld::ColumnBase::Display_Integer)); + CreatureColumns creatureColumns (actorsColumns); mColumns.push_back (RefIdColumn (Columns::ColumnId_CreatureType, ColumnBase::Display_CreatureType)); @@ -343,20 +470,70 @@ CSMWorld::RefIdCollection::RefIdCollection() weaponColumns.mFlags.insert (std::make_pair (&mColumns.back(), sWeaponFlagTable[i].mFlag)); } + // Nested table + mColumns.push_back(RefIdColumn (Columns::ColumnId_PartRefList, ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + const RefIdColumn *partRef = &mColumns.back(); + std::map partMap; + partMap.insert(std::make_pair(UniversalId::Type_Armor, + new BodyPartRefIdAdapter (UniversalId::Type_Armor))); + partMap.insert(std::make_pair(UniversalId::Type_Clothing, + new BodyPartRefIdAdapter (UniversalId::Type_Clothing))); + mNestedAdapters.push_back (std::make_pair(&mColumns.back(), partMap)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_PartRefType, CSMWorld::ColumnBase::Display_PartRefType)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_PartRefMale, CSMWorld::ColumnBase::Display_String)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_PartRefFemale, CSMWorld::ColumnBase::Display_String)); + + LevListColumns levListColumns (baseColumns); + + // Nested table + mColumns.push_back(RefIdColumn (Columns::ColumnId_LevelledList, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + levListColumns.mLevList = &mColumns.back(); + std::map levListMap; + levListMap.insert(std::make_pair(UniversalId::Type_CreatureLevelledList, + new NestedLevListRefIdAdapter (UniversalId::Type_CreatureLevelledList))); + levListMap.insert(std::make_pair(UniversalId::Type_ItemLevelledList, + new NestedLevListRefIdAdapter (UniversalId::Type_ItemLevelledList))); + mNestedAdapters.push_back (std::make_pair(&mColumns.back(), levListMap)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_LevelledItemId, CSMWorld::ColumnBase::Display_String)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_LevelledItemLevel, CSMWorld::ColumnBase::Display_Integer)); + + // Nested list + mColumns.push_back(RefIdColumn (Columns::ColumnId_LevelledList, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List)); + levListColumns.mNestedListLevList = &mColumns.back(); + std::map nestedListLevListMap; + nestedListLevListMap.insert(std::make_pair(UniversalId::Type_CreatureLevelledList, + new NestedListLevListRefIdAdapter (UniversalId::Type_CreatureLevelledList))); + nestedListLevListMap.insert(std::make_pair(UniversalId::Type_ItemLevelledList, + new NestedListLevListRefIdAdapter (UniversalId::Type_ItemLevelledList))); + mNestedAdapters.push_back (std::make_pair(&mColumns.back(), nestedListLevListMap)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_LevelledItemTypeEach, CSMWorld::ColumnBase::Display_Boolean)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_LevelledItemType, CSMWorld::ColumnBase::Display_Boolean)); + mColumns.back().addColumn( + new RefIdColumn (Columns::ColumnId_LevelledItemChanceNone, CSMWorld::ColumnBase::Display_Integer)); + mAdapters.insert (std::make_pair (UniversalId::Type_Activator, new NameRefIdAdapter (UniversalId::Type_Activator, nameColumns))); mAdapters.insert (std::make_pair (UniversalId::Type_Potion, - new PotionRefIdAdapter (inventoryColumns, autoCalc))); + new PotionRefIdAdapter (potionColumns, autoCalc))); mAdapters.insert (std::make_pair (UniversalId::Type_Apparatus, new ApparatusRefIdAdapter (inventoryColumns, apparatusType, toolsColumns.mQuality))); mAdapters.insert (std::make_pair (UniversalId::Type_Armor, - new ArmorRefIdAdapter (enchantableColumns, armorType, health, armor))); + new ArmorRefIdAdapter (enchantableColumns, armorType, health, armor, partRef))); mAdapters.insert (std::make_pair (UniversalId::Type_Book, new BookRefIdAdapter (enchantableColumns, scroll, attribute))); mAdapters.insert (std::make_pair (UniversalId::Type_Clothing, - new ClothingRefIdAdapter (enchantableColumns, clothingType))); + new ClothingRefIdAdapter (enchantableColumns, clothingType, partRef))); mAdapters.insert (std::make_pair (UniversalId::Type_Container, - new ContainerRefIdAdapter (nameColumns, weightCapacity, organic, respawn))); + new ContainerRefIdAdapter (nameColumns, weightCapacity, organic, respawn, content))); mAdapters.insert (std::make_pair (UniversalId::Type_Creature, new CreatureRefIdAdapter (creatureColumns))); mAdapters.insert (std::make_pair (UniversalId::Type_Door, @@ -364,10 +541,10 @@ CSMWorld::RefIdCollection::RefIdCollection() mAdapters.insert (std::make_pair (UniversalId::Type_Ingredient, new InventoryRefIdAdapter (UniversalId::Type_Ingredient, inventoryColumns))); mAdapters.insert (std::make_pair (UniversalId::Type_CreatureLevelledList, - new BaseRefIdAdapter ( - UniversalId::Type_CreatureLevelledList, baseColumns))); + new LevelledListRefIdAdapter ( + UniversalId::Type_CreatureLevelledList, levListColumns))); mAdapters.insert (std::make_pair (UniversalId::Type_ItemLevelledList, - new BaseRefIdAdapter (UniversalId::Type_ItemLevelledList, baseColumns))); + new LevelledListRefIdAdapter (UniversalId::Type_ItemLevelledList, levListColumns))); mAdapters.insert (std::make_pair (UniversalId::Type_Light, new LightRefIdAdapter (lightColumns))); mAdapters.insert (std::make_pair (UniversalId::Type_Lockpick, @@ -391,6 +568,14 @@ CSMWorld::RefIdCollection::~RefIdCollection() for (std::map::iterator iter (mAdapters.begin()); iter!=mAdapters.end(); ++iter) delete iter->second; + + for (std::vector > >::iterator iter (mNestedAdapters.begin()); + iter!=mNestedAdapters.end(); ++iter) + { + for (std::map::iterator it ((iter->second).begin()); + it!=(iter->second).end(); ++it) + delete it->second; + } } int CSMWorld::RefIdCollection::getSize() const @@ -427,25 +612,51 @@ QVariant CSMWorld::RefIdCollection::getData (int index, int column) const { RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); - const RefIdAdapter& adaptor = findAdaptor (localIndex.second); + const RefIdAdapter& adaptor = findAdapter (localIndex.second); return adaptor.getData (&mColumns.at (column), mData, localIndex.first); } +QVariant CSMWorld::RefIdCollection::getNestedData (int row, int column, int subRow, int subColumn) const +{ + RefIdData::LocalIndex localIndex = mData.globalToLocalIndex(row); + const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second); + + return nestedAdapter.getNestedData(&mColumns.at (column), mData, localIndex.first, subRow, subColumn); +} + void CSMWorld::RefIdCollection::setData (int index, int column, const QVariant& data) { RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); - const RefIdAdapter& adaptor = findAdaptor (localIndex.second); + const RefIdAdapter& adaptor = findAdapter (localIndex.second); adaptor.setData (&mColumns.at (column), mData, localIndex.first, data); } +void CSMWorld::RefIdCollection::setNestedData(int row, int column, const QVariant& data, int subRow, int subColumn) +{ + RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (row); + const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second); + + nestedAdapter.setNestedData(&mColumns.at (column), mData, localIndex.first, data, subRow, subColumn); + return; +} + void CSMWorld::RefIdCollection::removeRows (int index, int count) { mData.erase (index, count); } +void CSMWorld::RefIdCollection::removeNestedRows(int row, int column, int subRow) +{ + RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (row); + const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second); + + nestedAdapter.removeNestedRow(&mColumns.at (column), mData, localIndex.first, subRow); + return; +} + void CSMWorld::RefIdCollection::appendBlankRecord (const std::string& id, UniversalId::Type type) { mData.appendRecord (type, id, false); @@ -478,7 +689,7 @@ void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin, void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record, UniversalId::Type type) { - std::string id = findAdaptor (type).getId (record); + std::string id = findAdapter (type).getId (record); int index = mData.getAppendIndex (type); @@ -581,3 +792,68 @@ const CSMWorld::RefIdData& CSMWorld::RefIdCollection::getDataSet() const return mData; } +int CSMWorld::RefIdCollection::getNestedRowsCount(int row, int column) const +{ + RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (row); + const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second); + + return nestedAdapter.getNestedRowsCount(&mColumns.at(column), mData, localIndex.first); +} + +int CSMWorld::RefIdCollection::getNestedColumnsCount(int row, int column) const +{ + RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (row); + const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second); + + return nestedAdapter.getNestedColumnsCount(&mColumns.at(column), mData); +} + +CSMWorld::NestableColumn *CSMWorld::RefIdCollection::getNestableColumn(int column) +{ + return &mColumns.at(column); +} + +void CSMWorld::RefIdCollection::addNestedRow(int row, int col, int position) +{ + RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (row); + const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(col), localIndex.second); + + nestedAdapter.addNestedRow(&mColumns.at(col), mData, localIndex.first, position); + return; +} + +void CSMWorld::RefIdCollection::setNestedTable(int row, int column, const CSMWorld::NestedTableWrapperBase& nestedTable) +{ + RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (row); + const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second); + + nestedAdapter.setNestedTable(&mColumns.at(column), mData, localIndex.first, nestedTable); + return; +} + +CSMWorld::NestedTableWrapperBase* CSMWorld::RefIdCollection::nestedTable(int row, int column) const +{ + RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (row); + const CSMWorld::NestedRefIdAdapterBase& nestedAdapter = getNestedAdapter(mColumns.at(column), localIndex.second); + + return nestedAdapter.nestedTable(&mColumns.at(column), mData, localIndex.first); +} + +const CSMWorld::NestedRefIdAdapterBase& CSMWorld::RefIdCollection::getNestedAdapter(const CSMWorld::ColumnBase &column, UniversalId::Type type) const +{ + for (std::vector > >::const_iterator iter (mNestedAdapters.begin()); + iter!=mNestedAdapters.end(); ++iter) + { + if ((iter->first) == &column) + { + std::map::const_iterator it = + (iter->second).find(type); + + if (it == (iter->second).end()) + throw std::runtime_error("No such type in the nestedadapters"); + + return *it->second; + } + } + throw std::runtime_error("No such column in the nestedadapters"); +} diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp index dd6213677..4d511d12d 100644 --- a/apps/opencs/model/world/refidcollection.hpp +++ b/apps/opencs/model/world/refidcollection.hpp @@ -7,6 +7,7 @@ #include "columnbase.hpp" #include "collectionbase.hpp" +#include "nestedcollection.hpp" #include "refiddata.hpp" namespace ESM @@ -17,8 +18,10 @@ namespace ESM namespace CSMWorld { class RefIdAdapter; + struct NestedTableWrapperBase; + class NestedRefIdAdapterBase; - class RefIdColumn : public ColumnBase + class RefIdColumn : public NestableColumn { bool mEditable; bool mUserEditable; @@ -26,15 +29,15 @@ namespace CSMWorld public: RefIdColumn (int columnId, Display displayType, - int flag = Flag_Table | Flag_Dialogue, bool editable = true, - bool userEditable = true); + int flag = Flag_Table | Flag_Dialogue, bool editable = true, + bool userEditable = true); virtual bool isEditable() const; virtual bool isUserEditable() const; }; - class RefIdCollection : public CollectionBase + class RefIdCollection : public CollectionBase, public NestedCollection { private: @@ -42,11 +45,15 @@ namespace CSMWorld std::deque mColumns; std::map mAdapters; + std::vector > > mNestedAdapters; + private: - const RefIdAdapter& findAdaptor (UniversalId::Type) const; + const RefIdAdapter& findAdapter (UniversalId::Type) const; ///< Throws an exception if no adaptor for \a Type can be found. + const NestedRefIdAdapterBase& getNestedAdapter(const ColumnBase &column, UniversalId::Type type) const; + public: RefIdCollection(); @@ -69,7 +76,7 @@ namespace CSMWorld virtual void removeRows (int index, int count); - virtual void cloneRecord(const std::string& origin, + virtual void cloneRecord(const std::string& origin, const std::string& destination, const UniversalId::Type type); @@ -110,11 +117,28 @@ namespace CSMWorld /// /// \return Success? + virtual QVariant getNestedData(int row, int column, int subRow, int subColumn) const; + + virtual NestedTableWrapperBase* nestedTable(int row, int column) const; + + virtual void setNestedTable(int row, int column, const NestedTableWrapperBase& nestedTable); + + virtual int getNestedRowsCount(int row, int column) const; + + virtual int getNestedColumnsCount(int row, int column) const; + + NestableColumn *getNestableColumn(int column); + + virtual void setNestedData(int row, int column, const QVariant& data, int subRow, int subColumn); + + virtual void removeNestedRows(int row, int column, int subRow); + + virtual void addNestedRow(int row, int col, int position); + void save (int index, ESM::ESMWriter& writer) const; - const RefIdData& getDataSet() const; //I can't figure out a better name for this one :( + const RefIdData& getDataSet() const; //I can't figure out a better name for this one :( }; } #endif - diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 1b600364c..85d16a6eb 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -130,7 +130,7 @@ namespace CSMWorld template void RefIdDataContainer::erase (int index, int count) { - if (index<0 || index+count>=getSize()) + if (index<0 || index+count>getSize()) throw std::runtime_error ("invalid RefIdDataContainer index"); mContainer.erase (mContainer.begin()+index, mContainer.begin()+index+count); @@ -231,27 +231,27 @@ namespace CSMWorld void save (int index, ESM::ESMWriter& writer) const; - //RECORD CONTAINERS ACCESS METHODS - const RefIdDataContainer& getBooks() const; - const RefIdDataContainer& getActivators() const; - const RefIdDataContainer& getPotions() const; - const RefIdDataContainer& getApparati() const; - const RefIdDataContainer& getArmors() const; - const RefIdDataContainer& getClothing() const; - const RefIdDataContainer& getContainers() const; - const RefIdDataContainer& getCreatures() const; - const RefIdDataContainer& getDoors() const; - const RefIdDataContainer& getIngredients() const; - const RefIdDataContainer& getCreatureLevelledLists() const; - const RefIdDataContainer& getItemLevelledList() const; - const RefIdDataContainer& getLights() const; - const RefIdDataContainer& getLocpicks() const; - const RefIdDataContainer& getMiscellaneous() const; - const RefIdDataContainer& getNPCs() const; - const RefIdDataContainer& getWeapons() const; - const RefIdDataContainer& getProbes() const; - const RefIdDataContainer& getRepairs() const; - const RefIdDataContainer& getStatics() const; + //RECORD CONTAINERS ACCESS METHODS + const RefIdDataContainer& getBooks() const; + const RefIdDataContainer& getActivators() const; + const RefIdDataContainer& getPotions() const; + const RefIdDataContainer& getApparati() const; + const RefIdDataContainer& getArmors() const; + const RefIdDataContainer& getClothing() const; + const RefIdDataContainer& getContainers() const; + const RefIdDataContainer& getCreatures() const; + const RefIdDataContainer& getDoors() const; + const RefIdDataContainer& getIngredients() const; + const RefIdDataContainer& getCreatureLevelledLists() const; + const RefIdDataContainer& getItemLevelledList() const; + const RefIdDataContainer& getLights() const; + const RefIdDataContainer& getLocpicks() const; + const RefIdDataContainer& getMiscellaneous() const; + const RefIdDataContainer& getNPCs() const; + const RefIdDataContainer& getWeapons() const; + const RefIdDataContainer& getProbes() const; + const RefIdDataContainer& getRepairs() const; + const RefIdDataContainer& getStatics() const; }; } diff --git a/apps/opencs/model/world/subcellcollection.hpp b/apps/opencs/model/world/subcellcollection.hpp index 74bb6c955..df1f8a12e 100644 --- a/apps/opencs/model/world/subcellcollection.hpp +++ b/apps/opencs/model/world/subcellcollection.hpp @@ -1,6 +1,8 @@ #ifndef CSM_WOLRD_SUBCOLLECTION_H #define CSM_WOLRD_SUBCOLLECTION_H +#include "nestedidcollection.hpp" + namespace ESM { class ESMReader; @@ -14,7 +16,7 @@ namespace CSMWorld /// \brief Single type collection of top level records that are associated with cells template > - class SubCellCollection : public IdCollection + class SubCellCollection : public NestedIdCollection { const IdCollection& mCells; @@ -23,8 +25,6 @@ namespace CSMWorld public: SubCellCollection (const IdCollection& cells); - - }; template @@ -39,7 +39,6 @@ namespace CSMWorld const IdCollection& cells) : mCells (cells) {} - } #endif diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index fbc942f8e..e496fe79b 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -38,9 +38,9 @@ namespace { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Enchantments, "Enchantments", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_BodyParts, "Body Parts", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, - "Referenceables", 0 }, + "Objects", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, - "References", 0 }, + "Instances", 0 }, { CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, "Region Map", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", 0 }, @@ -79,7 +79,7 @@ namespace { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_JournalInfo, "JournalInfo", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell_Missing, "Cell", ":./cell.png" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Object", 0 }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":./potion.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Apparatus, "Apparatus", ":./apparatus.png" }, @@ -103,7 +103,7 @@ namespace { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Repair, "Repair", ":./repair.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, "Static", ":./static.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":./weapon.png" }, - { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Reference", 0 }, + { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Instance", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Filter, "Filter", ":./filter.png" }, { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", 0 }, { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", 0 }, diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 9b601243c..5636fff94 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -94,7 +94,7 @@ void CSVDoc::View::setupEditMenu() QAction *search = new QAction (tr ("Search"), this); connect (search, SIGNAL (triggered()), this, SLOT (addSearchSubView())); - edit->addAction (search); + edit->addAction (search); } void CSVDoc::View::setupViewMenu() @@ -131,11 +131,11 @@ void CSVDoc::View::setupWorldMenu() connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView())); world->addAction (cells); - QAction *referenceables = new QAction (tr ("Referenceables"), this); + QAction *referenceables = new QAction (tr ("Objects"), this); connect (referenceables, SIGNAL (triggered()), this, SLOT (addReferenceablesSubView())); world->addAction (referenceables); - QAction *references = new QAction (tr ("References"), this); + QAction *references = new QAction (tr ("Instances"), this); connect (references, SIGNAL (triggered()), this, SLOT (addReferencesSubView())); world->addAction (references); diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 9fee26078..5908c67a1 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -84,7 +84,13 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) { CSMWorld::ColumnBase::Display_MeshType, CSMWorld::Columns::ColumnId_MeshType, false }, { CSMWorld::ColumnBase::Display_Gender, CSMWorld::Columns::ColumnId_Gender, true }, { CSMWorld::ColumnBase::Display_SoundGeneratorType, CSMWorld::Columns::ColumnId_SoundGeneratorType, false }, - { CSMWorld::ColumnBase::Display_School, CSMWorld::Columns::ColumnId_School, true } + { CSMWorld::ColumnBase::Display_School, CSMWorld::Columns::ColumnId_School, true }, + { CSMWorld::ColumnBase::Display_SkillImpact, CSMWorld::Columns::ColumnId_SkillImpact, true }, + { CSMWorld::ColumnBase::Display_EffectRange, CSMWorld::Columns::ColumnId_EffectRange, false }, + { CSMWorld::ColumnBase::Display_EffectId, CSMWorld::Columns::ColumnId_EffectId, false }, + { CSMWorld::ColumnBase::Display_PartRefType, CSMWorld::Columns::ColumnId_PartRefType, false }, + { CSMWorld::ColumnBase::Display_AiPackageType, CSMWorld::Columns::ColumnId_AiPackageType, false }, + { CSMWorld::ColumnBase::Display_YesNo, CSMWorld::Columns::ColumnId_AiWanderRepeat, false } }; for (std::size_t i=0; iaddButton (Element_Reference, "References"); + tool->addButton (Element_Reference, "Instances"); tool->addButton (Element_Water, "Water"); tool->addButton (Element_Pathgrid, "Pathgrid"); } @@ -258,7 +258,7 @@ void CSVRender::WorldspaceWidget::addEditModeSelectorButtons (CSVWidget::SceneTo { /// \todo replace EditMode with suitable subclasses tool->addButton ( - new EditMode (this, QIcon (":placeholder"), Element_Reference, "Reference editing"), + new EditMode (this, QIcon (":placeholder"), Element_Reference, "Instance editing"), "object"); tool->addButton ( new EditMode (this, QIcon (":placeholder"), Element_Pathgrid, "Pathgrid editing"), diff --git a/apps/opencs/view/settings/rangeview.cpp b/apps/opencs/view/settings/rangeview.cpp index 246f7ece2..5893c5d0d 100644 --- a/apps/opencs/view/settings/rangeview.cpp +++ b/apps/opencs/view/settings/rangeview.cpp @@ -12,7 +12,7 @@ CSVSettings::RangeView::RangeView (CSMSettings::Setting *setting, Page *parent) - : mRangeWidget (0), mRangeType (setting->type()), View (setting, parent) + : View (setting, parent), mRangeWidget (0), mRangeType (setting->type()) { mRangeWidget = 0; diff --git a/apps/opencs/view/settings/textview.cpp b/apps/opencs/view/settings/textview.cpp index 6886732db..a6ab657fe 100644 --- a/apps/opencs/view/settings/textview.cpp +++ b/apps/opencs/view/settings/textview.cpp @@ -5,7 +5,7 @@ #include "../../model/settings/setting.hpp" CSVSettings::TextView::TextView(CSMSettings::Setting *setting, Page *parent) - : mDelimiter (setting->delimiter()), View (setting, parent) + : View (setting, parent), mDelimiter (setting->delimiter()) { if (setting->isMultiLine()) diff --git a/apps/opencs/view/tools/reporttable.cpp b/apps/opencs/view/tools/reporttable.cpp index 1b07d3c0f..7cfe8e4f0 100644 --- a/apps/opencs/view/tools/reporttable.cpp +++ b/apps/opencs/view/tools/reporttable.cpp @@ -131,8 +131,8 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document, setModel (mModel); setColumnHidden (2, true); - mIdTypeDelegate = CSVWorld::IdTypeDelegateFactory().makeDelegate ( - document, this); + mIdTypeDelegate = CSVWorld::IdTypeDelegateFactory().makeDelegate (0, + mDocument, this); setItemDelegateForColumn (0, mIdTypeDelegate); diff --git a/apps/opencs/view/tools/searchsubview.cpp b/apps/opencs/view/tools/searchsubview.cpp index 5743ad761..32c26ee96 100644 --- a/apps/opencs/view/tools/searchsubview.cpp +++ b/apps/opencs/view/tools/searchsubview.cpp @@ -7,6 +7,7 @@ #include "../../model/tools/search.hpp" #include "../../model/tools/reportmodel.hpp" #include "../../model/world/idtablebase.hpp" +#include "../../model/settings/usersettings.hpp" #include "reporttable.hpp" #include "searchbox.hpp" @@ -22,7 +23,13 @@ void CSVTools::SearchSubView::replace (bool selection) const CSMTools::ReportModel& model = dynamic_cast (*mTable->model()); - + + bool autoDelete = CSMSettings::UserSettings::instance().setting ( + "search/auto-delete", QString ("true"))=="true"; + + CSMTools::Search search (mSearch); + CSMWorld::IdTableBase *currentTable = 0; + // We are running through the indices in reverse order to avoid messing up multiple results // in a single string. for (std::vector::const_reverse_iterator iter (indices.rbegin()); iter!=indices.rend(); ++iter) @@ -33,17 +40,28 @@ void CSVTools::SearchSubView::replace (bool selection) CSMWorld::IdTableBase *table = &dynamic_cast ( *mDocument.getData().getTableModel (type)); - + + if (table!=currentTable) + { + search.configure (table); + currentTable = table; + } + std::string hint = model.getHint (*iter); - - mSearch.replace (mDocument, table, id, hint, replace); - mTable->flagAsReplaced (*iter); + + if (search.verify (mDocument, table, id, hint)) + { + search.replace (mDocument, table, id, hint, replace); + mTable->flagAsReplaced (*iter); + + if (autoDelete) + mTable->model()->removeRows (*iter, 1); + } } } CSVTools::SearchSubView::SearchSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) -: CSVDoc::SubView (id), mDocument (document), mPaddingBefore (10), mPaddingAfter (10), - mLocked (false) +: CSVDoc::SubView (id), mDocument (document), mLocked (false) { QVBoxLayout *layout = new QVBoxLayout; @@ -84,14 +102,6 @@ void CSVTools::SearchSubView::setEditLock (bool locked) void CSVTools::SearchSubView::updateUserSetting (const QString &name, const QStringList &list) { mTable->updateUserSetting (name, list); - - if (!list.empty()) - { - if (name=="search/char-before") - mPaddingBefore = list.at (0).toInt(); - else if (name=="search/char-after") - mPaddingAfter = list.at (0).toInt(); - } } void CSVTools::SearchSubView::stateChanged (int state, CSMDoc::Document *document) @@ -101,8 +111,13 @@ void CSVTools::SearchSubView::stateChanged (int state, CSMDoc::Document *documen void CSVTools::SearchSubView::startSearch (const CSMTools::Search& search) { + CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + + int paddingBefore = userSettings.setting ("search/char-before", QString ("5")).toInt(); + int paddingAfter = userSettings.setting ("search/char-after", QString ("5")).toInt(); + mSearch = search; - mSearch.setPadding (mPaddingBefore, mPaddingAfter); + mSearch.setPadding (paddingBefore, paddingAfter); mTable->clear(); mDocument.runSearch (getUniversalId(), mSearch); diff --git a/apps/opencs/view/tools/searchsubview.hpp b/apps/opencs/view/tools/searchsubview.hpp index eeefa9afb..6dedd6ef2 100644 --- a/apps/opencs/view/tools/searchsubview.hpp +++ b/apps/opencs/view/tools/searchsubview.hpp @@ -26,8 +26,6 @@ namespace CSVTools ReportTable *mTable; SearchBox mSearchBox; CSMDoc::Document& mDocument; - int mPaddingBefore; - int mPaddingAfter; CSMTools::Search mSearch; bool mLocked; diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp index 7c0422c88..506bdab2c 100644 --- a/apps/opencs/view/world/creator.hpp +++ b/apps/opencs/view/world/creator.hpp @@ -40,6 +40,9 @@ namespace CSVWorld /// Default implementation: Throw an exception if scope!=Scope_Content. virtual void setScope (unsigned int scope); + /// Focus main input widget + virtual void focus() = 0; + signals: void done(); diff --git a/apps/opencs/view/world/datadisplaydelegate.cpp b/apps/opencs/view/world/datadisplaydelegate.cpp index 46ca17a29..b9df52bf7 100644 --- a/apps/opencs/view/world/datadisplaydelegate.cpp +++ b/apps/opencs/view/world/datadisplaydelegate.cpp @@ -6,11 +6,12 @@ CSVWorld::DataDisplayDelegate::DataDisplayDelegate(const ValueList &values, const IconList &icons, + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, const QString &pageName, const QString &settingName, QObject *parent) - : EnumDelegate (values, document, parent), mDisplayMode (Mode_TextOnly), + : EnumDelegate (values, dispatcher, document, parent), mDisplayMode (Mode_TextOnly), mIcons (icons), mIconSize (QSize(16, 16)), mIconLeftOffset(3), mTextLeftOffset(8), mSettingKey (pageName + '/' + settingName) { @@ -136,9 +137,9 @@ void CSVWorld::DataDisplayDelegateFactory::add (int enumValue, QString enumName, } CSVWorld::CommandDelegate *CSVWorld::DataDisplayDelegateFactory::makeDelegate ( - CSMDoc::Document& document, QObject *parent) const + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const { - return new DataDisplayDelegate (mValues, mIcons, document, "", "", parent); + return new DataDisplayDelegate (mValues, mIcons, dispatcher, document, "", "", parent); } diff --git a/apps/opencs/view/world/datadisplaydelegate.hpp b/apps/opencs/view/world/datadisplaydelegate.hpp index 73790e3c6..f6e4c2688 100755 --- a/apps/opencs/view/world/datadisplaydelegate.hpp +++ b/apps/opencs/view/world/datadisplaydelegate.hpp @@ -38,12 +38,9 @@ namespace CSVWorld QString mSettingKey; public: - explicit DataDisplayDelegate (const ValueList & values, - const IconList & icons, - CSMDoc::Document& document, - const QString &pageName, - const QString &settingName, - QObject *parent); + DataDisplayDelegate (const ValueList & values, const IconList & icons, + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, + const QString &pageName, const QString &settingName, QObject *parent); ~DataDisplayDelegate(); @@ -82,7 +79,7 @@ namespace CSVWorld public: - virtual CommandDelegate *makeDelegate (CSMDoc::Document& document, QObject *parent) const; + virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. protected: diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index e383f5e9a..0d0e82dbf 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -17,21 +17,25 @@ #include #include #include -#include #include #include +#include +#include "../../model/world/nestedtableproxymodel.hpp" #include "../../model/world/columnbase.hpp" #include "../../model/world/idtable.hpp" +#include "../../model/world/idtree.hpp" #include "../../model/world/columns.hpp" #include "../../model/world/record.hpp" #include "../../model/world/tablemimedata.hpp" -#include "../../model/doc/document.hpp" +#include "../../model/world/idtree.hpp" #include "../../model/world/commands.hpp" +#include "../../model/doc/document.hpp" #include "recordstatusdelegate.hpp" #include "util.hpp" #include "tablebottombox.hpp" +#include "nestedtable.hpp" /* ==============================NotEditableSubDelegate========================================== */ @@ -136,7 +140,7 @@ void CSVWorld::DialogueDelegateDispatcherProxy::tableMimeDataDropped(const std:: CSMWorld::UniversalId::Type type = data[i].getType(); if (mDisplay == CSMWorld::ColumnBase::Display_Referenceable) { - if ( type == CSMWorld::UniversalId::Type_Activator + if (type == CSMWorld::UniversalId::Type_Activator || type == CSMWorld::UniversalId::Type_Potion || type == CSMWorld::UniversalId::Type_Apparatus || type == CSMWorld::UniversalId::Type_Armor @@ -172,10 +176,12 @@ void CSVWorld::DialogueDelegateDispatcherProxy::tableMimeDataDropped(const std:: ==============================DialogueDelegateDispatcher========================================== */ -CSVWorld::DialogueDelegateDispatcher::DialogueDelegateDispatcher(QObject* parent, CSMWorld::IdTable* table, CSMDoc::Document& document) : +CSVWorld::DialogueDelegateDispatcher::DialogueDelegateDispatcher(QObject* parent, + CSMWorld::IdTable* table, CSMWorld::CommandDispatcher& commandDispatcher, + CSMDoc::Document& document, QAbstractItemModel *model) : mParent(parent), -mTable(table), -mDocument (document), +mTable(model ? model : table), +mCommandDispatcher (commandDispatcher), mDocument (document), mNotEditableDelegate(table, parent) { } @@ -187,7 +193,7 @@ CSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CS if (delegateIt == mDelegates.end()) { delegate = CommandDelegateFactoryCollection::get().makeDelegate ( - display, mDocument, mParent); + display, &mCommandDispatcher, mDocument, mParent); mDelegates.insert(std::make_pair(display, delegate)); } else { @@ -196,7 +202,8 @@ CSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CS return delegate; } -void CSVWorld::DialogueDelegateDispatcher::editorDataCommited(QWidget* editor, const QModelIndex& index, CSMWorld::ColumnBase::Display display) +void CSVWorld::DialogueDelegateDispatcher::editorDataCommited(QWidget* editor, + const QModelIndex& index, CSMWorld::ColumnBase::Display display) { setModelData(editor, mTable, index, display); } @@ -228,12 +235,14 @@ void CSVWorld::DialogueDelegateDispatcher::setEditorData (QWidget* editor, const } } -void CSVWorld::DialogueDelegateDispatcher::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const +void CSVWorld::DialogueDelegateDispatcher::setModelData(QWidget* editor, + QAbstractItemModel* model, const QModelIndex& index) const { setModelData(editor, model, index, CSMWorld::ColumnBase::Display_None); } -void CSVWorld::DialogueDelegateDispatcher::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index, CSMWorld::ColumnBase::Display display) const +void CSVWorld::DialogueDelegateDispatcher::setModelData(QWidget* editor, + QAbstractItemModel* model, const QModelIndex& index, CSMWorld::ColumnBase::Display display) const { std::map::const_iterator delegateIt(mDelegates.find(display)); if (delegateIt != mDelegates.end()) @@ -242,17 +251,20 @@ void CSVWorld::DialogueDelegateDispatcher::setModelData(QWidget* editor, QAbstra } } -void CSVWorld::DialogueDelegateDispatcher::paint (QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +void CSVWorld::DialogueDelegateDispatcher::paint (QPainter* painter, + const QStyleOptionViewItem& option, const QModelIndex& index) const { //Does nothing } -QSize CSVWorld::DialogueDelegateDispatcher::sizeHint (const QStyleOptionViewItem& option, const QModelIndex& index) const +QSize CSVWorld::DialogueDelegateDispatcher::sizeHint (const QStyleOptionViewItem& option, + const QModelIndex& index) const { return QSize(); //silencing warning, otherwise does nothing } -QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase::Display display, const QModelIndex& index) +QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase::Display display, + const QModelIndex& index) { QVariant variant = index.data(); if (!variant.isValid()) @@ -267,13 +279,17 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase:: QWidget* editor = NULL; if (! (mTable->flags (index) & Qt::ItemIsEditable)) { - return mNotEditableDelegate.createEditor(qobject_cast(mParent), QStyleOptionViewItem(), index); + return mNotEditableDelegate.createEditor(qobject_cast(mParent), + QStyleOptionViewItem(), index); } std::map::iterator delegateIt(mDelegates.find(display)); + if (delegateIt != mDelegates.end()) { - editor = delegateIt->second->createEditor(qobject_cast(mParent), QStyleOptionViewItem(), index, display); + editor = delegateIt->second->createEditor(qobject_cast(mParent), + QStyleOptionViewItem(), index, display); + DialogueDelegateDispatcherProxy* proxy = new DialogueDelegateDispatcherProxy(editor, display); // NOTE: For each entry in CSVWorld::CommandDelegate::createEditor() a corresponding entry @@ -281,10 +297,13 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase:: if (qobject_cast(editor)) { connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited())); + connect(editor, SIGNAL(tableMimeDataDropped(const std::vector&, const CSMDoc::Document*)), proxy, SLOT(tableMimeDataDropped(const std::vector&, const CSMDoc::Document*))); + connect(proxy, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)), this, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*))); + } else if (qobject_cast(editor)) { @@ -305,7 +324,9 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase:: else // throw an exception because this is a coding error throw std::logic_error ("Dialogue editor type missing"); - connect(proxy, SIGNAL(editorDataCommited(QWidget*, const QModelIndex&, CSMWorld::ColumnBase::Display)), this, SLOT(editorDataCommited(QWidget*, const QModelIndex&, CSMWorld::ColumnBase::Display))); + connect(proxy, SIGNAL(editorDataCommited(QWidget*, const QModelIndex&, CSMWorld::ColumnBase::Display)), + this, SLOT(editorDataCommited(QWidget*, const QModelIndex&, CSMWorld::ColumnBase::Display))); + mProxys.push_back(proxy); //deleted in the destructor } return editor; @@ -323,20 +344,43 @@ CSVWorld::DialogueDelegateDispatcher::~DialogueDelegateDispatcher() =============================================================EditWidget===================================================== */ -CSVWorld::EditWidget::EditWidget(QWidget *parent, int row, CSMWorld::IdTable* table, CSMDoc::Document& document, bool createAndDelete) : -mDispatcher(this, table, document), +CSVWorld::EditWidget::~EditWidget() +{ + for (unsigned i = 0; i < mNestedModels.size(); ++i) + { + delete mNestedModels[i]; + } + delete mNestedTableDispatcher; +} + +CSVWorld::EditWidget::EditWidget(QWidget *parent, + int row, CSMWorld::IdTable* table, CSMWorld::CommandDispatcher& commandDispatcher, + CSMDoc::Document& document, bool createAndDelete) : +mDispatcher(this, table, commandDispatcher, document), +mNestedTableDispatcher(NULL), QScrollArea(parent), mWidgetMapper(NULL), +mNestedTableMapper(NULL), mMainWidget(NULL), +mCommandDispatcher (commandDispatcher), mDocument (document), mTable(table) { remake (row); - connect(&mDispatcher, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)), this, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*))); + + connect(&mDispatcher, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)), + this, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*))); } void CSVWorld::EditWidget::remake(int row) { + for (unsigned i = 0; i < mNestedModels.size(); ++i) + { + delete mNestedModels[i]; + } + mNestedModels.clear(); + delete mNestedTableDispatcher; + if (mMainWidget) { delete mMainWidget; @@ -350,7 +394,13 @@ void CSVWorld::EditWidget::remake(int row) delete mWidgetMapper; mWidgetMapper = 0; } + if (mNestedTableMapper) + { + delete mNestedTableMapper; + mNestedTableMapper = 0; + } mWidgetMapper = new QDataWidgetMapper (this); + mWidgetMapper->setModel(mTable); mWidgetMapper->setItemDelegate(&mDispatcher); @@ -360,17 +410,28 @@ void CSVWorld::EditWidget::remake(int row) line->setFrameShape(QFrame::HLine); line->setFrameShadow(QFrame::Sunken); + QFrame* line2 = new QFrame(mMainWidget); + line2->setObjectName(QString::fromUtf8("line")); + line2->setGeometry(QRect(320, 150, 118, 3)); + line2->setFrameShape(QFrame::HLine); + line2->setFrameShadow(QFrame::Sunken); + QVBoxLayout *mainLayout = new QVBoxLayout(mMainWidget); - QGridLayout *unlockedLayout = new QGridLayout(); QGridLayout *lockedLayout = new QGridLayout(); - mainLayout->addLayout(lockedLayout, 0); + QGridLayout *unlockedLayout = new QGridLayout(); + QVBoxLayout *tablesLayout = new QVBoxLayout(); + + mainLayout->addLayout(lockedLayout, QSizePolicy::Fixed); mainLayout->addWidget(line, 1); - mainLayout->addLayout(unlockedLayout, 2); + mainLayout->addLayout(unlockedLayout, QSizePolicy::Preferred); + mainLayout->addWidget(line2, 1); + mainLayout->addLayout(tablesLayout, QSizePolicy::Preferred); mainLayout->addStretch(1); int unlocked = 0; int locked = 0; const int columns = mTable->columnCount(); + for (int i=0; iheaderData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); @@ -380,32 +441,123 @@ void CSVWorld::EditWidget::remake(int row) CSMWorld::ColumnBase::Display display = static_cast (mTable->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); - mDispatcher.makeDelegate(display); - QWidget *editor = mDispatcher.makeEditor(display, (mTable->index (row, i))); + if (mTable->hasChildren(mTable->index(row, i)) && + !(flags & CSMWorld::ColumnBase::Flag_Dialogue_List)) + { + mNestedModels.push_back(new CSMWorld::NestedTableProxyModel ( + mTable->index(row, i), display, dynamic_cast(mTable))); + + int idColumn = mTable->findColumnIndex (CSMWorld::Columns::ColumnId_Id); + int typeColumn = mTable->findColumnIndex (CSMWorld::Columns::ColumnId_RecordType); + + CSMWorld::UniversalId id = CSMWorld::UniversalId( + static_cast (mTable->data (mTable->index (row, typeColumn)).toInt()), + mTable->data (mTable->index (row, idColumn)).toString().toUtf8().constData()); + + NestedTable* table = new NestedTable(mDocument, id, mNestedModels.back(), this); + // FIXME: does not work well when enum delegates are used + //table->resizeColumnsToContents(); + table->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::CurrentChanged); - if (editor) + int rows = mTable->rowCount(mTable->index(row, i)); + int rowHeight = (rows == 0) ? table->horizontalHeader()->height() : table->rowHeight(0); + int tableMaxHeight = (5 * rowHeight) + + table->horizontalHeader()->height() + 2 * table->frameWidth(); + table->setMinimumHeight(tableMaxHeight); + + QLabel* label = + new QLabel (mTable->headerData (i, Qt::Horizontal, Qt::DisplayRole).toString(), mMainWidget); + + label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed); + + tablesLayout->addWidget(label); + tablesLayout->addWidget(table); + } + else if (!(flags & CSMWorld::ColumnBase::Flag_Dialogue_List)) { - mWidgetMapper->addMapping (editor, i); - QLabel* label = new QLabel(mTable->headerData (i, Qt::Horizontal).toString(), mMainWidget); - label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - editor->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); - if (! (mTable->flags (mTable->index (row, i)) & Qt::ItemIsEditable)) + mDispatcher.makeDelegate (display); + QWidget* editor = mDispatcher.makeEditor (display, (mTable->index (row, i))); + + if (editor) { - lockedLayout->addWidget (label, locked, 0); - lockedLayout->addWidget (editor, locked, 1); - ++locked; - } else + mWidgetMapper->addMapping (editor, i); + + QLabel* label = new QLabel (mTable->headerData (i, Qt::Horizontal).toString(), mMainWidget); + + label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed); + editor->setSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); + + if (! (mTable->flags (mTable->index (row, i)) & Qt::ItemIsEditable)) + { + lockedLayout->addWidget (label, locked, 0); + lockedLayout->addWidget (editor, locked, 1); + ++locked; + } + else + { + unlockedLayout->addWidget (label, unlocked, 0); + unlockedLayout->addWidget (editor, unlocked, 1); + ++unlocked; + } + } + } + else + { + mNestedModels.push_back(new CSMWorld::NestedTableProxyModel ( + static_cast(mTable)->index(row, i), + display, static_cast(mTable))); + mNestedTableMapper = new QDataWidgetMapper (this); + + mNestedTableMapper->setModel(mNestedModels.back()); + // FIXME: lack MIME support? + mNestedTableDispatcher = + new DialogueDelegateDispatcher (this, mTable, mCommandDispatcher, mDocument, mNestedModels.back()); + mNestedTableMapper->setItemDelegate(mNestedTableDispatcher); + + int columnCount = + mTable->columnCount(mTable->getModelIndex (mNestedModels.back()->getParentId(), i)); + for (int col = 0; col < columnCount; ++col) { - unlockedLayout->addWidget (label, unlocked, 0); - unlockedLayout->addWidget (editor, unlocked, 1); - ++unlocked; + int displayRole = mNestedModels.back()->headerData (col, + Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt(); + + CSMWorld::ColumnBase::Display display = + static_cast (displayRole); + + mNestedTableDispatcher->makeDelegate (display); + + // FIXME: assumed all columns are editable + QWidget* editor = + mNestedTableDispatcher->makeEditor (display, mNestedModels.back()->index (0, col)); + if (editor) + { + mNestedTableMapper->addMapping (editor, col); + + std::string disString = mNestedModels.back()->headerData (col, + Qt::Horizontal, Qt::DisplayRole).toString().toStdString(); + // Need ot use Qt::DisplayRole in order to get the correct string + // from CSMWorld::Columns + QLabel* label = new QLabel (mNestedModels.back()->headerData (col, + Qt::Horizontal, Qt::DisplayRole).toString(), mMainWidget); + + label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed); + editor->setSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); + + unlockedLayout->addWidget (label, unlocked, 0); + unlockedLayout->addWidget (editor, unlocked, 1); + ++unlocked; + } } + mNestedTableMapper->setCurrentModelIndex(mNestedModels.back()->index(0, 0)); } } } mWidgetMapper->setCurrentModelIndex(mTable->index(row, 0)); + if (unlocked == 0) + mainLayout->removeWidget(line); + this->setWidget(mMainWidget); this->setWidgetResizable(true); } @@ -421,13 +573,14 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM mMainLayout(NULL), mUndoStack(document.getUndoStack()), mTable(dynamic_cast(document.getData().getTableModel(id))), - mRow (-1), mLocked(false), mDocument(document), mCommandDispatcher (document, CSMWorld::UniversalId::getParentType (id.getType())) { connect(mTable, SIGNAL(dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT(dataChanged(const QModelIndex&))); - mRow = mTable->getModelIndex (id.getId(), 0).row(); + + changeCurrentId(id.getId()); + QWidget *mainWidget = new QWidget(this); QHBoxLayout *buttonsLayout = new QHBoxLayout; @@ -480,12 +633,13 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM connect(nextButton, SIGNAL(clicked()), this, SLOT(nextId())); connect(prevButton, SIGNAL(clicked()), this, SLOT(prevId())); connect(cloneButton, SIGNAL(clicked()), this, SLOT(cloneRequest())); - connect(revertButton, SIGNAL(clicked()), this, SLOT(revertRecord())); - connect(deleteButton, SIGNAL(clicked()), this, SLOT(deleteRecord())); + connect(revertButton, SIGNAL(clicked()), &mCommandDispatcher, SLOT(executeRevert())); + connect(deleteButton, SIGNAL(clicked()), &mCommandDispatcher, SLOT(executeDelete())); mMainLayout = new QVBoxLayout(mainWidget); - mEditWidget = new EditWidget(mainWidget, mRow, mTable, document, false); + mEditWidget = new EditWidget(mainWidget, + mTable->getModelIndex(mCurrentId, 0).row(), mTable, mCommandDispatcher, document, false); connect(mEditWidget, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)), this, SLOT(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*))); @@ -496,24 +650,27 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this)); mBottom->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + connect(mBottom, SIGNAL(requestFocus(const std::string&)), this, SLOT(requestFocus(const std::string&))); + connect(addButton, SIGNAL(clicked()), mBottom, SLOT(createRequest())); if(!mBottom->canCreateAndDelete()) { - cloneButton->setDisabled(true); - addButton->setDisabled(true); - deleteButton->setDisabled(true); + cloneButton->setDisabled (true); + addButton->setDisabled (true); + deleteButton->setDisabled (true); } - dataChanged(mTable->index(mRow, 0)); - mMainLayout->addLayout(buttonsLayout); - setWidget(mainWidget); + dataChanged(mTable->getModelIndex (mCurrentId, 0)); + mMainLayout->addLayout (buttonsLayout); + setWidget (mainWidget); } -void CSVWorld::DialogueSubView::prevId() +void CSVWorld::DialogueSubView::prevId () { - int newRow = mRow - 1; + int newRow = mTable->getModelIndex(mCurrentId, 0).row() - 1; + if (newRow < 0) { return; @@ -531,19 +688,23 @@ void CSVWorld::DialogueSubView::prevId() if (!(state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased)) { mEditWidget->remake(newRow); + setUniversalId(CSMWorld::UniversalId (static_cast (mTable->data (mTable->index (newRow, 2)).toInt()), mTable->data (mTable->index (newRow, 0)).toString().toUtf8().constData())); - mRow = newRow; + + changeCurrentId(std::string(mTable->data (mTable->index (newRow, 0)).toString().toUtf8().constData())); + mEditWidget->setDisabled(mLocked); + return; } --newRow; } } -void CSVWorld::DialogueSubView::nextId() +void CSVWorld::DialogueSubView::nextId () { - int newRow = mRow + 1; + int newRow = mTable->getModelIndex(mCurrentId, 0).row() + 1; if (newRow >= mTable->rowCount()) { @@ -560,13 +721,17 @@ void CSVWorld::DialogueSubView::nextId() } CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (newRow, 1)).toInt()); - if (!(state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased)) + if (!(state == CSMWorld::RecordBase::State_Deleted)) { mEditWidget->remake(newRow); + setUniversalId(CSMWorld::UniversalId (static_cast (mTable->data (mTable->index (newRow, 2)).toInt()), - mTable->data (mTable->index (newRow, 0)).toString().toUtf8().constData())); - mRow = newRow; + mTable->data (mTable->index (newRow, 0)).toString().toUtf8().constData())); + + changeCurrentId(std::string(mTable->data (mTable->index (newRow, 0)).toString().toUtf8().constData())); + mEditWidget->setDisabled(mLocked); + return; } ++newRow; @@ -576,28 +741,35 @@ void CSVWorld::DialogueSubView::nextId() void CSVWorld::DialogueSubView::setEditLock (bool locked) { mLocked = locked; + QModelIndex currentIndex(mTable->getModelIndex(mCurrentId, 0)); - CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (mRow, 1)).toInt()); + if (currentIndex.isValid()) + { + CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (currentIndex.row(), 1)).toInt()); + + mEditWidget->setDisabled (state==CSMWorld::RecordBase::State_Deleted || locked); - mEditWidget->setDisabled (state==CSMWorld::RecordBase::State_Deleted || locked); + mCommandDispatcher.setEditLock (locked); + } - mCommandDispatcher.setEditLock (locked); } -void CSVWorld::DialogueSubView::dataChanged(const QModelIndex & index) +void CSVWorld::DialogueSubView::dataChanged (const QModelIndex & index) { - if (index.row() == mRow) + QModelIndex currentIndex(mTable->getModelIndex(mCurrentId, 0)); + + if (currentIndex.isValid() && index.row() == currentIndex.row()) { - CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (mRow, 1)).toInt()); + CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (currentIndex.row(), 1)).toInt()); mEditWidget->setDisabled (state==CSMWorld::RecordBase::State_Deleted || mLocked); } } -void CSVWorld::DialogueSubView::tableMimeDataDropped(QWidget* editor, - const QModelIndex& index, - const CSMWorld::UniversalId& id, - const CSMDoc::Document* document) +void CSVWorld::DialogueSubView::tableMimeDataDropped (QWidget* editor, + const QModelIndex& index, + const CSMWorld::UniversalId& id, + const CSMDoc::Document* document) { if (document == &mDocument) { @@ -605,95 +777,49 @@ void CSVWorld::DialogueSubView::tableMimeDataDropped(QWidget* editor, } } -void CSVWorld::DialogueSubView::revertRecord() -{ - int rows = mTable->rowCount(); - if (!mLocked && mTable->columnCount() > 0 && mRow < mTable->rowCount() ) - { - CSMWorld::RecordBase::State state = - static_cast (mTable->data (mTable->index (mRow, 1)).toInt()); - - if (state!=CSMWorld::RecordBase::State_BaseOnly) - { - mUndoStack.push(new CSMWorld::RevertCommand(*mTable, mTable->data(mTable->index (mRow, 0)).toString().toUtf8().constData())); - } - if (rows != mTable->rowCount()) - { - if (mTable->rowCount() == 0) - { - mEditWidget->setDisabled(true); //closing the editor is other option - return; - } - if (mRow >= mTable->rowCount()) - { - prevId(); - } else { - dataChanged(mTable->index(mRow, 0)); - } - } - } -} - -void CSVWorld::DialogueSubView::deleteRecord() -{ - int rows = mTable->rowCount(); - - //easier than disabling the button - CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (mRow, 1)).toInt()); - bool deledetedOrErased = (state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased); - - if (!mLocked && - mTable->columnCount() > 0 && - !deledetedOrErased && - mRow < rows && - mBottom->canCreateAndDelete()) - { - mUndoStack.push(new CSMWorld::DeleteCommand(*mTable, mTable->data(mTable->index (mRow, 0)).toString().toUtf8().constData())); - if (rows != mTable->rowCount()) - { - if (mTable->rowCount() == 0) - { - mEditWidget->setDisabled(true); //closing the editor is other option - return; - } - if (mRow >= mTable->rowCount()) - { - prevId(); - } else { - dataChanged(mTable->index(mRow, 0)); - } - } - } -} - void CSVWorld::DialogueSubView::requestFocus (const std::string& id) { - mRow = mTable->getModelIndex (id, 0).row(); - mEditWidget->remake(mRow); + changeCurrentId(id); + + mEditWidget->remake(mTable->getModelIndex (id, 0).row()); } void CSVWorld::DialogueSubView::cloneRequest () { - mBottom->cloneRequest(mTable->data(mTable->index (mRow, 0)).toString().toUtf8().constData(), - static_cast(mTable->data(mTable->index(mRow, 2)).toInt())); + mBottom->cloneRequest(mCurrentId, static_cast(mTable->data(mTable->getModelIndex(mCurrentId, 2)).toInt())); } void CSVWorld::DialogueSubView::showPreview () { - if ((mTable->getFeatures() & CSMWorld::IdTable::Feature_Preview) && - mRow < mTable->rowCount()) + QModelIndex currentIndex(mTable->getModelIndex(mCurrentId, 0)); + + if (currentIndex.isValid() && + mTable->getFeatures() & CSMWorld::IdTable::Feature_Preview && + currentIndex.row() < mTable->rowCount()) { - emit focusId(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Preview, mTable->data(mTable->index (mRow, 0)).toString().toUtf8().constData()), ""); + emit focusId(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Preview, mCurrentId), ""); } } -void CSVWorld::DialogueSubView::viewRecord() +void CSVWorld::DialogueSubView::viewRecord () { - if (mRow < mTable->rowCount()) + QModelIndex currentIndex(mTable->getModelIndex (mCurrentId, 0)); + + if (currentIndex.isValid() && + currentIndex.row() < mTable->rowCount()) { - std::pair params = mTable->view (mRow); + std::pair params = mTable->view (currentIndex.row()); if (params.first.getType()!=CSMWorld::UniversalId::Type_None) emit focusId (params.first, params.second); } } + +void CSVWorld::DialogueSubView::changeCurrentId (const std::string& newId) +{ + std::vector selection; + mCurrentId = std::string(newId); + + selection.push_back(mCurrentId); + mCommandDispatcher.setSelection(selection); +} diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index 5bd226960..b5a44d266 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -21,6 +21,7 @@ class QVBoxLayout; namespace CSMWorld { class IdTable; + class NestedTableProxyModel; } namespace CSMDoc @@ -38,16 +39,20 @@ namespace CSVWorld { const CSMWorld::IdTable* mTable; public: - NotEditableSubDelegate(const CSMWorld::IdTable* table, QObject * parent = 0); + NotEditableSubDelegate(const CSMWorld::IdTable* table, + QObject * parent = 0); virtual void setEditorData (QWidget* editor, const QModelIndex& index) const; virtual void setModelData (QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; - virtual void paint (QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + virtual void paint (QPainter* painter, + const QStyleOptionViewItem& option, + const QModelIndex& index) const; ///< does nothing - virtual QSize sizeHint (const QStyleOptionViewItem& option, const QModelIndex& index) const; + virtual QSize sizeHint (const QStyleOptionViewItem& option, + const QModelIndex& index) const; ///< does nothing virtual QWidget *createEditor (QWidget *parent, @@ -74,16 +79,20 @@ namespace CSVWorld std::auto_ptr mIndexWrapper; public: - DialogueDelegateDispatcherProxy(QWidget* editor, CSMWorld::ColumnBase::Display display); + DialogueDelegateDispatcherProxy(QWidget* editor, + CSMWorld::ColumnBase::Display display); QWidget* getEditor() const; public slots: void editorDataCommited(); void setIndex(const QModelIndex& index); - void tableMimeDataDropped(const std::vector& data, const CSMDoc::Document* document); + void tableMimeDataDropped(const std::vector& data, + const CSMDoc::Document* document); signals: - void editorDataCommited(QWidget* editor, const QModelIndex& index, CSMWorld::ColumnBase::Display display); + void editorDataCommited(QWidget* editor, + const QModelIndex& index, + CSMWorld::ColumnBase::Display display); void tableMimeDataDropped(QWidget* editor, const QModelIndex& index, const CSMWorld::UniversalId& id, @@ -98,59 +107,79 @@ namespace CSVWorld QObject* mParent; - CSMWorld::IdTable* mTable; + QAbstractItemModel* mTable; - CSMDoc::Document& mDocument; + CSMWorld::CommandDispatcher& mCommandDispatcher; + CSMDoc::Document& mDocument; NotEditableSubDelegate mNotEditableDelegate; - std::vector mProxys; //once we move to the C++11 we should use unique_ptr + std::vector mProxys; + //once we move to the C++11 we should use unique_ptr public: - DialogueDelegateDispatcher(QObject* parent, CSMWorld::IdTable* table, CSMDoc::Document& document); + DialogueDelegateDispatcher(QObject* parent, + CSMWorld::IdTable* table, + CSMWorld::CommandDispatcher& commandDispatcher, + CSMDoc::Document& document, + QAbstractItemModel* model = 0); ~DialogueDelegateDispatcher(); CSVWorld::CommandDelegate* makeDelegate(CSMWorld::ColumnBase::Display display); QWidget* makeEditor(CSMWorld::ColumnBase::Display display, const QModelIndex& index); - ///< will return null if delegate is not present, parent of the widget is same as for dispatcher itself + ///< will return null if delegate is not present, parent of the widget is + //same as for dispatcher itself virtual void setEditorData (QWidget* editor, const QModelIndex& index) const; - virtual void setModelData (QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; + virtual void setModelData (QWidget* editor, QAbstractItemModel* model, + const QModelIndex& index) const; - virtual void setModelData (QWidget* editor, QAbstractItemModel* model, const QModelIndex& index, CSMWorld::ColumnBase::Display display) const; + virtual void setModelData (QWidget* editor, + QAbstractItemModel* model, const QModelIndex& index, + CSMWorld::ColumnBase::Display display) const; - virtual void paint (QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + virtual void paint (QPainter* painter, + const QStyleOptionViewItem& option, + const QModelIndex& index) const; ///< does nothing - virtual QSize sizeHint (const QStyleOptionViewItem& option, const QModelIndex& index) const; + virtual QSize sizeHint (const QStyleOptionViewItem& option, + const QModelIndex& index) const; ///< does nothing private slots: - void editorDataCommited(QWidget* editor, const QModelIndex& index, CSMWorld::ColumnBase::Display display); + void editorDataCommited(QWidget* editor, const QModelIndex& index, + CSMWorld::ColumnBase::Display display); signals: void tableMimeDataDropped(QWidget* editor, const QModelIndex& index, const CSMWorld::UniversalId& id, const CSMDoc::Document* document); - - }; class EditWidget : public QScrollArea { Q_OBJECT QDataWidgetMapper *mWidgetMapper; + QDataWidgetMapper *mNestedTableMapper; DialogueDelegateDispatcher mDispatcher; + DialogueDelegateDispatcher *mNestedTableDispatcher; QWidget* mMainWidget; CSMWorld::IdTable* mTable; + CSMWorld::CommandDispatcher& mCommandDispatcher; CSMDoc::Document& mDocument; + std::vector mNestedModels; //Plain, raw C pointers, deleted in the dtor public: - EditWidget (QWidget *parent, int row, CSMWorld::IdTable* table, CSMDoc::Document& document, bool createAndDelete = false); + EditWidget (QWidget *parent, int row, CSMWorld::IdTable* table, + CSMWorld::CommandDispatcher& commandDispatcher, + CSMDoc::Document& document, bool createAndDelete = false); + + virtual ~EditWidget(); void remake(int row); @@ -168,7 +197,7 @@ namespace CSVWorld QVBoxLayout* mMainLayout; CSMWorld::IdTable* mTable; QUndoStack& mUndoStack; - int mRow; + std::string mCurrentId; bool mLocked; const CSMDoc::Document& mDocument; TableBottomBox* mBottom; @@ -183,6 +212,9 @@ namespace CSVWorld virtual void setEditLock (bool locked); + private: + void changeCurrentId(const std::string& newCurrent); + private slots: void nextId(); @@ -193,10 +225,6 @@ namespace CSVWorld void viewRecord(); - void revertRecord(); - - void deleteRecord(); - void cloneRequest(); void dataChanged(const QModelIndex & index); diff --git a/apps/opencs/view/world/enumdelegate.cpp b/apps/opencs/view/world/enumdelegate.cpp index 7c305b1b6..4b76bf9d6 100644 --- a/apps/opencs/view/world/enumdelegate.cpp +++ b/apps/opencs/view/world/enumdelegate.cpp @@ -21,7 +21,9 @@ void CSVWorld::EnumDelegate::setModelDataImp (QWidget *editor, QAbstractItemMode iter!=mValues.end(); ++iter) if (iter->second==value) { - addCommands (model, index, iter->first); + // do nothing if the value has not changed + if (model->data(index).toInt() != iter->first) + addCommands (model, index, iter->first); break; } } @@ -35,8 +37,8 @@ void CSVWorld::EnumDelegate::addCommands (QAbstractItemModel *model, CSVWorld::EnumDelegate::EnumDelegate (const std::vector >& values, - CSMDoc::Document& document, QObject *parent) -: CommandDelegate (document, parent), mValues (values) + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) +: CommandDelegate (dispatcher, document, parent), mValues (values) { } @@ -140,9 +142,9 @@ CSVWorld::EnumDelegateFactory::EnumDelegateFactory (const std::vector >& values, - CSMDoc::Document& document, QObject *parent); + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent); virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem& option, @@ -64,7 +64,7 @@ namespace CSVWorld EnumDelegateFactory (const std::vector& names, bool allowNone = false); /// \param allowNone Use value of -1 for "none selected" (empty string) - virtual CommandDelegate *makeDelegate (CSMDoc::Document& document, QObject *parent) const; + virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. void add (int value, const QString& name); diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index b4cf46040..4269679bf 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -225,6 +225,11 @@ void CSVWorld::GenericCreator::toggleWidgets(bool active) { } +void CSVWorld::GenericCreator::focus() +{ + mId->setFocus(); +} + void CSVWorld::GenericCreator::setScope (unsigned int scope) { mScopes = scope; diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 678005082..1f854c69e 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -101,6 +101,9 @@ namespace CSVWorld virtual void setScope (unsigned int scope); + /// Focus main input widget + virtual void focus(); + private slots: void textChanged (const QString& text); diff --git a/apps/opencs/view/world/idtypedelegate.cpp b/apps/opencs/view/world/idtypedelegate.cpp index 3b440ff71..34c8d12cd 100755 --- a/apps/opencs/view/world/idtypedelegate.cpp +++ b/apps/opencs/view/world/idtypedelegate.cpp @@ -3,8 +3,8 @@ #include "../../model/world/universalid.hpp" CSVWorld::IdTypeDelegate::IdTypeDelegate - (const ValueList &values, const IconList &icons, CSMDoc::Document& document, QObject *parent) - : DataDisplayDelegate (values, icons, document, + (const ValueList &values, const IconList &icons, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) + : DataDisplayDelegate (values, icons, dispatcher, document, "records", "type-format", parent) {} @@ -21,7 +21,7 @@ CSVWorld::IdTypeDelegateFactory::IdTypeDelegateFactory() } CSVWorld::CommandDelegate *CSVWorld::IdTypeDelegateFactory::makeDelegate ( - CSMDoc::Document& document, QObject *parent) const + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const { - return new IdTypeDelegate (mValues, mIcons, document, parent); + return new IdTypeDelegate (mValues, mIcons, dispatcher, document, parent); } diff --git a/apps/opencs/view/world/idtypedelegate.hpp b/apps/opencs/view/world/idtypedelegate.hpp index e9a0af68c..d0ed6997b 100755 --- a/apps/opencs/view/world/idtypedelegate.hpp +++ b/apps/opencs/view/world/idtypedelegate.hpp @@ -11,7 +11,7 @@ namespace CSVWorld class IdTypeDelegate : public DataDisplayDelegate { public: - IdTypeDelegate (const ValueList &mValues, const IconList &icons, CSMDoc::Document& document, QObject *parent); + IdTypeDelegate (const ValueList &mValues, const IconList &icons, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent); }; class IdTypeDelegateFactory : public DataDisplayDelegateFactory @@ -20,7 +20,7 @@ namespace CSVWorld IdTypeDelegateFactory(); - virtual CommandDelegate *makeDelegate (CSMDoc::Document& document, QObject *parent) const; + virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. }; } diff --git a/apps/opencs/view/world/infocreator.cpp b/apps/opencs/view/world/infocreator.cpp index 14034ea7f..f88b9f0b9 100644 --- a/apps/opencs/view/world/infocreator.cpp +++ b/apps/opencs/view/world/infocreator.cpp @@ -91,6 +91,11 @@ std::string CSVWorld::InfoCreator::getErrors() const return errors; } +void CSVWorld::InfoCreator::focus() +{ + mTopic->setFocus(); +} + void CSVWorld::InfoCreator::topicChanged() { update(); diff --git a/apps/opencs/view/world/infocreator.hpp b/apps/opencs/view/world/infocreator.hpp index 2296a8297..edc12975c 100644 --- a/apps/opencs/view/world/infocreator.hpp +++ b/apps/opencs/view/world/infocreator.hpp @@ -35,7 +35,10 @@ namespace CSVWorld 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. - + + /// Focus main input widget + virtual void focus(); + private slots: void topicChanged(); diff --git a/apps/opencs/view/world/nestedtable.cpp b/apps/opencs/view/world/nestedtable.cpp new file mode 100644 index 000000000..5c8762020 --- /dev/null +++ b/apps/opencs/view/world/nestedtable.cpp @@ -0,0 +1,95 @@ +#include "nestedtable.hpp" +#include "../../model/world/nestedtableproxymodel.hpp" +#include "../../model/world/universalid.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/commanddispatcher.hpp" +#include "util.hpp" + +#include +#include +#include +#include + +CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document, + CSMWorld::UniversalId id, + CSMWorld::NestedTableProxyModel* model, + QWidget* parent) + : QTableView(parent), + mUndoStack(document.getUndoStack()), + mModel(model) +{ + mDispatcher = new CSMWorld::CommandDispatcher (document, id, this); + + setSelectionBehavior (QAbstractItemView::SelectRows); + setSelectionMode (QAbstractItemView::ExtendedSelection); + + horizontalHeader()->setResizeMode (QHeaderView::Interactive); + verticalHeader()->hide(); + + int columns = model->columnCount(QModelIndex()); + + for(int i = 0 ; i < columns; ++i) + { + CSMWorld::ColumnBase::Display display = static_cast ( + model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + + CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate(display, + mDispatcher, + document, + this); + + setItemDelegateForColumn(i, delegate); + } + + setModel(model); + + setAcceptDrops(true); + + mAddNewRowAction = new QAction (tr ("Add new row"), this); + + connect(mAddNewRowAction, SIGNAL(triggered()), + this, SLOT(addNewRowActionTriggered())); + + mRemoveRowAction = new QAction (tr ("Remove row"), this); + + connect(mRemoveRowAction, SIGNAL(triggered()), + this, SLOT(removeRowActionTriggered())); +} + +void CSVWorld::NestedTable::dragEnterEvent(QDragEnterEvent *event) +{ +} + +void CSVWorld::NestedTable::dragMoveEvent(QDragMoveEvent *event) +{ +} + +void CSVWorld::NestedTable::contextMenuEvent (QContextMenuEvent *event) +{ + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + QMenu menu(this); + + if (selectionModel()->selectedRows().size() == 1) + menu.addAction(mRemoveRowAction); + + menu.addAction(mAddNewRowAction); + + menu.exec (event->globalPos()); +} + +void CSVWorld::NestedTable::removeRowActionTriggered() +{ + mUndoStack.push(new CSMWorld::DeleteNestedCommand(*(mModel->model()), + mModel->getParentId(), + selectionModel()->selectedRows().begin()->row(), + mModel->getParentColumn())); +} + +void CSVWorld::NestedTable::addNewRowActionTriggered() +{ + mUndoStack.push(new CSMWorld::AddNestedCommand(*(mModel->model()), + mModel->getParentId(), + selectionModel()->selectedRows().size(), + mModel->getParentColumn())); +} diff --git a/apps/opencs/view/world/nestedtable.hpp b/apps/opencs/view/world/nestedtable.hpp new file mode 100644 index 000000000..b8e91844c --- /dev/null +++ b/apps/opencs/view/world/nestedtable.hpp @@ -0,0 +1,56 @@ +#ifndef CSV_WORLD_NESTEDTABLE_H +#define CSV_WORLD_NESTEDTABLE_H + +#include +#include + +class QUndoStack; +class QAction; +class QContextMenuEvent; + +namespace CSMWorld +{ + class NestedTableProxyModel; + class UniversalId; + class CommandDispatcher; +} + +namespace CSMDoc +{ + class Document; +} + +namespace CSVWorld +{ + class NestedTable : public QTableView + { + Q_OBJECT + + QAction *mAddNewRowAction; + QAction *mRemoveRowAction; + QUndoStack& mUndoStack; + CSMWorld::NestedTableProxyModel* mModel; + CSMWorld::CommandDispatcher *mDispatcher; + + public: + NestedTable(CSMDoc::Document& document, + CSMWorld::UniversalId id, + CSMWorld::NestedTableProxyModel* model, + QWidget* parent = NULL); + + protected: + void dragEnterEvent(QDragEnterEvent *event); + + void dragMoveEvent(QDragMoveEvent *event); + + private: + void contextMenuEvent (QContextMenuEvent *event); + + private slots: + void removeRowActionTriggered(); + + void addNewRowActionTriggered(); + }; +} + +#endif diff --git a/apps/opencs/view/world/recordstatusdelegate.cpp b/apps/opencs/view/world/recordstatusdelegate.cpp index 708a78015..58a5c0177 100644 --- a/apps/opencs/view/world/recordstatusdelegate.cpp +++ b/apps/opencs/view/world/recordstatusdelegate.cpp @@ -9,16 +9,16 @@ CSVWorld::RecordStatusDelegate::RecordStatusDelegate(const ValueList& values, const IconList & icons, - CSMDoc::Document& document, QObject *parent) - : DataDisplayDelegate (values, icons, document, + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) + : DataDisplayDelegate (values, icons, dispatcher, document, "records", "status-format", parent) {} CSVWorld::CommandDelegate *CSVWorld::RecordStatusDelegateFactory::makeDelegate ( - CSMDoc::Document& document, QObject *parent) const + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const { - return new RecordStatusDelegate (mValues, mIcons, document, parent); + return new RecordStatusDelegate (mValues, mIcons, dispatcher, document, parent); } CSVWorld::RecordStatusDelegateFactory::RecordStatusDelegateFactory() diff --git a/apps/opencs/view/world/recordstatusdelegate.hpp b/apps/opencs/view/world/recordstatusdelegate.hpp index fbdaed538..acaf872a6 100644 --- a/apps/opencs/view/world/recordstatusdelegate.hpp +++ b/apps/opencs/view/world/recordstatusdelegate.hpp @@ -17,9 +17,9 @@ namespace CSVWorld { public: - explicit RecordStatusDelegate(const ValueList& values, - const IconList& icons, - CSMDoc::Document& document, QObject *parent = 0); + RecordStatusDelegate (const ValueList& values, const IconList& icons, + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, + QObject *parent = 0); }; class RecordStatusDelegateFactory : public DataDisplayDelegateFactory @@ -28,7 +28,7 @@ namespace CSVWorld RecordStatusDelegateFactory(); - virtual CommandDelegate *makeDelegate (CSMDoc::Document& document, QObject *parent) const; + virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. }; diff --git a/apps/opencs/view/world/referencecreator.cpp b/apps/opencs/view/world/referencecreator.cpp index 1e3cc00d7..e9bb04ba7 100644 --- a/apps/opencs/view/world/referencecreator.cpp +++ b/apps/opencs/view/world/referencecreator.cpp @@ -118,6 +118,11 @@ std::string CSVWorld::ReferenceCreator::getErrors() const return errors; } +void CSVWorld::ReferenceCreator::focus() +{ + mCell->setFocus(); +} + void CSVWorld::ReferenceCreator::cellChanged() { update(); diff --git a/apps/opencs/view/world/referencecreator.hpp b/apps/opencs/view/world/referencecreator.hpp index 002a62d87..877307c29 100644 --- a/apps/opencs/view/world/referencecreator.hpp +++ b/apps/opencs/view/world/referencecreator.hpp @@ -39,6 +39,9 @@ namespace CSVWorld ///< Return formatted error descriptions for the current state of the creator. if an empty /// string is returned, there is no error. + /// Focus main input widget + virtual void focus(); + private slots: void cellChanged(); diff --git a/apps/opencs/view/world/scriptedit.cpp b/apps/opencs/view/world/scriptedit.cpp index 271b0316d..b4f4234f1 100644 --- a/apps/opencs/view/world/scriptedit.cpp +++ b/apps/opencs/view/world/scriptedit.cpp @@ -5,11 +5,14 @@ #include #include #include +#include +#include #include "../../model/doc/document.hpp" #include "../../model/world/universalid.hpp" #include "../../model/world/tablemimedata.hpp" +#include "../../model/settings/usersettings.hpp" CSVWorld::ScriptEdit::ChangeLock::ChangeLock (ScriptEdit& edit) : mEdit (edit) @@ -28,7 +31,11 @@ CSVWorld::ScriptEdit::ScriptEdit (const CSMDoc::Document& document, ScriptHighli : QPlainTextEdit (parent), mDocument (document), mWhiteListQoutes("^[a-z|_]{1}[a-z|0-9|_]{0,}$", Qt::CaseInsensitive), - mChangeLocked (0) + mChangeLocked (0), + mLineNumberArea(0), + mShowLineNum(false), + mDefaultFont(font()), + mMonoFont(QFont("Monospace")) { // setAcceptRichText (false); setLineWrapMode (QPlainTextEdit::NoWrap); @@ -72,6 +79,41 @@ CSVWorld::ScriptEdit::ScriptEdit (const CSMDoc::Document& document, ScriptHighli connect (&mUpdateTimer, SIGNAL (timeout()), this, SLOT (updateHighlighting())); mUpdateTimer.setSingleShot (true); + + // TODO: provide a font selector dialogue + mMonoFont.setStyleHint(QFont::TypeWriter); + std::string useMonoFont = + CSMSettings::UserSettings::instance().setting("script-editor/mono-font", "true").toStdString(); + if (useMonoFont == "true") + setFont(mMonoFont); + + mLineNumberArea = new LineNumberArea(this); + updateLineNumberAreaWidth(0); + + connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int))); + connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int))); + + std::string showStatusBar = + CSMSettings::UserSettings::instance().settingValue("script-editor/show-linenum").toStdString(); + + showLineNum(showStatusBar == "true"); +} + +void CSVWorld::ScriptEdit::showLineNum(bool show) +{ + if(show!=mShowLineNum) + { + mShowLineNum = show; + updateLineNumberAreaWidth(0); + } +} + +void CSVWorld::ScriptEdit::setMonoFont(bool show) +{ + if(show) + setFont(mMonoFont); + else + setFont(mDefaultFont); } bool CSVWorld::ScriptEdit::isChangeLocked() const @@ -157,3 +199,112 @@ void CSVWorld::ScriptEdit::updateHighlighting() mHighlighter->rehighlight(); } + +int CSVWorld::ScriptEdit::lineNumberAreaWidth() +{ + if(!mShowLineNum) + return 0; + + int digits = 1; + int max = qMax(1, blockCount()); + while (max >= 10) + { + max /= 10; + ++digits; + } + + int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits; + + return space; +} + +void CSVWorld::ScriptEdit::updateLineNumberAreaWidth(int /* newBlockCount */) +{ + setViewportMargins(lineNumberAreaWidth(), 0, 0, 0); +} + +void CSVWorld::ScriptEdit::updateLineNumberArea(const QRect &rect, int dy) +{ + if (dy) + mLineNumberArea->scroll(0, dy); + else + mLineNumberArea->update(0, rect.y(), mLineNumberArea->width(), rect.height()); + + if (rect.contains(viewport()->rect())) + updateLineNumberAreaWidth(0); +} + +void CSVWorld::ScriptEdit::resizeEvent(QResizeEvent *e) +{ + QPlainTextEdit::resizeEvent(e); + + QRect cr = contentsRect(); + mLineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height())); +} + +void CSVWorld::ScriptEdit::lineNumberAreaPaintEvent(QPaintEvent *event) +{ + QPainter painter(mLineNumberArea); + + QTextBlock block = firstVisibleBlock(); + int blockNumber = block.blockNumber(); + int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top(); + int bottom = top + (int) blockBoundingRect(block).height(); + + int startBlock = textCursor().blockNumber(); + int endBlock = textCursor().blockNumber(); + if(textCursor().hasSelection()) + { + QString str = textCursor().selection().toPlainText(); + int selectedLines = str.count("\n")+1; + if(textCursor().position() < textCursor().anchor()) + endBlock += selectedLines; + else + startBlock -= selectedLines; + } + painter.setBackgroundMode(Qt::OpaqueMode); + QFont font = painter.font(); + QBrush background = painter.background(); + + while (block.isValid() && top <= event->rect().bottom()) + { + if (block.isVisible() && bottom >= event->rect().top()) + { + QFont newFont = painter.font(); + QString number = QString::number(blockNumber + 1); + if(blockNumber >= startBlock && blockNumber <= endBlock) + { + painter.setBackground(Qt::cyan); + painter.setPen(Qt::darkMagenta); + newFont.setBold(true); + } + else + { + painter.setBackground(background); + painter.setPen(Qt::black); + } + painter.setFont(newFont); + painter.drawText(0, top, mLineNumberArea->width(), fontMetrics().height(), + Qt::AlignRight, number); + painter.setFont(font); + } + + block = block.next(); + top = bottom; + bottom = top + (int) blockBoundingRect(block).height(); + ++blockNumber; + } +} + +CSVWorld::LineNumberArea::LineNumberArea(ScriptEdit *editor) : QWidget(editor), mScriptEdit(editor) +{} + +QSize CSVWorld::LineNumberArea::sizeHint() const +{ + return QSize(mScriptEdit->lineNumberAreaWidth(), 0); +} + +void CSVWorld::LineNumberArea::paintEvent(QPaintEvent *event) +{ + mScriptEdit->lineNumberAreaPaintEvent(event); +} diff --git a/apps/opencs/view/world/scriptedit.hpp b/apps/opencs/view/world/scriptedit.hpp index 0192bc550..a19cee486 100644 --- a/apps/opencs/view/world/scriptedit.hpp +++ b/apps/opencs/view/world/scriptedit.hpp @@ -2,14 +2,15 @@ #define SCRIPTEDIT_H #include +#include #include #include +#include #include "../../model/world/universalid.hpp" #include "scripthighlighter.hpp" -class QWidget; class QRegExp; namespace CSMDoc @@ -19,6 +20,8 @@ namespace CSMDoc namespace CSVWorld { + class LineNumberArea; + class ScriptEdit : public QPlainTextEdit { Q_OBJECT @@ -45,6 +48,10 @@ namespace CSVWorld int mChangeLocked; ScriptHighlighter *mHighlighter; QTimer mUpdateTimer; + bool mShowLineNum; + LineNumberArea *mLineNumberArea; + QFont mDefaultFont; + QFont mMonoFont; public: @@ -56,6 +63,15 @@ namespace CSVWorld /// \note This mechanism is used to avoid infinite update recursions bool isChangeLocked() const; + void lineNumberAreaPaintEvent(QPaintEvent *event); + int lineNumberAreaWidth(); + void showLineNum(bool show); + void setMonoFont(bool show); + + protected: + + virtual void resizeEvent(QResizeEvent *e); + private: QVector mAllowedTypes; const CSMDoc::Document& mDocument; @@ -74,6 +90,23 @@ namespace CSVWorld void idListChanged(); void updateHighlighting(); + + void updateLineNumberAreaWidth(int newBlockCount); + void updateLineNumberArea(const QRect &, int); + }; + + class LineNumberArea : public QWidget + { + ScriptEdit *mScriptEdit; + + public: + + LineNumberArea(ScriptEdit *editor); + QSize sizeHint() const; + + protected: + + void paintEvent(QPaintEvent *event); }; } #endif // SCRIPTEDIT_H diff --git a/apps/opencs/view/world/scriptsubview.cpp b/apps/opencs/view/world/scriptsubview.cpp index ea9dcee8c..411eb3660 100644 --- a/apps/opencs/view/world/scriptsubview.cpp +++ b/apps/opencs/view/world/scriptsubview.cpp @@ -1,21 +1,42 @@ - #include "scriptsubview.hpp" #include +#include +#include +#include + #include "../../model/doc/document.hpp" #include "../../model/world/universalid.hpp" #include "../../model/world/data.hpp" #include "../../model/world/columnbase.hpp" #include "../../model/world/commands.hpp" #include "../../model/world/idtable.hpp" +#include "../../model/settings/usersettings.hpp" #include "scriptedit.hpp" CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) -: SubView (id), mDocument (document), mColumn (-1) +: SubView (id), mDocument (document), mColumn (-1), mBottom(0), mStatus(0) { - setWidget (mEditor = new ScriptEdit (mDocument, ScriptHighlighter::Mode_General, this)); + QVBoxLayout *layout = new QVBoxLayout; + layout->setContentsMargins (QMargins (0, 0, 0, 0)); + + mBottom = new QWidget(this); + QStackedLayout *bottmLayout = new QStackedLayout(mBottom); + bottmLayout->setContentsMargins (0, 0, 0, 0); + QStatusBar *statusBar = new QStatusBar(mBottom); + mStatus = new QLabel(mBottom); + statusBar->addWidget (mStatus); + bottmLayout->addWidget (statusBar); + mBottom->setLayout (bottmLayout); + + layout->addWidget (mBottom, 0); + layout->insertWidget (0, mEditor = new ScriptEdit (mDocument, ScriptHighlighter::Mode_General, this), 2); + + QWidget *widget = new QWidget; + widget->setLayout (layout); + setWidget (widget); mModel = &dynamic_cast ( *document.getData().getTableModel (CSMWorld::UniversalId::Type_Scripts)); @@ -40,6 +61,33 @@ CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc: connect (mModel, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), this, SLOT (rowsAboutToBeRemoved (const QModelIndex&, int, int))); + + updateStatusBar(); + connect(mEditor, SIGNAL(cursorPositionChanged()), this, SLOT(updateStatusBar())); +} + +void CSVWorld::ScriptSubView::updateUserSetting (const QString& name, const QStringList& value) +{ + if (name == "script-editor/show-linenum") + { + std::string showLinenum = value.at(0).toStdString(); + mEditor->showLineNum(showLinenum == "true"); + mBottom->setVisible(showLinenum == "true"); + } + else if (name == "script-editor/mono-font") + { + mEditor->setMonoFont(value.at(0).toStdString() == "true"); + } +} + +void CSVWorld::ScriptSubView::updateStatusBar () +{ + std::ostringstream stream; + + stream << "(" << mEditor->textCursor().blockNumber() + 1 << ", " + << mEditor->textCursor().columnNumber() + 1 << ")"; + + mStatus->setText (QString::fromUtf8 (stream.str().c_str())); } void CSVWorld::ScriptSubView::setEditLock (bool locked) diff --git a/apps/opencs/view/world/scriptsubview.hpp b/apps/opencs/view/world/scriptsubview.hpp index 561476577..1c6474e54 100644 --- a/apps/opencs/view/world/scriptsubview.hpp +++ b/apps/opencs/view/world/scriptsubview.hpp @@ -4,6 +4,7 @@ #include "../doc/subview.hpp" class QModelIndex; +class QLabel; namespace CSMDoc { @@ -27,6 +28,8 @@ namespace CSVWorld CSMDoc::Document& mDocument; CSMWorld::IdTable *mModel; int mColumn; + QWidget *mBottom; + QLabel *mStatus; public: @@ -36,6 +39,8 @@ namespace CSVWorld virtual void useHint (const std::string& hint); + virtual void updateUserSetting (const QString& name, const QStringList& value); + public slots: void textChanged(); @@ -43,6 +48,10 @@ namespace CSVWorld void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); void rowsAboutToBeRemoved (const QModelIndex& parent, int start, int end); + + private slots: + + void updateStatusBar(); }; } diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 97a3bc2e3..5a650f98a 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -281,7 +281,7 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate (display, - mDocument, this); + mDispatcher, document, this); mDelegates.push_back (delegate); setItemDelegateForColumn (i, delegate); diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index 239c7410f..e9d644f61 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -157,6 +157,7 @@ void CSVWorld::TableBottomBox::createRequest() mLayout->setCurrentWidget (mCreator); setVisible (true); mCreating = true; + mCreator->focus(); } void CSVWorld::TableBottomBox::cloneRequest(const std::string& id, @@ -168,4 +169,5 @@ void CSVWorld::TableBottomBox::cloneRequest(const std::string& id, mCreator->toggleWidgets(false); setVisible (true); mCreating = true; + mCreator->focus(); } diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index b0f8a035a..a11b5bdde 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -18,6 +18,7 @@ #include "../../model/world/commands.hpp" #include "../../model/world/tablemimedata.hpp" +#include "../../model/world/commanddispatcher.hpp" #include "scriptedit.hpp" @@ -82,15 +83,15 @@ void CSVWorld::CommandDelegateFactoryCollection::add (CSMWorld::ColumnBase::Disp } CSVWorld::CommandDelegate *CSVWorld::CommandDelegateFactoryCollection::makeDelegate ( - CSMWorld::ColumnBase::Display display, CSMDoc::Document& document, QObject *parent) const + CSMWorld::ColumnBase::Display display, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const { std::map::const_iterator iter = mFactories.find (display); if (iter!=mFactories.end()) - return iter->second->makeDelegate (document, parent); + return iter->second->makeDelegate (dispatcher, document, parent); - return new CommandDelegate (document, parent); + return new CommandDelegate (dispatcher, document, parent); } const CSVWorld::CommandDelegateFactoryCollection& CSVWorld::CommandDelegateFactoryCollection::get() @@ -115,17 +116,22 @@ CSMDoc::Document& CSVWorld::CommandDelegate::getDocument() const void CSVWorld::CommandDelegate::setModelDataImp (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const { + if (!mCommandDispatcher) + return; + NastyTableModelHack hack (*model); QStyledItemDelegate::setModelData (editor, &hack, index); QVariant new_ = hack.getData(); - if (model->data (index)!=new_) - getUndoStack().push (new CSMWorld::ModifyCommand (*model, index, new_)); + if ((model->data (index)!=new_) && (model->flags(index) & Qt::ItemIsEditable)) + mCommandDispatcher->executeModify (model, index, new_); } -CSVWorld::CommandDelegate::CommandDelegate (CSMDoc::Document& document, QObject *parent) -: QStyledItemDelegate (parent), mDocument (document), mEditLock (false) +CSVWorld::CommandDelegate::CommandDelegate (CSMWorld::CommandDispatcher *commandDispatcher, + CSMDoc::Document& document, QObject *parent) +: QStyledItemDelegate (parent), mEditLock (false), + mCommandDispatcher (commandDispatcher), mDocument (document) {} void CSVWorld::CommandDelegate::setModelData (QWidget *editor, QAbstractItemModel *model, @@ -180,7 +186,7 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO case CSMWorld::ColumnBase::Display_Float: { QDoubleSpinBox *dsb = new QDoubleSpinBox(parent); - dsb->setRange(FLT_MIN, FLT_MAX); + dsb->setRange(-FLT_MAX, FLT_MAX); dsb->setSingleStep(0.01f); dsb->setDecimals(3); return dsb; diff --git a/apps/opencs/view/world/util.hpp b/apps/opencs/view/world/util.hpp index 10011798d..a12e6ae36 100644 --- a/apps/opencs/view/world/util.hpp +++ b/apps/opencs/view/world/util.hpp @@ -16,6 +16,7 @@ namespace CSMWorld { class TableMimeData; class UniversalId; + class CommandDispatcher; } namespace CSVWorld @@ -51,7 +52,8 @@ namespace CSVWorld virtual ~CommandDelegateFactory(); - virtual CommandDelegate *makeDelegate (CSMDoc::Document& document, QObject *parent) + virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, + CSMDoc::Document& document, QObject *parent) const = 0; ///< The ownership of the returned CommandDelegate is transferred to the caller. }; @@ -78,7 +80,8 @@ namespace CSVWorld /// /// This function must not be called more than once per value of \a display. - CommandDelegate *makeDelegate (CSMWorld::ColumnBase::Display display, CSMDoc::Document& document, + CommandDelegate *makeDelegate (CSMWorld::ColumnBase::Display display, + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. /// @@ -111,8 +114,9 @@ namespace CSVWorld { Q_OBJECT - CSMDoc::Document& mDocument; bool mEditLock; + CSMWorld::CommandDispatcher *mCommandDispatcher; + CSMDoc::Document& mDocument; protected: @@ -125,7 +129,9 @@ namespace CSVWorld public: - CommandDelegate (CSMDoc::Document& document, QObject *parent); + /// \param commandDispatcher If CommandDelegate will be only be used on read-only + /// cells, a 0-pointer can be passed here. + CommandDelegate (CSMWorld::CommandDispatcher *commandDispatcher, CSMDoc::Document& document, QObject *parent); virtual void setModelData (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const; diff --git a/apps/opencs/view/world/vartypedelegate.cpp b/apps/opencs/view/world/vartypedelegate.cpp index c3c98b800..90a686a67 100644 --- a/apps/opencs/view/world/vartypedelegate.cpp +++ b/apps/opencs/view/world/vartypedelegate.cpp @@ -47,8 +47,8 @@ void CSVWorld::VarTypeDelegate::addCommands (QAbstractItemModel *model, const QM } CSVWorld::VarTypeDelegate::VarTypeDelegate (const std::vector >& values, - CSMDoc::Document& document, QObject *parent) -: EnumDelegate (values, document, parent) + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) +: EnumDelegate (values, dispatcher, document, parent) {} @@ -69,9 +69,9 @@ CSVWorld::VarTypeDelegateFactory::VarTypeDelegateFactory (ESM::VarType type0, } CSVWorld::CommandDelegate *CSVWorld::VarTypeDelegateFactory::makeDelegate ( - CSMDoc::Document& document, QObject *parent) const + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const { - return new VarTypeDelegate (mValues, document, parent); + return new VarTypeDelegate (mValues, dispatcher, document, parent); } void CSVWorld::VarTypeDelegateFactory::add (ESM::VarType type) diff --git a/apps/opencs/view/world/vartypedelegate.hpp b/apps/opencs/view/world/vartypedelegate.hpp index c86b936f6..a8f39c318 100644 --- a/apps/opencs/view/world/vartypedelegate.hpp +++ b/apps/opencs/view/world/vartypedelegate.hpp @@ -17,7 +17,7 @@ namespace CSVWorld public: VarTypeDelegate (const std::vector >& values, - CSMDoc::Document& document, QObject *parent); + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent); }; class VarTypeDelegateFactory : public CommandDelegateFactory @@ -30,7 +30,8 @@ namespace CSVWorld ESM::VarType type1 = ESM::VT_Unknown, ESM::VarType type2 = ESM::VT_Unknown, ESM::VarType type3 = ESM::VT_Unknown); - virtual CommandDelegate *makeDelegate (CSMDoc::Document& document, QObject *parent) const; + virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, + CSMDoc::Document& document, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. void add (ESM::VarType type); diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index d7ad97e6e..ac51eace8 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -163,23 +163,23 @@ void OMW::Engine::frame(float frametime) } OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) - : mVerboseScripts (false) + : mEncoding(ToUTF8::WINDOWS_1252) + , mEncoder(NULL) + , mVerboseScripts (false) , mSkipMenu (false) , mUseSound (true) , mCompileAll (false) , mCompileAllDialogue (false) , mWarningsMode (1) - , mScriptContext (0) - , mFSStrict (false) , mScriptConsoleMode (false) - , mCfgMgr(configurationManager) - , mEncoding(ToUTF8::WINDOWS_1252) - , mEncoder(NULL) , mActivationDistanceOverride(-1) , mGrab(true) - , mScriptBlacklistUse (true) , mExportFonts(false) + , mScriptContext (0) + , mFSStrict (false) + , mScriptBlacklistUse (true) , mNewGame (false) + , mCfgMgr(configurationManager) { Misc::Rng::init(); std::srand ( static_cast(std::time(NULL)) ); diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 5f5f2eca3..a1e027f89 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -77,8 +77,6 @@ namespace MWGui , mMagicka(NULL) , mStamina(NULL) , mDrowning(NULL) - , mDrowningFrame(NULL) - , mDrowningFlash(NULL) , mWeapImage(NULL) , mSpellImage(NULL) , mWeapStatus(NULL) @@ -87,24 +85,26 @@ namespace MWGui , mMinimap(NULL) , mCompass(NULL) , mCrosshair(NULL) + , mCellNameBox(NULL) + , mDrowningFrame(NULL) + , mDrowningFlash(NULL) , mFpsBox(NULL) , mFpsCounter(NULL) , mHealthManaStaminaBaseLeft(0) , mWeapBoxBaseLeft(0) , mSpellBoxBaseLeft(0) - , mEffectBoxBaseRight(0) , mMinimapBoxBaseRight(0) + , mEffectBoxBaseRight(0) , mDragAndDrop(dragAndDrop) , mCellNameTimer(0.0f) - , mCellNameBox(NULL) + , mWeaponSpellTimer(0.f) , mMapVisible(true) , mWeaponVisible(true) , mSpellVisible(true) , mWorldMouseOver(false) - , mEnemyHealthTimer(-1) , mEnemyActorId(-1) + , mEnemyHealthTimer(-1) , mIsDrowning(false) - , mWeaponSpellTimer(0.f) , mDrowningFlashTheta(0.f) { mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize()); diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index ef86666f4..f594cd43c 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -30,12 +30,12 @@ namespace MWGui MainMenu::MainMenu(int w, int h, const VFS::Manager* vfs) : Layout("openmw_mainmenu.layout") - , mVFS(vfs) - , mButtonBox(0), mWidth (w), mHeight (h) - , mSaveGameDialog(NULL) + , mWidth (w), mHeight (h) + , mVFS(vfs), mButtonBox(0) , mBackground(NULL) , mVideoBackground(NULL) , mVideo(NULL) + , mSaveGameDialog(NULL) { getWidget(mVersionText, "VersionText"); std::stringstream sstream; diff --git a/apps/openmw/mwgui/spellicons.hpp b/apps/openmw/mwgui/spellicons.hpp index 5099fc4d6..26761f2fc 100644 --- a/apps/openmw/mwgui/spellicons.hpp +++ b/apps/openmw/mwgui/spellicons.hpp @@ -24,10 +24,10 @@ namespace MWGui struct MagicEffectInfo { MagicEffectInfo() - : mPermanent(false) - , mMagnitude(0) + : mMagnitude(0) , mRemainingTime(0.f) , mTotalTime(0.f) + , mPermanent(false) {} std::string mSource; // display name for effect source (e.g. potion name) MWMechanics::EffectKey mKey; diff --git a/apps/openmw/mwgui/spellmodel.hpp b/apps/openmw/mwgui/spellmodel.hpp index 7859c8a7b..21fbc9a6e 100644 --- a/apps/openmw/mwgui/spellmodel.hpp +++ b/apps/openmw/mwgui/spellmodel.hpp @@ -24,9 +24,9 @@ namespace MWGui bool mActive; // (Items only) is the item equipped? Spell() - : mSelected(false) + : mType(Type_Spell) + , mSelected(false) , mActive(false) - , mType(Type_Spell) { } }; diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index a7c1d781b..6d86b4a23 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -21,9 +21,9 @@ namespace MWGui } SpellView::SpellView() - : mShowCostColumn(true) + : mScrollView(NULL) + , mShowCostColumn(true) , mHighlightSelected(true) - , mScrollView(NULL) { } diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index f91c17370..14eed0e10 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -593,7 +593,10 @@ namespace MWGui for (std::vector >::const_iterator it = itemOwners.begin(); it != itemOwners.end(); ++it) { - ret += std::string("\nStolen from ") + it->first; + if (it->second == std::numeric_limits::max()) + ret += std::string("\nStolen from ") + it->first; // for legacy (ESS) savegames + else + ret += std::string("\nStolen ") + MyGUI::utility::toString(it->second) + " from " + it->first; } ret += getMiscString(cellref.getGlobalVariable(), "Global"); diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index 0ddd313df..d14993639 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -20,10 +20,10 @@ namespace MWGui { public: ToolTipInfo() - : isPotion(false) - , imageSize(32) - , wordWrap(true) + : imageSize(32) , remainingEnchantCharge(-1) + , isPotion(false) + , wordWrap(true) {} std::string caption; diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index a879bf21f..e6a2555b6 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -54,10 +54,10 @@ namespace MWGui TradeWindow::TradeWindow() : WindowBase("openmw_trade_window.layout") - , mCurrentBalance(0) - , mItemToSell(-1) - , mTradeModel(NULL) , mSortModel(NULL) + , mTradeModel(NULL) + , mItemToSell(-1) + , mCurrentBalance(0) , mCurrentMerchantOffer(0) { getWidget(mFilterAll, "AllButton"); diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index 09a3c11ac..6d9c0a580 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -36,17 +36,17 @@ namespace MWGui struct SpellEffectParams { SpellEffectParams() - : mMagnMin(-1) + : mNoTarget(false) + , mIsConstant(false) + , mKnown(true) + , mEffectID(-1) + , mSkill(-1) + , mAttribute(-1) + , mMagnMin(-1) , mMagnMax(-1) , mRange(-1) , mDuration(-1) - , mSkill(-1) , mArea(0) - , mAttribute(-1) - , mEffectID(-1) - , mNoTarget(false) - , mIsConstant(false) - , mKnown(true) { } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index abc19b3f9..6421df824 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -109,6 +109,7 @@ namespace MWGui : mResourceSystem(resourceSystem) , mViewer(viewer) , mConsoleOnlyScripts(consoleOnlyScripts) + , mCurrentModals() , mHud(NULL) , mMap(NULL) , mMenu(NULL) @@ -140,8 +141,8 @@ namespace MWGui , mTrainingWindow(NULL) , mMerchantRepair(NULL) , mSoulgemDialog(NULL) - , mRecharge(NULL) , mRepair(NULL) + , mRecharge(NULL) , mCompanionWindow(NULL) , mVideoBackground(NULL) , mVideoWidget(NULL) @@ -164,8 +165,8 @@ namespace MWGui , mPlayerName() , mPlayerRaceId() , mPlayerAttributes() - , mPlayerMinorSkills() , mPlayerMajorSkills() + , mPlayerMinorSkills() , mPlayerSkillValues() , mGui(NULL) , mGuiModes() @@ -176,7 +177,6 @@ namespace MWGui , mAllowed(GW_ALL) , mRestAllowed(true) , mFPS(0.0f) - , mCurrentModals() , mFallbackMap(fallbackMap) { mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getTextureManager()); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 442a5e8b2..62d3bdd03 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -100,32 +100,32 @@ namespace MWInput OMW::Engine& engine, const std::string& userFile, bool userFileExists, const std::string& controllerBindingsFile, bool grab) - : mInputManager(NULL) + : mJoystickLastUsed(false) , mPlayer(NULL) , mEngine(engine) - , mMouseLookEnabled(false) - , mMouseX(0)//ogre.getWindow()->getWidth ()/2.f) - , mMouseY(0)//ogre.getWindow()->getHeight ()/2.f) - , mMouseWheel(0) - , mDragDrop(false) - , mGuiCursorEnabled(true) + , mInputManager(NULL) , mUserFile(userFile) - , mUserFileExists(userFileExists) + , mDragDrop(false) + , mGrabCursor (Settings::Manager::getBool("grab cursor", "Input")) , mInvertY (Settings::Manager::getBool("invert y axis", "Input")) + , mControlsDisabled(false) , mCameraSensitivity (Settings::Manager::getFloat("camera sensitivity", "Input")) , mUISensitivity (Settings::Manager::getFloat("ui sensitivity", "Input")) , mCameraYMultiplier (Settings::Manager::getFloat("camera y multiplier", "Input")) - , mGrabCursor (Settings::Manager::getBool("grab cursor", "Input")) , mPreviewPOVDelay(0.f) , mTimeIdle(0.f) + , mMouseLookEnabled(false) + , mGuiCursorEnabled(true) + , mDetectingKeyboard(false) , mOverencumberedMessageDelay(0.f) + , mMouseX(0)//ogre.getWindow()->getWidth ()/2.f) + , mMouseY(0)//ogre.getWindow()->getHeight ()/2.f) + , mMouseWheel(0) + , mUserFileExists(userFileExists) , mAlwaysRunActive(Settings::Manager::getBool("always run", "Input")) , mSneakToggles(Settings::Manager::getBool("toggle sneak", "Input")) , mSneaking(false) , mAttemptJump(false) - , mControlsDisabled(false) - , mJoystickLastUsed(false) - , mDetectingKeyboard(false) , mFakeDeviceID(1) { diff --git a/apps/openmw/mwmechanics/aicombataction.hpp b/apps/openmw/mwmechanics/aicombataction.hpp index 1c7451c32..a4a398d05 100644 --- a/apps/openmw/mwmechanics/aicombataction.hpp +++ b/apps/openmw/mwmechanics/aicombataction.hpp @@ -66,7 +66,7 @@ namespace MWMechanics public: /// \a weapon may be empty for hand-to-hand combat ActionWeapon(const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo = MWWorld::Ptr()) - : mWeapon(weapon), mAmmunition(ammo) {} + : mAmmunition(ammo), mWeapon(weapon) {} /// Equips the given weapon. virtual void prepare(const MWWorld::Ptr& actor); virtual void getCombatRange (float& rangeAttack, float& rangeFollow); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 077f67b5e..b7fb97842 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -52,6 +52,31 @@ namespace return ((50.f - disposition) * fFightDispMult); } + void getPersuasionRatings(const MWMechanics::NpcStats& stats, float& rating1, float& rating2, float& rating3, bool player) + { + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + + float persTerm = stats.getAttribute(ESM::Attribute::Personality).getModified() / gmst.find("fPersonalityMod")->getFloat(); + float luckTerm = stats.getAttribute(ESM::Attribute::Luck).getModified() / gmst.find("fLuckMod")->getFloat(); + float repTerm = stats.getReputation() * gmst.find("fReputationMod")->getFloat(); + float fatigueTerm = stats.getFatigueTerm(); + float levelTerm = stats.getLevel() * gmst.find("fLevelMod")->getFloat(); + + rating1 = (repTerm + luckTerm + persTerm + stats.getSkill(ESM::Skill::Speechcraft).getModified()) * fatigueTerm; + + if (player) + { + rating2 = rating1 + levelTerm; + rating3 = (stats.getSkill(ESM::Skill::Mercantile).getModified() + luckTerm + persTerm) * fatigueTerm; + } + else + { + rating2 = (levelTerm + repTerm + luckTerm + persTerm + stats.getSkill(ESM::Skill::Speechcraft).getModified()) * fatigueTerm; + rating3 = (stats.getSkill(ESM::Skill::Mercantile).getModified() + repTerm + luckTerm + persTerm) * fatigueTerm; + } + } + } namespace MWMechanics @@ -685,24 +710,11 @@ namespace MWMechanics MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr(); const MWMechanics::NpcStats &playerStats = playerPtr.getClass().getNpcStats(playerPtr); - float persTerm = playerStats.getAttribute(ESM::Attribute::Personality).getModified() - / gmst.find("fPersonalityMod")->getFloat(); - - float luckTerm = playerStats.getAttribute(ESM::Attribute::Luck).getModified() - / gmst.find("fLuckMod")->getFloat(); + float npcRating1, npcRating2, npcRating3; + getPersuasionRatings(npcStats, npcRating1, npcRating2, npcRating3, false); - float repTerm = playerStats.getReputation() * gmst.find("fReputationMod")->getFloat(); - float levelTerm = playerStats.getLevel() * gmst.find("fLevelMod")->getFloat(); - - float fatigueTerm = playerStats.getFatigueTerm(); - - float playerRating1 = (repTerm + luckTerm + persTerm + playerStats.getSkill(ESM::Skill::Speechcraft).getModified()) * fatigueTerm; - float playerRating2 = playerRating1 + levelTerm; - float playerRating3 = (playerStats.getSkill(ESM::Skill::Mercantile).getModified() + luckTerm + persTerm) * fatigueTerm; - - float npcRating1 = (repTerm + luckTerm + persTerm + playerStats.getSkill(ESM::Skill::Speechcraft).getModified()) * fatigueTerm; - float npcRating2 = (levelTerm + repTerm + luckTerm + persTerm + npcStats.getSkill(ESM::Skill::Speechcraft).getModified()) * fatigueTerm; - float npcRating3 = (playerStats.getSkill(ESM::Skill::Mercantile).getModified() + repTerm + luckTerm + persTerm) * fatigueTerm; + float playerRating1, playerRating2, playerRating3; + getPersuasionRatings(playerStats, playerRating1, playerRating2, playerRating3, true); int currentDisposition = std::min(100, std::max(0, int(getDerivedDisposition(npc) + currentTemporaryDispositionDelta))); @@ -1030,7 +1042,9 @@ namespace MWMechanics owner.second = true; } Misc::StringUtils::toLower(owner.first); - mStolenItems[Misc::StringUtils::lowerCase(item.getClass().getId(item))][owner] += count; + + if (!Misc::StringUtils::ciEqual(item.getCellRef().getRefId(), MWWorld::ContainerStore::sGoldId)) + mStolenItems[Misc::StringUtils::lowerCase(item.getClass().getId(item))][owner] += count; commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); } diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 8071dd5fd..7d61e3b6c 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -25,7 +25,12 @@ namespace MWRender CharacterPreview::CharacterPreview(MWWorld::Ptr character, int sizeX, int sizeY, const std::string& name, Ogre::Vector3 position, Ogre::Vector3 lookAt) - : mSceneMgr (0) + : mRecover(false) + , mRenderTarget(NULL) + , mViewport(NULL) + , mCamera(NULL) + , mSceneMgr (0) + , mNode(NULL) , mPosition(position) , mLookAt(lookAt) , mCharacter(character) @@ -33,11 +38,6 @@ namespace MWRender , mName(name) , mSizeX(sizeX) , mSizeY(sizeY) - , mRenderTarget(NULL) - , mViewport(NULL) - , mCamera(NULL) - , mNode(NULL) - , mRecover(false) { mCharacter.mCell = NULL; } @@ -161,9 +161,9 @@ namespace MWRender InventoryPreview::InventoryPreview(MWWorld::Ptr character) : CharacterPreview(character, 512, 1024, "CharacterPreview", Ogre::Vector3(0, 71, -700), Ogre::Vector3(0,71,0)) - , mSelectionBuffer(NULL) , mSizeX(0) , mSizeY(0) + , mSelectionBuffer(NULL) { } diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 0299dc493..b69222664 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -25,9 +25,9 @@ using namespace MWRender; using namespace Ogre; LocalMap::LocalMap(OEngine::Render::OgreRenderer* rend, MWRender::RenderingManager* rendering) - : mInterior(false) + : mMapResolution(Settings::Manager::getInt("local map resolution", "Map")) , mAngle(0.f) - , mMapResolution(Settings::Manager::getInt("local map resolution", "Map")) + , mInterior(false) { mRendering = rend; mRenderingManager = rendering; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index d6aa05fcb..26f05e35f 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -71,7 +71,7 @@ namespace MWRender { HeadAnimationTime::HeadAnimationTime(MWWorld::Ptr reference) - : mReference(reference), mTalkStart(0), mTalkStop(0), mBlinkStart(0), mBlinkStop(0), mValue(0), mEnabled(true) + : mReference(reference), mTalkStart(0), mTalkStop(0), mBlinkStart(0), mBlinkStop(0), mEnabled(true), mValue(0) { resetBlinkTimer(); } diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 48de57239..2f3a87c5b 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -479,9 +479,14 @@ private: SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneManager) : mSceneManager(sceneManager) + , mCreated(false) + , mMoonRed(false) + , mIsStorm(false) , mHour(0.0f) , mDay(0) , mMonth(0) + , mCloudAnimationTimer(0.f) + , mRainTimer(0.f) , mClouds() , mNextClouds() , mCloudBlendFactor(0.0f) @@ -489,21 +494,16 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana , mCloudSpeed(0.0f) , mStarsOpacity(0.0f) , mRemainingTransitionTime(0.0f) - , mGlareFade(0.0f) , mGlare(0.0f) + , mGlareFade(0.0f) + , mRainEnabled(false) + , mRainSpeed(0) + , mRainFrequency(1) , mEnabled(true) , mSunEnabled(true) , mMasserEnabled(true) , mSecundaEnabled(true) - , mCreated(false) - , mCloudAnimationTimer(0.f) - , mMoonRed(false) - , mRainEnabled(false) - , mRainTimer(0) - , mRainSpeed(0) - , mRainFrequency(1) , mStormDirection(0,-1,0) - , mIsStorm(false) { osg::ref_ptr skyroot (new CameraRelativeTransform); parentNode->addChild(skyroot); diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index 775950431..53d54cdc9 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -46,10 +46,10 @@ namespace MWRender { { public: Reflection(Ogre::SceneManager* sceneManager) - : mSceneMgr(sceneManager) - , mIsUnderwater(false) - , mCamera(NULL) + : mCamera(NULL) , mParentCamera(NULL) + , mSceneMgr(sceneManager) + , mIsUnderwater(false) {} virtual ~Reflection() {} diff --git a/apps/wizard/mainwizard.cpp b/apps/wizard/mainwizard.cpp index a61daa5a4..7538511fe 100644 --- a/apps/wizard/mainwizard.cpp +++ b/apps/wizard/mainwizard.cpp @@ -25,10 +25,10 @@ using namespace Process; Wizard::MainWizard::MainWizard(QWidget *parent) : - mGameSettings(mCfgMgr), QWizard(parent), + mInstallations(), mError(false), - mInstallations() + mGameSettings(mCfgMgr) { #ifndef Q_OS_MAC setWizardStyle(QWizard::ModernStyle); diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 0dd2987b5..c3b889df5 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -4,13 +4,34 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +void ESM::RefNum::load (ESMReader& esm, bool wide) +{ + if (wide) + esm.getHNT (*this, "FRMR", 8); + else + esm.getHNT (mIndex, "FRMR"); +} + +void ESM::RefNum::save (ESMWriter &esm, bool wide, const std::string& tag) const +{ + if (wide) + esm.writeHNT (tag, *this, 8); + else + { + int refNum = (mIndex & 0xffffff) | ((hasContentFile() ? mContentFile : 0xff)<<24); + + esm.writeHNT (tag, refNum, 4); + } +} + + void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) { loadId(esm, wideRefNum); loadData(esm); } -void ESM::CellRef::loadId(ESMReader &esm, bool wideRefNum) +void ESM::CellRef::loadId (ESMReader& esm, bool wideRefNum) { // According to Hrnchamd, this does not belong to the actual ref. Instead, it is a marker indicating that // the following refs are part of a "temp refs" section. A temp ref is not being tracked by the moved references system. @@ -19,10 +40,7 @@ void ESM::CellRef::loadId(ESMReader &esm, bool wideRefNum) if (esm.isNextSub ("NAM0")) esm.skipHSub(); - if (wideRefNum) - esm.getHNT (mRefNum, "FRMR", 8); - else - esm.getHNT (mRefNum.mIndex, "FRMR"); + mRefNum.load (esm, wideRefNum); mRefID = esm.getHNString ("NAME"); } @@ -83,10 +101,7 @@ void ESM::CellRef::loadData(ESMReader &esm) void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) const { - if (wideRefNum) - esm.writeHNT ("FRMR", mRefNum, 8); - else - esm.writeHNT ("FRMR", mRefNum.mIndex, 4); + mRefNum.save (esm, wideRefNum); esm.writeHNCString("NAME", mRefID); diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 01b6a8473..e9959611b 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -16,6 +16,10 @@ namespace ESM unsigned int mIndex; int mContentFile; + void load (ESMReader& esm, bool wide = false); + + void save (ESMWriter &esm, bool wide = false, const std::string& tag = "FRMR") const; + enum { RefNum_NoContentFile = -1 }; inline bool hasContentFile() const { return mContentFile != RefNum_NoContentFile; } inline void unset() { mIndex = 0; mContentFile = RefNum_NoContentFile; } diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 87c000d9a..326a52e14 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -20,9 +20,9 @@ ESM_Context ESMReader::getContext() } ESMReader::ESMReader() - : mBuffer(50*1024) + : mIdx(0) , mRecordFlags(0) - , mIdx(0) + , mBuffer(50*1024) , mGlobalReaderList(NULL) , mEncoder(NULL) , mFileSize(0) diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index 14951608d..c64678e70 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -9,10 +9,10 @@ namespace ESM { ESMWriter::ESMWriter() - : mEncoder (0) + : mStream(NULL) + , mEncoder (0) , mRecordCount (0) , mCounting (true) - , mStream(NULL) {} unsigned int ESMWriter::getVersion() const diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index e4f847dec..0f8897c48 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -168,7 +168,7 @@ std::string Cell::getDescription() const } } -bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool& deleted) +bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool& deleted, bool ignoreMoves, MovedCellRef *mref) { // TODO: Try and document reference numbering, I don't think this has been done anywhere else. if (!esm.hasMoreSubs()) @@ -176,10 +176,20 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool& deleted) // NOTE: We should not need this check. It is a safety check until we have checked // more plugins, and how they treat these moved references. - if (esm.isNextSub("MVRF")) { - // skip rest of cell record (moved references), they are handled elsewhere - esm.skipRecord(); // skip MVRF, CNDT - return false; + if (esm.isNextSub("MVRF")) + { + if (ignoreMoves) + { + esm.getHT (mref->mRefNum.mIndex); + esm.getHNOT (mref->mTarget, "CNDT"); + adjustRefNum (mref->mRefNum, esm); + } + else + { + // skip rest of cell record (moved references), they are handled elsewhere + esm.skipRecord(); // skip MVRF, CNDT + return false; + } } ref.load (esm); diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index d982bbfd4..1aef97d9f 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -78,9 +78,9 @@ struct Cell float mFogDensity; }; - Cell() : mWater(0), - mName(""), + Cell() : mName(""), mRegion(""), + mWater(0), mWaterInt(false), mMapColor(0), mRefNumCounter(0) @@ -156,7 +156,9 @@ struct Cell All fields of the CellRef struct are overwritten. You can safely reuse one memory location without blanking it between calls. */ - static bool getNextRef(ESMReader &esm, CellRef &ref, bool& deleted); + /// \param ignoreMoves ignore MVRF record and read reference like a regular CellRef. + static bool getNextRef(ESMReader &esm, + CellRef &ref, bool& deleted, bool ignoreMoves = false, MovedCellRef *mref = 0); /* This fetches an MVRF record, which is used to track moved references. * Since they are comparably rare, we use a separate method for this. diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index b1c01fcba..b0897ec67 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -67,11 +67,11 @@ Land::Land() : mFlags(0) , mX(0) , mY(0) + , mPlugin(0) , mEsm(NULL) , mDataTypes(0) , mDataLoaded(false) , mLandData(NULL) - , mPlugin(0) { } diff --git a/components/files/fixedpath.hpp b/components/files/fixedpath.hpp index 9fb36d984..5e0ea6c86 100644 --- a/components/files/fixedpath.hpp +++ b/components/files/fixedpath.hpp @@ -57,8 +57,8 @@ struct FixedPath , mGlobalConfigPath(mPath.getGlobalConfigPath()) , mLocalPath(mPath.getLocalPath()) , mGlobalDataPath(mPath.getGlobalDataPath()) - , mInstallPath(mPath.getInstallPath()) , mCachePath(mPath.getCachePath()) + , mInstallPath(mPath.getInstallPath()) { } diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index 45733f48f..f4163ce47 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -34,7 +34,7 @@ public: NIFFile * const file; - NIFStream (NIFFile * file, Files::IStreamPtr inp): file (file), inp (inp) {} + NIFStream (NIFFile * file, Files::IStreamPtr inp): inp (inp), file (file) {} void skip(size_t size) { inp->ignore(size); } diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index 81e854a94..3ffb6ac8f 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -69,8 +69,8 @@ class ManualBulletShapeLoader : public OEngine::Physic::BulletShapeLoader public: ManualBulletShapeLoader(bool showMarkers=false) : mShape(NULL) - , mStaticMesh(NULL) , mCompoundShape(NULL) + , mStaticMesh(NULL) , mBoundingBox(NULL) , mShowMarkers(showMarkers) { diff --git a/components/terrain/buffercache.cpp b/components/terrain/buffercache.cpp index 01032bcda..1b000fabb 100644 --- a/components/terrain/buffercache.cpp +++ b/components/terrain/buffercache.cpp @@ -59,13 +59,27 @@ Ogre::HardwareIndexBufferSharedPtr createIndexBuffer(unsigned int flags, unsigne { for (size_t col = colStart; col < colEnd; col += increment) { - indices.push_back(verts*col+row); - indices.push_back(verts*(col+increment)+row+increment); - indices.push_back(verts*col+row+increment); + // diamond pattern + if ((row + col%2) % 2 == 1) + { + indices.push_back(verts*(col+increment)+row); + indices.push_back(verts*(col+increment)+row+increment); + indices.push_back(verts*col+row+increment); - indices.push_back(verts*col+row); - indices.push_back(verts*(col+increment)+row); - indices.push_back(verts*(col+increment)+row+increment); + indices.push_back(verts*col+row); + indices.push_back(verts*(col+increment)+row); + indices.push_back(verts*(col)+row+increment); + } + else + { + indices.push_back(verts*col+row); + indices.push_back(verts*(col+increment)+row+increment); + indices.push_back(verts*col+row+increment); + + indices.push_back(verts*col+row); + indices.push_back(verts*(col+increment)+row); + indices.push_back(verts*(col+increment)+row+increment); + } } } diff --git a/components/terrain/defaultworld.cpp b/components/terrain/defaultworld.cpp index c6f0f7136..7bc73ddda 100644 --- a/components/terrain/defaultworld.cpp +++ b/components/terrain/defaultworld.cpp @@ -78,15 +78,15 @@ namespace Terrain DefaultWorld::DefaultWorld(Ogre::SceneManager* sceneMgr, Storage* storage, int visibilityFlags, bool shaders, Alignment align, float minBatchSize, float maxBatchSize) : World(sceneMgr, storage, visibilityFlags, shaders, align) - , mMinBatchSize(minBatchSize) - , mMaxBatchSize(maxBatchSize) + , mWorkQueueChannel(0) , mVisible(true) - , mMaxX(0) + , mChunksLoading(0) , mMinX(0) - , mMaxY(0) + , mMaxX(0) , mMinY(0) - , mChunksLoading(0) - , mWorkQueueChannel(0) + , mMaxY(0) + , mMinBatchSize(minBatchSize) + , mMaxBatchSize(maxBatchSize) , mLayerLoadPending(true) { #if TERRAIN_USE_SHADER == 0 diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 6a9964213..89e5e34a3 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -133,6 +133,7 @@ namespace QuadTreeNode::QuadTreeNode(DefaultWorld* terrain, ChildDirection dir, float size, const Ogre::Vector2 ¢er, QuadTreeNode* parent) : mMaterialGenerator(NULL) + , mLoadState(LS_Unloaded) , mIsDummy(false) , mSize(size) , mLodLevel(Log2(static_cast(mSize))) @@ -142,9 +143,8 @@ QuadTreeNode::QuadTreeNode(DefaultWorld* terrain, ChildDirection dir, float size , mCenter(center) , mSceneNode(NULL) , mParent(parent) - , mTerrain(terrain) , mChunk(NULL) - , mLoadState(LS_Unloaded) + , mTerrain(terrain) { mBounds.setNull(); for (int i=0; i<4; ++i) diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 5cc2647c6..3baaaed44 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -30,14 +30,14 @@ namespace Terrain World::World(Ogre::SceneManager* sceneMgr, Storage* storage, int visibilityFlags, bool shaders, Alignment align) - : mStorage(storage) - , mSceneMgr(sceneMgr) - , mVisibilityFlags(visibilityFlags) - , mShaders(shaders) - , mAlign(align) - , mCache(storage->getCellVertices()) + : mShaders(shaders) , mShadows(false) , mSplitShadows(false) + , mAlign(align) + , mStorage(storage) + , mVisibilityFlags(visibilityFlags) + , mSceneMgr(sceneMgr) + , mCache(storage->getCellVertices()) { } diff --git a/components/widgets/list.cpp b/components/widgets/list.cpp index 8517a0303..5a79de3d1 100644 --- a/components/widgets/list.cpp +++ b/components/widgets/list.cpp @@ -9,8 +9,8 @@ namespace Gui { MWList::MWList() : - mClient(0) - , mScrollView(0) + mScrollView(0) + ,mClient(0) , mItemHeight(0) { } diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 11b6b1834..889986339 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -70,13 +70,11 @@ namespace Physic { PhysicActor::PhysicActor(const std::string &name, const std::string &mesh, PhysicEngine *engine, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, float scale) - : mName(name), mEngine(engine), mMesh(mesh) - , mBody(0), mOnGround(false), mInternalCollisionMode(true) + : mCanWaterWalk(false), mWalkingOnWater(false) + , mBody(0), mScale(scale), mForce(0.0f), mOnGround(false) + , mInternalCollisionMode(true) , mExternalCollisionMode(true) - , mForce(0.0f) - , mScale(scale) - , mWalkingOnWater(false) - , mCanWaterWalk(false) + , mMesh(mesh), mName(name), mEngine(engine) { /* if (!NifBullet::getBoundingBox(mMesh, mHalfExtents, mMeshTranslation, mMeshOrientation))