Merge branch 'master' of https://github.com/OpenMW/openmw into osg

Conflicts:
	apps/openmw/engine.cpp
	apps/openmw/mwgui/mainmenu.cpp
	apps/openmw/mwgui/windowmanagerimp.cpp
	apps/openmw/mwinput/inputmanagerimp.cpp
	apps/openmw/mwrender/animation.cpp
	apps/openmw/mwrender/debugging.cpp
	apps/openmw/mwrender/npcanimation.cpp
	apps/openmw/mwrender/renderingmanager.cpp
	apps/openmw/mwrender/sky.cpp
	components/nif/nifkey.hpp
	components/nif/nifstream.hpp
	components/nifbullet/bulletnifloader.cpp
	components/nifogre/ogrenifloader.hpp
	libs/openengine/bullet/physic.cpp
	libs/openengine/gui/manager.cpp
c++11
scrawl 10 years ago
commit e1f4a7f647

@ -1,7 +1,7 @@
OpenMW 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 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. Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work.

@ -24,10 +24,10 @@
const char *Launcher::DataFilesPage::mDefaultContentListName = "Default"; const char *Launcher::DataFilesPage::mDefaultContentListName = "Default";
Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings, Config::LauncherSettings &launcherSettings, QWidget *parent) Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings, Config::LauncherSettings &launcherSettings, QWidget *parent)
: mCfgMgr(cfg) : QWidget(parent)
, mCfgMgr(cfg)
, mGameSettings(gameSettings) , mGameSettings(gameSettings)
, mLauncherSettings(launcherSettings) , mLauncherSettings(launcherSettings)
, QWidget(parent)
{ {
ui.setupUi (this); ui.setupUi (this);
setObjectName ("DataFilesPage"); setObjectName ("DataFilesPage");

@ -25,7 +25,7 @@
using namespace Process; using namespace Process;
Launcher::MainDialog::MainDialog(QWidget *parent) Launcher::MainDialog::MainDialog(QWidget *parent)
: mGameSettings(mCfgMgr), QMainWindow (parent) : QMainWindow(parent), mGameSettings (mCfgMgr)
{ {
setupUi(this); setupUi(this);

@ -18,14 +18,14 @@ opencs_hdrs_noqt (model/doc
opencs_units (model/world 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 opencs_units_noqt (model/world
universalid record commands columnbase scriptcontext cell refidcollection universalid record commands columnbase scriptcontext cell refidcollection
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope 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 opencs_hdrs_noqt (model/world
@ -62,7 +62,7 @@ opencs_hdrs_noqt (view/doc
opencs_units (view/world opencs_units (view/world
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
cellcreator referenceablecreator referencecreator scenesubview cellcreator referenceablecreator referencecreator scenesubview
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
) )
opencs_units_noqt (view/world opencs_units_noqt (view/world

@ -2386,7 +2386,8 @@ CSMWorld::UniversalId CSMDoc::Document::newSearch()
void CSMDoc::Document::runSearch (const CSMWorld::UniversalId& searchId, const CSMTools::Search& search) 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) void CSMDoc::Document::abortOperation (int type)

@ -69,7 +69,7 @@ void CSMDoc::DocumentManager::removeDocument (CSMDoc::Document *document)
throw std::runtime_error ("removing invalid document"); throw std::runtime_error ("removing invalid document");
mDocuments.erase (iter); mDocuments.erase (iter);
delete document; document->deleteLater();
if (mDocuments.empty()) if (mDocuments.empty())
emit lastDocumentDeleted(); emit lastDocumentDeleted();

@ -231,8 +231,29 @@ void CSMDoc::CollectionReferencesStage::perform (int stage, Messages& messages)
record.mState==CSMWorld::RecordBase::State_Modified || record.mState==CSMWorld::RecordBase::State_Modified ||
record.mState==CSMWorld::RecordBase::State_ModifiedOnly) record.mState==CSMWorld::RecordBase::State_ModifiedOnly)
{ {
mState.getSubRecords()[Misc::StringUtils::lowerCase (record.get().mCell)] std::string cellId = record.get().mOriginalCell.empty() ?
.push_back (i); record.get().mCell : record.get().mOriginalCell;
std::deque<int>& 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<int, int> 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<CSMWorld::Cell>& cell = const CSMWorld::Record<CSMWorld::Cell>& cell =
mDocument.getData().getCells().getRecord (stage); mDocument.getData().getCells().getRecord (stage);
std::map<std::string, std::vector<int> >::const_iterator references = std::map<std::string, std::deque<int> >::const_iterator references =
mState.getSubRecords().find (Misc::StringUtils::lowerCase (cell.get().mId)); mState.getSubRecords().find (Misc::StringUtils::lowerCase (cell.get().mId));
if (cell.mState==CSMWorld::RecordBase::State_Modified || if (cell.mState==CSMWorld::RecordBase::State_Modified ||
@ -284,7 +305,7 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
// write references // write references
if (references!=mState.getSubRecords().end()) if (references!=mState.getSubRecords().end())
{ {
for (std::vector<int>::const_iterator iter (references->second.begin()); for (std::deque<int>::const_iterator iter (references->second.begin());
iter!=references->second.end(); ++iter) iter!=references->second.end(); ++iter)
{ {
const CSMWorld::Record<CSMWorld::CellRef>& ref = const CSMWorld::Record<CSMWorld::CellRef>& ref =
@ -293,6 +314,32 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
if (ref.mState==CSMWorld::RecordBase::State_Modified || if (ref.mState==CSMWorld::RecordBase::State_Modified ||
ref.mState==CSMWorld::RecordBase::State_ModifiedOnly) ref.mState==CSMWorld::RecordBase::State_ModifiedOnly)
{ {
// recalculate the ref's cell location
std::ostringstream stream;
if (!interior)
{
std::pair<int, int> 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()); ref.get().save (mState.getWriter());
} }
else if (ref.mState==CSMWorld::RecordBase::State_Deleted) else if (ref.mState==CSMWorld::RecordBase::State_Deleted)

@ -64,7 +64,7 @@ bool CSMDoc::SavingState::isProjectFile() const
return mProjectFile; return mProjectFile;
} }
std::map<std::string, std::vector<int> >& CSMDoc::SavingState::getSubRecords() std::map<std::string, std::deque<int> >& CSMDoc::SavingState::getSubRecords()
{ {
return mSubRecords; return mSubRecords;
} }

@ -3,6 +3,7 @@
#include <fstream> #include <fstream>
#include <map> #include <map>
#include <deque>
#include <boost/filesystem/path.hpp> #include <boost/filesystem/path.hpp>
#include <boost/filesystem/fstream.hpp> #include <boost/filesystem/fstream.hpp>
@ -26,7 +27,7 @@ namespace CSMDoc
ESM::ESMWriter mWriter; ESM::ESMWriter mWriter;
boost::filesystem::path mProjectPath; boost::filesystem::path mProjectPath;
bool mProjectFile; bool mProjectFile;
std::map<std::string, std::vector<int> > mSubRecords; // record ID, list of subrecords std::map<std::string, std::deque<int> > mSubRecords; // record ID, list of subrecords
public: public:
@ -49,7 +50,7 @@ namespace CSMDoc
bool isProjectFile() const; bool isProjectFile() const;
///< Currently saving project file? (instead of content file) ///< Currently saving project file? (instead of content file)
std::map<std::string, std::vector<int> >& getSubRecords(); std::map<std::string, std::deque<int> >& getSubRecords();
}; };

@ -203,9 +203,24 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
"Characters after search string"); "Characters after search string");
after->setDefaultValue (10); after->setDefaultValue (10);
after->setRange (0, 1000); 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: * There are three types of values:

@ -28,7 +28,7 @@ void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages &message
// Check for empty reference id // Check for empty reference id
if (cellRef.mRefID.empty()) { 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 { } else {
// Check for non existing referenced object // Check for non existing referenced object
if (mReferencables.searchId(cellRef.mRefID) == -1) { if (mReferencables.searchId(cellRef.mRefID) == -1) {

@ -120,7 +120,14 @@ bool CSMTools::ReportModel::removeRows (int row, int count, const QModelIndex& p
if (parent.isValid()) if (parent.isValid())
return false; 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; return true;
} }

@ -276,4 +276,21 @@ void CSMTools::Search::replace (CSMDoc::Document& document, CSMWorld::IdTableBas
new CSMWorld::ModifyCommand (*model, index, QString::fromUtf8 (newText.c_str()))); 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;
}

@ -88,6 +88,10 @@ namespace CSMTools
void replace (CSMDoc::Document& document, CSMWorld::IdTableBase *model, void replace (CSMDoc::Document& document, CSMWorld::IdTableBase *model,
const CSMWorld::UniversalId& id, const std::string& messageHint, const CSMWorld::UniversalId& id, const std::string& messageHint,
const std::string& replaceText) const; 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;
}; };
} }

@ -148,6 +148,8 @@ namespace CSMWorld
void setRecord (int index, const Record<ESXRecordT>& record); void setRecord (int index, const Record<ESXRecordT>& record);
///< \attention This function must not change the ID. ///< \attention This function must not change the ID.
NestableColumn *getNestableColumn (int column) const;
}; };
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
@ -289,6 +291,15 @@ namespace CSMWorld
return *mColumns.at (column); return *mColumns.at (column);
} }
template<typename ESXRecordT, typename IdAccessorT>
NestableColumn *Collection<ESXRecordT, IdAccessorT>::getNestableColumn (int column) const
{
if (column < 0 || column >= static_cast<int>(mColumns.size()))
throw std::runtime_error("column index out of range");
return mColumns.at (column);
}
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::addColumn (Column<ESXRecordT> *column) void Collection<ESXRecordT, IdAccessorT>::addColumn (Column<ESXRecordT> *column)
{ {

@ -1,10 +1,9 @@
#include "columnbase.hpp" #include "columnbase.hpp"
#include "columns.hpp" #include "columns.hpp"
CSMWorld::ColumnBase::ColumnBase (int columnId, Display displayType, int flags) CSMWorld::ColumnBase::ColumnBase (int columnId, Display displayType, int flags)
: mColumnId (columnId), mDisplayType (displayType), mFlags (flags) : mColumnId (columnId), mDisplayType (displayType), mFlags (flags)
{} {}
CSMWorld::ColumnBase::~ColumnBase() {} CSMWorld::ColumnBase::~ColumnBase() {}
@ -19,7 +18,7 @@ std::string CSMWorld::ColumnBase::getTitle() const
return Columns::getName (static_cast<Columns::ColumnId> (mColumnId)); return Columns::getName (static_cast<Columns::ColumnId> (mColumnId));
} }
int CSMWorld::ColumnBase::getId() const int CSMWorld::ColumnBase::getId() const
{ {
return mColumnId; return mColumnId;
} }
@ -76,6 +75,12 @@ bool CSMWorld::ColumnBase::isId (Display display)
Display_Video, Display_Video,
Display_Id, Display_Id,
Display_SkillImpact,
Display_EffectRange,
Display_EffectId,
Display_PartRefType,
Display_AiPackageType,
Display_YesNo,
Display_None Display_None
}; };
@ -84,7 +89,7 @@ bool CSMWorld::ColumnBase::isId (Display display)
if (ids[i]==display) if (ids[i]==display)
return true; return true;
return false; return false;
} }
bool CSMWorld::ColumnBase::isText (Display display) bool CSMWorld::ColumnBase::isText (Display display)
@ -96,3 +101,44 @@ bool CSMWorld::ColumnBase::isScript (Display display)
{ {
return display==Display_ScriptFile || display==Display_ScriptLines; 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;
}

@ -2,6 +2,8 @@
#define CSM_WOLRD_COLUMNBASE_H #define CSM_WOLRD_COLUMNBASE_H
#include <string> #include <string>
#include <vector>
#include <stdexcept>
#include <Qt> #include <Qt>
#include <QVariant> #include <QVariant>
@ -15,13 +17,15 @@ namespace CSMWorld
enum Roles enum Roles
{ {
Role_Flags = Qt::UserRole, Role_Flags = Qt::UserRole,
Role_Display = Qt::UserRole+1 Role_Display = Qt::UserRole+1,
Role_ColumnId = Qt::UserRole+2
}; };
enum Flags enum Flags
{ {
Flag_Table = 1, // column should be displayed in table view 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 enum Display
@ -30,7 +34,7 @@ namespace CSMWorld
Display_String, Display_String,
Display_LongString, Display_LongString,
//CONCRETE TYPES STARTS HERE //CONCRETE TYPES STARTS HERE (for drag and drop)
Display_Skill, Display_Skill,
Display_Class, Display_Class,
Display_Faction, Display_Faction,
@ -105,7 +109,16 @@ namespace CSMWorld
Display_ScriptLines, // console context Display_ScriptLines, // console context
Display_SoundGeneratorType, Display_SoundGeneratorType,
Display_School, 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; int mColumnId;
@ -132,11 +145,28 @@ namespace CSMWorld
static bool isScript (Display display); static bool isScript (Display display);
}; };
class NestableColumn : public ColumnBase
{
std::vector<NestableColumn *> 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<typename ESXRecordT> template<typename ESXRecordT>
struct Column : public ColumnBase struct Column : public NestableColumn
{ {
Column (int columnId, Display displayType, int flags = Flag_Table | Flag_Dialogue) Column (int columnId, Display displayType, int flags = Flag_Table | Flag_Dialogue)
: ColumnBase (columnId, displayType, flags) {} : NestableColumn (columnId, displayType, flags) {}
virtual QVariant get (const Record<ESXRecordT>& record) const = 0; virtual QVariant get (const Record<ESXRecordT>& record) const = 0;
@ -145,6 +175,34 @@ namespace CSMWorld
throw std::logic_error ("Column " + getTitle() + " is not editable"); throw std::logic_error ("Column " + getTitle() + " is not editable");
} }
}; };
template<typename ESXRecordT>
struct NestedParentColumn : public Column<ESXRecordT>
{
NestedParentColumn (int id, int flags = ColumnBase::Flag_Dialogue) : Column<ESXRecordT> (id,
ColumnBase::Display_NestedHeader, flags)
{}
virtual QVariant get (const Record<ESXRecordT>& 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 #endif

@ -870,7 +870,13 @@ namespace CSMWorld
template<typename ESXRecordT> template<typename ESXRecordT>
struct CellColumn : public Column<ESXRecordT> struct CellColumn : public Column<ESXRecordT>
{ {
CellColumn() : Column<ESXRecordT> (Columns::ColumnId_Cell, ColumnBase::Display_Cell) {} bool mBlocked;
/// \param blocked Do not allow user-modification
CellColumn (bool blocked = false)
: Column<ESXRecordT> (Columns::ColumnId_Cell, ColumnBase::Display_Cell),
mBlocked (blocked)
{}
virtual QVariant get (const Record<ESXRecordT>& record) const virtual QVariant get (const Record<ESXRecordT>& record) const
{ {
@ -892,9 +898,41 @@ namespace CSMWorld
} }
virtual bool isUserEditable() const virtual bool isUserEditable() const
{
return !mBlocked;
}
};
template<typename ESXRecordT>
struct OriginalCellColumn : public Column<ESXRecordT>
{
OriginalCellColumn()
: Column<ESXRecordT> (Columns::ColumnId_OriginalCell, ColumnBase::Display_Cell)
{}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return QString::fromUtf8 (record.get().mOriginalCell.c_str());
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mOriginalCell = data.toString().toUtf8().constData();
record.setModified (record2);
}
virtual bool isEditable() const
{ {
return true; return true;
} }
virtual bool isUserEditable() const
{
return false;
}
}; };
template<typename ESXRecordT> template<typename ESXRecordT>
@ -1277,7 +1315,6 @@ namespace CSMWorld
} }
}; };
template<typename ESXRecordT> template<typename ESXRecordT>
struct PosColumn : public Column<ESXRecordT> struct PosColumn : public Column<ESXRecordT>
{ {

@ -173,6 +173,11 @@ namespace CSMWorld
{ ColumnId_Gender, "Gender" }, { ColumnId_Gender, "Gender" },
{ ColumnId_PcRank, "PC Rank" }, { ColumnId_PcRank, "PC Rank" },
{ ColumnId_ReferenceableId, "Referenceable ID" }, { ColumnId_ReferenceableId, "Referenceable ID" },
{ ColumnId_ContainerContent, "Content" },
{ ColumnId_ItemCount, "Count" },
{ ColumnId_InventoryItemId, "ID"},
{ ColumnId_CombatState, "Combat" }, { ColumnId_CombatState, "Combat" },
{ ColumnId_MagicState, "Magic" }, { ColumnId_MagicState, "Magic" },
{ ColumnId_StealthState, "Stealth" }, { ColumnId_StealthState, "Stealth" },
@ -180,6 +185,22 @@ namespace CSMWorld
{ ColumnId_Vampire, "Vampire" }, { ColumnId_Vampire, "Vampire" },
{ ColumnId_BodyPartType, "Bodypart Type" }, { ColumnId_BodyPartType, "Bodypart Type" },
{ ColumnId_MeshType, "Mesh 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_OwnerGlobal, "Owner Global" },
{ ColumnId_DefaultProfile, "Default Profile" }, { ColumnId_DefaultProfile, "Default Profile" },
{ ColumnId_BypassNewGame, "Bypass New Game" }, { ColumnId_BypassNewGame, "Bypass New Game" },
@ -202,6 +223,59 @@ namespace CSMWorld
{ ColumnId_AreaSound, "Area Sound" }, { ColumnId_AreaSound, "Area Sound" },
{ ColumnId_BoltSound, "Bolt 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_UseValue1, "Use value 1" },
{ ColumnId_UseValue2, "Use value 2" }, { ColumnId_UseValue2, "Use value 2" },
{ ColumnId_UseValue3, "Use value 3" }, { ColumnId_UseValue3, "Use value 3" },
@ -228,6 +302,7 @@ namespace CSMWorld
{ ColumnId_Skill4, "Skill 4" }, { ColumnId_Skill4, "Skill 4" },
{ ColumnId_Skill5, "Skill 5" }, { ColumnId_Skill5, "Skill 5" },
{ ColumnId_Skill6, "Skill 6" }, { ColumnId_Skill6, "Skill 6" },
{ ColumnId_Skill7, "Skill 7" },
{ -1, 0 } // end marker { -1, 0 } // end marker
}; };
@ -261,6 +336,7 @@ namespace
"Combat", "Magic", "Stealth", 0 "Combat", "Magic", "Stealth", 0
}; };
// see ESM::Attribute::AttributeID in <component/esm/attr.hpp>
static const char *sAttributes[] = static const char *sAttributes[] =
{ {
"Strength", "Intelligence", "Willpower", "Agility", "Speed", "Endurance", "Personality", "Strength", "Intelligence", "Willpower", "Agility", "Speed", "Endurance", "Personality",
@ -353,6 +429,79 @@ namespace
"Alteration", "Conjuration", "Destruction", "Illusion", "Mysticism", "Restoration", 0 "Alteration", "Conjuration", "Destruction", "Illusion", "Mysticism", "Restoration", 0
}; };
// impact from magic effects, see ESM::Skill::SkillEnum in <component/esm/loadskil.hpp>
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 <component/esm/defs.hpp>
static const char *sEffectRange[] =
{
"Self", "Touch", "Target", 0
};
// magic effect names, see ESM::MagicEffect::Effects in <component/esm/loadmgef.hpp>
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 <component/esm/loadarmo.hpp>
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 <component/esm/aipackage.hpp>
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) const char **getEnumNames (CSMWorld::Columns::ColumnId column)
{ {
switch (column) switch (column)
@ -375,6 +524,12 @@ namespace
case CSMWorld::Columns::ColumnId_MeshType: return sMeshTypes; case CSMWorld::Columns::ColumnId_MeshType: return sMeshTypes;
case CSMWorld::Columns::ColumnId_SoundGeneratorType: return sSoundGeneratorType; case CSMWorld::Columns::ColumnId_SoundGeneratorType: return sSoundGeneratorType;
case CSMWorld::Columns::ColumnId_School: return sSchools; 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; default: return 0;
} }

@ -4,6 +4,8 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "columnbase.hpp"
namespace CSMWorld namespace CSMWorld
{ {
namespace Columns namespace Columns
@ -165,35 +167,106 @@ namespace CSMWorld
ColumnId_Rank = 152, ColumnId_Rank = 152,
ColumnId_Gender = 153, ColumnId_Gender = 153,
ColumnId_PcRank = 154, ColumnId_PcRank = 154,
ColumnId_ReferenceableId = 156, ColumnId_ReferenceableId = 155,
ColumnId_CombatState = 157, ColumnId_ContainerContent = 156,
ColumnId_MagicState = 158, ColumnId_ItemCount = 157,
ColumnId_StealthState = 159, ColumnId_InventoryItemId = 158,
ColumnId_EnchantmentType = 160, ColumnId_CombatState = 159,
ColumnId_Vampire = 161, ColumnId_MagicState = 160,
ColumnId_BodyPartType = 162, ColumnId_StealthState = 161,
ColumnId_MeshType = 163, ColumnId_EnchantmentType = 162,
ColumnId_OwnerGlobal = 164, ColumnId_Vampire = 163,
ColumnId_DefaultProfile = 165, ColumnId_BodyPartType = 164,
ColumnId_BypassNewGame = 166, ColumnId_MeshType = 165,
ColumnId_GlobalProfile = 167, ColumnId_ActorInventory = 166,
ColumnId_RefNumCounter = 168, ColumnId_SpellList = 167,
ColumnId_RefNum = 169, ColumnId_SpellId = 168,
ColumnId_Creature = 170, ColumnId_NpcDestinations = 169,
ColumnId_SoundGeneratorType = 171, ColumnId_DestinationCell = 170,
ColumnId_AllowSpellmaking = 172, ColumnId_PosX = 171, // these are float
ColumnId_AllowEnchanting = 173, ColumnId_PosY = 172, // these are float
ColumnId_BaseCost = 174, ColumnId_PosZ = 173, // these are float
ColumnId_School = 175, ColumnId_RotX = 174,
ColumnId_Particle = 176, ColumnId_RotY = 175,
ColumnId_CastingObject = 177, ColumnId_RotZ = 176,
ColumnId_HitObject = 178, ColumnId_Skill = 177,
ColumnId_AreaObject = 179, ColumnId_OwnerGlobal = 178,
ColumnId_BoltObject = 180, ColumnId_DefaultProfile = 179,
ColumnId_CastingSound = 177, ColumnId_BypassNewGame = 180,
ColumnId_HitSound = 178, ColumnId_GlobalProfile = 181,
ColumnId_AreaSound = 179, ColumnId_RefNumCounter = 182,
ColumnId_BoltSound = 180, 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 // Allocated to a separate value range, so we don't get a collision should we ever need
// to extend the number of use values. // to extend the number of use values.
ColumnId_UseValue1 = 0x10000, ColumnId_UseValue1 = 0x10000,
@ -227,7 +300,8 @@ namespace CSMWorld
ColumnId_Skill3 = 0x50002, ColumnId_Skill3 = 0x50002,
ColumnId_Skill4 = 0x50003, ColumnId_Skill4 = 0x50003,
ColumnId_Skill5 = 0x50004, ColumnId_Skill5 = 0x50004,
ColumnId_Skill6 = 0x50005 ColumnId_Skill6 = 0x50005,
ColumnId_Skill7 = 0x50006
}; };
std::string getName (ColumnId column); std::string getName (ColumnId column);

@ -2,6 +2,7 @@
#include "commanddispatcher.hpp" #include "commanddispatcher.hpp"
#include <algorithm> #include <algorithm>
#include <memory>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
@ -10,6 +11,7 @@
#include "idtable.hpp" #include "idtable.hpp"
#include "record.hpp" #include "record.hpp"
#include "commands.hpp" #include "commands.hpp"
#include "idtableproxymodel.hpp"
std::vector<std::string> CSMWorld::CommandDispatcher::getDeletableRecords() const std::vector<std::string> CSMWorld::CommandDispatcher::getDeletableRecords() const
{ {
@ -131,6 +133,54 @@ std::vector<CSMWorld::UniversalId> CSMWorld::CommandDispatcher::getExtendedTypes
return tables; return tables;
} }
void CSMWorld::CommandDispatcher::executeModify (QAbstractItemModel *model, const QModelIndex& index, const QVariant& new_)
{
if (mLocked)
return;
std::auto_ptr<CSMWorld::UpdateCellCommand> modifyCell;
int columnId = model->data (index, ColumnBase::Role_ColumnId).toInt();
if (columnId==Columns::ColumnId_PositionXPos || columnId==Columns::ColumnId_PositionYPos)
{
IdTableProxyModel *proxy = dynamic_cast<IdTableProxyModel *> (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<IdTable&> (*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<CSMWorld::ModifyCommand> 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() void CSMWorld::CommandDispatcher::executeDelete()
{ {
if (mLocked) if (mLocked)
@ -153,7 +203,15 @@ void CSMWorld::CommandDispatcher::executeDelete()
std::string id = model.data (model.getModelIndex (*iter, columnIndex)). std::string id = model.data (model.getModelIndex (*iter, columnIndex)).
toString().toUtf8().constData(); 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<CSMWorld::UniversalId::Type>(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) if (rows.size()>1)

@ -7,6 +7,9 @@
#include "universalid.hpp" #include "universalid.hpp"
class QModelIndex;
class QAbstractItemModel;
namespace CSMDoc namespace CSMDoc
{ {
class Document; class Document;
@ -53,6 +56,12 @@ namespace CSMWorld
/// the extended mode, the returned vector will be empty instead. /// the extended mode, the returned vector will be empty instead.
std::vector<UniversalId> getExtendedTypes() const; std::vector<UniversalId> 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: public slots:
void executeDelete(); void executeDelete();

@ -1,27 +1,43 @@
#include "commands.hpp" #include "commands.hpp"
#include <cmath>
#include <sstream>
#include <components/misc/stringops.hpp>
#include <QAbstractItemModel> #include <QAbstractItemModel>
#include <QAbstractProxyModel>
#include "idtable.hpp" #include "idtable.hpp"
#include <components/misc/stringops.hpp> #include "idtree.hpp"
#include "nestedtablewrapper.hpp"
CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
const QVariant& new_, QUndoCommand* parent) 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<QAbstractProxyModel *> (&model))
{
// Replace proxy with actual model
mIndex = proxy->mapToSource (index);
mModel = proxy->sourceModel();
setText ("Modify " + dynamic_cast<CSMWorld::IdTree*>(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() void CSMWorld::ModifyCommand::redo()
{ {
mOld = mModel.data (mIndex, Qt::EditRole); mOld = mModel->data (mIndex, Qt::EditRole);
mModel.setData (mIndex, mNew); mModel->setData (mIndex, mNew);
} }
void CSMWorld::ModifyCommand::undo() void CSMWorld::ModifyCommand::undo()
{ {
mModel.setData (mIndex, mOld); mModel->setData (mIndex, mOld);
} }
@ -93,8 +109,9 @@ void CSMWorld::RevertCommand::undo()
mModel.setRecord (mId, *mOld); mModel.setRecord (mId, *mOld);
} }
CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand* parent) CSMWorld::DeleteCommand::DeleteCommand (IdTable& model,
: QUndoCommand (parent), mModel (model), mId (id), mOld (0) 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()); setText (("Delete record " + id).c_str());
@ -125,7 +142,7 @@ void CSMWorld::DeleteCommand::redo()
void CSMWorld::DeleteCommand::undo() 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()); 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;
}

@ -12,6 +12,7 @@
#include <QModelIndex> #include <QModelIndex>
#include "universalid.hpp" #include "universalid.hpp"
#include "nestedtablewrapper.hpp"
class QModelIndex; class QModelIndex;
class QAbstractItemModel; class QAbstractItemModel;
@ -19,12 +20,13 @@ class QAbstractItemModel;
namespace CSMWorld namespace CSMWorld
{ {
class IdTable; class IdTable;
class IdTable; class IdTree;
struct RecordBase; struct RecordBase;
struct NestedTableWrapperBase;
class ModifyCommand : public QUndoCommand class ModifyCommand : public QUndoCommand
{ {
QAbstractItemModel& mModel; QAbstractItemModel *mModel;
QModelIndex mIndex; QModelIndex mIndex;
QVariant mNew; QVariant mNew;
QVariant mOld; QVariant mOld;
@ -109,6 +111,7 @@ namespace CSMWorld
IdTable& mModel; IdTable& mModel;
std::string mId; std::string mId;
RecordBase *mOld; RecordBase *mOld;
UniversalId::Type mType;
// not implemented // not implemented
DeleteCommand (const DeleteCommand&); DeleteCommand (const DeleteCommand&);
@ -116,7 +119,8 @@ namespace CSMWorld
public: 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(); virtual ~DeleteCommand();
@ -139,6 +143,81 @@ namespace CSMWorld
virtual void undo(); 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 #endif

@ -12,11 +12,13 @@
#include <components/esm/cellref.hpp> #include <components/esm/cellref.hpp>
#include "idtable.hpp" #include "idtable.hpp"
#include "idtree.hpp"
#include "columnimp.hpp" #include "columnimp.hpp"
#include "regionmap.hpp" #include "regionmap.hpp"
#include "columns.hpp" #include "columns.hpp"
#include "resourcesmanager.hpp" #include "resourcesmanager.hpp"
#include "resourcetable.hpp" #include "resourcetable.hpp"
#include "nestedcoladapterimp.hpp"
void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type, bool update) 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), : mEncoder (encoding), mPathgrids (mCells), mRefs (mCells),
mResourcesManager (resourcesManager), mReader (0), mDialogue (0), mReaderIndex(0), mResourceSystem(resourcesManager.getVFS()) mResourcesManager (resourcesManager), mReader (0), mDialogue (0), mReaderIndex(0), mResourceSystem(resourcesManager.getVFS())
{ {
int index = 0;
mGlobals.addColumn (new StringIdColumn<ESM::Global>); mGlobals.addColumn (new StringIdColumn<ESM::Global>);
mGlobals.addColumn (new RecordStateColumn<ESM::Global>); mGlobals.addColumn (new RecordStateColumn<ESM::Global>);
mGlobals.addColumn (new FixedRecordTypeColumn<ESM::Global> (UniversalId::Type_Global)); mGlobals.addColumn (new FixedRecordTypeColumn<ESM::Global> (UniversalId::Type_Global));
@ -106,6 +110,14 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mFactions.addColumn (new HiddenColumn<ESM::Faction>); mFactions.addColumn (new HiddenColumn<ESM::Faction>);
for (int i=0; i<7; ++i) for (int i=0; i<7; ++i)
mFactions.addColumn (new SkillsColumn<ESM::Faction> (i)); mFactions.addColumn (new SkillsColumn<ESM::Faction> (i));
// Faction Reactions
mFactions.addColumn (new NestedParentColumn<ESM::Faction> (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<ESM::Race>); mRaces.addColumn (new StringIdColumn<ESM::Race>);
mRaces.addColumn (new RecordStateColumn<ESM::Race>); mRaces.addColumn (new RecordStateColumn<ESM::Race>);
@ -118,6 +130,12 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mRaces.addColumn (new WeightHeightColumn<ESM::Race> (true, false)); mRaces.addColumn (new WeightHeightColumn<ESM::Race> (true, false));
mRaces.addColumn (new WeightHeightColumn<ESM::Race> (false, true)); mRaces.addColumn (new WeightHeightColumn<ESM::Race> (false, true));
mRaces.addColumn (new WeightHeightColumn<ESM::Race> (false, false)); mRaces.addColumn (new WeightHeightColumn<ESM::Race> (false, false));
// Race spells
mRaces.addColumn (new NestedParentColumn<ESM::Race> (Columns::ColumnId_PowerList));
index = mRaces.getColumns()-1;
mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new SpellListAdapter<ESM::Race> ()));
mRaces.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_SpellId, ColumnBase::Display_String));
mSounds.addColumn (new StringIdColumn<ESM::Sound>); mSounds.addColumn (new StringIdColumn<ESM::Sound>);
mSounds.addColumn (new RecordStateColumn<ESM::Sound>); mSounds.addColumn (new RecordStateColumn<ESM::Sound>);
@ -138,6 +156,14 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mRegions.addColumn (new NameColumn<ESM::Region>); mRegions.addColumn (new NameColumn<ESM::Region>);
mRegions.addColumn (new MapColourColumn<ESM::Region>); mRegions.addColumn (new MapColourColumn<ESM::Region>);
mRegions.addColumn (new SleepListColumn<ESM::Region>); mRegions.addColumn (new SleepListColumn<ESM::Region>);
// Region Sounds
mRegions.addColumn (new NestedParentColumn<ESM::Region> (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<ESM::BirthSign>); mBirthsigns.addColumn (new StringIdColumn<ESM::BirthSign>);
mBirthsigns.addColumn (new RecordStateColumn<ESM::BirthSign>); mBirthsigns.addColumn (new RecordStateColumn<ESM::BirthSign>);
@ -145,6 +171,13 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mBirthsigns.addColumn (new NameColumn<ESM::BirthSign>); mBirthsigns.addColumn (new NameColumn<ESM::BirthSign>);
mBirthsigns.addColumn (new TextureColumn<ESM::BirthSign>); mBirthsigns.addColumn (new TextureColumn<ESM::BirthSign>);
mBirthsigns.addColumn (new DescriptionColumn<ESM::BirthSign>); mBirthsigns.addColumn (new DescriptionColumn<ESM::BirthSign>);
// Birthsign spells
mBirthsigns.addColumn (new NestedParentColumn<ESM::BirthSign> (Columns::ColumnId_PowerList));
index = mBirthsigns.getColumns()-1;
mBirthsigns.addAdapter (std::make_pair(&mBirthsigns.getColumn(index),
new SpellListAdapter<ESM::BirthSign> ()));
mBirthsigns.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_SpellId, ColumnBase::Display_String));
mSpells.addColumn (new StringIdColumn<ESM::Spell>); mSpells.addColumn (new StringIdColumn<ESM::Spell>);
mSpells.addColumn (new RecordStateColumn<ESM::Spell>); mSpells.addColumn (new RecordStateColumn<ESM::Spell>);
@ -155,6 +188,26 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_AutoCalc, 0x1)); mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_AutoCalc, 0x1));
mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_StarterSpell, 0x2)); mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_StarterSpell, 0x2));
mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_AlwaysSucceeds, 0x4)); mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_AlwaysSucceeds, 0x4));
// Spell effects
mSpells.addColumn (new NestedParentColumn<ESM::Spell> (Columns::ColumnId_EffectList));
index = mSpells.getColumns()-1;
mSpells.addAdapter (std::make_pair(&mSpells.getColumn(index), new EffectsListAdapter<ESM::Spell> ()));
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<ESM::Dialogue>); mTopics.addColumn (new StringIdColumn<ESM::Dialogue>);
mTopics.addColumn (new RecordStateColumn<ESM::Dialogue>); mTopics.addColumn (new RecordStateColumn<ESM::Dialogue>);
@ -182,6 +235,13 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mTopicInfos.addColumn (new PcRankColumn<Info>); mTopicInfos.addColumn (new PcRankColumn<Info>);
mTopicInfos.addColumn (new SoundFileColumn<Info>); mTopicInfos.addColumn (new SoundFileColumn<Info>);
mTopicInfos.addColumn (new ResponseColumn<Info>); mTopicInfos.addColumn (new ResponseColumn<Info>);
// Result script
mTopicInfos.addColumn (new NestedParentColumn<Info> (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<Info> (true)); mJournalInfos.addColumn (new StringIdColumn<Info> (true));
mJournalInfos.addColumn (new RecordStateColumn<Info>); mJournalInfos.addColumn (new RecordStateColumn<Info>);
@ -208,6 +268,27 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mEnchantments.addColumn (new CostColumn<ESM::Enchantment>); mEnchantments.addColumn (new CostColumn<ESM::Enchantment>);
mEnchantments.addColumn (new ChargesColumn2<ESM::Enchantment>); mEnchantments.addColumn (new ChargesColumn2<ESM::Enchantment>);
mEnchantments.addColumn (new AutoCalcColumn<ESM::Enchantment>); mEnchantments.addColumn (new AutoCalcColumn<ESM::Enchantment>);
// Enchantment effects
mEnchantments.addColumn (new NestedParentColumn<ESM::Enchantment> (Columns::ColumnId_EffectList));
index = mEnchantments.getColumns()-1;
mEnchantments.addAdapter (std::make_pair(&mEnchantments.getColumn(index),
new EffectsListAdapter<ESM::Enchantment> ()));
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<ESM::BodyPart>); mBodyParts.addColumn (new StringIdColumn<ESM::BodyPart>);
mBodyParts.addColumn (new RecordStateColumn<ESM::BodyPart>); mBodyParts.addColumn (new RecordStateColumn<ESM::BodyPart>);
@ -254,6 +335,32 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mPathgrids.addColumn (new RecordStateColumn<Pathgrid>); mPathgrids.addColumn (new RecordStateColumn<Pathgrid>);
mPathgrids.addColumn (new FixedRecordTypeColumn<Pathgrid> (UniversalId::Type_Pathgrid)); mPathgrids.addColumn (new FixedRecordTypeColumn<Pathgrid> (UniversalId::Type_Pathgrid));
// new object deleted in dtor of Collection<T,A>
mPathgrids.addColumn (new NestedParentColumn<Pathgrid> (Columns::ColumnId_PathgridPoints));
index = mPathgrids.getColumns()-1;
// new object deleted in dtor of NestedCollection<T,A>
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<Pathgrid> (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<ESM::StartScript>); mStartScripts.addColumn (new StringIdColumn<ESM::StartScript>);
mStartScripts.addColumn (new RecordStateColumn<ESM::StartScript>); mStartScripts.addColumn (new RecordStateColumn<ESM::StartScript>);
mStartScripts.addColumn (new FixedRecordTypeColumn<ESM::StartScript> (UniversalId::Type_StartScript)); mStartScripts.addColumn (new FixedRecordTypeColumn<ESM::StartScript> (UniversalId::Type_StartScript));
@ -261,7 +368,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mRefs.addColumn (new StringIdColumn<CellRef> (true)); mRefs.addColumn (new StringIdColumn<CellRef> (true));
mRefs.addColumn (new RecordStateColumn<CellRef>); mRefs.addColumn (new RecordStateColumn<CellRef>);
mRefs.addColumn (new FixedRecordTypeColumn<CellRef> (UniversalId::Type_Reference)); mRefs.addColumn (new FixedRecordTypeColumn<CellRef> (UniversalId::Type_Reference));
mRefs.addColumn (new CellColumn<CellRef>); mRefs.addColumn (new CellColumn<CellRef> (true));
mRefs.addColumn (new OriginalCellColumn<CellRef>);
mRefs.addColumn (new IdColumn<CellRef>); mRefs.addColumn (new IdColumn<CellRef>);
mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mPos, 0, false)); mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mPos, 0, false));
mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mPos, 1, false)); mRefs.addColumn (new PosColumn<CellRef> (&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 (&mGmsts), UniversalId::Type_Gmst);
addModel (new IdTable (&mSkills), UniversalId::Type_Skill); addModel (new IdTable (&mSkills), UniversalId::Type_Skill);
addModel (new IdTable (&mClasses), UniversalId::Type_Class); addModel (new IdTable (&mClasses), UniversalId::Type_Class);
addModel (new IdTable (&mFactions), UniversalId::Type_Faction); addModel (new IdTree (&mFactions, &mFactions), UniversalId::Type_Faction);
addModel (new IdTable (&mRaces), UniversalId::Type_Race); addModel (new IdTree (&mRaces, &mRaces), UniversalId::Type_Race);
addModel (new IdTable (&mSounds), UniversalId::Type_Sound); addModel (new IdTable (&mSounds), UniversalId::Type_Sound);
addModel (new IdTable (&mScripts), UniversalId::Type_Script); addModel (new IdTable (&mScripts), UniversalId::Type_Script);
addModel (new IdTable (&mRegions), UniversalId::Type_Region); addModel (new IdTree (&mRegions, &mRegions), UniversalId::Type_Region);
addModel (new IdTable (&mBirthsigns), UniversalId::Type_Birthsign); addModel (new IdTree (&mBirthsigns, &mBirthsigns), UniversalId::Type_Birthsign);
addModel (new IdTable (&mSpells), UniversalId::Type_Spell); addModel (new IdTree (&mSpells, &mSpells), UniversalId::Type_Spell);
addModel (new IdTable (&mTopics), UniversalId::Type_Topic); addModel (new IdTable (&mTopics), UniversalId::Type_Topic);
addModel (new IdTable (&mJournals), UniversalId::Type_Journal); 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 (&mJournalInfos, IdTable::Feature_ReorderWithinTopic), UniversalId::Type_JournalInfo);
addModel (new IdTable (&mCells, IdTable::Feature_ViewId), UniversalId::Type_Cell); 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 (&mBodyParts), UniversalId::Type_BodyPart);
addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen); addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen);
addModel (new IdTable (&mMagicEffects), UniversalId::Type_MagicEffect); 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 (&mStartScripts), UniversalId::Type_StartScript);
addModel (new IdTable (&mReferenceables, IdTable::Feature_Preview), addModel (new IdTree (&mReferenceables, &mReferenceables, IdTable::Feature_Preview),
UniversalId::Type_Referenceable); UniversalId::Type_Referenceable);
addModel (new IdTable (&mRefs, IdTable::Feature_ViewCell | IdTable::Feature_Preview), UniversalId::Type_Reference); addModel (new IdTable (&mRefs, IdTable::Feature_ViewCell | IdTable::Feature_Preview), UniversalId::Type_Reference);
addModel (new IdTable (&mFilters), UniversalId::Type_Filter); addModel (new IdTable (&mFilters), UniversalId::Type_Filter);
@ -349,6 +458,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
UniversalId::Type_Texture); UniversalId::Type_Texture);
addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Videos)), addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Videos)),
UniversalId::Type_Video); UniversalId::Type_Video);
mRefLoadCache.clear(); // clear here rather than startLoading() and continueLoading() for multiple content files
} }
CSMWorld::Data::~Data() CSMWorld::Data::~Data()
@ -675,7 +786,6 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
mReader = 0; mReader = 0;
mDialogue = 0; mDialogue = 0;
mRefLoadCache.clear();
mReader = new ESM::ESMReader; mReader = new ESM::ESMReader;
mReader->setEncoder (&mEncoder); mReader->setEncoder (&mEncoder);
@ -712,7 +822,6 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
mReader = 0; mReader = 0;
mDialogue = 0; mDialogue = 0;
mRefLoadCache.clear();
return true; return true;
} }
@ -757,9 +866,16 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
case ESM::REC_CELL: case ESM::REC_CELL:
{ {
mCells.load (*mReader, mBase); int index = mCells.load (*mReader, mBase);
std::string cellId = Misc::StringUtils::lowerCase (mCells.getId (mCells.getSize()-1)); if (index < 0 || index >= mCells.getSize())
mRefs.load (*mReader, mCells.getSize()-1, mBase, mRefLoadCache[cellId], messages); {
// 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; break;
} }

@ -36,6 +36,7 @@
#include "../doc/stage.hpp" #include "../doc/stage.hpp"
#include "idcollection.hpp" #include "idcollection.hpp"
#include "nestedidcollection.hpp"
#include "universalid.hpp" #include "universalid.hpp"
#include "cell.hpp" #include "cell.hpp"
#include "land.hpp" #include "land.hpp"
@ -43,8 +44,11 @@
#include "refidcollection.hpp" #include "refidcollection.hpp"
#include "refcollection.hpp" #include "refcollection.hpp"
#include "infocollection.hpp" #include "infocollection.hpp"
#include "nestedinfocollection.hpp"
#include "pathgrid.hpp" #include "pathgrid.hpp"
#ifndef Q_MOC_RUN
#include "subcellcollection.hpp" #include "subcellcollection.hpp"
#endif
class QAbstractItemModel; class QAbstractItemModel;
@ -73,23 +77,23 @@ namespace CSMWorld
IdCollection<ESM::GameSetting> mGmsts; IdCollection<ESM::GameSetting> mGmsts;
IdCollection<ESM::Skill> mSkills; IdCollection<ESM::Skill> mSkills;
IdCollection<ESM::Class> mClasses; IdCollection<ESM::Class> mClasses;
IdCollection<ESM::Faction> mFactions; NestedIdCollection<ESM::Faction> mFactions;
IdCollection<ESM::Race> mRaces; NestedIdCollection<ESM::Race> mRaces;
IdCollection<ESM::Sound> mSounds; IdCollection<ESM::Sound> mSounds;
IdCollection<ESM::Script> mScripts; IdCollection<ESM::Script> mScripts;
IdCollection<ESM::Region> mRegions; NestedIdCollection<ESM::Region> mRegions;
IdCollection<ESM::BirthSign> mBirthsigns; NestedIdCollection<ESM::BirthSign> mBirthsigns;
IdCollection<ESM::Spell> mSpells; NestedIdCollection<ESM::Spell> mSpells;
IdCollection<ESM::Dialogue> mTopics; IdCollection<ESM::Dialogue> mTopics;
IdCollection<ESM::Dialogue> mJournals; IdCollection<ESM::Dialogue> mJournals;
IdCollection<ESM::Enchantment> mEnchantments; NestedIdCollection<ESM::Enchantment> mEnchantments;
IdCollection<ESM::BodyPart> mBodyParts; IdCollection<ESM::BodyPart> mBodyParts;
IdCollection<ESM::MagicEffect> mMagicEffects; IdCollection<ESM::MagicEffect> mMagicEffects;
SubCellCollection<Pathgrid> mPathgrids; SubCellCollection<Pathgrid> mPathgrids;
IdCollection<ESM::DebugProfile> mDebugProfiles; IdCollection<ESM::DebugProfile> mDebugProfiles;
IdCollection<ESM::SoundGenerator> mSoundGens; IdCollection<ESM::SoundGenerator> mSoundGens;
IdCollection<ESM::StartScript> mStartScripts; IdCollection<ESM::StartScript> mStartScripts;
InfoCollection mTopicInfos; NestedInfoCollection mTopicInfos;
InfoCollection mJournalInfos; InfoCollection mJournalInfos;
IdCollection<Cell> mCells; IdCollection<Cell> mCells;
IdCollection<LandTexture> mLandTextures; IdCollection<LandTexture> mLandTextures;

@ -74,6 +74,15 @@ namespace CSMWorld
{ {
ESXRecordT record; 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); int index = this->searchId (id);
if (index==-1) if (index==-1)

@ -1,6 +1,7 @@
#include "idtable.hpp" #include "idtable.hpp"
#include <stdexcept>
#include "collectionbase.hpp" #include "collectionbase.hpp"
#include "columnbase.hpp" #include "columnbase.hpp"
@ -27,9 +28,15 @@ int CSMWorld::IdTable::columnCount (const QModelIndex & parent) const
return mIdCollection->getColumns(); 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(); return QVariant();
if (role==Qt::EditRole && !mIdCollection->getColumn (index.column()).isEditable()) 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) if (orientation==Qt::Vertical)
return QVariant(); return QVariant();
if (orientation != Qt::Horizontal)
throw std::logic_error("Unknown header orientation specified");
if (role==Qt::DisplayRole) if (role==Qt::DisplayRole)
return tr (mIdCollection->getColumn (section).getTitle().c_str()); 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) if (role==ColumnBase::Role_Display)
return mIdCollection->getColumn (section).mDisplayType; return mIdCollection->getColumn (section).mDisplayType;
if (role==ColumnBase::Role_ColumnId)
return getColumnId (section);
return QVariant(); 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) 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) CSMWorld::UniversalId::Type type)
{ {
int index = mIdCollection->getAppendIndex (destination); int index = mIdCollection->getAppendIndex (destination);
beginInsertRows (QModelIndex(), index, index); beginInsertRows (QModelIndex(), index, index);
mIdCollection->cloneRecord(origin, destination, type); mIdCollection->cloneRecord(origin, destination, type);
endInsertRows(); endInsertRows();
} }
///This method can return only indexes to the top level table cells
QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const 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); int index = mIdCollection->searchId (id);
@ -150,7 +164,7 @@ void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& reco
beginInsertRows (QModelIndex(), index, index); beginInsertRows (QModelIndex(), index, index);
mIdCollection->appendRecord (record); mIdCollection->appendRecord (record, type);
endInsertRows(); endInsertRows();
} }
@ -221,6 +235,7 @@ std::pair<CSMWorld::UniversalId, std::string> CSMWorld::IdTable::view (int row)
return std::make_pair (UniversalId (UniversalId::Type_Scene, id), hint); return std::make_pair (UniversalId (UniversalId::Type_Scene, id), hint);
} }
///For top level data/columns
bool CSMWorld::IdTable::isDeleted (const std::string& id) const bool CSMWorld::IdTable::isDeleted (const std::string& id) const
{ {
return getRecord (id).isDeleted(); return getRecord (id).isDeleted();
@ -230,3 +245,8 @@ int CSMWorld::IdTable::getColumnId(int column) const
{ {
return mIdCollection->getColumn(column).getId(); return mIdCollection->getColumn(column).getId();
} }
CSMWorld::CollectionBase *CSMWorld::IdTable::idCollection() const
{
return mIdCollection;
}

@ -59,7 +59,8 @@ namespace CSMWorld
virtual QModelIndex getModelIndex (const std::string& id, int column) const; 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. ///< Add record or overwrite existing recrod.
const RecordBase& getRecord (const std::string& id) const; const RecordBase& getRecord (const std::string& id) const;
@ -83,6 +84,10 @@ namespace CSMWorld
virtual bool isDeleted (const std::string& id) const; virtual bool isDeleted (const std::string& id) const;
virtual int getColumnId(int column) const; virtual int getColumnId(int column) const;
protected:
virtual CollectionBase *idCollection() const;
}; };
} }

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

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

@ -0,0 +1,531 @@
#include "nestedcoladapterimp.hpp"
#include <components/esm/loadregn.hpp>
#include <components/esm/loadfact.hpp>
#include "idcollection.hpp"
#include "pathgrid.hpp"
#include "info.hpp"
namespace CSMWorld
{
PathgridPointListAdapter::PathgridPointListAdapter () {}
void PathgridPointListAdapter::addRow(Record<Pathgrid>& 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<ESM::Pathgrid::Edge>::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<Pathgrid>& record, int rowToRemove) const
{
Pathgrid pathgrid = record.get();
ESM::Pathgrid::PointList& points = pathgrid.mPoints;
if (rowToRemove < 0 || rowToRemove >= static_cast<int> (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<ESM::Pathgrid::Edge>::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<Pathgrid>& record,
const NestedTableWrapperBase& nestedTable) const
{
Pathgrid pathgrid = record.get();
pathgrid.mPoints =
static_cast<const PathgridPointsWrap &>(nestedTable).mRecord.mPoints;
pathgrid.mData.mS2 =
static_cast<const PathgridPointsWrap &>(nestedTable).mRecord.mData.mS2;
// also update edges in case points were added/removed
pathgrid.mEdges =
static_cast<const PathgridPointsWrap &>(nestedTable).mRecord.mEdges;
record.setModified (pathgrid);
}
NestedTableWrapperBase* PathgridPointListAdapter::table(const Record<Pathgrid>& record) const
{
// deleted by dtor of NestedTableStoring
return new PathgridPointsWrap(record.get());
}
QVariant PathgridPointListAdapter::getData(const Record<Pathgrid>& 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<Pathgrid>& 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<Pathgrid>& record) const
{
return 4;
}
int PathgridPointListAdapter::getRowsCount(const Record<Pathgrid>& record) const
{
return static_cast<int>(record.get().mPoints.size());
}
PathgridEdgeListAdapter::PathgridEdgeListAdapter () {}
// ToDo: seems to be auto-sorted in the dialog table display after insertion
void PathgridEdgeListAdapter::addRow(Record<Pathgrid>& 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<Pathgrid>& record, int rowToRemove) const
{
Pathgrid pathgrid = record.get();
ESM::Pathgrid::EdgeList& edges = pathgrid.mEdges;
if (rowToRemove < 0 || rowToRemove >= static_cast<int> (edges.size()))
throw std::runtime_error ("index out of range");
edges.erase(edges.begin()+rowToRemove);
record.setModified (pathgrid);
}
void PathgridEdgeListAdapter::setTable(Record<Pathgrid>& record,
const NestedTableWrapperBase& nestedTable) const
{
Pathgrid pathgrid = record.get();
pathgrid.mEdges =
static_cast<const NestedTableWrapper<ESM::Pathgrid::EdgeList> &>(nestedTable).mNestedTable;
record.setModified (pathgrid);
}
NestedTableWrapperBase* PathgridEdgeListAdapter::table(const Record<Pathgrid>& record) const
{
// deleted by dtor of NestedTableStoring
return new NestedTableWrapper<ESM::Pathgrid::EdgeList>(record.get().mEdges);
}
QVariant PathgridEdgeListAdapter::getData(const Record<Pathgrid>& record,
int subRowIndex, int subColIndex) const
{
Pathgrid pathgrid = record.get();
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (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<Pathgrid>& record,
const QVariant& value, int subRowIndex, int subColIndex) const
{
Pathgrid pathgrid = record.get();
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (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<Pathgrid>& record) const
{
return 3;
}
int PathgridEdgeListAdapter::getRowsCount(const Record<Pathgrid>& record) const
{
return static_cast<int>(record.get().mEdges.size());
}
FactionReactionsAdapter::FactionReactionsAdapter () {}
void FactionReactionsAdapter::addRow(Record<ESM::Faction>& record, int position) const
{
ESM::Faction faction = record.get();
std::map<std::string, int>& reactions = faction.mReactions;
// blank row
reactions.insert(std::make_pair("", 0));
record.setModified (faction);
}
void FactionReactionsAdapter::removeRow(Record<ESM::Faction>& record, int rowToRemove) const
{
ESM::Faction faction = record.get();
std::map<std::string, int>& reactions = faction.mReactions;
if (rowToRemove < 0 || rowToRemove >= static_cast<int> (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<std::string, int>::iterator iter = reactions.begin();
for(int i = 0; i < rowToRemove; ++i)
iter++;
reactions.erase(iter);
record.setModified (faction);
}
void FactionReactionsAdapter::setTable(Record<ESM::Faction>& record,
const NestedTableWrapperBase& nestedTable) const
{
ESM::Faction faction = record.get();
faction.mReactions =
static_cast<const NestedTableWrapper<std::map<std::string, int> >&>(nestedTable).mNestedTable;
record.setModified (faction);
}
NestedTableWrapperBase* FactionReactionsAdapter::table(const Record<ESM::Faction>& record) const
{
// deleted by dtor of NestedTableStoring
return new NestedTableWrapper<std::map<std::string, int> >(record.get().mReactions);
}
QVariant FactionReactionsAdapter::getData(const Record<ESM::Faction>& record,
int subRowIndex, int subColIndex) const
{
ESM::Faction faction = record.get();
std::map<std::string, int>& reactions = faction.mReactions;
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (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<std::string, int>::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<ESM::Faction>& record,
const QVariant& value, int subRowIndex, int subColIndex) const
{
ESM::Faction faction = record.get();
std::map<std::string, int>& reactions = faction.mReactions;
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (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<std::string, int>::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<ESM::Faction>& record) const
{
return 2;
}
int FactionReactionsAdapter::getRowsCount(const Record<ESM::Faction>& record) const
{
return static_cast<int>(record.get().mReactions.size());
}
RegionSoundListAdapter::RegionSoundListAdapter () {}
void RegionSoundListAdapter::addRow(Record<ESM::Region>& record, int position) const
{
ESM::Region region = record.get();
std::vector<ESM::Region::SoundRef>& 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<ESM::Region>& record, int rowToRemove) const
{
ESM::Region region = record.get();
std::vector<ESM::Region::SoundRef>& soundList = region.mSoundList;
if (rowToRemove < 0 || rowToRemove >= static_cast<int> (soundList.size()))
throw std::runtime_error ("index out of range");
soundList.erase(soundList.begin()+rowToRemove);
record.setModified (region);
}
void RegionSoundListAdapter::setTable(Record<ESM::Region>& record,
const NestedTableWrapperBase& nestedTable) const
{
ESM::Region region = record.get();
region.mSoundList =
static_cast<const NestedTableWrapper<std::vector<ESM::Region::SoundRef> >&>(nestedTable).mNestedTable;
record.setModified (region);
}
NestedTableWrapperBase* RegionSoundListAdapter::table(const Record<ESM::Region>& record) const
{
// deleted by dtor of NestedTableStoring
return new NestedTableWrapper<std::vector<ESM::Region::SoundRef> >(record.get().mSoundList);
}
QVariant RegionSoundListAdapter::getData(const Record<ESM::Region>& record,
int subRowIndex, int subColIndex) const
{
ESM::Region region = record.get();
std::vector<ESM::Region::SoundRef>& soundList = region.mSoundList;
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (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<ESM::Region>& record,
const QVariant& value, int subRowIndex, int subColIndex) const
{
ESM::Region region = record.get();
std::vector<ESM::Region::SoundRef>& soundList = region.mSoundList;
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (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<unsigned char>(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<ESM::Region>& record) const
{
return 2;
}
int RegionSoundListAdapter::getRowsCount(const Record<ESM::Region>& record) const
{
return static_cast<int>(record.get().mSoundList.size());
}
InfoListAdapter::InfoListAdapter () {}
void InfoListAdapter::addRow(Record<Info>& record, int position) const
{
throw std::logic_error ("cannot add a row to a fixed table");
}
void InfoListAdapter::removeRow(Record<Info>& record, int rowToRemove) const
{
throw std::logic_error ("cannot add a row to a fixed table");
}
void InfoListAdapter::setTable(Record<Info>& record,
const NestedTableWrapperBase& nestedTable) const
{
throw std::logic_error ("table operation not supported");
}
NestedTableWrapperBase* InfoListAdapter::table(const Record<Info>& record) const
{
throw std::logic_error ("table operation not supported");
}
QVariant InfoListAdapter::getData(const Record<Info>& 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<Info>& 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<Info>& record) const
{
return 1;
}
int InfoListAdapter::getRowsCount(const Record<Info>& record) const
{
return 1; // fixed at size 1
}
}

@ -0,0 +1,417 @@
#ifndef CSM_WOLRD_NESTEDCOLADAPTERIMP_H
#define CSM_WOLRD_NESTEDCOLADAPTERIMP_H
#include <QVariant>
#include <components/esm/loadpgrd.hpp>
#include <components/esm/effectlist.hpp>
#include <components/esm/loadmgef.hpp> // for converting magic effect id to string & back
#include <components/esm/loadskil.hpp> // for converting skill names
#include <components/esm/attr.hpp> // 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<Pathgrid>
{
public:
PathgridPointListAdapter ();
virtual void addRow(Record<Pathgrid>& record, int position) const;
virtual void removeRow(Record<Pathgrid>& record, int rowToRemove) const;
virtual void setTable(Record<Pathgrid>& record,
const NestedTableWrapperBase& nestedTable) const;
virtual NestedTableWrapperBase* table(const Record<Pathgrid>& record) const;
virtual QVariant getData(const Record<Pathgrid>& record,
int subRowIndex, int subColIndex) const;
virtual void setData(Record<Pathgrid>& record,
const QVariant& value, int subRowIndex, int subColIndex) const;
virtual int getColumnsCount(const Record<Pathgrid>& record) const;
virtual int getRowsCount(const Record<Pathgrid>& record) const;
};
class PathgridEdgeListAdapter : public NestedColumnAdapter<Pathgrid>
{
public:
PathgridEdgeListAdapter ();
virtual void addRow(Record<Pathgrid>& record, int position) const;
virtual void removeRow(Record<Pathgrid>& record, int rowToRemove) const;
virtual void setTable(Record<Pathgrid>& record,
const NestedTableWrapperBase& nestedTable) const;
virtual NestedTableWrapperBase* table(const Record<Pathgrid>& record) const;
virtual QVariant getData(const Record<Pathgrid>& record,
int subRowIndex, int subColIndex) const;
virtual void setData(Record<Pathgrid>& record,
const QVariant& value, int subRowIndex, int subColIndex) const;
virtual int getColumnsCount(const Record<Pathgrid>& record) const;
virtual int getRowsCount(const Record<Pathgrid>& record) const;
};
class FactionReactionsAdapter : public NestedColumnAdapter<ESM::Faction>
{
public:
FactionReactionsAdapter ();
virtual void addRow(Record<ESM::Faction>& record, int position) const;
virtual void removeRow(Record<ESM::Faction>& record, int rowToRemove) const;
virtual void setTable(Record<ESM::Faction>& record,
const NestedTableWrapperBase& nestedTable) const;
virtual NestedTableWrapperBase* table(const Record<ESM::Faction>& record) const;
virtual QVariant getData(const Record<ESM::Faction>& record,
int subRowIndex, int subColIndex) const;
virtual void setData(Record<ESM::Faction>& record,
const QVariant& value, int subRowIndex, int subColIndex) const;
virtual int getColumnsCount(const Record<ESM::Faction>& record) const;
virtual int getRowsCount(const Record<ESM::Faction>& record) const;
};
class RegionSoundListAdapter : public NestedColumnAdapter<ESM::Region>
{
public:
RegionSoundListAdapter ();
virtual void addRow(Record<ESM::Region>& record, int position) const;
virtual void removeRow(Record<ESM::Region>& record, int rowToRemove) const;
virtual void setTable(Record<ESM::Region>& record,
const NestedTableWrapperBase& nestedTable) const;
virtual NestedTableWrapperBase* table(const Record<ESM::Region>& record) const;
virtual QVariant getData(const Record<ESM::Region>& record,
int subRowIndex, int subColIndex) const;
virtual void setData(Record<ESM::Region>& record,
const QVariant& value, int subRowIndex, int subColIndex) const;
virtual int getColumnsCount(const Record<ESM::Region>& record) const;
virtual int getRowsCount(const Record<ESM::Region>& record) const;
};
template<typename ESXRecordT>
class SpellListAdapter : public NestedColumnAdapter<ESXRecordT>
{
public:
SpellListAdapter () {}
virtual void addRow(Record<ESXRecordT>& record, int position) const
{
ESXRecordT raceOrBthSgn = record.get();
std::vector<std::string>& spells = raceOrBthSgn.mPowers.mList;
// blank row
std::string spell = "";
spells.insert(spells.begin()+position, spell);
record.setModified (raceOrBthSgn);
}
virtual void removeRow(Record<ESXRecordT>& record, int rowToRemove) const
{
ESXRecordT raceOrBthSgn = record.get();
std::vector<std::string>& spells = raceOrBthSgn.mPowers.mList;
if (rowToRemove < 0 || rowToRemove >= static_cast<int> (spells.size()))
throw std::runtime_error ("index out of range");
spells.erase(spells.begin()+rowToRemove);
record.setModified (raceOrBthSgn);
}
virtual void setTable(Record<ESXRecordT>& record, const NestedTableWrapperBase& nestedTable) const
{
ESXRecordT raceOrBthSgn = record.get();
raceOrBthSgn.mPowers.mList =
static_cast<const NestedTableWrapper<std::vector<std::string> >&>(nestedTable).mNestedTable;
record.setModified (raceOrBthSgn);
}
virtual NestedTableWrapperBase* table(const Record<ESXRecordT>& record) const
{
// deleted by dtor of NestedTableStoring
return new NestedTableWrapper<std::vector<std::string> >(record.get().mPowers.mList);
}
virtual QVariant getData(const Record<ESXRecordT>& record, int subRowIndex, int subColIndex) const
{
ESXRecordT raceOrBthSgn = record.get();
std::vector<std::string>& spells = raceOrBthSgn.mPowers.mList;
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (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<ESXRecordT>& record, const QVariant& value,
int subRowIndex, int subColIndex) const
{
ESXRecordT raceOrBthSgn = record.get();
std::vector<std::string>& spells = raceOrBthSgn.mPowers.mList;
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (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<ESXRecordT>& record) const
{
return 1;
}
virtual int getRowsCount(const Record<ESXRecordT>& record) const
{
return static_cast<int>(record.get().mPowers.mList.size());
}
};
template<typename ESXRecordT>
class EffectsListAdapter : public NestedColumnAdapter<ESXRecordT>
{
public:
EffectsListAdapter () {}
virtual void addRow(Record<ESXRecordT>& record, int position) const
{
ESXRecordT magic = record.get();
std::vector<ESM::ENAMstruct>& 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<ESXRecordT>& record, int rowToRemove) const
{
ESXRecordT magic = record.get();
std::vector<ESM::ENAMstruct>& effectsList = magic.mEffects.mList;
if (rowToRemove < 0 || rowToRemove >= static_cast<int> (effectsList.size()))
throw std::runtime_error ("index out of range");
effectsList.erase(effectsList.begin()+rowToRemove);
record.setModified (magic);
}
virtual void setTable(Record<ESXRecordT>& record, const NestedTableWrapperBase& nestedTable) const
{
ESXRecordT magic = record.get();
magic.mEffects.mList =
static_cast<const NestedTableWrapper<std::vector<ESM::ENAMstruct> >&>(nestedTable).mNestedTable;
record.setModified (magic);
}
virtual NestedTableWrapperBase* table(const Record<ESXRecordT>& record) const
{
// deleted by dtor of NestedTableStoring
return new NestedTableWrapper<std::vector<ESM::ENAMstruct> >(record.get().mEffects.mList);
}
virtual QVariant getData(const Record<ESXRecordT>& record, int subRowIndex, int subColIndex) const
{
ESXRecordT magic = record.get();
std::vector<ESM::ENAMstruct>& effectsList = magic.mEffects.mList;
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (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<ESXRecordT>& record, const QVariant& value,
int subRowIndex, int subColIndex) const
{
ESXRecordT magic = record.get();
std::vector<ESM::ENAMstruct>& effectsList = magic.mEffects.mList;
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (effectsList.size()))
throw std::runtime_error ("index out of range");
ESM::ENAMstruct effect = effectsList[subRowIndex];
switch (subColIndex)
{
case 0:
{
effect.mEffectID = static_cast<short>(value.toInt());
break;
}
case 1:
{
effect.mSkill = static_cast<signed char>(value.toInt());
break;
}
case 2:
{
effect.mAttribute = static_cast<signed char>(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<ESXRecordT>& record) const
{
return 8;
}
virtual int getRowsCount(const Record<ESXRecordT>& record) const
{
return static_cast<int>(record.get().mEffects.mList.size());
}
};
class InfoListAdapter : public NestedColumnAdapter<Info>
{
public:
InfoListAdapter ();
virtual void addRow(Record<Info>& record, int position) const;
virtual void removeRow(Record<Info>& record, int rowToRemove) const;
virtual void setTable(Record<Info>& record,
const NestedTableWrapperBase& nestedTable) const;
virtual NestedTableWrapperBase* table(const Record<Info>& record) const;
virtual QVariant getData(const Record<Info>& record,
int subRowIndex, int subColIndex) const;
virtual void setData(Record<Info>& record,
const QVariant& value, int subRowIndex, int subColIndex) const;
virtual int getColumnsCount(const Record<Info>& record) const;
virtual int getRowsCount(const Record<Info>& record) const;
};
}
#endif // CSM_WOLRD_NESTEDCOLADAPTERIMP_H

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

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

@ -0,0 +1,40 @@
#ifndef CSM_WOLRD_NESTEDCOLUMNADAPTER_H
#define CSM_WOLRD_NESTEDCOLUMNADAPTER_H
class QVariant;
namespace CSMWorld
{
struct NestedTableWrapperBase;
template <typename ESXRecordT>
struct Record;
template<typename ESXRecordT>
class NestedColumnAdapter
{
public:
NestedColumnAdapter() {}
virtual ~NestedColumnAdapter() {}
virtual void addRow(Record<ESXRecordT>& record, int position) const = 0;
virtual void removeRow(Record<ESXRecordT>& record, int rowToRemove) const = 0;
virtual void setTable(Record<ESXRecordT>& record, const NestedTableWrapperBase& nestedTable) const = 0;
virtual NestedTableWrapperBase* table(const Record<ESXRecordT>& record) const = 0;
virtual QVariant getData(const Record<ESXRecordT>& record, int subRowIndex, int subColIndex) const = 0;
virtual void setData(Record<ESXRecordT>& record, const QVariant& value, int subRowIndex, int subColIndex) const = 0;
virtual int getColumnsCount(const Record<ESXRecordT>& record) const = 0;
virtual int getRowsCount(const Record<ESXRecordT>& record) const = 0;
};
}
#endif // CSM_WOLRD_NESTEDCOLUMNADAPTER_H

@ -0,0 +1,175 @@
#ifndef CSM_WOLRD_NESTEDIDCOLLECTION_H
#define CSM_WOLRD_NESTEDIDCOLLECTION_H
#include <map>
#include <stdexcept>
#include "nestedcollection.hpp"
#include "nestedcoladapterimp.hpp"
namespace ESM
{
class ESMReader;
}
namespace CSMWorld
{
struct NestedTableWrapperBase;
struct Cell;
template<typename T, typename AT>
class IdCollection;
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
class NestedIdCollection : public IdCollection<ESXRecordT, IdAccessorT>, public NestedCollection
{
std::map<const ColumnBase*, NestedColumnAdapter<ESXRecordT>* > mAdapters;
const NestedColumnAdapter<ESXRecordT>& 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<ESXRecordT>
virtual NestableColumn *getNestableColumn(int column);
void addAdapter(std::pair<const ColumnBase*, NestedColumnAdapter<ESXRecordT>* > adapter);
};
template<typename ESXRecordT, typename IdAccessorT>
NestedIdCollection<ESXRecordT, IdAccessorT>::NestedIdCollection ()
{}
template<typename ESXRecordT, typename IdAccessorT>
NestedIdCollection<ESXRecordT, IdAccessorT>::~NestedIdCollection()
{
for (typename std::map<const ColumnBase *, NestedColumnAdapter<ESXRecordT>* >::iterator
iter (mAdapters.begin()); iter!=mAdapters.end(); ++iter)
{
delete (*iter).second;
}
}
template<typename ESXRecordT, typename IdAccessorT>
void NestedIdCollection<ESXRecordT, IdAccessorT>::addAdapter(std::pair<const ColumnBase*,
NestedColumnAdapter<ESXRecordT>* > adapter)
{
mAdapters.insert(adapter);
}
template<typename ESXRecordT, typename IdAccessorT>
const NestedColumnAdapter<ESXRecordT>& NestedIdCollection<ESXRecordT, IdAccessorT>::getAdapter(const ColumnBase &column) const
{
typename std::map<const ColumnBase *, NestedColumnAdapter<ESXRecordT>* >::const_iterator iter =
mAdapters.find (&column);
if (iter==mAdapters.end())
throw std::logic_error("No such column in the nestedidadapter");
return *iter->second;
}
template<typename ESXRecordT, typename IdAccessorT>
void NestedIdCollection<ESXRecordT, IdAccessorT>::addNestedRow(int row, int column, int position)
{
Record<ESXRecordT> record;
record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).addRow(record, position);
Collection<ESXRecordT, IdAccessorT>::setRecord(row, record);
}
template<typename ESXRecordT, typename IdAccessorT>
void NestedIdCollection<ESXRecordT, IdAccessorT>::removeNestedRows(int row, int column, int subRow)
{
Record<ESXRecordT> record;
record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).removeRow(record, subRow);
Collection<ESXRecordT, IdAccessorT>::setRecord(row, record);
}
template<typename ESXRecordT, typename IdAccessorT>
QVariant NestedIdCollection<ESXRecordT, IdAccessorT>::getNestedData (int row,
int column, int subRow, int subColumn) const
{
return getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).getData(
Collection<ESXRecordT, IdAccessorT>::getRecord(row), subRow, subColumn);
}
template<typename ESXRecordT, typename IdAccessorT>
void NestedIdCollection<ESXRecordT, IdAccessorT>::setNestedData(int row,
int column, const QVariant& data, int subRow, int subColumn)
{
Record<ESXRecordT> record;
record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).setData(
record, data, subRow, subColumn);
Collection<ESXRecordT, IdAccessorT>::setRecord(row, record);
}
template<typename ESXRecordT, typename IdAccessorT>
CSMWorld::NestedTableWrapperBase* NestedIdCollection<ESXRecordT, IdAccessorT>::nestedTable(int row,
int column) const
{
return getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).table(
Collection<ESXRecordT, IdAccessorT>::getRecord(row));
}
template<typename ESXRecordT, typename IdAccessorT>
void NestedIdCollection<ESXRecordT, IdAccessorT>::setNestedTable(int row,
int column, const CSMWorld::NestedTableWrapperBase& nestedTable)
{
Record<ESXRecordT> record;
record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).setTable(
record, nestedTable);
Collection<ESXRecordT, IdAccessorT>::setRecord(row, record);
}
template<typename ESXRecordT, typename IdAccessorT>
int NestedIdCollection<ESXRecordT, IdAccessorT>::getNestedRowsCount(int row, int column) const
{
return getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).getRowsCount(
Collection<ESXRecordT, IdAccessorT>::getRecord(row));
}
template<typename ESXRecordT, typename IdAccessorT>
int NestedIdCollection<ESXRecordT, IdAccessorT>::getNestedColumnsCount(int row, int column) const
{
return getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).getColumnsCount(
Collection<ESXRecordT, IdAccessorT>::getRecord(row));
}
template<typename ESXRecordT, typename IdAccessorT>
CSMWorld::NestableColumn *NestedIdCollection<ESXRecordT, IdAccessorT>::getNestableColumn(int column)
{
return Collection<ESXRecordT, IdAccessorT>::getNestableColumn(column);
}
}
#endif // CSM_WOLRD_NESTEDIDCOLLECTION_H

@ -0,0 +1,110 @@
#include "nestedinfocollection.hpp"
#include "nestedcoladapterimp.hpp"
namespace CSMWorld
{
NestedInfoCollection::NestedInfoCollection ()
{}
NestedInfoCollection::~NestedInfoCollection()
{
for (std::map<const ColumnBase *, NestedColumnAdapter<Info>* >::iterator
iter (mAdapters.begin()); iter!=mAdapters.end(); ++iter)
{
delete (*iter).second;
}
}
void NestedInfoCollection::addAdapter(std::pair<const ColumnBase*,
NestedColumnAdapter<Info>* > adapter)
{
mAdapters.insert(adapter);
}
const NestedColumnAdapter<Info>& NestedInfoCollection::getAdapter(const ColumnBase &column) const
{
std::map<const ColumnBase *, NestedColumnAdapter<Info>* >::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<Info> record;
record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).addRow(record, position);
Collection<Info, IdAccessor<Info> >::setRecord(row, record);
}
void NestedInfoCollection::removeNestedRows(int row, int column, int subRow)
{
Record<Info> record;
record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).removeRow(record, subRow);
Collection<Info, IdAccessor<Info> >::setRecord(row, record);
}
QVariant NestedInfoCollection::getNestedData (int row,
int column, int subRow, int subColumn) const
{
return getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).getData(
Collection<Info, IdAccessor<Info> >::getRecord(row), subRow, subColumn);
}
void NestedInfoCollection::setNestedData(int row,
int column, const QVariant& data, int subRow, int subColumn)
{
Record<Info> record;
record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).setData(
record, data, subRow, subColumn);
Collection<Info, IdAccessor<Info> >::setRecord(row, record);
}
CSMWorld::NestedTableWrapperBase* NestedInfoCollection::nestedTable(int row,
int column) const
{
return getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).table(
Collection<Info, IdAccessor<Info> >::getRecord(row));
}
void NestedInfoCollection::setNestedTable(int row,
int column, const CSMWorld::NestedTableWrapperBase& nestedTable)
{
Record<Info> record;
record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).setTable(
record, nestedTable);
Collection<Info, IdAccessor<Info> >::setRecord(row, record);
}
int NestedInfoCollection::getNestedRowsCount(int row, int column) const
{
return getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).getRowsCount(
Collection<Info, IdAccessor<Info> >::getRecord(row));
}
int NestedInfoCollection::getNestedColumnsCount(int row, int column) const
{
return getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).getColumnsCount(
Collection<Info, IdAccessor<Info> >::getRecord(row));
}
CSMWorld::NestableColumn *NestedInfoCollection::getNestableColumn(int column)
{
return Collection<Info, IdAccessor<Info> >::getNestableColumn(column);
}
}

@ -0,0 +1,50 @@
#ifndef CSM_WOLRD_NESTEDINFOCOLLECTION_H
#define CSM_WOLRD_NESTEDINFOCOLLECTION_H
#include <map>
#include "infocollection.hpp"
#include "nestedcollection.hpp"
namespace CSMWorld
{
struct NestedTableWrapperBase;
template<typename ESXRecordT>
class NestedColumnAdapter;
class NestedInfoCollection : public InfoCollection, public NestedCollection
{
std::map<const ColumnBase*, NestedColumnAdapter<Info>* > mAdapters;
const NestedColumnAdapter<Info>& 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<Info, IdAccessor<Info> >
virtual NestableColumn *getNestableColumn(int column);
void addAdapter(std::pair<const ColumnBase*, NestedColumnAdapter<Info>* > adapter);
};
}
#endif // CSM_WOLRD_NESTEDINFOCOLLECTION_H

@ -0,0 +1,195 @@
#include "nestedtableproxymodel.hpp"
#include <cassert>
#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));
}
}

@ -0,0 +1,84 @@
#ifndef CSM_WOLRD_NESTEDTABLEPROXYMODEL_H
#define CSM_WOLRD_NESTEDTABLEPROXYMODEL_H
#include <vector>
#include <QAbstractProxyModel>
#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

@ -0,0 +1,12 @@
#include "nestedtablewrapper.hpp"
CSMWorld::NestedTableWrapperBase::NestedTableWrapperBase()
{}
CSMWorld::NestedTableWrapperBase::~NestedTableWrapperBase()
{}
int CSMWorld::NestedTableWrapperBase::size() const
{
return -5;
}

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

@ -1,8 +1,18 @@
#include "ref.hpp" #include "ref.hpp"
#include <cmath>
CSMWorld::CellRef::CellRef() CSMWorld::CellRef::CellRef()
{ {
mRefNum.mIndex = 0; mRefNum.mIndex = 0;
mRefNum.mContentFile = 0; mRefNum.mContentFile = 0;
} }
std::pair<int, int> CSMWorld::CellRef::getCellIndex() const
{
const int cellSize = 8192;
return std::make_pair (
std::floor (mPos.pos[0]/cellSize), std::floor (mPos.pos[1]/cellSize));
}

@ -1,6 +1,8 @@
#ifndef CSM_WOLRD_REF_H #ifndef CSM_WOLRD_REF_H
#define CSM_WOLRD_REF_H #define CSM_WOLRD_REF_H
#include <utility>
#include <components/esm/cellref.hpp> #include <components/esm/cellref.hpp>
namespace CSMWorld namespace CSMWorld
@ -10,8 +12,12 @@ namespace CSMWorld
{ {
std::string mId; std::string mId;
std::string mCell; std::string mCell;
std::string mOriginalCell;
CellRef(); CellRef();
/// Calculate cell index based on coordinates (x and y)
std::pair<int, int> getCellIndex() const;
}; };
} }

@ -2,8 +2,10 @@
#include "refcollection.hpp" #include "refcollection.hpp"
#include <sstream> #include <sstream>
#include <iostream>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <components/esm/loadcell.hpp>
#include "ref.hpp" #include "ref.hpp"
#include "cell.hpp" #include "cell.hpp"
@ -20,14 +22,75 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
CellRef ref; CellRef ref;
bool deleted = false; 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<int, int> 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<ESM::RefNum, std::string>::iterator iter = cache.find (ref.mRefNum); // ignore content file number
std::map<ESM::RefNum, std::string>::iterator iter = cache.begin();
for (; iter != cache.end(); ++iter)
{
if (ref.mRefNum.mIndex == iter->first.mIndex)
break;
}
if (deleted) if (deleted)
{ {

@ -27,8 +27,7 @@ namespace CSMWorld
{} {}
void load (ESM::ESMReader& reader, int cellIndex, bool base, void load (ESM::ESMReader& reader, int cellIndex, bool base,
std::map<ESM::RefNum, std::string>& cache, std::map<ESM::RefNum, std::string>& cache, CSMDoc::Messages& messages);
CSMDoc::Messages& messages);
///< Load a sequence of references. ///< Load a sequence of references.
std::string getNewId(); std::string getNewId();

@ -1,6 +1,9 @@
#include "refidadapter.hpp" #include "refidadapter.hpp"
CSMWorld::RefIdAdapter::RefIdAdapter() {} CSMWorld::RefIdAdapter::RefIdAdapter() {}
CSMWorld::RefIdAdapter::~RefIdAdapter() {} CSMWorld::RefIdAdapter::~RefIdAdapter() {}
CSMWorld::NestedRefIdAdapterBase::NestedRefIdAdapterBase() {}
CSMWorld::NestedRefIdAdapterBase::~NestedRefIdAdapterBase() {}

@ -2,6 +2,14 @@
#define CSM_WOLRD_REFIDADAPTER_H #define CSM_WOLRD_REFIDADAPTER_H
#include <string> #include <string>
#include <vector>
/*! \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; class QVariant;
@ -10,6 +18,8 @@ namespace CSMWorld
class RefIdColumn; class RefIdColumn;
class RefIdData; class RefIdData;
struct RecordBase; struct RecordBase;
struct NestedTableWrapperBase;
class HelperBase;
class RefIdAdapter class RefIdAdapter
{ {
@ -25,13 +35,45 @@ namespace CSMWorld
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int idnex) virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int idnex)
const = 0; const = 0;
///< If called on the nest column, should return QVariant(true).
virtual void setData (const RefIdColumn *column, RefIdData& data, int index, virtual void setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const = 0; const QVariant& value) const = 0;
///< If the data type does not match an exception is thrown. ///< If the data type does not match an exception is thrown.
virtual std::string getId (const RecordBase& record) const = 0; 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;
}; };
} }

@ -1,10 +1,19 @@
#include "refidadapterimp.hpp" #include "refidadapterimp.hpp"
CSMWorld::PotionRefIdAdapter::PotionRefIdAdapter (const InventoryColumns& columns, #include <cassert>
#include <stdexcept>
#include <utility>
#include <components/esm/loadcont.hpp>
#include "nestedtablewrapper.hpp"
CSMWorld::PotionColumns::PotionColumns (const InventoryColumns& columns)
: InventoryColumns (columns) {}
CSMWorld::PotionRefIdAdapter::PotionRefIdAdapter (const PotionColumns& columns,
const RefIdColumn *autoCalc) const RefIdColumn *autoCalc)
: InventoryRefIdAdapter<ESM::Potion> (UniversalId::Type_Potion, columns), : InventoryRefIdAdapter<ESM::Potion> (UniversalId::Type_Potion, columns),
mAutoCalc (autoCalc) mAutoCalc (autoCalc), mColumns(columns)
{} {}
QVariant CSMWorld::PotionRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, 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) if (column==mAutoCalc)
return record.get().mData.mAutoCalc!=0; return record.get().mData.mAutoCalc!=0;
if (column==mColumns.mEffects)
return true; // to show nested tables in dialogue subview, see IdTree::hasChildren()
return InventoryRefIdAdapter<ESM::Potion>::getData (column, data, index); return InventoryRefIdAdapter<ESM::Potion>::getData (column, data, index);
} }
@ -69,9 +81,10 @@ void CSMWorld::ApparatusRefIdAdapter::setData (const RefIdColumn *column, RefIdD
CSMWorld::ArmorRefIdAdapter::ArmorRefIdAdapter (const EnchantableColumns& columns, 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<ESM::Armor> (UniversalId::Type_Armor, columns), : EnchantableRefIdAdapter<ESM::Armor> (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, QVariant CSMWorld::ArmorRefIdAdapter::getData (const RefIdColumn *column,
@ -89,6 +102,9 @@ QVariant CSMWorld::ArmorRefIdAdapter::getData (const RefIdColumn *column,
if (column==mArmor) if (column==mArmor)
return record.get().mData.mArmor; return record.get().mData.mArmor;
if (column==mPartRef)
return true; // to show nested tables in dialogue subview, see IdTree::hasChildren()
return EnchantableRefIdAdapter<ESM::Armor>::getData (column, data, index); return EnchantableRefIdAdapter<ESM::Armor>::getData (column, data, index);
} }
@ -144,8 +160,9 @@ void CSMWorld::BookRefIdAdapter::setData (const RefIdColumn *column, RefIdData&
} }
CSMWorld::ClothingRefIdAdapter::ClothingRefIdAdapter (const EnchantableColumns& columns, CSMWorld::ClothingRefIdAdapter::ClothingRefIdAdapter (const EnchantableColumns& columns,
const RefIdColumn *type) const RefIdColumn *type, const RefIdColumn *partRef)
: EnchantableRefIdAdapter<ESM::Clothing> (UniversalId::Type_Clothing, columns), mType (type) : EnchantableRefIdAdapter<ESM::Clothing> (UniversalId::Type_Clothing, columns), mType (type),
mPartRef(partRef)
{} {}
QVariant CSMWorld::ClothingRefIdAdapter::getData (const RefIdColumn *column, QVariant CSMWorld::ClothingRefIdAdapter::getData (const RefIdColumn *column,
@ -157,6 +174,9 @@ QVariant CSMWorld::ClothingRefIdAdapter::getData (const RefIdColumn *column,
if (column==mType) if (column==mType)
return record.get().mData.mType; return record.get().mData.mType;
if (column==mPartRef)
return true; // to show nested tables in dialogue subview, see IdTree::hasChildren()
return EnchantableRefIdAdapter<ESM::Clothing>::getData (column, data, index); return EnchantableRefIdAdapter<ESM::Clothing>::getData (column, data, index);
} }
@ -173,13 +193,14 @@ void CSMWorld::ClothingRefIdAdapter::setData (const RefIdColumn *column, RefIdDa
} }
CSMWorld::ContainerRefIdAdapter::ContainerRefIdAdapter (const NameColumns& columns, 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<ESM::Container> (UniversalId::Type_Container, columns), mWeight (weight), : NameRefIdAdapter<ESM::Container> (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, QVariant CSMWorld::ContainerRefIdAdapter::getData (const RefIdColumn *column,
int index) const const RefIdData& data,
int index) const
{ {
const Record<ESM::Container>& record = static_cast<const Record<ESM::Container>&> ( const Record<ESM::Container>& record = static_cast<const Record<ESM::Container>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Container))); data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Container)));
@ -193,6 +214,9 @@ QVariant CSMWorld::ContainerRefIdAdapter::getData (const RefIdColumn *column, co
if (column==mRespawn) if (column==mRespawn)
return (record.get().mFlags & ESM::Container::Respawn)!=0; return (record.get().mFlags & ESM::Container::Respawn)!=0;
if (column==mContent)
return true; // Required to show nested tables in dialogue subview
return NameRefIdAdapter<ESM::Container>::getData (column, data, index); return NameRefIdAdapter<ESM::Container>::getData (column, data, index);
} }

File diff suppressed because it is too large Load Diff

@ -1,4 +1,3 @@
#include "refidcollection.hpp" #include "refidcollection.hpp"
#include <stdexcept> #include <stdexcept>
@ -9,10 +8,12 @@
#include "refidadapter.hpp" #include "refidadapter.hpp"
#include "refidadapterimp.hpp" #include "refidadapterimp.hpp"
#include "columns.hpp" #include "columns.hpp"
#include "nestedtablewrapper.hpp"
#include "nestedcoladapterimp.hpp"
CSMWorld::RefIdColumn::RefIdColumn (int columnId, Display displayType, int flag, CSMWorld::RefIdColumn::RefIdColumn (int columnId, Display displayType, int flag,
bool editable, bool userEditable) 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 bool CSMWorld::RefIdColumn::isEditable() const
@ -25,8 +26,7 @@ bool CSMWorld::RefIdColumn::isUserEditable() const
return mUserEditable; return mUserEditable;
} }
const CSMWorld::RefIdAdapter& CSMWorld::RefIdCollection::findAdapter (UniversalId::Type type) const
const CSMWorld::RefIdAdapter& CSMWorld::RefIdCollection::findAdaptor (UniversalId::Type type) const
{ {
std::map<UniversalId::Type, RefIdAdapter *>::const_iterator iter = mAdapters.find (type); std::map<UniversalId::Type, RefIdAdapter *>::const_iterator iter = mAdapters.find (type);
@ -71,6 +71,32 @@ CSMWorld::RefIdCollection::RefIdCollection()
mColumns.push_back (RefIdColumn (Columns::ColumnId_CoinValue, ColumnBase::Display_Integer)); mColumns.push_back (RefIdColumn (Columns::ColumnId_CoinValue, ColumnBase::Display_Integer));
inventoryColumns.mValue = &mColumns.back(); 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<UniversalId::Type, NestedRefIdAdapterBase*> effectsMap;
effectsMap.insert(std::make_pair(UniversalId::Type_Potion,
new EffectsRefIdAdapter<ESM::Potion> (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); EnchantableColumns enchantableColumns (inventoryColumns);
mColumns.push_back (RefIdColumn (Columns::ColumnId_Enchantment, ColumnBase::Display_String)); 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)); mColumns.push_back (RefIdColumn (Columns::ColumnId_AiAlarm, ColumnBase::Display_Integer));
actorsColumns.mAlarm = &mColumns.back(); 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<UniversalId::Type, NestedRefIdAdapterBase*> inventoryMap;
inventoryMap.insert(std::make_pair(UniversalId::Type_Npc,
new NestedInventoryRefIdAdapter<ESM::NPC> (UniversalId::Type_Npc)));
inventoryMap.insert(std::make_pair(UniversalId::Type_Creature,
new NestedInventoryRefIdAdapter<ESM::Creature> (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<UniversalId::Type, NestedRefIdAdapterBase*> spellsMap;
spellsMap.insert(std::make_pair(UniversalId::Type_Npc,
new NestedSpellRefIdAdapter<ESM::NPC> (UniversalId::Type_Npc)));
spellsMap.insert(std::make_pair(UniversalId::Type_Creature,
new NestedSpellRefIdAdapter<ESM::Creature> (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<UniversalId::Type, NestedRefIdAdapterBase*> destMap;
destMap.insert(std::make_pair(UniversalId::Type_Npc,
new NestedTravelRefIdAdapter<ESM::NPC> (UniversalId::Type_Npc)));
destMap.insert(std::make_pair(UniversalId::Type_Creature,
new NestedTravelRefIdAdapter<ESM::Creature> (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<UniversalId::Type, NestedRefIdAdapterBase*> aiMap;
aiMap.insert(std::make_pair(UniversalId::Type_Npc,
new ActorAiRefIdAdapter<ESM::NPC> (UniversalId::Type_Npc)));
aiMap.insert(std::make_pair(UniversalId::Type_Creature,
new ActorAiRefIdAdapter<ESM::Creature> (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 static const struct
{ {
int mName; int mName;
@ -165,6 +279,19 @@ CSMWorld::RefIdCollection::RefIdCollection()
mColumns.push_back (RefIdColumn (Columns::ColumnId_Respawn, ColumnBase::Display_Boolean)); mColumns.push_back (RefIdColumn (Columns::ColumnId_Respawn, ColumnBase::Display_Boolean));
const RefIdColumn *respawn = &mColumns.back(); 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<UniversalId::Type, NestedRefIdAdapterBase*> contMap;
contMap.insert(std::make_pair(UniversalId::Type_Container,
new NestedInventoryRefIdAdapter<ESM::Container> (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); CreatureColumns creatureColumns (actorsColumns);
mColumns.push_back (RefIdColumn (Columns::ColumnId_CreatureType, ColumnBase::Display_CreatureType)); 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)); 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<UniversalId::Type, NestedRefIdAdapterBase*> partMap;
partMap.insert(std::make_pair(UniversalId::Type_Armor,
new BodyPartRefIdAdapter<ESM::Armor> (UniversalId::Type_Armor)));
partMap.insert(std::make_pair(UniversalId::Type_Clothing,
new BodyPartRefIdAdapter<ESM::Clothing> (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<UniversalId::Type, NestedRefIdAdapterBase*> levListMap;
levListMap.insert(std::make_pair(UniversalId::Type_CreatureLevelledList,
new NestedLevListRefIdAdapter<ESM::CreatureLevList> (UniversalId::Type_CreatureLevelledList)));
levListMap.insert(std::make_pair(UniversalId::Type_ItemLevelledList,
new NestedLevListRefIdAdapter<ESM::ItemLevList> (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<UniversalId::Type, NestedRefIdAdapterBase*> nestedListLevListMap;
nestedListLevListMap.insert(std::make_pair(UniversalId::Type_CreatureLevelledList,
new NestedListLevListRefIdAdapter<ESM::CreatureLevList> (UniversalId::Type_CreatureLevelledList)));
nestedListLevListMap.insert(std::make_pair(UniversalId::Type_ItemLevelledList,
new NestedListLevListRefIdAdapter<ESM::ItemLevList> (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, mAdapters.insert (std::make_pair (UniversalId::Type_Activator,
new NameRefIdAdapter<ESM::Activator> (UniversalId::Type_Activator, nameColumns))); new NameRefIdAdapter<ESM::Activator> (UniversalId::Type_Activator, nameColumns)));
mAdapters.insert (std::make_pair (UniversalId::Type_Potion, mAdapters.insert (std::make_pair (UniversalId::Type_Potion,
new PotionRefIdAdapter (inventoryColumns, autoCalc))); new PotionRefIdAdapter (potionColumns, autoCalc)));
mAdapters.insert (std::make_pair (UniversalId::Type_Apparatus, mAdapters.insert (std::make_pair (UniversalId::Type_Apparatus,
new ApparatusRefIdAdapter (inventoryColumns, apparatusType, toolsColumns.mQuality))); new ApparatusRefIdAdapter (inventoryColumns, apparatusType, toolsColumns.mQuality)));
mAdapters.insert (std::make_pair (UniversalId::Type_Armor, 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, mAdapters.insert (std::make_pair (UniversalId::Type_Book,
new BookRefIdAdapter (enchantableColumns, scroll, attribute))); new BookRefIdAdapter (enchantableColumns, scroll, attribute)));
mAdapters.insert (std::make_pair (UniversalId::Type_Clothing, 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, 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, mAdapters.insert (std::make_pair (UniversalId::Type_Creature,
new CreatureRefIdAdapter (creatureColumns))); new CreatureRefIdAdapter (creatureColumns)));
mAdapters.insert (std::make_pair (UniversalId::Type_Door, mAdapters.insert (std::make_pair (UniversalId::Type_Door,
@ -364,10 +541,10 @@ CSMWorld::RefIdCollection::RefIdCollection()
mAdapters.insert (std::make_pair (UniversalId::Type_Ingredient, mAdapters.insert (std::make_pair (UniversalId::Type_Ingredient,
new InventoryRefIdAdapter<ESM::Ingredient> (UniversalId::Type_Ingredient, inventoryColumns))); new InventoryRefIdAdapter<ESM::Ingredient> (UniversalId::Type_Ingredient, inventoryColumns)));
mAdapters.insert (std::make_pair (UniversalId::Type_CreatureLevelledList, mAdapters.insert (std::make_pair (UniversalId::Type_CreatureLevelledList,
new BaseRefIdAdapter<ESM::CreatureLevList> ( new LevelledListRefIdAdapter<ESM::CreatureLevList> (
UniversalId::Type_CreatureLevelledList, baseColumns))); UniversalId::Type_CreatureLevelledList, levListColumns)));
mAdapters.insert (std::make_pair (UniversalId::Type_ItemLevelledList, mAdapters.insert (std::make_pair (UniversalId::Type_ItemLevelledList,
new BaseRefIdAdapter<ESM::ItemLevList> (UniversalId::Type_ItemLevelledList, baseColumns))); new LevelledListRefIdAdapter<ESM::ItemLevList> (UniversalId::Type_ItemLevelledList, levListColumns)));
mAdapters.insert (std::make_pair (UniversalId::Type_Light, mAdapters.insert (std::make_pair (UniversalId::Type_Light,
new LightRefIdAdapter (lightColumns))); new LightRefIdAdapter (lightColumns)));
mAdapters.insert (std::make_pair (UniversalId::Type_Lockpick, mAdapters.insert (std::make_pair (UniversalId::Type_Lockpick,
@ -391,6 +568,14 @@ CSMWorld::RefIdCollection::~RefIdCollection()
for (std::map<UniversalId::Type, RefIdAdapter *>::iterator iter (mAdapters.begin()); for (std::map<UniversalId::Type, RefIdAdapter *>::iterator iter (mAdapters.begin());
iter!=mAdapters.end(); ++iter) iter!=mAdapters.end(); ++iter)
delete iter->second; delete iter->second;
for (std::vector<std::pair<const ColumnBase*, std::map<UniversalId::Type, NestedRefIdAdapterBase*> > >::iterator iter (mNestedAdapters.begin());
iter!=mNestedAdapters.end(); ++iter)
{
for (std::map<UniversalId::Type, NestedRefIdAdapterBase *>::iterator it ((iter->second).begin());
it!=(iter->second).end(); ++it)
delete it->second;
}
} }
int CSMWorld::RefIdCollection::getSize() const int CSMWorld::RefIdCollection::getSize() const
@ -427,25 +612,51 @@ QVariant CSMWorld::RefIdCollection::getData (int index, int column) const
{ {
RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); 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); 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) void CSMWorld::RefIdCollection::setData (int index, int column, const QVariant& data)
{ {
RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); 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); 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) void CSMWorld::RefIdCollection::removeRows (int index, int count)
{ {
mData.erase (index, 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) void CSMWorld::RefIdCollection::appendBlankRecord (const std::string& id, UniversalId::Type type)
{ {
mData.appendRecord (type, id, false); mData.appendRecord (type, id, false);
@ -478,7 +689,7 @@ void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin,
void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record, void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record,
UniversalId::Type type) UniversalId::Type type)
{ {
std::string id = findAdaptor (type).getId (record); std::string id = findAdapter (type).getId (record);
int index = mData.getAppendIndex (type); int index = mData.getAppendIndex (type);
@ -581,3 +792,68 @@ const CSMWorld::RefIdData& CSMWorld::RefIdCollection::getDataSet() const
return mData; 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<std::pair<const ColumnBase*, std::map<UniversalId::Type, NestedRefIdAdapterBase*> > >::const_iterator iter (mNestedAdapters.begin());
iter!=mNestedAdapters.end(); ++iter)
{
if ((iter->first) == &column)
{
std::map<UniversalId::Type, NestedRefIdAdapterBase*>::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");
}

@ -7,6 +7,7 @@
#include "columnbase.hpp" #include "columnbase.hpp"
#include "collectionbase.hpp" #include "collectionbase.hpp"
#include "nestedcollection.hpp"
#include "refiddata.hpp" #include "refiddata.hpp"
namespace ESM namespace ESM
@ -17,8 +18,10 @@ namespace ESM
namespace CSMWorld namespace CSMWorld
{ {
class RefIdAdapter; class RefIdAdapter;
struct NestedTableWrapperBase;
class NestedRefIdAdapterBase;
class RefIdColumn : public ColumnBase class RefIdColumn : public NestableColumn
{ {
bool mEditable; bool mEditable;
bool mUserEditable; bool mUserEditable;
@ -26,15 +29,15 @@ namespace CSMWorld
public: public:
RefIdColumn (int columnId, Display displayType, RefIdColumn (int columnId, Display displayType,
int flag = Flag_Table | Flag_Dialogue, bool editable = true, int flag = Flag_Table | Flag_Dialogue, bool editable = true,
bool userEditable = true); bool userEditable = true);
virtual bool isEditable() const; virtual bool isEditable() const;
virtual bool isUserEditable() const; virtual bool isUserEditable() const;
}; };
class RefIdCollection : public CollectionBase class RefIdCollection : public CollectionBase, public NestedCollection
{ {
private: private:
@ -42,11 +45,15 @@ namespace CSMWorld
std::deque<RefIdColumn> mColumns; std::deque<RefIdColumn> mColumns;
std::map<UniversalId::Type, RefIdAdapter *> mAdapters; std::map<UniversalId::Type, RefIdAdapter *> mAdapters;
std::vector<std::pair<const ColumnBase*, std::map<UniversalId::Type, NestedRefIdAdapterBase*> > > mNestedAdapters;
private: 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. ///< Throws an exception if no adaptor for \a Type can be found.
const NestedRefIdAdapterBase& getNestedAdapter(const ColumnBase &column, UniversalId::Type type) const;
public: public:
RefIdCollection(); RefIdCollection();
@ -69,7 +76,7 @@ namespace CSMWorld
virtual void removeRows (int index, int count); 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 std::string& destination,
const UniversalId::Type type); const UniversalId::Type type);
@ -110,11 +117,28 @@ namespace CSMWorld
/// ///
/// \return Success? /// \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; 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 #endif

@ -130,7 +130,7 @@ namespace CSMWorld
template<typename RecordT> template<typename RecordT>
void RefIdDataContainer<RecordT>::erase (int index, int count) void RefIdDataContainer<RecordT>::erase (int index, int count)
{ {
if (index<0 || index+count>=getSize()) if (index<0 || index+count>getSize())
throw std::runtime_error ("invalid RefIdDataContainer index"); throw std::runtime_error ("invalid RefIdDataContainer index");
mContainer.erase (mContainer.begin()+index, mContainer.begin()+index+count); mContainer.erase (mContainer.begin()+index, mContainer.begin()+index+count);
@ -231,27 +231,27 @@ namespace CSMWorld
void save (int index, ESM::ESMWriter& writer) const; void save (int index, ESM::ESMWriter& writer) const;
//RECORD CONTAINERS ACCESS METHODS //RECORD CONTAINERS ACCESS METHODS
const RefIdDataContainer<ESM::Book>& getBooks() const; const RefIdDataContainer<ESM::Book>& getBooks() const;
const RefIdDataContainer<ESM::Activator>& getActivators() const; const RefIdDataContainer<ESM::Activator>& getActivators() const;
const RefIdDataContainer<ESM::Potion>& getPotions() const; const RefIdDataContainer<ESM::Potion>& getPotions() const;
const RefIdDataContainer<ESM::Apparatus>& getApparati() const; const RefIdDataContainer<ESM::Apparatus>& getApparati() const;
const RefIdDataContainer<ESM::Armor>& getArmors() const; const RefIdDataContainer<ESM::Armor>& getArmors() const;
const RefIdDataContainer<ESM::Clothing>& getClothing() const; const RefIdDataContainer<ESM::Clothing>& getClothing() const;
const RefIdDataContainer<ESM::Container>& getContainers() const; const RefIdDataContainer<ESM::Container>& getContainers() const;
const RefIdDataContainer<ESM::Creature>& getCreatures() const; const RefIdDataContainer<ESM::Creature>& getCreatures() const;
const RefIdDataContainer<ESM::Door>& getDoors() const; const RefIdDataContainer<ESM::Door>& getDoors() const;
const RefIdDataContainer<ESM::Ingredient>& getIngredients() const; const RefIdDataContainer<ESM::Ingredient>& getIngredients() const;
const RefIdDataContainer<ESM::CreatureLevList>& getCreatureLevelledLists() const; const RefIdDataContainer<ESM::CreatureLevList>& getCreatureLevelledLists() const;
const RefIdDataContainer<ESM::ItemLevList>& getItemLevelledList() const; const RefIdDataContainer<ESM::ItemLevList>& getItemLevelledList() const;
const RefIdDataContainer<ESM::Light>& getLights() const; const RefIdDataContainer<ESM::Light>& getLights() const;
const RefIdDataContainer<ESM::Lockpick>& getLocpicks() const; const RefIdDataContainer<ESM::Lockpick>& getLocpicks() const;
const RefIdDataContainer<ESM::Miscellaneous>& getMiscellaneous() const; const RefIdDataContainer<ESM::Miscellaneous>& getMiscellaneous() const;
const RefIdDataContainer<ESM::NPC>& getNPCs() const; const RefIdDataContainer<ESM::NPC>& getNPCs() const;
const RefIdDataContainer<ESM::Weapon >& getWeapons() const; const RefIdDataContainer<ESM::Weapon >& getWeapons() const;
const RefIdDataContainer<ESM::Probe >& getProbes() const; const RefIdDataContainer<ESM::Probe >& getProbes() const;
const RefIdDataContainer<ESM::Repair>& getRepairs() const; const RefIdDataContainer<ESM::Repair>& getRepairs() const;
const RefIdDataContainer<ESM::Static>& getStatics() const; const RefIdDataContainer<ESM::Static>& getStatics() const;
}; };
} }

@ -1,6 +1,8 @@
#ifndef CSM_WOLRD_SUBCOLLECTION_H #ifndef CSM_WOLRD_SUBCOLLECTION_H
#define CSM_WOLRD_SUBCOLLECTION_H #define CSM_WOLRD_SUBCOLLECTION_H
#include "nestedidcollection.hpp"
namespace ESM namespace ESM
{ {
class ESMReader; class ESMReader;
@ -14,7 +16,7 @@ namespace CSMWorld
/// \brief Single type collection of top level records that are associated with cells /// \brief Single type collection of top level records that are associated with cells
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> > template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
class SubCellCollection : public IdCollection<ESXRecordT, IdAccessorT> class SubCellCollection : public NestedIdCollection<ESXRecordT, IdAccessorT>
{ {
const IdCollection<Cell>& mCells; const IdCollection<Cell>& mCells;
@ -23,8 +25,6 @@ namespace CSMWorld
public: public:
SubCellCollection (const IdCollection<Cell>& cells); SubCellCollection (const IdCollection<Cell>& cells);
}; };
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
@ -39,7 +39,6 @@ namespace CSMWorld
const IdCollection<Cell>& cells) const IdCollection<Cell>& cells)
: mCells (cells) : mCells (cells)
{} {}
} }
#endif #endif

@ -38,9 +38,9 @@ namespace
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Enchantments, "Enchantments", 0 }, { 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_BodyParts, "Body Parts", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables,
"Referenceables", 0 }, "Objects", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References,
"References", 0 }, "Instances", 0 },
{ CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, { CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap,
"Region Map", 0 }, "Region Map", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", 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_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, "Cell", ":./cell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell_Missing, "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_Activator, "Activator", ":./activator.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":./potion.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":./potion.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Apparatus, "Apparatus", ":./apparatus.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_Repair, "Repair", ":./repair.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, "Static", ":./static.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_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_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_Scene, "Scene", 0 },
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", 0 }, { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", 0 },

@ -94,7 +94,7 @@ void CSVDoc::View::setupEditMenu()
QAction *search = new QAction (tr ("Search"), this); QAction *search = new QAction (tr ("Search"), this);
connect (search, SIGNAL (triggered()), this, SLOT (addSearchSubView())); connect (search, SIGNAL (triggered()), this, SLOT (addSearchSubView()));
edit->addAction (search); edit->addAction (search);
} }
void CSVDoc::View::setupViewMenu() void CSVDoc::View::setupViewMenu()
@ -131,11 +131,11 @@ void CSVDoc::View::setupWorldMenu()
connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView())); connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView()));
world->addAction (cells); world->addAction (cells);
QAction *referenceables = new QAction (tr ("Referenceables"), this); QAction *referenceables = new QAction (tr ("Objects"), this);
connect (referenceables, SIGNAL (triggered()), this, SLOT (addReferenceablesSubView())); connect (referenceables, SIGNAL (triggered()), this, SLOT (addReferenceablesSubView()));
world->addAction (referenceables); world->addAction (referenceables);
QAction *references = new QAction (tr ("References"), this); QAction *references = new QAction (tr ("Instances"), this);
connect (references, SIGNAL (triggered()), this, SLOT (addReferencesSubView())); connect (references, SIGNAL (triggered()), this, SLOT (addReferencesSubView()));
world->addAction (references); world->addAction (references);

@ -84,7 +84,13 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager)
{ CSMWorld::ColumnBase::Display_MeshType, CSMWorld::Columns::ColumnId_MeshType, false }, { CSMWorld::ColumnBase::Display_MeshType, CSMWorld::Columns::ColumnId_MeshType, false },
{ CSMWorld::ColumnBase::Display_Gender, CSMWorld::Columns::ColumnId_Gender, true }, { CSMWorld::ColumnBase::Display_Gender, CSMWorld::Columns::ColumnId_Gender, true },
{ CSMWorld::ColumnBase::Display_SoundGeneratorType, CSMWorld::Columns::ColumnId_SoundGeneratorType, false }, { 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; i<sizeof (sMapping)/sizeof (Mapping); ++i) for (std::size_t i=0; i<sizeof (sMapping)/sizeof (Mapping); ++i)

@ -249,7 +249,7 @@ unsigned int CSVRender::WorldspaceWidget::getInteractionMask() const
void CSVRender::WorldspaceWidget::addVisibilitySelectorButtons ( void CSVRender::WorldspaceWidget::addVisibilitySelectorButtons (
CSVWidget::SceneToolToggle2 *tool) CSVWidget::SceneToolToggle2 *tool)
{ {
tool->addButton (Element_Reference, "References"); tool->addButton (Element_Reference, "Instances");
tool->addButton (Element_Water, "Water"); tool->addButton (Element_Water, "Water");
tool->addButton (Element_Pathgrid, "Pathgrid"); tool->addButton (Element_Pathgrid, "Pathgrid");
} }
@ -258,7 +258,7 @@ void CSVRender::WorldspaceWidget::addEditModeSelectorButtons (CSVWidget::SceneTo
{ {
/// \todo replace EditMode with suitable subclasses /// \todo replace EditMode with suitable subclasses
tool->addButton ( tool->addButton (
new EditMode (this, QIcon (":placeholder"), Element_Reference, "Reference editing"), new EditMode (this, QIcon (":placeholder"), Element_Reference, "Instance editing"),
"object"); "object");
tool->addButton ( tool->addButton (
new EditMode (this, QIcon (":placeholder"), Element_Pathgrid, "Pathgrid editing"), new EditMode (this, QIcon (":placeholder"), Element_Pathgrid, "Pathgrid editing"),

@ -12,7 +12,7 @@
CSVSettings::RangeView::RangeView (CSMSettings::Setting *setting, CSVSettings::RangeView::RangeView (CSMSettings::Setting *setting,
Page *parent) Page *parent)
: mRangeWidget (0), mRangeType (setting->type()), View (setting, parent) : View (setting, parent), mRangeWidget (0), mRangeType (setting->type())
{ {
mRangeWidget = 0; mRangeWidget = 0;

@ -5,7 +5,7 @@
#include "../../model/settings/setting.hpp" #include "../../model/settings/setting.hpp"
CSVSettings::TextView::TextView(CSMSettings::Setting *setting, Page *parent) CSVSettings::TextView::TextView(CSMSettings::Setting *setting, Page *parent)
: mDelimiter (setting->delimiter()), View (setting, parent) : View (setting, parent), mDelimiter (setting->delimiter())
{ {
if (setting->isMultiLine()) if (setting->isMultiLine())

@ -131,8 +131,8 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document,
setModel (mModel); setModel (mModel);
setColumnHidden (2, true); setColumnHidden (2, true);
mIdTypeDelegate = CSVWorld::IdTypeDelegateFactory().makeDelegate ( mIdTypeDelegate = CSVWorld::IdTypeDelegateFactory().makeDelegate (0,
document, this); mDocument, this);
setItemDelegateForColumn (0, mIdTypeDelegate); setItemDelegateForColumn (0, mIdTypeDelegate);

@ -7,6 +7,7 @@
#include "../../model/tools/search.hpp" #include "../../model/tools/search.hpp"
#include "../../model/tools/reportmodel.hpp" #include "../../model/tools/reportmodel.hpp"
#include "../../model/world/idtablebase.hpp" #include "../../model/world/idtablebase.hpp"
#include "../../model/settings/usersettings.hpp"
#include "reporttable.hpp" #include "reporttable.hpp"
#include "searchbox.hpp" #include "searchbox.hpp"
@ -22,7 +23,13 @@ void CSVTools::SearchSubView::replace (bool selection)
const CSMTools::ReportModel& model = const CSMTools::ReportModel& model =
dynamic_cast<const CSMTools::ReportModel&> (*mTable->model()); dynamic_cast<const CSMTools::ReportModel&> (*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 // We are running through the indices in reverse order to avoid messing up multiple results
// in a single string. // in a single string.
for (std::vector<int>::const_reverse_iterator iter (indices.rbegin()); iter!=indices.rend(); ++iter) for (std::vector<int>::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<CSMWorld::IdTableBase&> ( CSMWorld::IdTableBase *table = &dynamic_cast<CSMWorld::IdTableBase&> (
*mDocument.getData().getTableModel (type)); *mDocument.getData().getTableModel (type));
if (table!=currentTable)
{
search.configure (table);
currentTable = table;
}
std::string hint = model.getHint (*iter); std::string hint = model.getHint (*iter);
mSearch.replace (mDocument, table, id, hint, replace); if (search.verify (mDocument, table, id, hint))
mTable->flagAsReplaced (*iter); {
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) CSVTools::SearchSubView::SearchSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document)
: CSVDoc::SubView (id), mDocument (document), mPaddingBefore (10), mPaddingAfter (10), : CSVDoc::SubView (id), mDocument (document), mLocked (false)
mLocked (false)
{ {
QVBoxLayout *layout = new QVBoxLayout; QVBoxLayout *layout = new QVBoxLayout;
@ -84,14 +102,6 @@ void CSVTools::SearchSubView::setEditLock (bool locked)
void CSVTools::SearchSubView::updateUserSetting (const QString &name, const QStringList &list) void CSVTools::SearchSubView::updateUserSetting (const QString &name, const QStringList &list)
{ {
mTable->updateUserSetting (name, 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) 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) 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 = search;
mSearch.setPadding (mPaddingBefore, mPaddingAfter); mSearch.setPadding (paddingBefore, paddingAfter);
mTable->clear(); mTable->clear();
mDocument.runSearch (getUniversalId(), mSearch); mDocument.runSearch (getUniversalId(), mSearch);

@ -26,8 +26,6 @@ namespace CSVTools
ReportTable *mTable; ReportTable *mTable;
SearchBox mSearchBox; SearchBox mSearchBox;
CSMDoc::Document& mDocument; CSMDoc::Document& mDocument;
int mPaddingBefore;
int mPaddingAfter;
CSMTools::Search mSearch; CSMTools::Search mSearch;
bool mLocked; bool mLocked;

@ -40,6 +40,9 @@ namespace CSVWorld
/// Default implementation: Throw an exception if scope!=Scope_Content. /// Default implementation: Throw an exception if scope!=Scope_Content.
virtual void setScope (unsigned int scope); virtual void setScope (unsigned int scope);
/// Focus main input widget
virtual void focus() = 0;
signals: signals:
void done(); void done();

@ -6,11 +6,12 @@
CSVWorld::DataDisplayDelegate::DataDisplayDelegate(const ValueList &values, CSVWorld::DataDisplayDelegate::DataDisplayDelegate(const ValueList &values,
const IconList &icons, const IconList &icons,
CSMWorld::CommandDispatcher *dispatcher,
CSMDoc::Document& document, CSMDoc::Document& document,
const QString &pageName, const QString &pageName,
const QString &settingName, const QString &settingName,
QObject *parent) 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), mIcons (icons), mIconSize (QSize(16, 16)), mIconLeftOffset(3),
mTextLeftOffset(8), mSettingKey (pageName + '/' + settingName) mTextLeftOffset(8), mSettingKey (pageName + '/' + settingName)
{ {
@ -136,9 +137,9 @@ void CSVWorld::DataDisplayDelegateFactory::add (int enumValue, QString enumName,
} }
CSVWorld::CommandDelegate *CSVWorld::DataDisplayDelegateFactory::makeDelegate ( 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);
} }

@ -38,12 +38,9 @@ namespace CSVWorld
QString mSettingKey; QString mSettingKey;
public: public:
explicit DataDisplayDelegate (const ValueList & values, DataDisplayDelegate (const ValueList & values, const IconList & icons,
const IconList & icons, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document,
CSMDoc::Document& document, const QString &pageName, const QString &settingName, QObject *parent);
const QString &pageName,
const QString &settingName,
QObject *parent);
~DataDisplayDelegate(); ~DataDisplayDelegate();
@ -82,7 +79,7 @@ namespace CSVWorld
public: 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. ///< The ownership of the returned CommandDelegate is transferred to the caller.
protected: protected:

@ -17,21 +17,25 @@
#include <QLineEdit> #include <QLineEdit>
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <QComboBox> #include <QComboBox>
#include <QScrollArea>
#include <QPushButton> #include <QPushButton>
#include <QToolButton> #include <QToolButton>
#include <QHeaderView>
#include "../../model/world/nestedtableproxymodel.hpp"
#include "../../model/world/columnbase.hpp" #include "../../model/world/columnbase.hpp"
#include "../../model/world/idtable.hpp" #include "../../model/world/idtable.hpp"
#include "../../model/world/idtree.hpp"
#include "../../model/world/columns.hpp" #include "../../model/world/columns.hpp"
#include "../../model/world/record.hpp" #include "../../model/world/record.hpp"
#include "../../model/world/tablemimedata.hpp" #include "../../model/world/tablemimedata.hpp"
#include "../../model/doc/document.hpp" #include "../../model/world/idtree.hpp"
#include "../../model/world/commands.hpp" #include "../../model/world/commands.hpp"
#include "../../model/doc/document.hpp"
#include "recordstatusdelegate.hpp" #include "recordstatusdelegate.hpp"
#include "util.hpp" #include "util.hpp"
#include "tablebottombox.hpp" #include "tablebottombox.hpp"
#include "nestedtable.hpp"
/* /*
==============================NotEditableSubDelegate========================================== ==============================NotEditableSubDelegate==========================================
*/ */
@ -136,7 +140,7 @@ void CSVWorld::DialogueDelegateDispatcherProxy::tableMimeDataDropped(const std::
CSMWorld::UniversalId::Type type = data[i].getType(); CSMWorld::UniversalId::Type type = data[i].getType();
if (mDisplay == CSMWorld::ColumnBase::Display_Referenceable) 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_Potion
|| type == CSMWorld::UniversalId::Type_Apparatus || type == CSMWorld::UniversalId::Type_Apparatus
|| type == CSMWorld::UniversalId::Type_Armor || type == CSMWorld::UniversalId::Type_Armor
@ -172,10 +176,12 @@ void CSVWorld::DialogueDelegateDispatcherProxy::tableMimeDataDropped(const std::
==============================DialogueDelegateDispatcher========================================== ==============================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), mParent(parent),
mTable(table), mTable(model ? model : table),
mDocument (document), mCommandDispatcher (commandDispatcher), mDocument (document),
mNotEditableDelegate(table, parent) mNotEditableDelegate(table, parent)
{ {
} }
@ -187,7 +193,7 @@ CSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CS
if (delegateIt == mDelegates.end()) if (delegateIt == mDelegates.end())
{ {
delegate = CommandDelegateFactoryCollection::get().makeDelegate ( delegate = CommandDelegateFactoryCollection::get().makeDelegate (
display, mDocument, mParent); display, &mCommandDispatcher, mDocument, mParent);
mDelegates.insert(std::make_pair(display, delegate)); mDelegates.insert(std::make_pair(display, delegate));
} else } else
{ {
@ -196,7 +202,8 @@ CSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CS
return delegate; 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); 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); 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<int, CommandDelegate*>::const_iterator delegateIt(mDelegates.find(display)); std::map<int, CommandDelegate*>::const_iterator delegateIt(mDelegates.find(display));
if (delegateIt != mDelegates.end()) 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 //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 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(); QVariant variant = index.data();
if (!variant.isValid()) if (!variant.isValid())
@ -267,13 +279,17 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase::
QWidget* editor = NULL; QWidget* editor = NULL;
if (! (mTable->flags (index) & Qt::ItemIsEditable)) if (! (mTable->flags (index) & Qt::ItemIsEditable))
{ {
return mNotEditableDelegate.createEditor(qobject_cast<QWidget*>(mParent), QStyleOptionViewItem(), index); return mNotEditableDelegate.createEditor(qobject_cast<QWidget*>(mParent),
QStyleOptionViewItem(), index);
} }
std::map<int, CommandDelegate*>::iterator delegateIt(mDelegates.find(display)); std::map<int, CommandDelegate*>::iterator delegateIt(mDelegates.find(display));
if (delegateIt != mDelegates.end()) if (delegateIt != mDelegates.end())
{ {
editor = delegateIt->second->createEditor(qobject_cast<QWidget*>(mParent), QStyleOptionViewItem(), index, display); editor = delegateIt->second->createEditor(qobject_cast<QWidget*>(mParent),
QStyleOptionViewItem(), index, display);
DialogueDelegateDispatcherProxy* proxy = new DialogueDelegateDispatcherProxy(editor, display); DialogueDelegateDispatcherProxy* proxy = new DialogueDelegateDispatcherProxy(editor, display);
// NOTE: For each entry in CSVWorld::CommandDelegate::createEditor() a corresponding entry // NOTE: For each entry in CSVWorld::CommandDelegate::createEditor() a corresponding entry
@ -281,10 +297,13 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase::
if (qobject_cast<DropLineEdit*>(editor)) if (qobject_cast<DropLineEdit*>(editor))
{ {
connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited())); connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited()));
connect(editor, SIGNAL(tableMimeDataDropped(const std::vector<CSMWorld::UniversalId>&, const CSMDoc::Document*)), connect(editor, SIGNAL(tableMimeDataDropped(const std::vector<CSMWorld::UniversalId>&, const CSMDoc::Document*)),
proxy, SLOT(tableMimeDataDropped(const std::vector<CSMWorld::UniversalId>&, const CSMDoc::Document*))); proxy, SLOT(tableMimeDataDropped(const std::vector<CSMWorld::UniversalId>&, const CSMDoc::Document*)));
connect(proxy, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, 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*))); this, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)));
} }
else if (qobject_cast<QCheckBox*>(editor)) else if (qobject_cast<QCheckBox*>(editor))
{ {
@ -305,7 +324,9 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase::
else // throw an exception because this is a coding error else // throw an exception because this is a coding error
throw std::logic_error ("Dialogue editor type missing"); 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 mProxys.push_back(proxy); //deleted in the destructor
} }
return editor; return editor;
@ -323,20 +344,43 @@ CSVWorld::DialogueDelegateDispatcher::~DialogueDelegateDispatcher()
=============================================================EditWidget===================================================== =============================================================EditWidget=====================================================
*/ */
CSVWorld::EditWidget::EditWidget(QWidget *parent, int row, CSMWorld::IdTable* table, CSMDoc::Document& document, bool createAndDelete) : CSVWorld::EditWidget::~EditWidget()
mDispatcher(this, table, document), {
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), QScrollArea(parent),
mWidgetMapper(NULL), mWidgetMapper(NULL),
mNestedTableMapper(NULL),
mMainWidget(NULL), mMainWidget(NULL),
mCommandDispatcher (commandDispatcher),
mDocument (document), mDocument (document),
mTable(table) mTable(table)
{ {
remake (row); 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) void CSVWorld::EditWidget::remake(int row)
{ {
for (unsigned i = 0; i < mNestedModels.size(); ++i)
{
delete mNestedModels[i];
}
mNestedModels.clear();
delete mNestedTableDispatcher;
if (mMainWidget) if (mMainWidget)
{ {
delete mMainWidget; delete mMainWidget;
@ -350,7 +394,13 @@ void CSVWorld::EditWidget::remake(int row)
delete mWidgetMapper; delete mWidgetMapper;
mWidgetMapper = 0; mWidgetMapper = 0;
} }
if (mNestedTableMapper)
{
delete mNestedTableMapper;
mNestedTableMapper = 0;
}
mWidgetMapper = new QDataWidgetMapper (this); mWidgetMapper = new QDataWidgetMapper (this);
mWidgetMapper->setModel(mTable); mWidgetMapper->setModel(mTable);
mWidgetMapper->setItemDelegate(&mDispatcher); mWidgetMapper->setItemDelegate(&mDispatcher);
@ -360,17 +410,28 @@ void CSVWorld::EditWidget::remake(int row)
line->setFrameShape(QFrame::HLine); line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken); 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); QVBoxLayout *mainLayout = new QVBoxLayout(mMainWidget);
QGridLayout *unlockedLayout = new QGridLayout();
QGridLayout *lockedLayout = 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->addWidget(line, 1);
mainLayout->addLayout(unlockedLayout, 2); mainLayout->addLayout(unlockedLayout, QSizePolicy::Preferred);
mainLayout->addWidget(line2, 1);
mainLayout->addLayout(tablesLayout, QSizePolicy::Preferred);
mainLayout->addStretch(1); mainLayout->addStretch(1);
int unlocked = 0; int unlocked = 0;
int locked = 0; int locked = 0;
const int columns = mTable->columnCount(); const int columns = mTable->columnCount();
for (int i=0; i<columns; ++i) for (int i=0; i<columns; ++i)
{ {
int flags = mTable->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); int flags = mTable->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt();
@ -380,32 +441,123 @@ void CSVWorld::EditWidget::remake(int row)
CSMWorld::ColumnBase::Display display = static_cast<CSMWorld::ColumnBase::Display> CSMWorld::ColumnBase::Display display = static_cast<CSMWorld::ColumnBase::Display>
(mTable->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); (mTable->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt());
mDispatcher.makeDelegate(display); if (mTable->hasChildren(mTable->index(row, i)) &&
QWidget *editor = mDispatcher.makeEditor(display, (mTable->index (row, i))); !(flags & CSMWorld::ColumnBase::Flag_Dialogue_List))
{
mNestedModels.push_back(new CSMWorld::NestedTableProxyModel (
mTable->index(row, i), display, dynamic_cast<CSMWorld::IdTree*>(mTable)));
int idColumn = mTable->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
int typeColumn = mTable->findColumnIndex (CSMWorld::Columns::ColumnId_RecordType);
CSMWorld::UniversalId id = CSMWorld::UniversalId(
static_cast<CSMWorld::UniversalId::Type> (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); mDispatcher.makeDelegate (display);
QLabel* label = new QLabel(mTable->headerData (i, Qt::Horizontal).toString(), mMainWidget); QWidget* editor = mDispatcher.makeEditor (display, (mTable->index (row, i)));
label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
editor->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); if (editor)
if (! (mTable->flags (mTable->index (row, i)) & Qt::ItemIsEditable))
{ {
lockedLayout->addWidget (label, locked, 0); mWidgetMapper->addMapping (editor, i);
lockedLayout->addWidget (editor, locked, 1);
++locked; QLabel* label = new QLabel (mTable->headerData (i, Qt::Horizontal).toString(), mMainWidget);
} else
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<CSMWorld::IdTree *>(mTable)->index(row, i),
display, static_cast<CSMWorld::IdTree *>(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); int displayRole = mNestedModels.back()->headerData (col,
unlockedLayout->addWidget (editor, unlocked, 1); Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt();
++unlocked;
CSMWorld::ColumnBase::Display display =
static_cast<CSMWorld::ColumnBase::Display> (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)); mWidgetMapper->setCurrentModelIndex(mTable->index(row, 0));
if (unlocked == 0)
mainLayout->removeWidget(line);
this->setWidget(mMainWidget); this->setWidget(mMainWidget);
this->setWidgetResizable(true); this->setWidgetResizable(true);
} }
@ -421,13 +573,14 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM
mMainLayout(NULL), mMainLayout(NULL),
mUndoStack(document.getUndoStack()), mUndoStack(document.getUndoStack()),
mTable(dynamic_cast<CSMWorld::IdTable*>(document.getData().getTableModel(id))), mTable(dynamic_cast<CSMWorld::IdTable*>(document.getData().getTableModel(id))),
mRow (-1),
mLocked(false), mLocked(false),
mDocument(document), mDocument(document),
mCommandDispatcher (document, CSMWorld::UniversalId::getParentType (id.getType())) mCommandDispatcher (document, CSMWorld::UniversalId::getParentType (id.getType()))
{ {
connect(mTable, SIGNAL(dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT(dataChanged(const QModelIndex&))); 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); QWidget *mainWidget = new QWidget(this);
QHBoxLayout *buttonsLayout = new QHBoxLayout; QHBoxLayout *buttonsLayout = new QHBoxLayout;
@ -480,12 +633,13 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM
connect(nextButton, SIGNAL(clicked()), this, SLOT(nextId())); connect(nextButton, SIGNAL(clicked()), this, SLOT(nextId()));
connect(prevButton, SIGNAL(clicked()), this, SLOT(prevId())); connect(prevButton, SIGNAL(clicked()), this, SLOT(prevId()));
connect(cloneButton, SIGNAL(clicked()), this, SLOT(cloneRequest())); connect(cloneButton, SIGNAL(clicked()), this, SLOT(cloneRequest()));
connect(revertButton, SIGNAL(clicked()), this, SLOT(revertRecord())); connect(revertButton, SIGNAL(clicked()), &mCommandDispatcher, SLOT(executeRevert()));
connect(deleteButton, SIGNAL(clicked()), this, SLOT(deleteRecord())); connect(deleteButton, SIGNAL(clicked()), &mCommandDispatcher, SLOT(executeDelete()));
mMainLayout = new QVBoxLayout(mainWidget); 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*)), 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*))); 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)); new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this));
mBottom->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); mBottom->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
connect(mBottom, SIGNAL(requestFocus(const std::string&)), this, SLOT(requestFocus(const std::string&))); connect(mBottom, SIGNAL(requestFocus(const std::string&)), this, SLOT(requestFocus(const std::string&)));
connect(addButton, SIGNAL(clicked()), mBottom, SLOT(createRequest())); connect(addButton, SIGNAL(clicked()), mBottom, SLOT(createRequest()));
if(!mBottom->canCreateAndDelete()) if(!mBottom->canCreateAndDelete())
{ {
cloneButton->setDisabled(true); cloneButton->setDisabled (true);
addButton->setDisabled(true); addButton->setDisabled (true);
deleteButton->setDisabled(true); deleteButton->setDisabled (true);
} }
dataChanged(mTable->index(mRow, 0)); dataChanged(mTable->getModelIndex (mCurrentId, 0));
mMainLayout->addLayout(buttonsLayout); mMainLayout->addLayout (buttonsLayout);
setWidget(mainWidget); 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) if (newRow < 0)
{ {
return; return;
@ -531,19 +688,23 @@ void CSVWorld::DialogueSubView::prevId()
if (!(state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased)) if (!(state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased))
{ {
mEditWidget->remake(newRow); mEditWidget->remake(newRow);
setUniversalId(CSMWorld::UniversalId (static_cast<CSMWorld::UniversalId::Type> (mTable->data (mTable->index (newRow, 2)).toInt()), setUniversalId(CSMWorld::UniversalId (static_cast<CSMWorld::UniversalId::Type> (mTable->data (mTable->index (newRow, 2)).toInt()),
mTable->data (mTable->index (newRow, 0)).toString().toUtf8().constData())); 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); mEditWidget->setDisabled(mLocked);
return; return;
} }
--newRow; --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()) if (newRow >= mTable->rowCount())
{ {
@ -560,13 +721,17 @@ void CSVWorld::DialogueSubView::nextId()
} }
CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State>(mTable->data (mTable->index (newRow, 1)).toInt()); CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State>(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); mEditWidget->remake(newRow);
setUniversalId(CSMWorld::UniversalId (static_cast<CSMWorld::UniversalId::Type> (mTable->data (mTable->index (newRow, 2)).toInt()), setUniversalId(CSMWorld::UniversalId (static_cast<CSMWorld::UniversalId::Type> (mTable->data (mTable->index (newRow, 2)).toInt()),
mTable->data (mTable->index (newRow, 0)).toString().toUtf8().constData())); 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); mEditWidget->setDisabled(mLocked);
return; return;
} }
++newRow; ++newRow;
@ -576,28 +741,35 @@ void CSVWorld::DialogueSubView::nextId()
void CSVWorld::DialogueSubView::setEditLock (bool locked) void CSVWorld::DialogueSubView::setEditLock (bool locked)
{ {
mLocked = locked; mLocked = locked;
QModelIndex currentIndex(mTable->getModelIndex(mCurrentId, 0));
CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State>(mTable->data (mTable->index (mRow, 1)).toInt()); if (currentIndex.isValid())
{
CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State>(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<CSMWorld::RecordBase::State>(mTable->data (mTable->index (mRow, 1)).toInt()); CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State>(mTable->data (mTable->index (currentIndex.row(), 1)).toInt());
mEditWidget->setDisabled (state==CSMWorld::RecordBase::State_Deleted || mLocked); mEditWidget->setDisabled (state==CSMWorld::RecordBase::State_Deleted || mLocked);
} }
} }
void CSVWorld::DialogueSubView::tableMimeDataDropped(QWidget* editor, void CSVWorld::DialogueSubView::tableMimeDataDropped (QWidget* editor,
const QModelIndex& index, const QModelIndex& index,
const CSMWorld::UniversalId& id, const CSMWorld::UniversalId& id,
const CSMDoc::Document* document) const CSMDoc::Document* document)
{ {
if (document == &mDocument) 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<CSMWorld::RecordBase::State> (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<CSMWorld::RecordBase::State>(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) void CSVWorld::DialogueSubView::requestFocus (const std::string& id)
{ {
mRow = mTable->getModelIndex (id, 0).row(); changeCurrentId(id);
mEditWidget->remake(mRow);
mEditWidget->remake(mTable->getModelIndex (id, 0).row());
} }
void CSVWorld::DialogueSubView::cloneRequest () void CSVWorld::DialogueSubView::cloneRequest ()
{ {
mBottom->cloneRequest(mTable->data(mTable->index (mRow, 0)).toString().toUtf8().constData(), mBottom->cloneRequest(mCurrentId, static_cast<CSMWorld::UniversalId::Type>(mTable->data(mTable->getModelIndex(mCurrentId, 2)).toInt()));
static_cast<CSMWorld::UniversalId::Type>(mTable->data(mTable->index(mRow, 2)).toInt()));
} }
void CSVWorld::DialogueSubView::showPreview () void CSVWorld::DialogueSubView::showPreview ()
{ {
if ((mTable->getFeatures() & CSMWorld::IdTable::Feature_Preview) && QModelIndex currentIndex(mTable->getModelIndex(mCurrentId, 0));
mRow < mTable->rowCount())
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<CSMWorld::UniversalId, std::string> params = mTable->view (mRow); std::pair<CSMWorld::UniversalId, std::string> params = mTable->view (currentIndex.row());
if (params.first.getType()!=CSMWorld::UniversalId::Type_None) if (params.first.getType()!=CSMWorld::UniversalId::Type_None)
emit focusId (params.first, params.second); emit focusId (params.first, params.second);
} }
} }
void CSVWorld::DialogueSubView::changeCurrentId (const std::string& newId)
{
std::vector<std::string> selection;
mCurrentId = std::string(newId);
selection.push_back(mCurrentId);
mCommandDispatcher.setSelection(selection);
}

@ -21,6 +21,7 @@ class QVBoxLayout;
namespace CSMWorld namespace CSMWorld
{ {
class IdTable; class IdTable;
class NestedTableProxyModel;
} }
namespace CSMDoc namespace CSMDoc
@ -38,16 +39,20 @@ namespace CSVWorld
{ {
const CSMWorld::IdTable* mTable; const CSMWorld::IdTable* mTable;
public: 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 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 paint (QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; virtual void paint (QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const;
///< does nothing ///< does nothing
virtual QSize sizeHint (const QStyleOptionViewItem& option, const QModelIndex& index) const; virtual QSize sizeHint (const QStyleOptionViewItem& option,
const QModelIndex& index) const;
///< does nothing ///< does nothing
virtual QWidget *createEditor (QWidget *parent, virtual QWidget *createEditor (QWidget *parent,
@ -74,16 +79,20 @@ namespace CSVWorld
std::auto_ptr<refWrapper> mIndexWrapper; std::auto_ptr<refWrapper> mIndexWrapper;
public: public:
DialogueDelegateDispatcherProxy(QWidget* editor, CSMWorld::ColumnBase::Display display); DialogueDelegateDispatcherProxy(QWidget* editor,
CSMWorld::ColumnBase::Display display);
QWidget* getEditor() const; QWidget* getEditor() const;
public slots: public slots:
void editorDataCommited(); void editorDataCommited();
void setIndex(const QModelIndex& index); void setIndex(const QModelIndex& index);
void tableMimeDataDropped(const std::vector<CSMWorld::UniversalId>& data, const CSMDoc::Document* document); void tableMimeDataDropped(const std::vector<CSMWorld::UniversalId>& data,
const CSMDoc::Document* document);
signals: 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, void tableMimeDataDropped(QWidget* editor, const QModelIndex& index,
const CSMWorld::UniversalId& id, const CSMWorld::UniversalId& id,
@ -98,59 +107,79 @@ namespace CSVWorld
QObject* mParent; QObject* mParent;
CSMWorld::IdTable* mTable; QAbstractItemModel* mTable;
CSMDoc::Document& mDocument; CSMWorld::CommandDispatcher& mCommandDispatcher;
CSMDoc::Document& mDocument;
NotEditableSubDelegate mNotEditableDelegate; NotEditableSubDelegate mNotEditableDelegate;
std::vector<DialogueDelegateDispatcherProxy*> mProxys; //once we move to the C++11 we should use unique_ptr std::vector<DialogueDelegateDispatcherProxy*> mProxys;
//once we move to the C++11 we should use unique_ptr
public: 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(); ~DialogueDelegateDispatcher();
CSVWorld::CommandDelegate* makeDelegate(CSMWorld::ColumnBase::Display display); CSVWorld::CommandDelegate* makeDelegate(CSMWorld::ColumnBase::Display display);
QWidget* makeEditor(CSMWorld::ColumnBase::Display display, const QModelIndex& index); 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 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 ///< does nothing
virtual QSize sizeHint (const QStyleOptionViewItem& option, const QModelIndex& index) const; virtual QSize sizeHint (const QStyleOptionViewItem& option,
const QModelIndex& index) const;
///< does nothing ///< does nothing
private slots: 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: signals:
void tableMimeDataDropped(QWidget* editor, const QModelIndex& index, void tableMimeDataDropped(QWidget* editor, const QModelIndex& index,
const CSMWorld::UniversalId& id, const CSMWorld::UniversalId& id,
const CSMDoc::Document* document); const CSMDoc::Document* document);
}; };
class EditWidget : public QScrollArea class EditWidget : public QScrollArea
{ {
Q_OBJECT Q_OBJECT
QDataWidgetMapper *mWidgetMapper; QDataWidgetMapper *mWidgetMapper;
QDataWidgetMapper *mNestedTableMapper;
DialogueDelegateDispatcher mDispatcher; DialogueDelegateDispatcher mDispatcher;
DialogueDelegateDispatcher *mNestedTableDispatcher;
QWidget* mMainWidget; QWidget* mMainWidget;
CSMWorld::IdTable* mTable; CSMWorld::IdTable* mTable;
CSMWorld::CommandDispatcher& mCommandDispatcher;
CSMDoc::Document& mDocument; CSMDoc::Document& mDocument;
std::vector<CSMWorld::NestedTableProxyModel*> mNestedModels; //Plain, raw C pointers, deleted in the dtor
public: 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); void remake(int row);
@ -168,7 +197,7 @@ namespace CSVWorld
QVBoxLayout* mMainLayout; QVBoxLayout* mMainLayout;
CSMWorld::IdTable* mTable; CSMWorld::IdTable* mTable;
QUndoStack& mUndoStack; QUndoStack& mUndoStack;
int mRow; std::string mCurrentId;
bool mLocked; bool mLocked;
const CSMDoc::Document& mDocument; const CSMDoc::Document& mDocument;
TableBottomBox* mBottom; TableBottomBox* mBottom;
@ -183,6 +212,9 @@ namespace CSVWorld
virtual void setEditLock (bool locked); virtual void setEditLock (bool locked);
private:
void changeCurrentId(const std::string& newCurrent);
private slots: private slots:
void nextId(); void nextId();
@ -193,10 +225,6 @@ namespace CSVWorld
void viewRecord(); void viewRecord();
void revertRecord();
void deleteRecord();
void cloneRequest(); void cloneRequest();
void dataChanged(const QModelIndex & index); void dataChanged(const QModelIndex & index);

@ -21,7 +21,9 @@ void CSVWorld::EnumDelegate::setModelDataImp (QWidget *editor, QAbstractItemMode
iter!=mValues.end(); ++iter) iter!=mValues.end(); ++iter)
if (iter->second==value) 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; break;
} }
} }
@ -35,8 +37,8 @@ void CSVWorld::EnumDelegate::addCommands (QAbstractItemModel *model,
CSVWorld::EnumDelegate::EnumDelegate (const std::vector<std::pair<int, QString> >& values, CSVWorld::EnumDelegate::EnumDelegate (const std::vector<std::pair<int, QString> >& values,
CSMDoc::Document& document, QObject *parent) CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent)
: CommandDelegate (document, parent), mValues (values) : CommandDelegate (dispatcher, document, parent), mValues (values)
{ {
} }
@ -140,9 +142,9 @@ CSVWorld::EnumDelegateFactory::EnumDelegateFactory (const std::vector<std::strin
} }
CSVWorld::CommandDelegate *CSVWorld::EnumDelegateFactory::makeDelegate ( CSVWorld::CommandDelegate *CSVWorld::EnumDelegateFactory::makeDelegate (
CSMDoc::Document& document, QObject *parent) const CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const
{ {
return new EnumDelegate (mValues, document, parent); return new EnumDelegate (mValues, dispatcher, document, parent);
} }
void CSVWorld::EnumDelegateFactory::add (int value, const QString& name) void CSVWorld::EnumDelegateFactory::add (int value, const QString& name)

@ -30,7 +30,7 @@ namespace CSVWorld
public: public:
EnumDelegate (const std::vector<std::pair<int, QString> >& values, EnumDelegate (const std::vector<std::pair<int, QString> >& values,
CSMDoc::Document& document, QObject *parent); CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent);
virtual QWidget *createEditor(QWidget *parent, virtual QWidget *createEditor(QWidget *parent,
const QStyleOptionViewItem& option, const QStyleOptionViewItem& option,
@ -64,7 +64,7 @@ namespace CSVWorld
EnumDelegateFactory (const std::vector<std::string>& names, bool allowNone = false); EnumDelegateFactory (const std::vector<std::string>& names, bool allowNone = false);
/// \param allowNone Use value of -1 for "none selected" (empty string) /// \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. ///< The ownership of the returned CommandDelegate is transferred to the caller.
void add (int value, const QString& name); void add (int value, const QString& name);

@ -225,6 +225,11 @@ void CSVWorld::GenericCreator::toggleWidgets(bool active)
{ {
} }
void CSVWorld::GenericCreator::focus()
{
mId->setFocus();
}
void CSVWorld::GenericCreator::setScope (unsigned int scope) void CSVWorld::GenericCreator::setScope (unsigned int scope)
{ {
mScopes = scope; mScopes = scope;

@ -101,6 +101,9 @@ namespace CSVWorld
virtual void setScope (unsigned int scope); virtual void setScope (unsigned int scope);
/// Focus main input widget
virtual void focus();
private slots: private slots:
void textChanged (const QString& text); void textChanged (const QString& text);

@ -3,8 +3,8 @@
#include "../../model/world/universalid.hpp" #include "../../model/world/universalid.hpp"
CSVWorld::IdTypeDelegate::IdTypeDelegate CSVWorld::IdTypeDelegate::IdTypeDelegate
(const ValueList &values, const IconList &icons, CSMDoc::Document& document, QObject *parent) (const ValueList &values, const IconList &icons, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent)
: DataDisplayDelegate (values, icons, document, : DataDisplayDelegate (values, icons, dispatcher, document,
"records", "type-format", "records", "type-format",
parent) parent)
{} {}
@ -21,7 +21,7 @@ CSVWorld::IdTypeDelegateFactory::IdTypeDelegateFactory()
} }
CSVWorld::CommandDelegate *CSVWorld::IdTypeDelegateFactory::makeDelegate ( 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);
} }

@ -11,7 +11,7 @@ namespace CSVWorld
class IdTypeDelegate : public DataDisplayDelegate class IdTypeDelegate : public DataDisplayDelegate
{ {
public: 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 class IdTypeDelegateFactory : public DataDisplayDelegateFactory
@ -20,7 +20,7 @@ namespace CSVWorld
IdTypeDelegateFactory(); 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. ///< The ownership of the returned CommandDelegate is transferred to the caller.
}; };
} }

@ -91,6 +91,11 @@ std::string CSVWorld::InfoCreator::getErrors() const
return errors; return errors;
} }
void CSVWorld::InfoCreator::focus()
{
mTopic->setFocus();
}
void CSVWorld::InfoCreator::topicChanged() void CSVWorld::InfoCreator::topicChanged()
{ {
update(); update();

@ -35,7 +35,10 @@ namespace CSVWorld
virtual std::string getErrors() const; virtual std::string getErrors() const;
///< Return formatted error descriptions for the current state of the creator. if an empty ///< Return formatted error descriptions for the current state of the creator. if an empty
/// string is returned, there is no error. /// string is returned, there is no error.
/// Focus main input widget
virtual void focus();
private slots: private slots:
void topicChanged(); void topicChanged();

@ -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 <QHeaderView>
#include <QContextMenuEvent>
#include <QMenu>
#include <QDebug>
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<CSMWorld::ColumnBase::Display> (
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()));
}

@ -0,0 +1,56 @@
#ifndef CSV_WORLD_NESTEDTABLE_H
#define CSV_WORLD_NESTEDTABLE_H
#include <QTableView>
#include <QtGui/qevent.h>
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

@ -9,16 +9,16 @@
CSVWorld::RecordStatusDelegate::RecordStatusDelegate(const ValueList& values, CSVWorld::RecordStatusDelegate::RecordStatusDelegate(const ValueList& values,
const IconList & icons, const IconList & icons,
CSMDoc::Document& document, QObject *parent) CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent)
: DataDisplayDelegate (values, icons, document, : DataDisplayDelegate (values, icons, dispatcher, document,
"records", "status-format", "records", "status-format",
parent) parent)
{} {}
CSVWorld::CommandDelegate *CSVWorld::RecordStatusDelegateFactory::makeDelegate ( 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() CSVWorld::RecordStatusDelegateFactory::RecordStatusDelegateFactory()

@ -17,9 +17,9 @@ namespace CSVWorld
{ {
public: public:
explicit RecordStatusDelegate(const ValueList& values, RecordStatusDelegate (const ValueList& values, const IconList& icons,
const IconList& icons, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document,
CSMDoc::Document& document, QObject *parent = 0); QObject *parent = 0);
}; };
class RecordStatusDelegateFactory : public DataDisplayDelegateFactory class RecordStatusDelegateFactory : public DataDisplayDelegateFactory
@ -28,7 +28,7 @@ namespace CSVWorld
RecordStatusDelegateFactory(); 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. ///< The ownership of the returned CommandDelegate is transferred to the caller.
}; };

@ -118,6 +118,11 @@ std::string CSVWorld::ReferenceCreator::getErrors() const
return errors; return errors;
} }
void CSVWorld::ReferenceCreator::focus()
{
mCell->setFocus();
}
void CSVWorld::ReferenceCreator::cellChanged() void CSVWorld::ReferenceCreator::cellChanged()
{ {
update(); update();

@ -39,6 +39,9 @@ namespace CSVWorld
///< Return formatted error descriptions for the current state of the creator. if an empty ///< Return formatted error descriptions for the current state of the creator. if an empty
/// string is returned, there is no error. /// string is returned, there is no error.
/// Focus main input widget
virtual void focus();
private slots: private slots:
void cellChanged(); void cellChanged();

@ -5,11 +5,14 @@
#include <QDragEnterEvent> #include <QDragEnterEvent>
#include <QRegExp> #include <QRegExp>
#include <QString> #include <QString>
#include <QPainter>
#include <QTextDocumentFragment>
#include "../../model/doc/document.hpp" #include "../../model/doc/document.hpp"
#include "../../model/world/universalid.hpp" #include "../../model/world/universalid.hpp"
#include "../../model/world/tablemimedata.hpp" #include "../../model/world/tablemimedata.hpp"
#include "../../model/settings/usersettings.hpp"
CSVWorld::ScriptEdit::ChangeLock::ChangeLock (ScriptEdit& edit) : mEdit (edit) CSVWorld::ScriptEdit::ChangeLock::ChangeLock (ScriptEdit& edit) : mEdit (edit)
@ -28,7 +31,11 @@ CSVWorld::ScriptEdit::ScriptEdit (const CSMDoc::Document& document, ScriptHighli
: QPlainTextEdit (parent), : QPlainTextEdit (parent),
mDocument (document), mDocument (document),
mWhiteListQoutes("^[a-z|_]{1}[a-z|0-9|_]{0,}$", Qt::CaseInsensitive), 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); // setAcceptRichText (false);
setLineWrapMode (QPlainTextEdit::NoWrap); setLineWrapMode (QPlainTextEdit::NoWrap);
@ -72,6 +79,41 @@ CSVWorld::ScriptEdit::ScriptEdit (const CSMDoc::Document& document, ScriptHighli
connect (&mUpdateTimer, SIGNAL (timeout()), this, SLOT (updateHighlighting())); connect (&mUpdateTimer, SIGNAL (timeout()), this, SLOT (updateHighlighting()));
mUpdateTimer.setSingleShot (true); 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 bool CSVWorld::ScriptEdit::isChangeLocked() const
@ -157,3 +199,112 @@ void CSVWorld::ScriptEdit::updateHighlighting()
mHighlighter->rehighlight(); 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);
}

@ -2,14 +2,15 @@
#define SCRIPTEDIT_H #define SCRIPTEDIT_H
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <QWidget>
#include <QVector> #include <QVector>
#include <QTimer> #include <QTimer>
#include <QFont>
#include "../../model/world/universalid.hpp" #include "../../model/world/universalid.hpp"
#include "scripthighlighter.hpp" #include "scripthighlighter.hpp"
class QWidget;
class QRegExp; class QRegExp;
namespace CSMDoc namespace CSMDoc
@ -19,6 +20,8 @@ namespace CSMDoc
namespace CSVWorld namespace CSVWorld
{ {
class LineNumberArea;
class ScriptEdit : public QPlainTextEdit class ScriptEdit : public QPlainTextEdit
{ {
Q_OBJECT Q_OBJECT
@ -45,6 +48,10 @@ namespace CSVWorld
int mChangeLocked; int mChangeLocked;
ScriptHighlighter *mHighlighter; ScriptHighlighter *mHighlighter;
QTimer mUpdateTimer; QTimer mUpdateTimer;
bool mShowLineNum;
LineNumberArea *mLineNumberArea;
QFont mDefaultFont;
QFont mMonoFont;
public: public:
@ -56,6 +63,15 @@ namespace CSVWorld
/// \note This mechanism is used to avoid infinite update recursions /// \note This mechanism is used to avoid infinite update recursions
bool isChangeLocked() const; bool isChangeLocked() const;
void lineNumberAreaPaintEvent(QPaintEvent *event);
int lineNumberAreaWidth();
void showLineNum(bool show);
void setMonoFont(bool show);
protected:
virtual void resizeEvent(QResizeEvent *e);
private: private:
QVector<CSMWorld::UniversalId::Type> mAllowedTypes; QVector<CSMWorld::UniversalId::Type> mAllowedTypes;
const CSMDoc::Document& mDocument; const CSMDoc::Document& mDocument;
@ -74,6 +90,23 @@ namespace CSVWorld
void idListChanged(); void idListChanged();
void updateHighlighting(); 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 #endif // SCRIPTEDIT_H

@ -1,21 +1,42 @@
#include "scriptsubview.hpp" #include "scriptsubview.hpp"
#include <stdexcept> #include <stdexcept>
#include <QStatusBar>
#include <QStackedLayout>
#include <QLabel>
#include "../../model/doc/document.hpp" #include "../../model/doc/document.hpp"
#include "../../model/world/universalid.hpp" #include "../../model/world/universalid.hpp"
#include "../../model/world/data.hpp" #include "../../model/world/data.hpp"
#include "../../model/world/columnbase.hpp" #include "../../model/world/columnbase.hpp"
#include "../../model/world/commands.hpp" #include "../../model/world/commands.hpp"
#include "../../model/world/idtable.hpp" #include "../../model/world/idtable.hpp"
#include "../../model/settings/usersettings.hpp"
#include "scriptedit.hpp" #include "scriptedit.hpp"
CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) 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<CSMWorld::IdTable&> ( mModel = &dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Scripts)); *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)), connect (mModel, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
this, SLOT (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) void CSVWorld::ScriptSubView::setEditLock (bool locked)

@ -4,6 +4,7 @@
#include "../doc/subview.hpp" #include "../doc/subview.hpp"
class QModelIndex; class QModelIndex;
class QLabel;
namespace CSMDoc namespace CSMDoc
{ {
@ -27,6 +28,8 @@ namespace CSVWorld
CSMDoc::Document& mDocument; CSMDoc::Document& mDocument;
CSMWorld::IdTable *mModel; CSMWorld::IdTable *mModel;
int mColumn; int mColumn;
QWidget *mBottom;
QLabel *mStatus;
public: public:
@ -36,6 +39,8 @@ namespace CSVWorld
virtual void useHint (const std::string& hint); virtual void useHint (const std::string& hint);
virtual void updateUserSetting (const QString& name, const QStringList& value);
public slots: public slots:
void textChanged(); void textChanged();
@ -43,6 +48,10 @@ namespace CSVWorld
void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void rowsAboutToBeRemoved (const QModelIndex& parent, int start, int end); void rowsAboutToBeRemoved (const QModelIndex& parent, int start, int end);
private slots:
void updateStatusBar();
}; };
} }

@ -281,7 +281,7 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt());
CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate (display, CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate (display,
mDocument, this); mDispatcher, document, this);
mDelegates.push_back (delegate); mDelegates.push_back (delegate);
setItemDelegateForColumn (i, delegate); setItemDelegateForColumn (i, delegate);

@ -157,6 +157,7 @@ void CSVWorld::TableBottomBox::createRequest()
mLayout->setCurrentWidget (mCreator); mLayout->setCurrentWidget (mCreator);
setVisible (true); setVisible (true);
mCreating = true; mCreating = true;
mCreator->focus();
} }
void CSVWorld::TableBottomBox::cloneRequest(const std::string& id, void CSVWorld::TableBottomBox::cloneRequest(const std::string& id,
@ -168,4 +169,5 @@ void CSVWorld::TableBottomBox::cloneRequest(const std::string& id,
mCreator->toggleWidgets(false); mCreator->toggleWidgets(false);
setVisible (true); setVisible (true);
mCreating = true; mCreating = true;
mCreator->focus();
} }

@ -18,6 +18,7 @@
#include "../../model/world/commands.hpp" #include "../../model/world/commands.hpp"
#include "../../model/world/tablemimedata.hpp" #include "../../model/world/tablemimedata.hpp"
#include "../../model/world/commanddispatcher.hpp"
#include "scriptedit.hpp" #include "scriptedit.hpp"
@ -82,15 +83,15 @@ void CSVWorld::CommandDelegateFactoryCollection::add (CSMWorld::ColumnBase::Disp
} }
CSVWorld::CommandDelegate *CSVWorld::CommandDelegateFactoryCollection::makeDelegate ( 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<CSMWorld::ColumnBase::Display, CommandDelegateFactory *>::const_iterator iter = std::map<CSMWorld::ColumnBase::Display, CommandDelegateFactory *>::const_iterator iter =
mFactories.find (display); mFactories.find (display);
if (iter!=mFactories.end()) 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() const CSVWorld::CommandDelegateFactoryCollection& CSVWorld::CommandDelegateFactoryCollection::get()
@ -115,17 +116,22 @@ CSMDoc::Document& CSVWorld::CommandDelegate::getDocument() const
void CSVWorld::CommandDelegate::setModelDataImp (QWidget *editor, QAbstractItemModel *model, void CSVWorld::CommandDelegate::setModelDataImp (QWidget *editor, QAbstractItemModel *model,
const QModelIndex& index) const const QModelIndex& index) const
{ {
if (!mCommandDispatcher)
return;
NastyTableModelHack hack (*model); NastyTableModelHack hack (*model);
QStyledItemDelegate::setModelData (editor, &hack, index); QStyledItemDelegate::setModelData (editor, &hack, index);
QVariant new_ = hack.getData(); QVariant new_ = hack.getData();
if (model->data (index)!=new_) if ((model->data (index)!=new_) && (model->flags(index) & Qt::ItemIsEditable))
getUndoStack().push (new CSMWorld::ModifyCommand (*model, index, new_)); mCommandDispatcher->executeModify (model, index, new_);
} }
CSVWorld::CommandDelegate::CommandDelegate (CSMDoc::Document& document, QObject *parent) CSVWorld::CommandDelegate::CommandDelegate (CSMWorld::CommandDispatcher *commandDispatcher,
: QStyledItemDelegate (parent), mDocument (document), mEditLock (false) CSMDoc::Document& document, QObject *parent)
: QStyledItemDelegate (parent), mEditLock (false),
mCommandDispatcher (commandDispatcher), mDocument (document)
{} {}
void CSVWorld::CommandDelegate::setModelData (QWidget *editor, QAbstractItemModel *model, 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: case CSMWorld::ColumnBase::Display_Float:
{ {
QDoubleSpinBox *dsb = new QDoubleSpinBox(parent); QDoubleSpinBox *dsb = new QDoubleSpinBox(parent);
dsb->setRange(FLT_MIN, FLT_MAX); dsb->setRange(-FLT_MAX, FLT_MAX);
dsb->setSingleStep(0.01f); dsb->setSingleStep(0.01f);
dsb->setDecimals(3); dsb->setDecimals(3);
return dsb; return dsb;

@ -16,6 +16,7 @@ namespace CSMWorld
{ {
class TableMimeData; class TableMimeData;
class UniversalId; class UniversalId;
class CommandDispatcher;
} }
namespace CSVWorld namespace CSVWorld
@ -51,7 +52,8 @@ namespace CSVWorld
virtual ~CommandDelegateFactory(); virtual ~CommandDelegateFactory();
virtual CommandDelegate *makeDelegate (CSMDoc::Document& document, QObject *parent) virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher,
CSMDoc::Document& document, QObject *parent)
const = 0; const = 0;
///< The ownership of the returned CommandDelegate is transferred to the caller. ///< 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. /// 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; QObject *parent) const;
///< The ownership of the returned CommandDelegate is transferred to the caller. ///< The ownership of the returned CommandDelegate is transferred to the caller.
/// ///
@ -111,8 +114,9 @@ namespace CSVWorld
{ {
Q_OBJECT Q_OBJECT
CSMDoc::Document& mDocument;
bool mEditLock; bool mEditLock;
CSMWorld::CommandDispatcher *mCommandDispatcher;
CSMDoc::Document& mDocument;
protected: protected:
@ -125,7 +129,9 @@ namespace CSVWorld
public: 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, virtual void setModelData (QWidget *editor, QAbstractItemModel *model,
const QModelIndex& index) const; const QModelIndex& index) const;

@ -47,8 +47,8 @@ void CSVWorld::VarTypeDelegate::addCommands (QAbstractItemModel *model, const QM
} }
CSVWorld::VarTypeDelegate::VarTypeDelegate (const std::vector<std::pair<int, QString> >& values, CSVWorld::VarTypeDelegate::VarTypeDelegate (const std::vector<std::pair<int, QString> >& values,
CSMDoc::Document& document, QObject *parent) CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent)
: EnumDelegate (values, document, parent) : EnumDelegate (values, dispatcher, document, parent)
{} {}
@ -69,9 +69,9 @@ CSVWorld::VarTypeDelegateFactory::VarTypeDelegateFactory (ESM::VarType type0,
} }
CSVWorld::CommandDelegate *CSVWorld::VarTypeDelegateFactory::makeDelegate ( 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) void CSVWorld::VarTypeDelegateFactory::add (ESM::VarType type)

@ -17,7 +17,7 @@ namespace CSVWorld
public: public:
VarTypeDelegate (const std::vector<std::pair<int, QString> >& values, VarTypeDelegate (const std::vector<std::pair<int, QString> >& values,
CSMDoc::Document& document, QObject *parent); CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent);
}; };
class VarTypeDelegateFactory : public CommandDelegateFactory class VarTypeDelegateFactory : public CommandDelegateFactory
@ -30,7 +30,8 @@ namespace CSVWorld
ESM::VarType type1 = ESM::VT_Unknown, ESM::VarType type2 = ESM::VT_Unknown, ESM::VarType type1 = ESM::VT_Unknown, ESM::VarType type2 = ESM::VT_Unknown,
ESM::VarType type3 = 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. ///< The ownership of the returned CommandDelegate is transferred to the caller.
void add (ESM::VarType type); void add (ESM::VarType type);

@ -163,23 +163,23 @@ void OMW::Engine::frame(float frametime)
} }
OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
: mVerboseScripts (false) : mEncoding(ToUTF8::WINDOWS_1252)
, mEncoder(NULL)
, mVerboseScripts (false)
, mSkipMenu (false) , mSkipMenu (false)
, mUseSound (true) , mUseSound (true)
, mCompileAll (false) , mCompileAll (false)
, mCompileAllDialogue (false) , mCompileAllDialogue (false)
, mWarningsMode (1) , mWarningsMode (1)
, mScriptContext (0)
, mFSStrict (false)
, mScriptConsoleMode (false) , mScriptConsoleMode (false)
, mCfgMgr(configurationManager)
, mEncoding(ToUTF8::WINDOWS_1252)
, mEncoder(NULL)
, mActivationDistanceOverride(-1) , mActivationDistanceOverride(-1)
, mGrab(true) , mGrab(true)
, mScriptBlacklistUse (true)
, mExportFonts(false) , mExportFonts(false)
, mScriptContext (0)
, mFSStrict (false)
, mScriptBlacklistUse (true)
, mNewGame (false) , mNewGame (false)
, mCfgMgr(configurationManager)
{ {
Misc::Rng::init(); Misc::Rng::init();
std::srand ( static_cast<unsigned int>(std::time(NULL)) ); std::srand ( static_cast<unsigned int>(std::time(NULL)) );

@ -77,8 +77,6 @@ namespace MWGui
, mMagicka(NULL) , mMagicka(NULL)
, mStamina(NULL) , mStamina(NULL)
, mDrowning(NULL) , mDrowning(NULL)
, mDrowningFrame(NULL)
, mDrowningFlash(NULL)
, mWeapImage(NULL) , mWeapImage(NULL)
, mSpellImage(NULL) , mSpellImage(NULL)
, mWeapStatus(NULL) , mWeapStatus(NULL)
@ -87,24 +85,26 @@ namespace MWGui
, mMinimap(NULL) , mMinimap(NULL)
, mCompass(NULL) , mCompass(NULL)
, mCrosshair(NULL) , mCrosshair(NULL)
, mCellNameBox(NULL)
, mDrowningFrame(NULL)
, mDrowningFlash(NULL)
, mFpsBox(NULL) , mFpsBox(NULL)
, mFpsCounter(NULL) , mFpsCounter(NULL)
, mHealthManaStaminaBaseLeft(0) , mHealthManaStaminaBaseLeft(0)
, mWeapBoxBaseLeft(0) , mWeapBoxBaseLeft(0)
, mSpellBoxBaseLeft(0) , mSpellBoxBaseLeft(0)
, mEffectBoxBaseRight(0)
, mMinimapBoxBaseRight(0) , mMinimapBoxBaseRight(0)
, mEffectBoxBaseRight(0)
, mDragAndDrop(dragAndDrop) , mDragAndDrop(dragAndDrop)
, mCellNameTimer(0.0f) , mCellNameTimer(0.0f)
, mCellNameBox(NULL) , mWeaponSpellTimer(0.f)
, mMapVisible(true) , mMapVisible(true)
, mWeaponVisible(true) , mWeaponVisible(true)
, mSpellVisible(true) , mSpellVisible(true)
, mWorldMouseOver(false) , mWorldMouseOver(false)
, mEnemyHealthTimer(-1)
, mEnemyActorId(-1) , mEnemyActorId(-1)
, mEnemyHealthTimer(-1)
, mIsDrowning(false) , mIsDrowning(false)
, mWeaponSpellTimer(0.f)
, mDrowningFlashTheta(0.f) , mDrowningFlashTheta(0.f)
{ {
mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize()); mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize());

@ -30,12 +30,12 @@ namespace MWGui
MainMenu::MainMenu(int w, int h, const VFS::Manager* vfs) MainMenu::MainMenu(int w, int h, const VFS::Manager* vfs)
: Layout("openmw_mainmenu.layout") : Layout("openmw_mainmenu.layout")
, mVFS(vfs) , mWidth (w), mHeight (h)
, mButtonBox(0), mWidth (w), mHeight (h) , mVFS(vfs), mButtonBox(0)
, mSaveGameDialog(NULL)
, mBackground(NULL) , mBackground(NULL)
, mVideoBackground(NULL) , mVideoBackground(NULL)
, mVideo(NULL) , mVideo(NULL)
, mSaveGameDialog(NULL)
{ {
getWidget(mVersionText, "VersionText"); getWidget(mVersionText, "VersionText");
std::stringstream sstream; std::stringstream sstream;

@ -24,10 +24,10 @@ namespace MWGui
struct MagicEffectInfo struct MagicEffectInfo
{ {
MagicEffectInfo() MagicEffectInfo()
: mPermanent(false) : mMagnitude(0)
, mMagnitude(0)
, mRemainingTime(0.f) , mRemainingTime(0.f)
, mTotalTime(0.f) , mTotalTime(0.f)
, mPermanent(false)
{} {}
std::string mSource; // display name for effect source (e.g. potion name) std::string mSource; // display name for effect source (e.g. potion name)
MWMechanics::EffectKey mKey; MWMechanics::EffectKey mKey;

@ -24,9 +24,9 @@ namespace MWGui
bool mActive; // (Items only) is the item equipped? bool mActive; // (Items only) is the item equipped?
Spell() Spell()
: mSelected(false) : mType(Type_Spell)
, mSelected(false)
, mActive(false) , mActive(false)
, mType(Type_Spell)
{ {
} }
}; };

@ -21,9 +21,9 @@ namespace MWGui
} }
SpellView::SpellView() SpellView::SpellView()
: mShowCostColumn(true) : mScrollView(NULL)
, mShowCostColumn(true)
, mHighlightSelected(true) , mHighlightSelected(true)
, mScrollView(NULL)
{ {
} }

@ -593,7 +593,10 @@ namespace MWGui
for (std::vector<std::pair<std::string, int> >::const_iterator it = itemOwners.begin(); it != itemOwners.end(); ++it) for (std::vector<std::pair<std::string, int> >::const_iterator it = itemOwners.begin(); it != itemOwners.end(); ++it)
{ {
ret += std::string("\nStolen from ") + it->first; if (it->second == std::numeric_limits<int>::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"); ret += getMiscString(cellref.getGlobalVariable(), "Global");

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save