Imported Upstream version 0.26.0

This commit is contained in:
Bret Curtis 2013-10-17 16:37:22 +02:00
commit 9a2b6c69b6
1398 changed files with 212217 additions and 0 deletions

158
apps/opencs/CMakeLists.txt Normal file
View file

@ -0,0 +1,158 @@
set (OPENCS_SRC main.cpp)
opencs_units (. editor)
set (CMAKE_BUILD_TYPE DEBUG)
opencs_units (model/doc
document
)
opencs_units_noqt (model/doc
documentmanager
)
opencs_hdrs_noqt (model/doc
state
)
opencs_units (model/world
idtable idtableproxymodel regionmap
)
opencs_units_noqt (model/world
universalid data record commands columnbase scriptcontext cell refidcollection
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns
)
opencs_hdrs_noqt (model/world
columnimp idcollection collection
)
opencs_units (model/tools
tools operation reportmodel
)
opencs_units_noqt (model/tools
stage verifier mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck
)
opencs_units (view/doc
viewmanager view operations operation subview startup filedialog
)
opencs_units_noqt (view/doc
subviewfactory
)
opencs_hdrs_noqt (view/doc
subviewfactoryimp
)
opencs_units (view/world
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
cellcreator referenceablecreator referencecreator
)
opencs_units_noqt (view/world
dialoguesubview subviews
enumdelegate vartypedelegate recordstatusdelegate refidtypedelegate datadisplaydelegate
scripthighlighter idvalidator
)
opencs_units (view/tools
reportsubview
)
opencs_units_noqt (view/tools
subviews
)
opencs_units (view/settings
abstractblock
proxyblock
abstractwidget
usersettingsdialog
datadisplayformatpage
windowpage
)
opencs_units_noqt (view/settings
abstractpage
blankpage
groupblock
customblock
groupbox
itemblock
settingwidget
toggleblock
support
)
opencs_units (model/settings
usersettings
settingcontainer
)
opencs_units_noqt (model/settings
support
settingsitem
)
opencs_units_noqt (model/filter
node unarynode narynode leafnode booleannode parser andnode ornode notnode textnode valuenode
)
opencs_hdrs_noqt (model/filter
filter
)
opencs_units (view/filter
filtercreator filterbox recordfilterbox editwidget
)
set (OPENCS_US
)
set (OPENCS_RES ../../files/opencs/resources.qrc
../../files/launcher/launcher.qrc
)
set (OPENCS_UI ../../files/ui/datafilespage.ui
)
source_group (opencs FILES ${OPENCS_SRC} ${OPENCS_HDR})
if(WIN32)
set(QT_USE_QTMAIN TRUE)
endif(WIN32)
find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork QtXml QtXmlPatterns REQUIRED)
include(${QT_USE_FILE})
qt4_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI})
qt4_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT})
qt4_add_resources(OPENCS_RES_SRC ${OPENCS_RES})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
add_executable(opencs
${OPENCS_SRC}
${OPENCS_UI_HDR}
${OPENCS_MOC_SRC}
${OPENCS_RES_SRC}
)
target_link_libraries(opencs
${Boost_LIBRARIES}
${QT_LIBRARIES}
components
)

157
apps/opencs/editor.cpp Normal file
View file

@ -0,0 +1,157 @@
#include "editor.hpp"
#include <QApplication>
#include <QLocalServer>
#include <QLocalSocket>
#include "model/doc/document.hpp"
#include "model/world/data.hpp"
CS::Editor::Editor() : mViewManager (mDocumentManager)
{
mIpcServerName = "org.openmw.OpenCS";
connect (&mViewManager, SIGNAL (newDocumentRequest ()), this, SLOT (createDocument ()));
connect (&mViewManager, SIGNAL (loadDocumentRequest ()), this, SLOT (loadDocument ()));
connect (&mStartup, SIGNAL (createDocument()), this, SLOT (createDocument ()));
connect (&mStartup, SIGNAL (loadDocument()), this, SLOT (loadDocument ()));
connect (&mFileDialog, SIGNAL(openFiles()), this, SLOT(openFiles()));
connect (&mFileDialog, SIGNAL(createNewFile()), this, SLOT(createNewFile()));
setupDataFiles();
}
void CS::Editor::setupDataFiles()
{
boost::program_options::variables_map variables;
boost::program_options::options_description desc;
desc.add_options()
("data", boost::program_options::value<Files::PathContainer>()->default_value(Files::PathContainer(), "data")->multitoken())
("data-local", boost::program_options::value<std::string>()->default_value(""))
("fs-strict", boost::program_options::value<bool>()->implicit_value(true)->default_value(false))
("encoding", boost::program_options::value<std::string>()->default_value("win1252"));
boost::program_options::notify(variables);
mCfgMgr.readConfiguration(variables, desc);
Files::PathContainer mDataDirs, mDataLocal;
if (!variables["data"].empty()) {
mDataDirs = Files::PathContainer(variables["data"].as<Files::PathContainer>());
}
std::string local = variables["data-local"].as<std::string>();
if (!local.empty()) {
mDataLocal.push_back(Files::PathContainer::value_type(local));
}
mCfgMgr.processPaths(mDataDirs);
mCfgMgr.processPaths(mDataLocal);
// Set the charset for reading the esm/esp files
QString encoding = QString::fromStdString(variables["encoding"].as<std::string>());
mFileDialog.setEncoding(encoding);
Files::PathContainer dataDirs;
dataDirs.insert(dataDirs.end(), mDataDirs.begin(), mDataDirs.end());
dataDirs.insert(dataDirs.end(), mDataLocal.begin(), mDataLocal.end());
for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)
{
QString path = QString::fromStdString(iter->string());
mFileDialog.addFiles(path);
}
//load the settings into the userSettings instance.
const QString settingFileName = "opencs.cfg";
CSMSettings::UserSettings::instance().loadSettings(settingFileName);
}
void CS::Editor::createDocument()
{
mStartup.hide();
mFileDialog.newFile();
}
void CS::Editor::loadDocument()
{
mStartup.hide();
mFileDialog.openFile();
}
void CS::Editor::openFiles()
{
std::vector<boost::filesystem::path> files;
QStringList paths = mFileDialog.checkedItemsPaths();
foreach (const QString &path, paths) {
files.push_back(path.toStdString());
}
CSMDoc::Document *document = mDocumentManager.addDocument(files, false);
mViewManager.addView (document);
mFileDialog.hide();
}
void CS::Editor::createNewFile()
{
std::vector<boost::filesystem::path> files;
QStringList paths = mFileDialog.checkedItemsPaths();
foreach (const QString &path, paths) {
files.push_back(path.toStdString());
}
files.push_back(mFileDialog.fileName().toStdString());
CSMDoc::Document *document = mDocumentManager.addDocument (files, true);
mViewManager.addView (document);
mFileDialog.hide();
}
void CS::Editor::showStartup()
{
if(mStartup.isHidden())
mStartup.show();
mStartup.raise();
mStartup.activateWindow();
}
bool CS::Editor::makeIPCServer()
{
mServer = new QLocalServer(this);
if(mServer->listen(mIpcServerName))
{
connect(mServer, SIGNAL(newConnection()), this, SLOT(showStartup()));
return true;
}
mServer->close();
return false;
}
void CS::Editor::connectToIPCServer()
{
mClientSocket = new QLocalSocket(this);
mClientSocket->connectToServer(mIpcServerName);
mClientSocket->close();
}
int CS::Editor::run()
{
mStartup.show();
QApplication::setQuitOnLastWindowClosed (true);
return QApplication::exec();
}

66
apps/opencs/editor.hpp Normal file
View file

@ -0,0 +1,66 @@
#ifndef CS_EDITOR_H
#define CS_EDITOR_H
#include <QObject>
#include <QString>
#include <QLocalServer>
#include <QLocalSocket>
#ifndef Q_MOC_RUN
#include <components/files/configurationmanager.hpp>
#endif
#include "model/doc/documentmanager.hpp"
#include "view/doc/viewmanager.hpp"
#include "view/doc/startup.hpp"
#include "view/doc/filedialog.hpp"
#include "model/settings/usersettings.hpp"
namespace CS
{
class Editor : public QObject
{
Q_OBJECT
CSMSettings::UserSettings mUserSettings;
CSMDoc::DocumentManager mDocumentManager;
CSVDoc::ViewManager mViewManager;
CSVDoc::StartupDialogue mStartup;
FileDialog mFileDialog;
Files::ConfigurationManager mCfgMgr;
void setupDataFiles();
// not implemented
Editor (const Editor&);
Editor& operator= (const Editor&);
public:
Editor();
bool makeIPCServer();
void connectToIPCServer();
int run();
///< \return error status
private slots:
void createDocument();
void loadDocument();
void openFiles();
void createNewFile();
void showStartup();
private:
QString mIpcServerName;
QLocalServer *mServer;
QLocalSocket *mClientSocket;
};
}
#endif

49
apps/opencs/main.cpp Normal file
View file

@ -0,0 +1,49 @@
#include "editor.hpp"
#include <exception>
#include <iostream>
#include <QApplication>
#include <QIcon>
class Application : public QApplication
{
private:
bool notify (QObject *receiver, QEvent *event)
{
try
{
return QApplication::notify (receiver, event);
}
catch (const std::exception& exception)
{
std::cerr << "An exception has been caught: " << exception.what() << std::endl;
}
return false;
}
public:
Application (int& argc, char *argv[]) : QApplication (argc, argv) {}
};
int main(int argc, char *argv[])
{
Q_INIT_RESOURCE (resources);
Application mApplication (argc, argv);
mApplication.setWindowIcon (QIcon (":./opencs.png"));
CS::Editor editor;
if(!editor.makeIPCServer())
{
editor.connectToIPCServer();
return 0;
}
return editor.run();
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,111 @@
#ifndef CSM_DOC_DOCUMENT_H
#define CSM_DOC_DOCUMENT_H
#include <string>
#include <boost/filesystem/path.hpp>
#include <QUndoStack>
#include <QObject>
#include <QTimer>
#include "../world/data.hpp"
#include "../tools/tools.hpp"
#include "state.hpp"
class QAbstractItemModel;
namespace ESM
{
struct GameSetting;
struct Global;
}
namespace CSMDoc
{
class Document : public QObject
{
Q_OBJECT
private:
std::string mName; ///< \todo replace name with ESX list
CSMWorld::Data mData;
CSMTools::Tools mTools;
// It is important that the undo stack is declared last, because on desctruction it fires a signal, that is connected to a slot, that is
// using other member variables. Unfortunately this connection is cut only in the QObject destructor, which is way too late.
QUndoStack mUndoStack;
int mSaveCount; ///< dummy implementation -> remove when proper save is implemented.
QTimer mSaveTimer; ///< dummy implementation -> remove when proper save is implemented.
// not implemented
Document (const Document&);
Document& operator= (const Document&);
void load (const std::vector<boost::filesystem::path>::const_iterator& begin,
const std::vector<boost::filesystem::path>::const_iterator& end, bool lastAsModified);
///< \param lastAsModified Store the last file in Modified instead of merging it into Base.
void createBase();
void addGmsts();
void addOptionalGmsts();
void addOptionalGlobals();
void addOptionalGmst (const ESM::GameSetting& gmst);
void addOptionalGlobal (const ESM::Global& global);
public:
Document (const std::vector<boost::filesystem::path>& files, bool new_);
~Document();
QUndoStack& getUndoStack();
int getState() const;
const std::string& getName() const;
///< \todo replace with ESX list
void save();
CSMWorld::UniversalId verify();
void abortOperation (int type);
const CSMWorld::Data& getData() const;
CSMWorld::Data& getData();
CSMTools::ReportModel *getReport (const CSMWorld::UniversalId& id);
///< The ownership of the returned report is not transferred.
signals:
void stateChanged (int state, CSMDoc::Document *document);
void progress (int current, int max, int type, int threads, CSMDoc::Document *document);
private slots:
void modificationStateChanged (bool clean);
void operationDone (int type);
void saving();
///< dummy implementation -> remove when proper save is implemented.
public slots:
void progress (int current, int max, int type);
};
}
#endif

View file

@ -0,0 +1,38 @@
#include "documentmanager.hpp"
#include <algorithm>
#include <stdexcept>
#include "document.hpp"
CSMDoc::DocumentManager::DocumentManager() {}
CSMDoc::DocumentManager::~DocumentManager()
{
for (std::vector<Document *>::iterator iter (mDocuments.begin()); iter!=mDocuments.end(); ++iter)
delete *iter;
}
CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files,
bool new_)
{
Document *document = new Document (files, new_);
mDocuments.push_back (document);
return document;
}
bool CSMDoc::DocumentManager::removeDocument (Document *document)
{
std::vector<Document *>::iterator iter = std::find (mDocuments.begin(), mDocuments.end(), document);
if (iter==mDocuments.end())
throw std::runtime_error ("removing invalid document");
mDocuments.erase (iter);
delete document;
return mDocuments.empty();
}

View file

@ -0,0 +1,37 @@
#ifndef CSM_DOC_DOCUMENTMGR_H
#define CSM_DOC_DOCUMENTMGR_H
#include <vector>
#include <string>
#include <boost/filesystem/path.hpp>
namespace CSMDoc
{
class Document;
class DocumentManager
{
std::vector<Document *> mDocuments;
DocumentManager (const DocumentManager&);
DocumentManager& operator= (const DocumentManager&);
public:
DocumentManager();
~DocumentManager();
Document *addDocument (const std::vector<boost::filesystem::path>& files, bool new_);
///< The ownership of the returned document is not transferred to the caller.
///
/// \param new_ Do not load the last content file in \a files and instead create in an
/// appropriate way.
bool removeDocument (Document *document);
///< \return last document removed?
};
}
#endif

View file

@ -0,0 +1,19 @@
#ifndef CSM_DOC_STATE_H
#define CSM_DOC_STATE_H
namespace CSMDoc
{
enum State
{
State_Modified = 1,
State_Locked = 2,
State_Operation = 4,
State_Saving = 8,
State_Verifying = 16,
State_Compiling = 32, // not implemented yet
State_Searching = 64 // not implemented yet
};
}
#endif

View file

@ -0,0 +1,20 @@
#include "andnode.hpp"
#include <sstream>
CSMFilter::AndNode::AndNode (const std::vector<boost::shared_ptr<Node> >& nodes)
: NAryNode (nodes, "and")
{}
bool CSMFilter::AndNode::test (const CSMWorld::IdTable& table, int row,
const std::map<int, int>& columns) const
{
int size = getSize();
for (int i=0; i<size; ++i)
if (!(*this)[i].test (table, row, columns))
return false;
return true;
}

View file

@ -0,0 +1,23 @@
#ifndef CSM_FILTER_ANDNODE_H
#define CSM_FILTER_ANDNODE_H
#include "narynode.hpp"
namespace CSMFilter
{
class AndNode : public NAryNode
{
bool mAnd;
public:
AndNode (const std::vector<boost::shared_ptr<Node> >& nodes);
virtual bool test (const CSMWorld::IdTable& table, int row,
const std::map<int, int>& columns) const;
///< \return Can the specified table row pass through to filter?
/// \param columns column ID to column index mapping
};
}
#endif

View file

@ -0,0 +1,15 @@
#include "booleannode.hpp"
CSMFilter::BooleanNode::BooleanNode (bool true_) : mTrue (true_) {}
bool CSMFilter::BooleanNode::test (const CSMWorld::IdTable& table, int row,
const std::map<int, int>& columns) const
{
return mTrue;
}
std::string CSMFilter::BooleanNode::toString (bool numericColumns) const
{
return mTrue ? "true" : "false";
}

View file

@ -0,0 +1,29 @@
#ifndef CSM_FILTER_BOOLEANNODE_H
#define CSM_FILTER_BOOLEANNODE_H
#include "leafnode.hpp"
namespace CSMFilter
{
class BooleanNode : public LeafNode
{
bool mTrue;
public:
BooleanNode (bool true_);
virtual bool test (const CSMWorld::IdTable& table, int row,
const std::map<int, int>& columns) const;
///< \return Can the specified table row pass through to filter?
/// \param columns column ID to column index mapping
virtual std::string toString (bool numericColumns) const;
///< Return a string that represents this node.
///
/// \param numericColumns Use numeric IDs instead of string to represent columns.
};
}
#endif

View file

@ -0,0 +1,25 @@
#ifndef CSM_FILTER_FILTER_H
#define CSM_FILTER_FILTER_H
#include <vector>
#include <string>
#include <components/esm/filter.hpp>
namespace CSMFilter
{
/// \brief Wrapper for Filter record
struct Filter : public ESM::Filter
{
enum Scope
{
Scope_Project = 0, // per project
Scope_Session = 1, // exists only for one editing session; not saved
Scope_Content = 2 // embedded in the edited content file
};
Scope mScope;
};
}
#endif

View file

@ -0,0 +1,8 @@
#include "leafnode.hpp"
std::vector<int> CSMFilter::LeafNode::getReferencedColumns() const
{
return std::vector<int>();
}

View file

@ -0,0 +1,20 @@
#ifndef CSM_FILTER_LEAFNODE_H
#define CSM_FILTER_LEAFNODE_H
#include <memory>
#include "node.hpp"
namespace CSMFilter
{
class LeafNode : public Node
{
public:
virtual std::vector<int> getReferencedColumns() const;
///< Return a list of the IDs of the columns referenced by this node. The column mapping
/// passed into test as columns must contain all columns listed here.
};
}
#endif

View file

@ -0,0 +1,60 @@
#include "narynode.hpp"
#include <sstream>
CSMFilter::NAryNode::NAryNode (const std::vector<boost::shared_ptr<Node> >& nodes,
const std::string& name)
: mNodes (nodes), mName (name)
{}
int CSMFilter::NAryNode::getSize() const
{
return mNodes.size();
}
const CSMFilter::Node& CSMFilter::NAryNode::operator[] (int index) const
{
return *mNodes.at (index);
}
std::vector<int> CSMFilter::NAryNode::getReferencedColumns() const
{
std::vector<int> columns;
for (std::vector<boost::shared_ptr<Node> >::const_iterator iter (mNodes.begin());
iter!=mNodes.end(); ++iter)
{
std::vector<int> columns2 = (*iter)->getReferencedColumns();
columns.insert (columns.end(), columns2.begin(), columns2.end());
}
return columns;
}
std::string CSMFilter::NAryNode::toString (bool numericColumns) const
{
std::ostringstream stream;
stream << mName << " (";
bool first = true;
int size = getSize();
for (int i=0; i<size; ++i)
{
if (first)
first = false;
else
stream << ", ";
stream << (*this)[i].toString (numericColumns);
}
stream << ")";
return stream.str();
}

View file

@ -0,0 +1,37 @@
#ifndef CSM_FILTER_NARYNODE_H
#define CSM_FILTER_NARYNODE_H
#include <vector>
#include <string>
#include <boost/shared_ptr.hpp>
#include "node.hpp"
namespace CSMFilter
{
class NAryNode : public Node
{
std::vector<boost::shared_ptr<Node> > mNodes;
std::string mName;
public:
NAryNode (const std::vector<boost::shared_ptr<Node> >& nodes, const std::string& name);
int getSize() const;
const Node& operator[] (int index) const;
virtual std::vector<int> getReferencedColumns() const;
///< Return a list of the IDs of the columns referenced by this node. The column mapping
/// passed into test as columns must contain all columns listed here.
virtual std::string toString (bool numericColumns) const;
///< Return a string that represents this node.
///
/// \param numericColumns Use numeric IDs instead of string to represent columns.
};
}
#endif

View file

@ -0,0 +1,6 @@
#include "node.hpp"
CSMFilter::Node::Node() {}
CSMFilter::Node::~Node() {}

View file

@ -0,0 +1,53 @@
#ifndef CSM_FILTER_NODE_H
#define CSM_FILTER_NODE_H
#include <string>
#include <map>
#include <vector>
#include <boost/shared_ptr.hpp>
#include <QMetaType>
namespace CSMWorld
{
class IdTable;
}
namespace CSMFilter
{
/// \brief Root class for the filter node hierarchy
///
/// \note When the function documentation for this class mentions "this node", this should be
/// interpreted as "the node and all its children".
class Node
{
// not implemented
Node (const Node&);
Node& operator= (const Node&);
public:
Node();
virtual ~Node();
virtual bool test (const CSMWorld::IdTable& table, int row,
const std::map<int, int>& columns) const = 0;
///< \return Can the specified table row pass through to filter?
/// \param columns column ID to column index mapping
virtual std::vector<int> getReferencedColumns() const = 0;
///< Return a list of the IDs of the columns referenced by this node. The column mapping
/// passed into test as columns must contain all columns listed here.
virtual std::string toString (bool numericColumns) const = 0;
///< Return a string that represents this node.
///
/// \param numericColumns Use numeric IDs instead of string to represent columns.
};
}
Q_DECLARE_METATYPE (boost::shared_ptr<CSMFilter::Node>)
#endif

View file

@ -0,0 +1,10 @@
#include "notnode.hpp"
CSMFilter::NotNode::NotNode (boost::shared_ptr<Node> child) : UnaryNode (child, "not") {}
bool CSMFilter::NotNode::test (const CSMWorld::IdTable& table, int row,
const std::map<int, int>& columns) const
{
return !getChild().test (table, row, columns);
}

View file

@ -0,0 +1,21 @@
#ifndef CSM_FILTER_NOTNODE_H
#define CSM_FILTER_NOTNODE_H
#include "unarynode.hpp"
namespace CSMFilter
{
class NotNode : public UnaryNode
{
public:
NotNode (boost::shared_ptr<Node> child);
virtual bool test (const CSMWorld::IdTable& table, int row,
const std::map<int, int>& columns) const;
///< \return Can the specified table row pass through to filter?
/// \param columns column ID to column index mapping
};
}
#endif

View file

@ -0,0 +1,20 @@
#include "ornode.hpp"
#include <sstream>
CSMFilter::OrNode::OrNode (const std::vector<boost::shared_ptr<Node> >& nodes)
: NAryNode (nodes, "or")
{}
bool CSMFilter::OrNode::test (const CSMWorld::IdTable& table, int row,
const std::map<int, int>& columns) const
{
int size = getSize();
for (int i=0; i<size; ++i)
if ((*this)[i].test (table, row, columns))
return true;
return false;
}

View file

@ -0,0 +1,23 @@
#ifndef CSM_FILTER_ORNODE_H
#define CSM_FILTER_ORNODE_H
#include "narynode.hpp"
namespace CSMFilter
{
class OrNode : public NAryNode
{
bool mAnd;
public:
OrNode (const std::vector<boost::shared_ptr<Node> >& nodes);
virtual bool test (const CSMWorld::IdTable& table, int row,
const std::map<int, int>& columns) const;
///< \return Can the specified table row pass through to filter?
/// \param columns column ID to column index mapping
};
}
#endif

View file

@ -0,0 +1,595 @@
#include "parser.hpp"
#include <cctype>
#include <stdexcept>
#include <sstream>
#include <components/misc/stringops.hpp>
#include "../world/columns.hpp"
#include "../world/data.hpp"
#include "../world/idcollection.hpp"
#include "booleannode.hpp"
#include "ornode.hpp"
#include "andnode.hpp"
#include "notnode.hpp"
#include "textnode.hpp"
#include "valuenode.hpp"
namespace CSMFilter
{
struct Token
{
enum Type
{
Type_EOS,
Type_None,
Type_String,
Type_Number,
Type_Open,
Type_Close,
Type_OpenSquare,
Type_CloseSquare,
Type_Comma,
Type_OneShot,
Type_Keyword_True, ///< \attention Keyword enums must be arranged continuously.
Type_Keyword_False,
Type_Keyword_And,
Type_Keyword_Or,
Type_Keyword_Not,
Type_Keyword_Text,
Type_Keyword_Value
};
Type mType;
std::string mString;
double mNumber;
Token (Type type = Type_None);
Token (const std::string& string);
Token (double number);
operator bool() const;
};
Token::Token (Type type) : mType (type) {}
Token::Token (const std::string& string) : mType (Type_String), mString (string) {}
Token::Token (double number) : mType (Type_Number), mNumber (number) {}
Token::operator bool() const
{
return mType!=Type_None;
}
bool operator== (const Token& left, const Token& right)
{
if (left.mType!=right.mType)
return false;
switch (left.mType)
{
case Token::Type_String: return left.mString==right.mString;
case Token::Type_Number: return left.mNumber==right.mNumber;
default: return true;
}
}
}
CSMFilter::Token CSMFilter::Parser::getStringToken()
{
std::string string;
int size = static_cast<int> (mInput.size());
for (; mIndex<size; ++mIndex)
{
char c = mInput[mIndex];
if (std::isalpha (c) || c==':' || c=='_' || (!string.empty() && std::isdigit (c)) || c=='"' ||
(!string.empty() && string[0]=='"'))
string += c;
else
break;
if (c=='"' && string.size()>1)
{
++mIndex;
break;
}
};
if (!string.empty())
{
if (string[0]=='"' && (string[string.size()-1]!='"' || string.size()<2) )
{
error();
return Token (Token::Type_None);
}
if (string[0]!='"' && string[string.size()-1]=='"')
{
error();
return Token (Token::Type_None);
}
if (string[0]=='"')
string = string.substr (1, string.size()-2);
}
return checkKeywords (string);
}
CSMFilter::Token CSMFilter::Parser::getNumberToken()
{
std::string string;
int size = static_cast<int> (mInput.size());
bool hasDecimalPoint = false;
bool hasDigit = false;
for (; mIndex<size; ++mIndex)
{
char c = mInput[mIndex];
if (std::isdigit (c))
{
string += c;
hasDigit = true;
}
else if (c=='.' && !hasDecimalPoint)
{
string += c;
hasDecimalPoint = true;
}
else if (string.empty() && c=='-')
string += c;
else
break;
}
if (!hasDigit)
{
error();
return Token (Token::Type_None);
}
float value;
std::istringstream stream (string.c_str());
stream >> value;
return value;
}
CSMFilter::Token CSMFilter::Parser::checkKeywords (const Token& token)
{
static const char *sKeywords[] =
{
"true", "false",
"and", "or", "not",
"string", "value",
0
};
std::string string = Misc::StringUtils::lowerCase (token.mString);
for (int i=0; sKeywords[i]; ++i)
if (sKeywords[i]==string || (string.size()==1 && sKeywords[i][0]==string[0]))
return Token (static_cast<Token::Type> (i+Token::Type_Keyword_True));
return token;
}
CSMFilter::Token CSMFilter::Parser::getNextToken()
{
int size = static_cast<int> (mInput.size());
char c = 0;
for (; mIndex<size; ++mIndex)
{
c = mInput[mIndex];
if (c!=' ')
break;
}
if (mIndex>=size)
return Token (Token::Type_EOS);
switch (c)
{
case '(': ++mIndex; return Token (Token::Type_Open);
case ')': ++mIndex; return Token (Token::Type_Close);
case '[': ++mIndex; return Token (Token::Type_OpenSquare);
case ']': ++mIndex; return Token (Token::Type_CloseSquare);
case ',': ++mIndex; return Token (Token::Type_Comma);
case '!': ++mIndex; return Token (Token::Type_OneShot);
}
if (c=='"' || c=='_' || std::isalpha (c) || c==':')
return getStringToken();
if (c=='-' || c=='.' || std::isdigit (c))
return getNumberToken();
error();
return Token (Token::Type_None);
}
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseImp (bool allowEmpty, bool ignoreOneShot)
{
if (Token token = getNextToken())
{
if (token==Token (Token::Type_OneShot))
token = getNextToken();
if (token)
switch (token.mType)
{
case Token::Type_Keyword_True:
return boost::shared_ptr<CSMFilter::Node> (new BooleanNode (true));
case Token::Type_Keyword_False:
return boost::shared_ptr<CSMFilter::Node> (new BooleanNode (false));
case Token::Type_Keyword_And:
case Token::Type_Keyword_Or:
return parseNAry (token);
case Token::Type_Keyword_Not:
{
boost::shared_ptr<CSMFilter::Node> node = parseImp();
if (mError)
return boost::shared_ptr<Node>();
return boost::shared_ptr<CSMFilter::Node> (new NotNode (node));
}
case Token::Type_Keyword_Text:
return parseText();
case Token::Type_Keyword_Value:
return parseValue();
case Token::Type_EOS:
if (!allowEmpty)
error();
return boost::shared_ptr<Node>();
default:
error();
}
}
return boost::shared_ptr<Node>();
}
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseNAry (const Token& keyword)
{
std::vector<boost::shared_ptr<Node> > nodes;
Token token = getNextToken();
if (token.mType!=Token::Type_Open)
{
error();
return boost::shared_ptr<Node>();
}
for (;;)
{
boost::shared_ptr<Node> node = parseImp();
if (mError)
return boost::shared_ptr<Node>();
nodes.push_back (node);
Token token = getNextToken();
if (!token || (token.mType!=Token::Type_Close && token.mType!=Token::Type_Comma))
{
error();
return boost::shared_ptr<Node>();
}
if (token.mType==Token::Type_Close)
break;
}
if (nodes.empty())
{
error();
return boost::shared_ptr<Node>();
}
switch (keyword.mType)
{
case Token::Type_Keyword_And: return boost::shared_ptr<CSMFilter::Node> (new AndNode (nodes));
case Token::Type_Keyword_Or: return boost::shared_ptr<CSMFilter::Node> (new OrNode (nodes));
default: error(); return boost::shared_ptr<Node>();
}
}
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseText()
{
Token token = getNextToken();
if (token.mType!=Token::Type_Open)
{
error();
return boost::shared_ptr<Node>();
}
token = getNextToken();
if (!token)
return boost::shared_ptr<Node>();
// parse column ID
int columnId = -1;
if (token.mType==Token::Type_Number)
{
if (static_cast<int> (token.mNumber)==token.mNumber)
columnId = static_cast<int> (token.mNumber);
}
else if (token.mType==Token::Type_String)
{
columnId = CSMWorld::Columns::getId (token.mString);
}
if (columnId<0)
{
error();
return boost::shared_ptr<Node>();
}
token = getNextToken();
if (token.mType!=Token::Type_Comma)
{
error();
return boost::shared_ptr<Node>();
}
// parse text pattern
token = getNextToken();
if (token.mType!=Token::Type_String)
{
error();
return boost::shared_ptr<Node>();
}
std::string text = token.mString;
token = getNextToken();
if (token.mType!=Token::Type_Close)
{
error();
return boost::shared_ptr<Node>();
}
return boost::shared_ptr<Node> (new TextNode (columnId, text));
}
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseValue()
{
Token token = getNextToken();
if (token.mType!=Token::Type_Open)
{
error();
return boost::shared_ptr<Node>();
}
token = getNextToken();
if (!token)
return boost::shared_ptr<Node>();
// parse column ID
int columnId = -1;
if (token.mType==Token::Type_Number)
{
if (static_cast<int> (token.mNumber)==token.mNumber)
columnId = static_cast<int> (token.mNumber);
}
else if (token.mType==Token::Type_String)
{
columnId = CSMWorld::Columns::getId (token.mString);
}
if (columnId<0)
{
error();
return boost::shared_ptr<Node>();
}
token = getNextToken();
if (token.mType!=Token::Type_Comma)
{
error();
return boost::shared_ptr<Node>();
}
// parse value
double lower = 0;
double upper = 0;
bool min = false;
bool max = false;
token = getNextToken();
if (token.mType==Token::Type_Number)
{
// single value
min = max = true;
lower = upper = token.mNumber;
}
else
{
// interval
if (token.mType==Token::Type_OpenSquare)
min = true;
else if (token.mType!=Token::Type_CloseSquare && token.mType!=Token::Type_Open)
{
error();
return boost::shared_ptr<Node>();
}
token = getNextToken();
if (token.mType!=Token::Type_Number)
{
error();
return boost::shared_ptr<Node>();
}
lower = token.mNumber;
token = getNextToken();
if (token.mType!=Token::Type_Comma)
{
error();
return boost::shared_ptr<Node>();
}
token = getNextToken();
if (token.mType!=Token::Type_Number)
{
error();
return boost::shared_ptr<Node>();
}
upper = token.mNumber;
token = getNextToken();
if (token.mType==Token::Type_CloseSquare)
max = true;
else if (token.mType!=Token::Type_OpenSquare && token.mType!=Token::Type_Close)
{
error();
return boost::shared_ptr<Node>();
}
}
token = getNextToken();
if (token.mType!=Token::Type_Close)
{
error();
return boost::shared_ptr<Node>();
}
return boost::shared_ptr<Node> (new ValueNode (columnId, lower, upper, min, max));
}
void CSMFilter::Parser::error()
{
mError = true;
}
CSMFilter::Parser::Parser (const CSMWorld::Data& data)
: mIndex (0), mError (false), mData (data) {}
bool CSMFilter::Parser::parse (const std::string& filter, bool allowPredefined)
{
// reset
mFilter.reset();
mError = false;
mInput = filter;
mIndex = 0;
Token token;
if (allowPredefined)
token = getNextToken();
if (!allowPredefined || token==Token (Token::Type_OneShot))
{
boost::shared_ptr<Node> node = parseImp (true, token!=Token (Token::Type_OneShot));
if (mError)
return false;
if (getNextToken()!=Token (Token::Type_EOS))
{
error();
return false;
}
if (node)
mFilter = node;
else
{
// Empty filter string equals to filter "true".
mFilter.reset (new BooleanNode (true));
}
return true;
}
else if (token.mType==Token::Type_String && allowPredefined)
{
if (getNextToken()!=Token (Token::Type_EOS))
{
error();
return false;
}
int index = mData.getFilters().searchId (token.mString);
if (index==-1)
{
error();
return false;
}
const CSMWorld::Record<CSMFilter::Filter>& record = mData.getFilters().getRecord (index);
if (record.isDeleted())
{
error();
return false;
}
return parse (record.get().mFilter, false);
}
else
{
error();
return false;
}
}
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::getFilter() const
{
if (mError)
throw std::logic_error ("No filter available");
return mFilter;
}

View file

@ -0,0 +1,59 @@
#ifndef CSM_FILTER_PARSER_H
#define CSM_FILTER_PARSER_H
#include <boost/shared_ptr.hpp>
#include "node.hpp"
namespace CSMWorld
{
class Data;
}
namespace CSMFilter
{
struct Token;
class Parser
{
boost::shared_ptr<Node> mFilter;
std::string mInput;
int mIndex;
bool mError;
const CSMWorld::Data& mData;
Token getStringToken();
Token getNumberToken();
Token getNextToken();
Token checkKeywords (const Token& token);
///< Turn string token into keyword token, if possible.
boost::shared_ptr<Node> parseImp (bool allowEmpty = false, bool ignoreOneShot = false);
///< Will return a null-pointer, if there is nothing more to parse.
boost::shared_ptr<Node> parseNAry (const Token& keyword);
boost::shared_ptr<Node> parseText();
boost::shared_ptr<Node> parseValue();
void error();
public:
Parser (const CSMWorld::Data& data);
bool parse (const std::string& filter, bool allowPredefined = true);
///< Discards any previous calls to parse
///
/// \return Success?
boost::shared_ptr<Node> getFilter() const;
///< Throws an exception if the last call to parse did not return true.
};
}
#endif

View file

@ -0,0 +1,62 @@
#include "textnode.hpp"
#include <sstream>
#include <stdexcept>
#include <QRegExp>
#include "../world/columns.hpp"
#include "../world/idtable.hpp"
CSMFilter::TextNode::TextNode (int columnId, const std::string& text)
: mColumnId (columnId), mText (text)
{}
bool CSMFilter::TextNode::test (const CSMWorld::IdTable& table, int row,
const std::map<int, int>& columns) const
{
const std::map<int, int>::const_iterator iter = columns.find (mColumnId);
if (iter==columns.end())
throw std::logic_error ("invalid column in text node test");
if (iter->second==-1)
return true;
QModelIndex index = table.index (row, iter->second);
QVariant data = table.data (index);
if (data.type()!=QVariant::String)
return false;
/// \todo make pattern syntax configurable
QRegExp regExp (QString::fromUtf8 (mText.c_str()), Qt::CaseInsensitive);
return regExp.exactMatch (data.toString());
}
std::vector<int> CSMFilter::TextNode::getReferencedColumns() const
{
return std::vector<int> (1, mColumnId);
}
std::string CSMFilter::TextNode::toString (bool numericColumns) const
{
std::ostringstream stream;
stream << "text (";
if (numericColumns)
stream << mColumnId;
else
stream
<< "\""
<< CSMWorld::Columns::getName (static_cast<CSMWorld::Columns::ColumnId> (mColumnId))
<< "\"";
stream << ", \"" << mText << "\")";
return stream.str();
}

View file

@ -0,0 +1,33 @@
#ifndef CSM_FILTER_TEXTNODE_H
#define CSM_FILTER_TEXTNODE_H
#include "leafnode.hpp"
namespace CSMFilter
{
class TextNode : public LeafNode
{
int mColumnId;
std::string mText;
public:
TextNode (int columnId, const std::string& text);
virtual bool test (const CSMWorld::IdTable& table, int row,
const std::map<int, int>& columns) const;
///< \return Can the specified table row pass through to filter?
/// \param columns column ID to column index mapping
virtual std::vector<int> getReferencedColumns() const;
///< Return a list of the IDs of the columns referenced by this node. The column mapping
/// passed into test as columns must contain all columns listed here.
virtual std::string toString (bool numericColumns) const;
///< Return a string that represents this node.
///
/// \param numericColumns Use numeric IDs instead of string to represent columns.
};
}
#endif

View file

@ -0,0 +1,26 @@
#include "unarynode.hpp"
CSMFilter::UnaryNode::UnaryNode (boost::shared_ptr<Node> child, const std::string& name)
: mChild (child), mName (name)
{}
const CSMFilter::Node& CSMFilter::UnaryNode::getChild() const
{
return *mChild;
}
CSMFilter::Node& CSMFilter::UnaryNode::getChild()
{
return *mChild;
}
std::vector<int> CSMFilter::UnaryNode::getReferencedColumns() const
{
return mChild->getReferencedColumns();
}
std::string CSMFilter::UnaryNode::toString (bool numericColumns) const
{
return mName + " " + mChild->toString (numericColumns);
}

View file

@ -0,0 +1,34 @@
#ifndef CSM_FILTER_UNARYNODE_H
#define CSM_FILTER_UNARYNODE_H
#include <boost/shared_ptr.hpp>
#include "node.hpp"
namespace CSMFilter
{
class UnaryNode : public Node
{
boost::shared_ptr<Node> mChild;
std::string mName;
public:
UnaryNode (boost::shared_ptr<Node> child, const std::string& name);
const Node& getChild() const;
Node& getChild();
virtual std::vector<int> getReferencedColumns() const;
///< Return a list of the IDs of the columns referenced by this node. The column mapping
/// passed into test as columns must contain all columns listed here.
virtual std::string toString (bool numericColumns) const;
///< Return a string that represents this node.
///
/// \param numericColumns Use numeric IDs instead of string to represent columns.
};
}
#endif

View file

@ -0,0 +1,71 @@
#include "valuenode.hpp"
#include <sstream>
#include <stdexcept>
#include "../world/columns.hpp"
#include "../world/idtable.hpp"
CSMFilter::ValueNode::ValueNode (int columnId,
double lower, double upper, bool min, bool max)
: mColumnId (columnId), mLower (lower), mUpper (upper), mMin (min), mMax (max)
{}
bool CSMFilter::ValueNode::test (const CSMWorld::IdTable& table, int row,
const std::map<int, int>& columns) const
{
const std::map<int, int>::const_iterator iter = columns.find (mColumnId);
if (iter==columns.end())
throw std::logic_error ("invalid column in test value test");
if (iter->second==-1)
return true;
QModelIndex index = table.index (row, iter->second);
QVariant data = table.data (index);
if (data.type()!=QVariant::Double && data.type()!=QVariant::Bool && data.type()!=QVariant::Int &&
data.type()!=QVariant::UInt)
return false;
double value = data.toDouble();
if (mLower==mUpper && mMin && mMax)
return value==mLower;
return (mMin ? value>=mLower : value>mLower) && (mMax ? value<=mUpper : value<mUpper);
}
std::vector<int> CSMFilter::ValueNode::getReferencedColumns() const
{
return std::vector<int> (1, mColumnId);
}
std::string CSMFilter::ValueNode::toString (bool numericColumns) const
{
std::ostringstream stream;
stream << "value (";
if (numericColumns)
stream << mColumnId;
else
stream
<< "\""
<< CSMWorld::Columns::getName (static_cast<CSMWorld::Columns::ColumnId> (mColumnId))
<< "\"";
stream << ", \"";
if (mLower==mUpper && mMin && mMax)
stream << mLower;
else
stream << (mMin ? "[" : "(") << mLower << ", " << mUpper << (mMax ? "]" : ")");
stream << ")";
return stream.str();
}

View file

@ -0,0 +1,37 @@
#ifndef CSM_FILTER_VALUENODE_H
#define CSM_FILTER_VALUENODE_H
#include "leafnode.hpp"
namespace CSMFilter
{
class ValueNode : public LeafNode
{
int mColumnId;
std::string mText;
double mLower;
double mUpper;
bool mMin;
bool mMax;
public:
ValueNode (int columnId, double lower, double upper, bool min, bool max);
virtual bool test (const CSMWorld::IdTable& table, int row,
const std::map<int, int>& columns) const;
///< \return Can the specified table row pass through to filter?
/// \param columns column ID to column index mapping
virtual std::vector<int> getReferencedColumns() const;
///< Return a list of the IDs of the columns referenced by this node. The column mapping
/// passed into test as columns must contain all columns listed here.
virtual std::string toString (bool numericColumns) const;
///< Return a string that represents this node.
///
/// \param numericColumns Use numeric IDs instead of string to represent columns.
};
}
#endif

View file

@ -0,0 +1,82 @@
#include "settingcontainer.hpp"
#include <QStringList>
CSMSettings::SettingContainer::SettingContainer(QObject *parent) :
QObject(parent), mValue (0), mValues (0)
{
}
CSMSettings::SettingContainer::SettingContainer(const QString &value, QObject *parent) :
QObject(parent), mValue (new QString (value)), mValues (0)
{
}
void CSMSettings::SettingContainer::insert (const QString &value)
{
if (mValue)
{
mValues = new QStringList;
mValues->push_back (*mValue);
mValues->push_back (value);
delete mValue;
mValue = 0;
}
else
{
delete mValue;
mValue = new QString (value);
}
}
void CSMSettings::SettingContainer::update (const QString &value, int index)
{
if (isEmpty())
mValue = new QString(value);
else if (mValue)
*mValue = value;
else if (mValues)
mValues->replace(index, value);
}
QString CSMSettings::SettingContainer::getValue (int index) const
{
QString retVal("");
//if mValue is valid, it's a single-value property.
//ignore the index and return the value
if (mValue)
retVal = *mValue;
//otherwise, if it's a multivalued property
//return the appropriate value at the index
else if (mValues)
{
if (index == -1)
retVal = mValues->at(0);
else if (index < mValues->size())
retVal = mValues->at(index);
}
return retVal;
}
int CSMSettings::SettingContainer::count () const
{
int retVal = 0;
if (!isEmpty())
{
if (mValues)
retVal = mValues->size();
else
retVal = 1;
}
return retVal;
}

View file

@ -0,0 +1,47 @@
#ifndef SETTINGCONTAINER_HPP
#define SETTINGCONTAINER_HPP
#include <QObject>
class QStringList;
namespace CSMSettings
{
class SettingContainer : public QObject
{
Q_OBJECT
QString *mValue;
QStringList *mValues;
public:
explicit SettingContainer (QObject *parent = 0);
explicit SettingContainer (const QString &value, QObject *parent = 0);
/// add a value to the container
/// multiple values supported
void insert (const QString &value);
/// update an existing value
/// index specifies multiple values
void update (const QString &value, int index = 0);
/// return value at specified index
QString getValue (int index = -1) const;
/// retrieve list of all values
inline QStringList *getValues() const { return mValues; }
/// return size of list
int count() const;
/// test for empty container
/// useful for default-constructed containers returned by QMap when invalid key is passed
inline bool isEmpty() const { return (!mValue && !mValues); }
inline bool isMultiValue() const { return (mValues); }
};
}
#endif // SETTINGCONTAINER_HPP

View file

@ -0,0 +1,104 @@
#include "settingsitem.hpp"
#include <QStringList>
bool CSMSettings::SettingsItem::updateItem (const QStringList *values)
{
QStringList::ConstIterator it = values->begin();
//if the item is not multivalued,
//save the last value passed in the container
if (!mIsMultiValue)
{
it = values->end();
it--;
}
bool isValid = true;
QString value ("");
for (; it != values->end(); ++it)
{
value = *it;
isValid = validate(value);
//skip only the invalid values
if (!isValid)
continue;
insert(value);
}
return isValid;
}
bool CSMSettings::SettingsItem::updateItem (const QString &value)
{
//takes a value or a SettingsContainer and updates itself accordingly
//after validating the data against it's own definition
QString newValue = value;
if (!validate (newValue))
newValue = mDefaultValue;
bool success = (getValue() != newValue);
if (success)
{
if (mIsMultiValue)
insert (newValue);
else
update (newValue);
}
return success;
}
bool CSMSettings::SettingsItem::updateItem(int valueListIndex)
{
bool success = false;
if (mValueList)
{
if (mValueList->size() > valueListIndex)
success = updateItem (mValueList->at(valueListIndex));
}
return success;
}
bool CSMSettings::SettingsItem::validate (const QString &value)
{
//if there is no value list or value pair, there is no validation to do
bool isValid = !(!mValueList->isEmpty() || mValuePair);
if (!isValid && !mValueList->isEmpty())
{
for (QStringList::Iterator it = mValueList->begin(); it != mValueList->end(); ++it)
// foreach (QString listItem, *mValueList)
{
isValid = (value == *it);
if (isValid)
break;
}
}
else if (!isValid && mValuePair)
{
int numVal = value.toInt();
isValid = (numVal > mValuePair->left.toInt() && numVal < mValuePair->right.toInt());
}
return isValid;
}
void CSMSettings::SettingsItem::setDefaultValue (const QString &value)
{
mDefaultValue = value;
update (value);
}
QString CSMSettings::SettingsItem::getDefaultValue() const
{
return mDefaultValue;
}

View file

@ -0,0 +1,63 @@
#ifndef SETTINGSITEM_HPP
#define SETTINGSITEM_HPP
#include <QObject>
#include "support.hpp"
#include "settingcontainer.hpp"
namespace CSMSettings
{
/// Represents a setting including metadata
/// (valid values, ranges, defaults, and multivalue status
class SettingsItem : public SettingContainer
{
QStringPair *mValuePair;
QStringList *mValueList;
bool mIsMultiValue;
QString mDefaultValue;
public:
explicit SettingsItem(QString name, bool isMultiValue,
const QString& defaultValue, QObject *parent = 0)
: SettingContainer(defaultValue, parent),
mIsMultiValue (isMultiValue), mValueList (0),
mValuePair (0), mDefaultValue (defaultValue)
{
QObject::setObjectName(name);
}
/// updateItem overloads for updating setting value
/// provided a list of values (multi-valued),
/// a specific value
/// or an index value corresponding to the mValueList
bool updateItem (const QStringList *values);
bool updateItem (const QString &value);
bool updateItem (int valueListIndex);
/// retrieve list of valid values for setting
inline QStringList *getValueList() { return mValueList; }
/// write list of valid values for setting
inline void setValueList (QStringList *valueList) { mValueList = valueList; }
/// valuePair used for spin boxes (max / min)
inline QStringPair *getValuePair() { return mValuePair; }
/// set value range (spinbox / integer use)
inline void setValuePair (QStringPair valuePair) { mValuePair = new QStringPair(valuePair); }
inline bool isMultivalue () { return mIsMultiValue; }
void setDefaultValue (const QString &value);
QString getDefaultValue () const;
private:
/// Verifies that the supplied value is one of the following:
/// 1. Within the limits of the value pair (min / max)
/// 2. One of the values indicated in the value list
bool validate (const QString &value);
};
}
#endif // SETTINGSITEM_HPP

View file

@ -0,0 +1 @@
#include "support.hpp"

View file

@ -0,0 +1,39 @@
#ifndef MODEL_SUPPORT_HPP
#define MODEL_SUPPORT_HPP
#include <QObject>
#include <QStringList>
class QLayout;
class QWidget;
class QListWidgetItem;
namespace CSMSettings
{
class SettingContainer;
typedef QList<SettingContainer *> SettingList;
typedef QMap<QString, SettingContainer *> SettingMap;
typedef QMap<QString, SettingMap *> SectionMap;
struct QStringPair
{
QStringPair(): left (""), right ("")
{}
QStringPair (const QString &leftValue, const QString &rightValue)
: left (leftValue), right(rightValue)
{}
QStringPair (const QStringPair &pair)
: left (pair.left), right (pair.right)
{}
QString left;
QString right;
bool isEmpty() const
{ return (left.isEmpty() && right.isEmpty()); }
};
}
#endif // MODEL_SUPPORT_HPP

View file

@ -0,0 +1,355 @@
#include "usersettings.hpp"
#include <QTextStream>
#include <QDir>
#include <QString>
#include <QRegExp>
#include <QMap>
#include <QMessageBox>
#include <QTextCodec>
#include <QFile>
#include <components/files/configurationmanager.hpp>
#include "settingcontainer.hpp"
#include <boost/version.hpp>
/**
* Workaround for problems with whitespaces in paths in older versions of Boost library
*/
#if (BOOST_VERSION <= 104600)
namespace boost
{
template<>
inline boost::filesystem::path lexical_cast<boost::filesystem::path, std::string>(const std::string& arg)
{
return boost::filesystem::path(arg);
}
} /* namespace boost */
#endif /* (BOOST_VERSION <= 104600) */
CSMSettings::UserSettings *CSMSettings::UserSettings::mUserSettingsInstance = 0;
CSMSettings::UserSettings::UserSettings()
{
assert(!mUserSettingsInstance);
mUserSettingsInstance = this;
mReadWriteMessage = QObject::tr("<br><b>Could not open or create file for writing</b><br><br> \
Please make sure you have the right permissions and try again.<br>");
mReadOnlyMessage = QObject::tr("<br><b>Could not open file for reading</b><br><br> \
Please make sure you have the right permissions and try again.<br>");
buildEditorSettingDefaults();
}
void CSMSettings::UserSettings::buildEditorSettingDefaults()
{
SettingContainer *windowHeight = new SettingContainer("768", this);
SettingContainer *windowWidth = new SettingContainer("1024", this);
SettingContainer *rsDelegate = new SettingContainer("Icon and Text", this);
SettingContainer *refIdTypeDelegate = new SettingContainer("Icon and Text", this);
windowHeight->setObjectName ("Height");
windowWidth->setObjectName ("Width");
rsDelegate->setObjectName ("Record Status Display");
refIdTypeDelegate->setObjectName ("Referenceable ID Type Display");
SettingMap *displayFormatMap = new SettingMap;
SettingMap *windowSizeMap = new SettingMap;
displayFormatMap->insert (rsDelegate->objectName(), rsDelegate );
displayFormatMap->insert (refIdTypeDelegate->objectName(), refIdTypeDelegate);
windowSizeMap->insert (windowWidth->objectName(), windowWidth );
windowSizeMap->insert (windowHeight->objectName(), windowHeight );
mEditorSettingDefaults.insert ("Display Format", displayFormatMap);
mEditorSettingDefaults.insert ("Window Size", windowSizeMap);
}
CSMSettings::UserSettings::~UserSettings()
{
mUserSettingsInstance = 0;
}
QTextStream *CSMSettings::UserSettings::openFileStream (const QString &filePath, bool isReadOnly) const
{
QIODevice::OpenMode openFlags = QIODevice::Text;
if (isReadOnly)
openFlags = QIODevice::ReadOnly | openFlags;
else
openFlags = QIODevice::ReadWrite | QIODevice::Truncate | openFlags;
QFile *file = new QFile(filePath);
QTextStream *stream = 0;
if (file->open(openFlags))
{
stream = new QTextStream(file);
stream->setCodec(QTextCodec::codecForName("UTF-8"));
}
return stream;
}
bool CSMSettings::UserSettings::writeSettings(QMap<QString, CSMSettings::SettingList *> &settings)
{
QTextStream *stream = openFileStream(mUserFilePath);
bool success = (stream);
if (success)
{
QList<QString> keyList = settings.keys();
foreach (QString key, keyList)
{
SettingList *sectionSettings = settings[key];
*stream << "[" << key << "]" << '\n';
foreach (SettingContainer *item, *sectionSettings)
*stream << item->objectName() << " = " << item->getValue() << '\n';
}
stream->device()->close();
delete stream;
stream = 0;
}
else
{
displayFileErrorMessage(mReadWriteMessage, false);
}
return (success);
}
const CSMSettings::SectionMap &CSMSettings::UserSettings::getSectionMap() const
{
return mSectionSettings;
}
const CSMSettings::SettingMap *CSMSettings::UserSettings::getSettings(const QString &sectionName) const
{
return getValidSettings(sectionName);
}
bool CSMSettings::UserSettings::loadFromFile(const QString &filePath)
{
if (filePath.isEmpty())
return false;
SectionMap loadedSettings;
QTextStream *stream = openFileStream (filePath, true);
bool success = (stream);
if (success)
{
//looks for a square bracket, "'\\["
//that has one or more "not nothing" in it, "([^]]+)"
//and is closed with a square bracket, "\\]"
QRegExp sectionRe("^\\[([^]]+)\\]");
//Find any character(s) that is/are not equal sign(s), "[^=]+"
//followed by an optional whitespace, an equal sign, and another optional whitespace, "\\s*=\\s*"
//and one or more periods, "(.+)"
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
CSMSettings::SettingMap *settings = 0;
QString section = "none";
while (!stream->atEnd())
{
QString line = stream->readLine().simplified();
if (line.isEmpty() || line.startsWith("#"))
continue;
//if a section is found, push it onto a new QStringList
//and push the QStringList onto
if (sectionRe.exactMatch(line))
{
//add the previous section's settings to the member map
if (settings)
loadedSettings.insert(section, settings);
//save new section and create a new list
section = sectionRe.cap(1);
settings = new SettingMap;
continue;
}
if (keyRe.indexIn(line) != -1)
{
SettingContainer *sc = new SettingContainer (keyRe.cap(2).simplified());
sc->setObjectName(keyRe.cap(1).simplified());
(*settings)[keyRe.cap(1).simplified()] = sc;
}
}
loadedSettings.insert(section, settings);
stream->device()->close();
delete stream;
stream = 0;
}
mergeMap (loadedSettings);
return success;
}
void CSMSettings::UserSettings::mergeMap (const CSMSettings::SectionMap &sectionSettings)
{
foreach (QString key, sectionSettings.uniqueKeys())
{
// insert entire section if it does not already exist in the loaded files
if (mSectionSettings.find(key) == mSectionSettings.end())
mSectionSettings.insert(key, sectionSettings.value(key));
else
{
SettingMap *passedSettings = sectionSettings.value(key);
SettingMap *settings = mSectionSettings.value(key);
foreach (QString key2, passedSettings->uniqueKeys())
{
//insert section settings individially if they do not already exist
if (settings->find(key2) == settings->end())
settings->insert(key2, passedSettings->value(key2));
else
{
settings->value(key2)->update(passedSettings->value(key2)->getValue());
}
}
}
}
}
void CSMSettings::UserSettings::loadSettings (const QString &fileName)
{
mSectionSettings.clear();
//global
QString globalFilePath = QString::fromStdString(mCfgMgr.getGlobalPath().string()) + fileName;
bool globalOk = loadFromFile(globalFilePath);
//local
QString localFilePath = QString::fromStdString(mCfgMgr.getLocalPath().string()) + fileName;
bool localOk = loadFromFile(localFilePath);
//user
mUserFilePath = QString::fromStdString(mCfgMgr.getUserPath().string()) + fileName;
loadFromFile(mUserFilePath);
if (!(localOk || globalOk))
{
QString message = QObject::tr("<br><b>Could not open user settings files for reading</b><br><br> \
Global and local settings files could not be read.\
You may have incorrect file permissions or the OpenCS installation may be corrupted.<br>");
message += QObject::tr("<br>Global filepath: ") + globalFilePath;
message += QObject::tr("<br>Local filepath: ") + localFilePath;
displayFileErrorMessage ( message, true);
}
}
void CSMSettings::UserSettings::updateSettings (const QString &sectionName, const QString &settingName)
{
SettingMap *settings = getValidSettings(sectionName);
if (!settings)
return;
if (settingName.isEmpty())
{
foreach (const SettingContainer *setting, *settings)
emit signalUpdateEditorSetting (setting->objectName(), setting->getValue());
}
else
{
if (settings->find(settingName) != settings->end())
{
const SettingContainer *setting = settings->value(settingName);
emit signalUpdateEditorSetting (setting->objectName(), setting->getValue());
}
}
}
QString CSMSettings::UserSettings::getSetting (const QString &section, const QString &setting) const
{
SettingMap *settings = getValidSettings(section);
QString retVal = "";
if (settings->find(setting) != settings->end())
retVal = settings->value(setting)->getValue();
return retVal;
}
CSMSettings::UserSettings& CSMSettings::UserSettings::instance()
{
assert(mUserSettingsInstance);
return *mUserSettingsInstance;
}
void CSMSettings::UserSettings::displayFileErrorMessage(const QString &message, bool isReadOnly)
{
// File cannot be opened or created
QMessageBox msgBox;
msgBox.setWindowTitle(QObject::tr("OpenCS configuration file I/O error"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
if (!isReadOnly)
msgBox.setText (mReadWriteMessage + message);
else
msgBox.setText (message);
msgBox.exec();
}
CSMSettings::SettingMap *
CSMSettings::UserSettings::getValidSettings (const QString &sectionName) const
{
SettingMap *settings = 0;
//copy the default values for the entire section if it's not found
if (mSectionSettings.find(sectionName) == mSectionSettings.end())
{
if (mEditorSettingDefaults.find(sectionName) != mEditorSettingDefaults.end())
settings = mEditorSettingDefaults.value (sectionName);
}
//otherwise, iterate the section's settings, looking for missing values and replacing them with defaults.
else
{
SettingMap *loadedSettings = mSectionSettings[sectionName];
SettingMap *defaultSettings = mEditorSettingDefaults[sectionName];
foreach (QString key, defaultSettings->uniqueKeys())
{
//write the default value to the loaded settings
if (loadedSettings->find((key))==loadedSettings->end())
loadedSettings->insert(key, defaultSettings->value(key));
}
settings = mSectionSettings.value (sectionName);
}
return settings;
}

View file

@ -0,0 +1,94 @@
#ifndef USERSETTINGS_HPP
#define USERSETTINGS_HPP
#include <QTextStream>
#include <QStringList>
#include <QString>
#include <QMap>
#include <boost/filesystem/path.hpp>
#include "support.hpp"
#ifndef Q_MOC_RUN
#include <components/files/configurationmanager.hpp>
#endif
namespace Files { typedef std::vector<boost::filesystem::path> PathContainer;
struct ConfigurationManager;}
class QFile;
namespace CSMSettings {
struct UserSettings: public QObject
{
Q_OBJECT
SectionMap mSectionSettings;
SectionMap mEditorSettingDefaults;
static UserSettings *mUserSettingsInstance;
QString mUserFilePath;
Files::ConfigurationManager mCfgMgr;
QString mReadOnlyMessage;
QString mReadWriteMessage;
public:
/// Singleton implementation
static UserSettings& instance();
UserSettings();
~UserSettings();
UserSettings (UserSettings const &); //not implemented
void operator= (UserSettings const &); //not implemented
/// Writes settings to the last loaded settings file
bool writeSettings(QMap<QString, SettingList *> &sections);
/// Called from editor to trigger signal to update the specified setting.
/// If no setting name is specified, all settings found in the specified section are updated.
void updateSettings (const QString &sectionName, const QString &settingName = "");
/// Retrieves the settings file at all three levels (global, local and user).
/// \todo Multi-valued settings are not fully implemented. Setting values
/// \todo loaded in later files will always overwrite previously loaded values.
void loadSettings (const QString &fileName);
/// Returns the entire map of settings across all sections
const SectionMap &getSectionMap () const;
const SettingMap *getSettings (const QString &sectionName) const;
/// Retrieves the value as a QString of the specified setting in the specified section
QString getSetting(const QString &section, const QString &setting) const;
private:
/// Opens a QTextStream from the provided path as read-only or read-write.
QTextStream *openFileStream (const QString &filePath, bool isReadOnly = false) const;
/// Parses a setting file specified in filePath from the provided text stream.
bool loadFromFile (const QString &filePath = "");
/// merge the passed map into mSectionSettings
void mergeMap (const SectionMap &);
void displayFileErrorMessage(const QString &message, bool isReadOnly);
void buildEditorSettingDefaults();
SettingMap *getValidSettings (const QString &sectionName) const;
signals:
void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue);
};
}
#endif // USERSETTINGS_HPP

View file

@ -0,0 +1,39 @@
#include "birthsigncheck.hpp"
#include <sstream>
#include <map>
#include <components/esm/loadbsgn.hpp>
#include "../world/universalid.hpp"
CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign>& birthsigns)
: mBirthsigns (birthsigns)
{}
int CSMTools::BirthsignCheckStage::setup()
{
return mBirthsigns.getSize();
}
void CSMTools::BirthsignCheckStage::perform (int stage, std::vector<std::string>& messages)
{
const ESM::BirthSign& birthsign = mBirthsigns.getRecord (stage).get();
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Birthsign, birthsign.mId);
// test for empty name, description and texture
if (birthsign.mName.empty())
messages.push_back (id.toString() + "|" + birthsign.mId + " has an empty name");
if (birthsign.mDescription.empty())
messages.push_back (id.toString() + "|" + birthsign.mId + " has an empty description");
if (birthsign.mTexture.empty())
messages.push_back (id.toString() + "|" + birthsign.mId + " is missing a texture");
/// \todo test if the texture exists
/// \todo check data members that can't be edited in the table view
}

View file

@ -0,0 +1,29 @@
#ifndef CSM_TOOLS_BIRTHSIGNCHECK_H
#define CSM_TOOLS_BIRTHSIGNCHECK_H
#include <components/esm/loadbsgn.hpp>
#include "../world/idcollection.hpp"
#include "stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that birthsign records are internally consistent
class BirthsignCheckStage : public Stage
{
const CSMWorld::IdCollection<ESM::BirthSign>& mBirthsigns;
public:
BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign>& birthsigns);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages);
///< Messages resulting from this tage will be appended to \a messages.
};
}
#endif

View file

@ -0,0 +1,72 @@
#include "classcheck.hpp"
#include <sstream>
#include <map>
#include <components/esm/loadclas.hpp>
#include <components/esm/loadskil.hpp>
#include "../world/universalid.hpp"
CSMTools::ClassCheckStage::ClassCheckStage (const CSMWorld::IdCollection<ESM::Class>& classes)
: mClasses (classes)
{}
int CSMTools::ClassCheckStage::setup()
{
return mClasses.getSize();
}
void CSMTools::ClassCheckStage::perform (int stage, std::vector<std::string>& messages)
{
const ESM::Class& class_= mClasses.getRecord (stage).get();
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Class, class_.mId);
// test for empty name and description
if (class_.mName.empty())
messages.push_back (id.toString() + "|" + class_.mId + " has an empty name");
if (class_.mDescription.empty())
messages.push_back (id.toString() + "|" + class_.mId + " has an empty description");
// test for invalid attributes
for (int i=0; i<2; ++i)
if (class_.mData.mAttribute[i]==-1)
{
std::ostringstream stream;
stream << id.toString() << "|Attribute #" << i << " of " << class_.mId << " is not set";
messages.push_back (stream.str());
}
if (class_.mData.mAttribute[0]==class_.mData.mAttribute[1] && class_.mData.mAttribute[0]!=-1)
{
std::ostringstream stream;
stream << id.toString() << "|Class lists same attribute twice";
messages.push_back (stream.str());
}
// test for non-unique skill
std::map<int, int> skills; // ID, number of occurrences
for (int i=0; i<5; ++i)
for (int i2=0; i2<2; ++i2)
++skills[class_.mData.mSkills[i][i2]];
for (std::map<int, int>::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter)
if (iter->second>1)
{
std::ostringstream stream;
stream
<< id.toString() << "|"
<< ESM::Skill::indexToId (iter->first) << " is listed more than once";
messages.push_back (stream.str());
}
}

View file

@ -0,0 +1,29 @@
#ifndef CSM_TOOLS_CLASSCHECK_H
#define CSM_TOOLS_CLASSCHECK_H
#include <components/esm/loadclas.hpp>
#include "../world/idcollection.hpp"
#include "stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that class records are internally consistent
class ClassCheckStage : public Stage
{
const CSMWorld::IdCollection<ESM::Class>& mClasses;
public:
ClassCheckStage (const CSMWorld::IdCollection<ESM::Class>& classes);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages);
///< Messages resulting from this tage will be appended to \a messages.
};
}
#endif

View file

@ -0,0 +1,61 @@
#include "factioncheck.hpp"
#include <sstream>
#include <map>
#include <components/esm/loadfact.hpp>
#include <components/esm/loadskil.hpp>
#include "../world/universalid.hpp"
CSMTools::FactionCheckStage::FactionCheckStage (const CSMWorld::IdCollection<ESM::Faction>& factions)
: mFactions (factions)
{}
int CSMTools::FactionCheckStage::setup()
{
return mFactions.getSize();
}
void CSMTools::FactionCheckStage::perform (int stage, std::vector<std::string>& messages)
{
const ESM::Faction& faction = mFactions.getRecord (stage).get();
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Faction, faction.mId);
// test for empty name
if (faction.mName.empty())
messages.push_back (id.toString() + "|" + faction.mId + " has an empty name");
// test for invalid attributes
if (faction.mData.mAttribute[0]==faction.mData.mAttribute[1] && faction.mData.mAttribute[0]!=-1)
{
std::ostringstream stream;
stream << id.toString() << "|Faction lists same attribute twice";
messages.push_back (stream.str());
}
// test for non-unique skill
std::map<int, int> skills; // ID, number of occurrences
for (int i=0; i<6; ++i)
if (faction.mData.mSkills[i]!=-1)
++skills[faction.mData.mSkills[i]];
for (std::map<int, int>::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter)
if (iter->second>1)
{
std::ostringstream stream;
stream
<< id.toString() << "|"
<< ESM::Skill::indexToId (iter->first) << " is listed more than once";
messages.push_back (stream.str());
}
/// \todo check data members that can't be edited in the table view
}

View file

@ -0,0 +1,29 @@
#ifndef CSM_TOOLS_FACTIONCHECK_H
#define CSM_TOOLS_FACTIONCHECK_H
#include <components/esm/loadfact.hpp>
#include "../world/idcollection.hpp"
#include "stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that faction records are internally consistent
class FactionCheckStage : public Stage
{
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
public:
FactionCheckStage (const CSMWorld::IdCollection<ESM::Faction>& factions);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages);
///< Messages resulting from this tage will be appended to \a messages.
};
}
#endif

View file

@ -0,0 +1,23 @@
#include "mandatoryid.hpp"
#include "../world/collectionbase.hpp"
#include "../world/record.hpp"
CSMTools::MandatoryIdStage::MandatoryIdStage (const CSMWorld::CollectionBase& idCollection,
const CSMWorld::UniversalId& collectionId, const std::vector<std::string>& ids)
: mIdCollection (idCollection), mCollectionId (collectionId), mIds (ids)
{}
int CSMTools::MandatoryIdStage::setup()
{
return mIds.size();
}
void CSMTools::MandatoryIdStage::perform (int stage, std::vector<std::string>& messages)
{
if (mIdCollection.searchId (mIds.at (stage))==-1 ||
mIdCollection.getRecord (mIds.at (stage)).isDeleted())
messages.push_back (mCollectionId.toString() + "|Missing mandatory record: " + mIds.at (stage));
}

View file

@ -0,0 +1,38 @@
#ifndef CSM_TOOLS_MANDATORYID_H
#define CSM_TOOLS_MANDATORYID_H
#include <string>
#include <vector>
#include "../world/universalid.hpp"
#include "stage.hpp"
namespace CSMWorld
{
class CollectionBase;
}
namespace CSMTools
{
/// \brief Verify stage: make sure that records with specific IDs exist.
class MandatoryIdStage : public Stage
{
const CSMWorld::CollectionBase& mIdCollection;
CSMWorld::UniversalId mCollectionId;
std::vector<std::string> mIds;
public:
MandatoryIdStage (const CSMWorld::CollectionBase& idCollection, const CSMWorld::UniversalId& collectionId,
const std::vector<std::string>& ids);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages);
///< Messages resulting from this tage will be appended to \a messages.
};
}
#endif

View file

@ -0,0 +1,84 @@
#include "operation.hpp"
#include <string>
#include <vector>
#include <QTimer>
#include "../doc/state.hpp"
#include "stage.hpp"
void CSMTools::Operation::prepareStages()
{
mCurrentStage = mStages.begin();
mCurrentStep = 0;
mCurrentStepTotal = 0;
mTotalSteps = 0;
for (std::vector<std::pair<Stage *, int> >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter)
{
iter->second = iter->first->setup();
mTotalSteps += iter->second;
}
}
CSMTools::Operation::Operation (int type) : mType (type) {}
CSMTools::Operation::~Operation()
{
for (std::vector<std::pair<Stage *, int> >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter)
delete iter->first;
}
void CSMTools::Operation::run()
{
prepareStages();
QTimer timer;
timer.connect (&timer, SIGNAL (timeout()), this, SLOT (verify()));
timer.start (0);
exec();
}
void CSMTools::Operation::appendStage (Stage *stage)
{
mStages.push_back (std::make_pair (stage, 0));
}
void CSMTools::Operation::abort()
{
exit();
}
void CSMTools::Operation::verify()
{
std::vector<std::string> messages;
while (mCurrentStage!=mStages.end())
{
if (mCurrentStep>=mCurrentStage->second)
{
mCurrentStep = 0;
++mCurrentStage;
}
else
{
mCurrentStage->first->perform (mCurrentStep++, messages);
++mCurrentStepTotal;
break;
}
}
emit progress (mCurrentStepTotal, mTotalSteps ? mTotalSteps : 1, mType);
for (std::vector<std::string>::const_iterator iter (messages.begin()); iter!=messages.end(); ++iter)
emit reportMessage (iter->c_str(), mType);
if (mCurrentStage==mStages.end())
exit();
}

View file

@ -0,0 +1,54 @@
#ifndef CSM_TOOLS_OPERATION_H
#define CSM_TOOLS_OPERATION_H
#include <vector>
#include <QThread>
namespace CSMTools
{
class Stage;
class Operation : public QThread
{
Q_OBJECT
int mType;
std::vector<std::pair<Stage *, int> > mStages; // stage, number of steps
std::vector<std::pair<Stage *, int> >::iterator mCurrentStage;
int mCurrentStep;
int mCurrentStepTotal;
int mTotalSteps;
void prepareStages();
public:
Operation (int type);
virtual ~Operation();
virtual void run();
void appendStage (Stage *stage);
///< The ownership of \a stage is transferred to *this.
///
/// \attention Do no call this function while this Operation is running.
signals:
void progress (int current, int max, int type);
void reportMessage (const QString& message, int type);
public slots:
void abort();
private slots:
void verify();
};
}
#endif

View file

@ -0,0 +1,68 @@
#include "racecheck.hpp"
#include <sstream>
#include <components/esm/loadrace.hpp>
#include "../world/universalid.hpp"
void CSMTools::RaceCheckStage::performPerRecord (int stage, std::vector<std::string>& messages)
{
const ESM::Race& race = mRaces.getRecord (stage).get();
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Race, race.mId);
// test for empty name and description
if (race.mName.empty())
messages.push_back (id.toString() + "|" + race.mId + " has an empty name");
if (race.mDescription.empty())
messages.push_back (id.toString() + "|" + race.mId + " has an empty description");
// test for positive height
if (race.mData.mHeight.mMale<=0)
messages.push_back (id.toString() + "|male " + race.mId + " has non-positive height");
if (race.mData.mHeight.mFemale<=0)
messages.push_back (id.toString() + "|female " + race.mId + " has non-positive height");
// test for non-negative weight
if (race.mData.mWeight.mMale<0)
messages.push_back (id.toString() + "|male " + race.mId + " has negative weight");
if (race.mData.mWeight.mFemale<0)
messages.push_back (id.toString() + "|female " + race.mId + " has negative weight");
// remember playable flag
if (race.mData.mFlags & 0x1)
mPlayable = true;
/// \todo check data members that can't be edited in the table view
}
void CSMTools::RaceCheckStage::performFinal (std::vector<std::string>& messages)
{
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Races);
if (!mPlayable)
messages.push_back (id.toString() + "|No playable race");
}
CSMTools::RaceCheckStage::RaceCheckStage (const CSMWorld::IdCollection<ESM::Race>& races)
: mRaces (races), mPlayable (false)
{}
int CSMTools::RaceCheckStage::setup()
{
mPlayable = false;
return mRaces.getSize()+1;
}
void CSMTools::RaceCheckStage::perform (int stage, std::vector<std::string>& messages)
{
if (stage==mRaces.getSize())
performFinal (messages);
else
performPerRecord (stage, messages);
}

View file

@ -0,0 +1,34 @@
#ifndef CSM_TOOLS_RACECHECK_H
#define CSM_TOOLS_RACECHECK_H
#include <components/esm/loadrace.hpp>
#include "../world/idcollection.hpp"
#include "stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that race records are internally consistent
class RaceCheckStage : public Stage
{
const CSMWorld::IdCollection<ESM::Race>& mRaces;
bool mPlayable;
void performPerRecord (int stage, std::vector<std::string>& messages);
void performFinal (std::vector<std::string>& messages);
public:
RaceCheckStage (const CSMWorld::IdCollection<ESM::Race>& races);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages);
///< Messages resulting from this tage will be appended to \a messages.
};
}
#endif

View file

@ -0,0 +1,33 @@
#include "regioncheck.hpp"
#include <sstream>
#include <map>
#include <components/esm/loadregn.hpp>
#include "../world/universalid.hpp"
CSMTools::RegionCheckStage::RegionCheckStage (const CSMWorld::IdCollection<ESM::Region>& regions)
: mRegions (regions)
{}
int CSMTools::RegionCheckStage::setup()
{
return mRegions.getSize();
}
void CSMTools::RegionCheckStage::perform (int stage, std::vector<std::string>& messages)
{
const ESM::Region& region = mRegions.getRecord (stage).get();
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Region, region.mId);
// test for empty name
if (region.mName.empty())
messages.push_back (id.toString() + "|" + region.mId + " has an empty name");
/// \todo test that the ID in mSleeplist exists
/// \todo check data members that can't be edited in the table view
}

View file

@ -0,0 +1,29 @@
#ifndef CSM_TOOLS_REGIONCHECK_H
#define CSM_TOOLS_REGIONCHECK_H
#include <components/esm/loadregn.hpp>
#include "../world/idcollection.hpp"
#include "stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that region records are internally consistent
class RegionCheckStage : public Stage
{
const CSMWorld::IdCollection<ESM::Region>& mRegions;
public:
RegionCheckStage (const CSMWorld::IdCollection<ESM::Region>& regions);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages);
///< Messages resulting from this tage will be appended to \a messages.
};
}
#endif

View file

@ -0,0 +1,71 @@
#include "reportmodel.hpp"
#include <stdexcept>
int CSMTools::ReportModel::rowCount (const QModelIndex & parent) const
{
if (parent.isValid())
return 0;
return mRows.size();
}
int CSMTools::ReportModel::columnCount (const QModelIndex & parent) const
{
if (parent.isValid())
return 0;
return 2;
}
QVariant CSMTools::ReportModel::data (const QModelIndex & index, int role) const
{
if (role!=Qt::DisplayRole)
return QVariant();
if (index.column()==0)
return static_cast<int> (mRows.at (index.row()).first.getType());
else
return mRows.at (index.row()).second.c_str();
}
QVariant CSMTools::ReportModel::headerData (int section, Qt::Orientation orientation, int role) const
{
if (role!=Qt::DisplayRole)
return QVariant();
if (orientation==Qt::Vertical)
return QVariant();
return tr (section==0 ? "Type" : "Description");
}
bool CSMTools::ReportModel::removeRows (int row, int count, const QModelIndex& parent)
{
if (parent.isValid())
return false;
mRows.erase (mRows.begin()+row, mRows.begin()+row+count);
return true;
}
void CSMTools::ReportModel::add (const std::string& row)
{
std::string::size_type index = row.find ('|');
if (index==std::string::npos)
throw std::logic_error ("invalid report message");
beginInsertRows (QModelIndex(), mRows.size(), mRows.size());
mRows.push_back (std::make_pair (row.substr (0, index), row.substr (index+1)));
endInsertRows();
}
const CSMWorld::UniversalId& CSMTools::ReportModel::getUniversalId (int row) const
{
return mRows.at (row).first;
}

View file

@ -0,0 +1,37 @@
#ifndef CSM_TOOLS_REPORTMODEL_H
#define CSM_TOOLS_REPORTMODEL_H
#include <vector>
#include <string>
#include <QAbstractTableModel>
#include "../world/universalid.hpp"
namespace CSMTools
{
class ReportModel : public QAbstractTableModel
{
Q_OBJECT
std::vector<std::pair<CSMWorld::UniversalId, std::string> > mRows;
public:
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 QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
virtual bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex());
void add (const std::string& row);
const CSMWorld::UniversalId& getUniversalId (int row) const;
};
}
#endif

View file

@ -0,0 +1,37 @@
#include "skillcheck.hpp"
#include <sstream>
#include <components/esm/loadskil.hpp>
#include "../world/universalid.hpp"
CSMTools::SkillCheckStage::SkillCheckStage (const CSMWorld::IdCollection<ESM::Skill>& skills)
: mSkills (skills)
{}
int CSMTools::SkillCheckStage::setup()
{
return mSkills.getSize();
}
void CSMTools::SkillCheckStage::perform (int stage, std::vector<std::string>& messages)
{
const ESM::Skill& skill = mSkills.getRecord (stage).get();
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Skill, skill.mId);
for (int i=0; i<4; ++i)
if (skill.mData.mUseValue[i]<0)
{
std::ostringstream stream;
stream << id.toString() << "|Use value #" << i << " of " << skill.mId << " is negative";
messages.push_back (stream.str());
}
if (skill.mDescription.empty())
messages.push_back (id.toString() + "|" + skill.mId + " has an empty description");
}

View file

@ -0,0 +1,29 @@
#ifndef CSM_TOOLS_SKILLCHECK_H
#define CSM_TOOLS_SKILLCHECK_H
#include <components/esm/loadskil.hpp>
#include "../world/idcollection.hpp"
#include "stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that skill records are internally consistent
class SkillCheckStage : public Stage
{
const CSMWorld::IdCollection<ESM::Skill>& mSkills;
public:
SkillCheckStage (const CSMWorld::IdCollection<ESM::Skill>& skills);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages);
///< Messages resulting from this tage will be appended to \a messages.
};
}
#endif

View file

@ -0,0 +1,29 @@
#include "soundcheck.hpp"
#include <sstream>
#include <components/esm/loadskil.hpp>
#include "../world/universalid.hpp"
CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds)
: mSounds (sounds)
{}
int CSMTools::SoundCheckStage::setup()
{
return mSounds.getSize();
}
void CSMTools::SoundCheckStage::perform (int stage, std::vector<std::string>& messages)
{
const ESM::Sound& sound = mSounds.getRecord (stage).get();
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Sound, sound.mId);
if (sound.mData.mMinRange>sound.mData.mMaxRange)
messages.push_back (id.toString() + "|Maximum range larger than minimum range");
/// \todo check, if the sound file exists
}

View file

@ -0,0 +1,29 @@
#ifndef CSM_TOOLS_SOUNDCHECK_H
#define CSM_TOOLS_SOUNDCHECK_H
#include <components/esm/loadsoun.hpp>
#include "../world/idcollection.hpp"
#include "stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that sound records are internally consistent
class SoundCheckStage : public Stage
{
const CSMWorld::IdCollection<ESM::Sound>& mSounds;
public:
SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages);
///< Messages resulting from this tage will be appended to \a messages.
};
}
#endif

View file

@ -0,0 +1,35 @@
#include "spellcheck.hpp"
#include <sstream>
#include <map>
#include <components/esm/loadspel.hpp>
#include "../world/universalid.hpp"
CSMTools::SpellCheckStage::SpellCheckStage (const CSMWorld::IdCollection<ESM::Spell>& spells)
: mSpells (spells)
{}
int CSMTools::SpellCheckStage::setup()
{
return mSpells.getSize();
}
void CSMTools::SpellCheckStage::perform (int stage, std::vector<std::string>& messages)
{
const ESM::Spell& spell = mSpells.getRecord (stage).get();
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Spell, spell.mId);
// test for empty name and description
if (spell.mName.empty())
messages.push_back (id.toString() + "|" + spell.mId + " has an empty name");
// test for invalid cost values
if (spell.mData.mCost<0)
messages.push_back (id.toString() + "|" + spell.mId + " has a negative spell costs");
/// \todo check data members that can't be edited in the table view
}

View file

@ -0,0 +1,29 @@
#ifndef CSM_TOOLS_SPELLCHECK_H
#define CSM_TOOLS_SPELLCHECK_H
#include <components/esm/loadspel.hpp>
#include "../world/idcollection.hpp"
#include "stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that spell records are internally consistent
class SpellCheckStage : public Stage
{
const CSMWorld::IdCollection<ESM::Spell>& mSpells;
public:
SpellCheckStage (const CSMWorld::IdCollection<ESM::Spell>& spells);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages);
///< Messages resulting from this tage will be appended to \a messages.
};
}
#endif

View file

@ -0,0 +1,4 @@
#include "stage.hpp"
CSMTools::Stage::~Stage() {}

View file

@ -0,0 +1,24 @@
#ifndef CSM_TOOLS_STAGE_H
#define CSM_TOOLS_STAGE_H
#include <vector>
#include <string>
namespace CSMTools
{
class Stage
{
public:
virtual ~Stage();
virtual int setup() = 0;
///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages) = 0;
///< Messages resulting from this tage will be appended to \a messages.
};
}
#endif

View file

@ -0,0 +1,147 @@
#include "tools.hpp"
#include <QThreadPool>
#include "verifier.hpp"
#include "../doc/state.hpp"
#include "../world/data.hpp"
#include "../world/universalid.hpp"
#include "reportmodel.hpp"
#include "mandatoryid.hpp"
#include "skillcheck.hpp"
#include "classcheck.hpp"
#include "factioncheck.hpp"
#include "racecheck.hpp"
#include "soundcheck.hpp"
#include "regioncheck.hpp"
#include "birthsigncheck.hpp"
#include "spellcheck.hpp"
CSMTools::Operation *CSMTools::Tools::get (int type)
{
switch (type)
{
case CSMDoc::State_Verifying: return mVerifier;
}
return 0;
}
const CSMTools::Operation *CSMTools::Tools::get (int type) const
{
return const_cast<Tools *> (this)->get (type);
}
CSMTools::Verifier *CSMTools::Tools::getVerifier()
{
if (!mVerifier)
{
mVerifier = new Verifier;
connect (mVerifier, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int)));
connect (mVerifier, SIGNAL (finished()), this, SLOT (verifierDone()));
connect (mVerifier, SIGNAL (reportMessage (const QString&, int)),
this, SLOT (verifierMessage (const QString&, int)));
std::vector<std::string> mandatoryIds; // I want C++11, damn it!
mandatoryIds.push_back ("Day");
mandatoryIds.push_back ("DaysPassed");
mandatoryIds.push_back ("GameHour");
mandatoryIds.push_back ("Month");
mandatoryIds.push_back ("PCRace");
mandatoryIds.push_back ("PCVampire");
mandatoryIds.push_back ("PCWerewolf");
mandatoryIds.push_back ("PCYear");
mVerifier->appendStage (new MandatoryIdStage (mData.getGlobals(),
CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Globals), mandatoryIds));
mVerifier->appendStage (new SkillCheckStage (mData.getSkills()));
mVerifier->appendStage (new ClassCheckStage (mData.getClasses()));
mVerifier->appendStage (new FactionCheckStage (mData.getFactions()));
mVerifier->appendStage (new RaceCheckStage (mData.getRaces()));
mVerifier->appendStage (new SoundCheckStage (mData.getSounds()));
mVerifier->appendStage (new RegionCheckStage (mData.getRegions()));
mVerifier->appendStage (new BirthsignCheckStage (mData.getBirthsigns()));
mVerifier->appendStage (new SpellCheckStage (mData.getSpells()));
}
return mVerifier;
}
CSMTools::Tools::Tools (CSMWorld::Data& data) : mData (data), mVerifier (0), mNextReportNumber (0)
{
for (std::map<int, ReportModel *>::iterator iter (mReports.begin()); iter!=mReports.end(); ++iter)
delete iter->second;
}
CSMTools::Tools::~Tools()
{
delete mVerifier;
}
CSMWorld::UniversalId CSMTools::Tools::runVerifier()
{
mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel));
mActiveReports[CSMDoc::State_Verifying] = mNextReportNumber-1;
getVerifier()->start();
return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_VerificationResults, mNextReportNumber-1);
}
void CSMTools::Tools::abortOperation (int type)
{
if (Operation *operation = get (type))
operation->abort();
}
int CSMTools::Tools::getRunningOperations() const
{
static const int sOperations[] =
{
CSMDoc::State_Verifying,
-1
};
int result = 0;
for (int i=0; sOperations[i]!=-1; ++i)
if (const Operation *operation = get (sOperations[i]))
if (operation->isRunning())
result |= sOperations[i];
return result;
}
CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId& id)
{
if (id.getType()!=CSMWorld::UniversalId::Type_VerificationResults)
throw std::logic_error ("invalid request for report model: " + id.toString());
return mReports.at (id.getIndex());
}
void CSMTools::Tools::verifierDone()
{
emit done (CSMDoc::State_Verifying);
}
void CSMTools::Tools::verifierMessage (const QString& message, int type)
{
std::map<int, int>::iterator iter = mActiveReports.find (type);
if (iter!=mActiveReports.end())
mReports[iter->second]->add (message.toStdString());
}

View file

@ -0,0 +1,73 @@
#ifndef CSM_TOOLS_TOOLS_H
#define CSM_TOOLS_TOOLS_H
#include <QObject>
#include <map>
namespace CSMWorld
{
class Data;
class UniversalId;
}
namespace CSMTools
{
class Verifier;
class Operation;
class ReportModel;
class Tools : public QObject
{
Q_OBJECT
CSMWorld::Data& mData;
Verifier *mVerifier;
std::map<int, ReportModel *> mReports;
int mNextReportNumber;
std::map<int, int> mActiveReports; // type, report number
// not implemented
Tools (const Tools&);
Tools& operator= (const Tools&);
Verifier *getVerifier();
Operation *get (int type);
///< Returns a 0-pointer, if operation hasn't been used yet.
const Operation *get (int type) const;
///< Returns a 0-pointer, if operation hasn't been used yet.
public:
Tools (CSMWorld::Data& data);
virtual ~Tools();
CSMWorld::UniversalId runVerifier();
///< \return ID of the report for this verification run
void abortOperation (int type);
///< \attention The operation is not aborted immediately.
int getRunningOperations() const;
ReportModel *getReport (const CSMWorld::UniversalId& id);
///< The ownership of the returned report is not transferred.
private slots:
void verifierDone();
void verifierMessage (const QString& message, int type);
signals:
void progress (int current, int max, int type);
void done (int type);
};
}
#endif

View file

@ -0,0 +1,7 @@
#include "verifier.hpp"
#include "../doc/state.hpp"
CSMTools::Verifier::Verifier() : Operation (CSMDoc::State_Verifying)
{}

View file

@ -0,0 +1,17 @@
#ifndef CSM_TOOLS_VERIFIER_H
#define CSM_TOOLS_VERIFIER_H
#include "operation.hpp"
namespace CSMTools
{
class Verifier : public Operation
{
public:
Verifier();
};
}
#endif

View file

@ -0,0 +1,25 @@
#include "cell.hpp"
#include <sstream>
void CSMWorld::Cell::load (ESM::ESMReader &esm)
{
mName = mId;
ESM::Cell::load (esm, false);
if (!(mData.mFlags & Interior))
{
std::ostringstream stream;
stream << "#" << mData.mX << " " << mData.mY;
mId = stream.str();
}
}
void CSMWorld::Cell::addRef (const std::string& id)
{
mRefs.push_back (std::make_pair (id, false));
}

View file

@ -0,0 +1,24 @@
#ifndef CSM_WOLRD_CELL_H
#define CSM_WOLRD_CELL_H
#include <vector>
#include <string>
#include <components/esm/loadcell.hpp>
namespace CSMWorld
{
/// \brief Wrapper for Cell record
struct Cell : public ESM::Cell
{
std::string mId;
std::vector<std::pair<std::string, bool> > mRefs; // ID, modified
std::vector<std::string> mDeletedRefs;
void load (ESM::ESMReader &esm);
void addRef (const std::string& id);
};
}
#endif

View file

@ -0,0 +1,319 @@
#ifndef CSM_WOLRD_COLLECTION_H
#define CSM_WOLRD_COLLECTION_H
#include <vector>
#include <map>
#include <algorithm>
#include <cctype>
#include <stdexcept>
#include <functional>
#include <QVariant>
#include <components/misc/stringops.hpp>
#include "columnbase.hpp"
#include "collectionbase.hpp"
namespace CSMWorld
{
/// \brief Access to ID field in records
template<typename ESXRecordT>
struct IdAccessor
{
std::string& getId (ESXRecordT& record);
const std::string getId (const ESXRecordT& record) const;
};
template<typename ESXRecordT>
std::string& IdAccessor<ESXRecordT>::getId (ESXRecordT& record)
{
return record.mId;
}
template<typename ESXRecordT>
const std::string IdAccessor<ESXRecordT>::getId (const ESXRecordT& record) const
{
return record.mId;
}
/// \brief Single-type record collection
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
class Collection : public CollectionBase
{
std::vector<Record<ESXRecordT> > mRecords;
std::map<std::string, int> mIndex;
std::vector<Column<ESXRecordT> *> mColumns;
// not implemented
Collection (const Collection&);
Collection& operator= (const Collection&);
public:
Collection();
virtual ~Collection();
void add (const ESXRecordT& record);
///< Add a new record (modified)
virtual int getSize() const;
virtual std::string getId (int index) const;
virtual int getIndex (const std::string& id) const;
virtual int getColumns() const;
virtual QVariant getData (int index, int column) const;
virtual void setData (int index, int column, const QVariant& data);
virtual const ColumnBase& getColumn (int column) const;
virtual void merge();
///< Merge modified into base.
virtual void purge();
///< Remove records that are flagged as erased.
virtual void removeRows (int index, int count) ;
virtual void appendBlankRecord (const std::string& id,
UniversalId::Type type = UniversalId::Type_None);
///< \param type Will be ignored, unless the collection supports multiple record types
virtual int searchId (const std::string& id) const;
////< Search record with \a id.
/// \return index of record (if found) or -1 (not found)
virtual void replace (int index, const RecordBase& record);
///< If the record type does not match, an exception is thrown.
///
/// \attention \a record must not change the ID.
virtual void appendRecord (const RecordBase& record,
UniversalId::Type type = UniversalId::Type_None);
///< If the record type does not match, an exception is thrown.
///< \param type Will be ignored, unless the collection supports multiple record types
virtual const Record<ESXRecordT>& getRecord (const std::string& id) const;
virtual const Record<ESXRecordT>& getRecord (int index) const;
virtual int getAppendIndex (UniversalId::Type type = UniversalId::Type_None) const;
///< \param type Will be ignored, unless the collection supports multiple record types
void addColumn (Column<ESXRecordT> *column);
void setRecord (int index, const Record<ESXRecordT>& record);
///< \attention This function must not change the ID.
};
template<typename ESXRecordT, typename IdAccessorT>
Collection<ESXRecordT, IdAccessorT>::Collection()
{}
template<typename ESXRecordT, typename IdAccessorT>
Collection<ESXRecordT, IdAccessorT>::~Collection()
{
for (typename std::vector<Column<ESXRecordT> *>::iterator iter (mColumns.begin()); iter!=mColumns.end(); ++iter)
delete *iter;
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::add (const ESXRecordT& record)
{
std::string id = Misc::StringUtils::lowerCase (IdAccessorT().getId (record));
std::map<std::string, int>::iterator iter = mIndex.find (id);
if (iter==mIndex.end())
{
Record<ESXRecordT> record2;
record2.mState = Record<ESXRecordT>::State_ModifiedOnly;
record2.mModified = record;
mRecords.push_back (record2);
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), mRecords.size()-1));
}
else
{
mRecords[iter->second].setModified (record);
}
}
template<typename ESXRecordT, typename IdAccessorT>
int Collection<ESXRecordT, IdAccessorT>::getSize() const
{
return mRecords.size();
}
template<typename ESXRecordT, typename IdAccessorT>
std::string Collection<ESXRecordT, IdAccessorT>::getId (int index) const
{
return IdAccessorT().getId (mRecords.at (index).get());
}
template<typename ESXRecordT, typename IdAccessorT>
int Collection<ESXRecordT, IdAccessorT>::getIndex (const std::string& id) const
{
int index = searchId (id);
if (index==-1)
throw std::runtime_error ("invalid ID: " + id);
return index;
}
template<typename ESXRecordT, typename IdAccessorT>
int Collection<ESXRecordT, IdAccessorT>::getColumns() const
{
return mColumns.size();
}
template<typename ESXRecordT, typename IdAccessorT>
QVariant Collection<ESXRecordT, IdAccessorT>::getData (int index, int column) const
{
return mColumns.at (column)->get (mRecords.at (index));
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::setData (int index, int column, const QVariant& data)
{
return mColumns.at (column)->set (mRecords.at (index), data);
}
template<typename ESXRecordT, typename IdAccessorT>
const ColumnBase& Collection<ESXRecordT, IdAccessorT>::getColumn (int column) const
{
return *mColumns.at (column);
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::addColumn (Column<ESXRecordT> *column)
{
mColumns.push_back (column);
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::merge()
{
for (typename std::vector<Record<ESXRecordT> >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter)
iter->merge();
purge();
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::purge()
{
int i = 0;
while (i<static_cast<int> (mRecords.size()))
{
if (mRecords[i].isErased())
removeRows (i, 1);
else
++i;
}
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::removeRows (int index, int count)
{
mRecords.erase (mRecords.begin()+index, mRecords.begin()+index+count);
typename std::map<std::string, int>::iterator iter = mIndex.begin();
while (iter!=mIndex.end())
{
if (iter->second>=index)
{
if (iter->second>=index+count)
{
iter->second -= count;
++iter;
}
else
{
mIndex.erase (iter++);
}
}
else
++iter;
}
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::appendBlankRecord (const std::string& id,
UniversalId::Type type)
{
ESXRecordT record;
IdAccessorT().getId (record) = id;
record.blank();
add (record);
}
template<typename ESXRecordT, typename IdAccessorT>
int Collection<ESXRecordT, IdAccessorT>::searchId (const std::string& id) const
{
std::string id2 = Misc::StringUtils::lowerCase(id);
std::map<std::string, int>::const_iterator iter = mIndex.find (id2);
if (iter==mIndex.end())
return -1;
return iter->second;
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::replace (int index, const RecordBase& record)
{
mRecords.at (index) = dynamic_cast<const Record<ESXRecordT>&> (record);
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::appendRecord (const RecordBase& record,
UniversalId::Type type)
{
mRecords.push_back (dynamic_cast<const Record<ESXRecordT>&> (record));
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (IdAccessorT().getId (
dynamic_cast<const Record<ESXRecordT>&> (record).get())),
mRecords.size()-1));
}
template<typename ESXRecordT, typename IdAccessorT>
int Collection<ESXRecordT, IdAccessorT>::getAppendIndex (UniversalId::Type type) const
{
return static_cast<int> (mRecords.size());
}
template<typename ESXRecordT, typename IdAccessorT>
const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (const std::string& id) const
{
int index = getIndex (id);
return mRecords.at (index);
}
template<typename ESXRecordT, typename IdAccessorT>
const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (int index) const
{
return mRecords.at (index);
}
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::setRecord (int index, const Record<ESXRecordT>& record)
{
if (IdAccessorT().getId (mRecords.at (index).get())!=IdAccessorT().getId (record.get()))
throw std::runtime_error ("attempt to change the ID of a record");
mRecords.at (index) = record;
}
}
#endif

View file

@ -0,0 +1,6 @@
#include "collectionbase.hpp"
CSMWorld::CollectionBase::CollectionBase() {}
CSMWorld::CollectionBase::~CollectionBase() {}

View file

@ -0,0 +1,85 @@
#ifndef CSM_WOLRD_COLLECTIONBASE_H
#define CSM_WOLRD_COLLECTIONBASE_H
#include <string>
#include "universalid.hpp"
class QVariant;
namespace CSMWorld
{
struct ColumnBase;
struct RecordBase;
/// \brief Base class for record collections
///
/// \attention Modifying records through the interface does not update connected views.
/// Such modifications should be done through the table model interface instead unless no views
/// are connected to the model or special precautions have been taken to send update signals
/// manually.
class CollectionBase
{
// not implemented
CollectionBase (const CollectionBase&);
CollectionBase& operator= (const CollectionBase&);
public:
CollectionBase();
virtual ~CollectionBase();
virtual int getSize() const = 0;
virtual std::string getId (int index) const = 0;
virtual int getIndex (const std::string& id) const = 0;
virtual int getColumns() const = 0;
virtual const ColumnBase& getColumn (int column) const = 0;
virtual QVariant getData (int index, int column) const = 0;
virtual void setData (int index, int column, const QVariant& data) = 0;
// Not in use. Temporarily removed so that the implementation of RefIdCollection can continue without
// these functions for now.
// virtual void merge() = 0;
///< Merge modified into base.
// virtual void purge() = 0;
///< Remove records that are flagged as erased.
virtual void removeRows (int index, int count) = 0;
virtual void appendBlankRecord (const std::string& id,
UniversalId::Type type = UniversalId::Type_None) = 0;
///< \param type Will be ignored, unless the collection supports multiple record types
virtual int searchId (const std::string& id) const = 0;
////< Search record with \a id.
/// \return index of record (if found) or -1 (not found)
virtual void replace (int index, const RecordBase& record) = 0;
///< If the record type does not match, an exception is thrown.
///
/// \attention \a record must not change the ID.
///< \param type Will be ignored, unless the collection supports multiple record types
virtual void appendRecord (const RecordBase& record,
UniversalId::Type type = UniversalId::Type_None) = 0;
///< If the record type does not match, an exception is thrown.
virtual const RecordBase& getRecord (const std::string& id) const = 0;
virtual const RecordBase& getRecord (int index) const = 0;
virtual int getAppendIndex (UniversalId::Type type = UniversalId::Type_None) const = 0;
///< \param type Will be ignored, unless the collection supports multiple record types
};
}
#endif

View file

@ -0,0 +1,20 @@
#include "columnbase.hpp"
#include "columns.hpp"
CSMWorld::ColumnBase::ColumnBase (int columnId, Display displayType, int flags)
: mColumnId (columnId), mDisplayType (displayType), mFlags (flags)
{}
CSMWorld::ColumnBase::~ColumnBase() {}
bool CSMWorld::ColumnBase::isUserEditable() const
{
return isEditable();
}
std::string CSMWorld::ColumnBase::getTitle() const
{
return Columns::getName (static_cast<Columns::ColumnId> (mColumnId));
}

View file

@ -0,0 +1,82 @@
#ifndef CSM_WOLRD_COLUMNBASE_H
#define CSM_WOLRD_COLUMNBASE_H
#include <string>
#include <Qt>
#include <QVariant>
#include "record.hpp"
namespace CSMWorld
{
struct ColumnBase
{
enum Roles
{
Role_Flags = Qt::UserRole,
Role_Display = Qt::UserRole+1
};
enum Flags
{
Flag_Table = 1, // column should be displayed in table view
Flag_Dialogue = 2 // column should be displayed in dialogue view
};
enum Display
{
Display_String,
Display_Integer,
Display_Float,
Display_Var,
Display_GmstVarType,
Display_GlobalVarType,
Display_Specialisation,
Display_Attribute,
Display_Boolean,
Display_SpellType,
Display_Script,
Display_ApparatusType,
Display_ArmorType,
Display_ClothingType,
Display_CreatureType,
Display_WeaponType,
Display_RecordState,
Display_RefRecordType
};
int mColumnId;
int mFlags;
Display mDisplayType;
ColumnBase (int columnId, Display displayType, int flag);
virtual ~ColumnBase();
virtual bool isEditable() const = 0;
virtual bool isUserEditable() const;
///< Can this column be edited directly by the user?
virtual std::string getTitle() const;
};
template<typename ESXRecordT>
struct Column : public ColumnBase
{
int mFlags;
Column (int columnId, Display displayType, int flags = Flag_Table | Flag_Dialogue)
: ColumnBase (columnId, displayType, flags) {}
virtual QVariant get (const Record<ESXRecordT>& record) const = 0;
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
throw std::logic_error ("Column " + getTitle() + " is not editable");
}
};
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,199 @@
#include "columns.hpp"
#include <components/misc/stringops.hpp>
namespace CSMWorld
{
namespace Columns
{
struct ColumnDesc
{
int mId;
const char *mName;
};
const ColumnDesc sNames[] =
{
{ ColumnId_Value, "Value" },
{ ColumnId_Id, "ID" },
{ ColumnId_Modification, "Modified" },
{ ColumnId_RecordType, "Record Type" },
{ ColumnId_ValueType, "Value Type" },
{ ColumnId_Description, "Description" },
{ ColumnId_Specialisation, "Specialisation" },
{ ColumnId_Attribute, "Attribute" },
{ ColumnId_Name, "Name" },
{ ColumnId_Playable, "Playable" },
{ ColumnId_Hidden, "Hidden" },
{ ColumnId_MaleWeight, "Male Weight" },
{ ColumnId_FemaleWeight, "Female Weight" },
{ ColumnId_MaleHeight, "Male Height" },
{ ColumnId_FemaleHeight, "Female Height" },
{ ColumnId_Volume, "Volume" },
{ ColumnId_MinRange, "Min Range" },
{ ColumnId_MaxRange, "Max Range" },
{ ColumnId_SoundFile, "Sound File" },
{ ColumnId_MapColour, "Map Colour" },
{ ColumnId_SleepEncounter, "Sleep Encounter" },
{ ColumnId_Texture, "Texture" },
{ ColumnId_SpellType, "Spell Type" },
{ ColumnId_Cost, "Cost" },
{ ColumnId_ScriptText, "Script Text" },
{ ColumnId_Region, "Region" },
{ ColumnId_Cell, "Cell" },
{ ColumnId_Scale, "Scale" },
{ ColumnId_Owner, "Owner" },
{ ColumnId_Soul, "Soul" },
{ ColumnId_Faction, "Faction" },
{ ColumnId_FactionIndex, "Faction Index" },
{ ColumnId_Charges, "Charges" },
{ ColumnId_Enchantment, "Enchantment" },
{ ColumnId_Value, "Coin Value" },
{ ColumnId_Teleport, "Teleport" },
{ ColumnId_TeleportCell, "Teleport Cell" },
{ ColumnId_LockLevel, "Lock Level" },
{ ColumnId_Key, "Key" },
{ ColumnId_Trap, "Trap" },
{ ColumnId_BeastRace, "Beast Race" },
{ ColumnId_AutoCalc, "Auto Calc" },
{ ColumnId_StarterSpell, "Starter Spell" },
{ ColumnId_AlwaysSucceeds, "Always Succeeds" },
{ ColumnId_SleepForbidden, "Sleep Forbidden" },
{ ColumnId_InteriorWater, "Interior Water" },
{ ColumnId_InteriorSky, "Interior Sky" },
{ ColumnId_Model, "Model" },
{ ColumnId_Script, "Script" },
{ ColumnId_Icon, "Icon" },
{ ColumnId_Weight, "Weight" },
{ ColumnId_EnchantmentPoints, "Enchantment Points" },
{ ColumnId_Quality, "Quality" },
{ ColumnId_Ai, "AI" },
{ ColumnId_AiHello, "AI Hello" },
{ ColumnId_AiFlee, "AI Flee" },
{ ColumnId_AiFight, "AI Fight" },
{ ColumnId_AiAlarm, "AI Alarm" },
{ ColumnId_BuysWeapons, "Buys Weapons" },
{ ColumnId_BuysArmor, "Buys Armor" },
{ ColumnId_BuysClothing, "Buys Clothing" },
{ ColumnId_BuysBooks, "Buys Books" },
{ ColumnId_BuysIngredients, "Buys Ingredients" },
{ ColumnId_BuysLockpicks, "Buys Lockpicks" },
{ ColumnId_BuysProbes, "Buys Probes" },
{ ColumnId_BuysLights, "Buys Lights" },
{ ColumnId_BuysApparati, "Buys Apparati" },
{ ColumnId_BuysRepairItems, "Buys Repair Items" },
{ ColumnId_BuysMiscItems, "Buys Misc Items" },
{ ColumnId_BuysPotions, "Buys Potions" },
{ ColumnId_BuysMagicItems, "Buys Magic Items" },
{ ColumnId_SellsSpells, "Sells Spells" },
{ ColumnId_Trainer, "Trainer" },
{ ColumnId_Spellmaking, "Spellmaking" },
{ ColumnId_EnchantingService, "Enchanting Service" },
{ ColumnId_RepairService, "Repair Serivce" },
{ ColumnId_ApparatusType, "Apparatus Type" },
{ ColumnId_ArmorType, "Armor Type" },
{ ColumnId_Health, "Health" },
{ ColumnId_ArmorValue, "Armor Value" },
{ ColumnId_Scroll, "Scroll" },
{ ColumnId_ClothingType, "Clothing Type" },
{ ColumnId_WeightCapacity, "Weight Capacity" },
{ ColumnId_OrganicContainer, "Organic Container" },
{ ColumnId_Respawn, "Respawn" },
{ ColumnId_CreatureType, "Creature Type" },
{ ColumnId_SoulPoints, "Soul Points" },
{ ColumnId_OriginalCreature, "Original Creature" },
{ ColumnId_Biped, "Biped" },
{ ColumnId_HasWeapon, "Has Weapon" },
{ ColumnId_NoMovement, "No Movement" },
{ ColumnId_Swims, "Swims" },
{ ColumnId_Flies, "Flies" },
{ ColumnId_Walks, "Walks" },
{ ColumnId_Essential, "Essential" },
{ ColumnId_SkeletonBlood, "Skeleton Blood" },
{ ColumnId_MetalBlood, "Metal Blood" },
{ ColumnId_OpenSound, "Open Sound" },
{ ColumnId_CloseSound, "Close Sound" },
{ ColumnId_Duration, "Duration" },
{ ColumnId_Radius, "Radius" },
{ ColumnId_Colour, "Colour" },
{ ColumnId_Sound, "Sound" },
{ ColumnId_Dynamic, "Dynamic" },
{ ColumnId_Portable, "Portable" },
{ ColumnId_NegativeLight, "Negative Light" },
{ ColumnId_Flickering, "Flickering" },
{ ColumnId_SlowFlickering, "Slow Flickering" },
{ ColumnId_Pulsing, "Pulsing" },
{ ColumnId_SlowPulsing, "Slow Pulsing" },
{ ColumnId_Fire, "Fire" },
{ ColumnId_OffByDefault, "Off by default" },
{ ColumnId_IsKey, "Is Key" },
{ ColumnId_Race, "Race" },
{ ColumnId_Class, "Class" },
{ Columnid_Hair, "Hair" },
{ ColumnId_Head, "Head" },
{ ColumnId_Female, "Female" },
{ ColumnId_WeaponType, "Weapon Type" },
{ ColumnId_WeaponSpeed, "Weapon Speed" },
{ ColumnId_WeaponReach, "Weapon Reach" },
{ ColumnId_MinChop, "Min Chop" },
{ ColumnId_MaxChip, "Max Chop" },
{ Columnid_MinSlash, "Min Slash" },
{ ColumnId_MaxSlash, "Max Slash" },
{ ColumnId_MinThrust, "Min Thrust" },
{ ColumnId_MaxThrust, "Max Thrust" },
{ ColumnId_Magical, "Magical" },
{ ColumnId_Silver, "Silver" },
{ ColumnId_Filter, "Filter" },
{ ColumnId_UseValue1, "Use value 1" },
{ ColumnId_UseValue2, "Use value 2" },
{ ColumnId_UseValue3, "Use value 3" },
{ ColumnId_UseValue4, "Use value 4" },
{ ColumnId_Attribute1, "Attribute 1" },
{ ColumnId_Attribute2, "Attribute 2" },
{ ColumnId_MajorSkill1, "Major Skill 1" },
{ ColumnId_MajorSkill2, "Major Skill 2" },
{ ColumnId_MajorSkill3, "Major Skill 3" },
{ ColumnId_MajorSkill4, "Major Skill 4" },
{ ColumnId_MajorSkill5, "Major Skill 5" },
{ ColumnId_MinorSkill1, "Minor Skill 1" },
{ ColumnId_MinorSkill2, "Minor Skill 2" },
{ ColumnId_MinorSkill3, "Minor Skill 3" },
{ ColumnId_MinorSkill4, "Minor Skill 4" },
{ ColumnId_MinorSkill5, "Minor Skill 5" },
{ ColumnId_Skill1, "Skill 1" },
{ ColumnId_Skill2, "Skill 2" },
{ ColumnId_Skill3, "Skill 3" },
{ ColumnId_Skill4, "Skill 4" },
{ ColumnId_Skill5, "Skill 5" },
{ ColumnId_Skill6, "Skill 6" },
{ -1, 0 } // end marker
};
}
}
std::string CSMWorld::Columns::getName (ColumnId column)
{
for (int i=0; sNames[i].mName; ++i)
if (column==sNames[i].mId)
return sNames[i].mName;
return "";
}
int CSMWorld::Columns::getId (const std::string& name)
{
std::string name2 = Misc::StringUtils::lowerCase (name);
for (int i=0; sNames[i].mName; ++i)
if (name2==Misc::StringUtils::lowerCase (sNames[i].mName))
return sNames[i].mId;
return -1;
}

View file

@ -0,0 +1,186 @@
#ifndef CSM_WOLRD_COLUMNS_H
#define CSM_WOLRD_COLUMNS_H
#include <string>
namespace CSMWorld
{
namespace Columns
{
enum ColumnId
{
ColumnId_Value = 0,
ColumnId_Id = 1,
ColumnId_Modification = 2,
ColumnId_RecordType = 3,
ColumnId_ValueType = 4,
ColumnId_Description = 5,
ColumnId_Specialisation = 6,
ColumnId_Attribute = 7,
ColumnId_Name = 8,
ColumnId_Playable = 9,
ColumnId_Hidden = 10,
ColumnId_MaleWeight = 11,
ColumnId_FemaleWeight = 12,
ColumnId_MaleHeight = 13,
ColumnId_FemaleHeight = 14,
ColumnId_Volume = 15,
ColumnId_MinRange = 16,
ColumnId_MaxRange = 17,
ColumnId_SoundFile = 18,
ColumnId_MapColour = 19,
ColumnId_SleepEncounter = 20,
ColumnId_Texture = 21,
ColumnId_SpellType = 22,
ColumnId_Cost = 23,
ColumnId_ScriptText = 24,
ColumnId_Region = 25,
ColumnId_Cell = 26,
ColumnId_Scale = 27,
ColumnId_Owner = 28,
ColumnId_Soul = 29,
ColumnId_Faction = 30,
ColumnId_FactionIndex = 31,
ColumnId_Charges = 32,
ColumnId_Enchantment = 33,
ColumnId_CoinValue = 34,
ColumnId_Teleport = 25,
ColumnId_TeleportCell = 26,
ColumnId_LockLevel = 27,
ColumnId_Key = 28,
ColumnId_Trap = 29,
ColumnId_BeastRace = 30,
ColumnId_AutoCalc = 31,
ColumnId_StarterSpell = 32,
ColumnId_AlwaysSucceeds = 33,
ColumnId_SleepForbidden = 34,
ColumnId_InteriorWater = 35,
ColumnId_InteriorSky = 36,
ColumnId_Model = 37,
ColumnId_Script = 38,
ColumnId_Icon = 39,
ColumnId_Weight = 40,
ColumnId_EnchantmentPoints = 31,
ColumnId_Quality = 32,
ColumnId_Ai = 33,
ColumnId_AiHello = 34,
ColumnId_AiFlee = 35,
ColumnId_AiFight = 36,
ColumnId_AiAlarm = 37,
ColumnId_BuysWeapons = 38,
ColumnId_BuysArmor = 39,
ColumnId_BuysClothing = 40,
ColumnId_BuysBooks = 41,
ColumnId_BuysIngredients = 42,
ColumnId_BuysLockpicks = 43,
ColumnId_BuysProbes = 44,
ColumnId_BuysLights = 45,
ColumnId_BuysApparati = 46,
ColumnId_BuysRepairItems = 47,
ColumnId_BuysMiscItems = 48,
ColumnId_BuysPotions = 49,
ColumnId_BuysMagicItems = 50,
ColumnId_SellsSpells = 51,
ColumnId_Trainer = 52,
ColumnId_Spellmaking = 53,
ColumnId_EnchantingService = 54,
ColumnId_RepairService = 55,
ColumnId_ApparatusType = 56,
ColumnId_ArmorType = 57,
ColumnId_Health = 58,
ColumnId_ArmorValue = 59,
ColumnId_Scroll = 60,
ColumnId_ClothingType = 61,
ColumnId_WeightCapacity = 62,
ColumnId_OrganicContainer = 63,
ColumnId_Respawn = 64,
ColumnId_CreatureType = 65,
ColumnId_SoulPoints = 66,
ColumnId_OriginalCreature = 67,
ColumnId_Biped = 68,
ColumnId_HasWeapon = 69,
ColumnId_NoMovement = 70,
ColumnId_Swims = 71,
ColumnId_Flies = 72,
ColumnId_Walks = 73,
ColumnId_Essential = 74,
ColumnId_SkeletonBlood = 75,
ColumnId_MetalBlood = 76,
ColumnId_OpenSound = 77,
ColumnId_CloseSound = 78,
ColumnId_Duration = 79,
ColumnId_Radius = 80,
ColumnId_Colour = 81,
ColumnId_Sound = 82,
ColumnId_Dynamic = 83,
ColumnId_Portable = 84,
ColumnId_NegativeLight = 85,
ColumnId_Flickering = 86,
ColumnId_SlowFlickering = 87,
ColumnId_Pulsing = 88,
ColumnId_SlowPulsing = 89,
ColumnId_Fire = 90,
ColumnId_OffByDefault = 91,
ColumnId_IsKey = 92,
ColumnId_Race = 93,
ColumnId_Class = 94,
Columnid_Hair = 95,
ColumnId_Head = 96,
ColumnId_Female = 97,
ColumnId_WeaponType = 98,
ColumnId_WeaponSpeed = 99,
ColumnId_WeaponReach = 100,
ColumnId_MinChop = 101,
ColumnId_MaxChip = 102,
Columnid_MinSlash = 103,
ColumnId_MaxSlash = 104,
ColumnId_MinThrust = 105,
ColumnId_MaxThrust = 106,
ColumnId_Magical = 107,
ColumnId_Silver = 108,
ColumnId_Filter = 109,
// Allocated to a separate value range, so we don't get a collision should we ever need
// to extend the number of use values.
ColumnId_UseValue1 = 0x10000,
ColumnId_UseValue2 = 0x10001,
ColumnId_UseValue3 = 0x10002,
ColumnId_UseValue4 = 0x10003,
// Allocated to a separate value range, so we don't get a collision should we ever need
// to extend the number of attributes. Note that this is not the number of different
// attributes, but the number of attributes that can be references from a record.
ColumnId_Attribute1 = 0x20000,
ColumnId_Attribute2 = 0x20001,
// Allocated to a separate value range, so we don't get a collision should we ever need
// to extend the number of skills. Note that this is not the number of different
// skills, but the number of skills that can be references from a record.
ColumnId_MajorSkill1 = 0x30000,
ColumnId_MajorSkill2 = 0x30001,
ColumnId_MajorSkill3 = 0x30002,
ColumnId_MajorSkill4 = 0x30003,
ColumnId_MajorSkill5 = 0x30004,
ColumnId_MinorSkill1 = 0x40000,
ColumnId_MinorSkill2 = 0x40001,
ColumnId_MinorSkill3 = 0x40002,
ColumnId_MinorSkill4 = 0x40003,
ColumnId_MinorSkill5 = 0x40004,
ColumnId_Skill1 = 0x50000,
ColumnId_Skill2 = 0x50001,
ColumnId_Skill3 = 0x50002,
ColumnId_Skill4 = 0x50003,
ColumnId_Skill5 = 0x50004,
ColumnId_Skill6 = 0x50005
};
std::string getName (ColumnId column);
int getId (const std::string& name);
///< Will return -1 for an invalid name.
}
}
#endif

View file

@ -0,0 +1,125 @@
#include "commands.hpp"
#include <QAbstractItemModel>
#include "idtable.hpp"
#include "idtable.hpp"
CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
const QVariant& new_, QUndoCommand *parent)
: QUndoCommand (parent), mModel (model), mIndex (index), mNew (new_)
{
mOld = mModel.data (mIndex, Qt::EditRole);
setText ("Modify " + mModel.headerData (mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString());
}
void CSMWorld::ModifyCommand::redo()
{
mModel.setData (mIndex, mNew);
}
void CSMWorld::ModifyCommand::undo()
{
mModel.setData (mIndex, mOld);
}
CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent)
: QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None)
{
setText (("Create record " + id).c_str());
}
void CSMWorld::CreateCommand::addValue (int column, const QVariant& value)
{
mValues[column] = value;
}
void CSMWorld::CreateCommand::setType (UniversalId::Type type)
{
mType = type;
}
void CSMWorld::CreateCommand::redo()
{
mModel.addRecord (mId, mType);
for (std::map<int, QVariant>::const_iterator iter (mValues.begin()); iter!=mValues.end(); ++iter)
mModel.setData (mModel.getModelIndex (mId, iter->first), iter->second);
}
void CSMWorld::CreateCommand::undo()
{
mModel.removeRow (mModel.getModelIndex (mId, 0).row());
}
CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand *parent)
: QUndoCommand (parent), mModel (model), mId (id), mOld (0)
{
setText (("Revert record " + id).c_str());
mOld = model.getRecord (id).clone();
}
CSMWorld::RevertCommand::~RevertCommand()
{
delete mOld;
}
void CSMWorld::RevertCommand::redo()
{
int column = mModel.findColumnIndex (Columns::ColumnId_Modification);
QModelIndex index = mModel.getModelIndex (mId, column);
RecordBase::State state = static_cast<RecordBase::State> (mModel.data (index).toInt());
if (state==RecordBase::State_ModifiedOnly)
{
mModel.removeRows (index.row(), 1);
}
else
{
mModel.setData (index, static_cast<int> (RecordBase::State_BaseOnly));
}
}
void CSMWorld::RevertCommand::undo()
{
mModel.setRecord (mId, *mOld);
}
CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand *parent)
: QUndoCommand (parent), mModel (model), mId (id), mOld (0)
{
setText (("Delete record " + id).c_str());
mOld = model.getRecord (id).clone();
}
CSMWorld::DeleteCommand::~DeleteCommand()
{
delete mOld;
}
void CSMWorld::DeleteCommand::redo()
{
int column = mModel.findColumnIndex (Columns::ColumnId_Modification);
QModelIndex index = mModel.getModelIndex (mId, column);
RecordBase::State state = static_cast<RecordBase::State> (mModel.data (index).toInt());
if (state==RecordBase::State_ModifiedOnly)
{
mModel.removeRows (index.row(), 1);
}
else
{
mModel.setData (index, static_cast<int> (RecordBase::State_Deleted));
}
}
void CSMWorld::DeleteCommand::undo()
{
mModel.setRecord (mId, *mOld);
}

View file

@ -0,0 +1,104 @@
#ifndef CSM_WOLRD_COMMANDS_H
#define CSM_WOLRD_COMMANDS_H
#include "record.hpp"
#include <string>
#include <map>
#include <QVariant>
#include <QUndoCommand>
#include <QModelIndex>
#include "universalid.hpp"
class QModelIndex;
class QAbstractItemModel;
namespace CSMWorld
{
class IdTable;
class IdTable;
class RecordBase;
class ModifyCommand : public QUndoCommand
{
QAbstractItemModel& mModel;
QModelIndex mIndex;
QVariant mNew;
QVariant mOld;
public:
ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_,
QUndoCommand *parent = 0);
virtual void redo();
virtual void undo();
};
class CreateCommand : public QUndoCommand
{
IdTable& mModel;
std::string mId;
UniversalId::Type mType;
std::map<int, QVariant> mValues;
public:
CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent = 0);
void setType (UniversalId::Type type);
void addValue (int column, const QVariant& value);
virtual void redo();
virtual void undo();
};
class RevertCommand : public QUndoCommand
{
IdTable& mModel;
std::string mId;
RecordBase *mOld;
// not implemented
RevertCommand (const RevertCommand&);
RevertCommand& operator= (const RevertCommand&);
public:
RevertCommand (IdTable& model, const std::string& id, QUndoCommand *parent = 0);
virtual ~RevertCommand();
virtual void redo();
virtual void undo();
};
class DeleteCommand : public QUndoCommand
{
IdTable& mModel;
std::string mId;
RecordBase *mOld;
// not implemented
DeleteCommand (const DeleteCommand&);
DeleteCommand& operator= (const DeleteCommand&);
public:
DeleteCommand (IdTable& model, const std::string& id, QUndoCommand *parent = 0);
virtual ~DeleteCommand();
virtual void redo();
virtual void undo();
};
}
#endif

View file

@ -0,0 +1,442 @@
#include "data.hpp"
#include <stdexcept>
#include <QAbstractItemModel>
#include <components/esm/esmreader.hpp>
#include <components/esm/defs.hpp>
#include <components/esm/loadglob.hpp>
#include "idtable.hpp"
#include "columnimp.hpp"
#include "regionmap.hpp"
#include "columns.hpp"
void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type1,
UniversalId::Type type2)
{
mModels.push_back (model);
mModelIndex.insert (std::make_pair (type1, model));
if (type2!=UniversalId::Type_None)
mModelIndex.insert (std::make_pair (type2, model));
}
CSMWorld::Data::Data() : mRefs (mCells)
{
mGlobals.addColumn (new StringIdColumn<ESM::Global>);
mGlobals.addColumn (new RecordStateColumn<ESM::Global>);
mGlobals.addColumn (new FixedRecordTypeColumn<ESM::Global> (UniversalId::Type_Global));
mGlobals.addColumn (new VarTypeColumn<ESM::Global> (ColumnBase::Display_GlobalVarType));
mGlobals.addColumn (new VarValueColumn<ESM::Global>);
mGmsts.addColumn (new StringIdColumn<ESM::GameSetting>);
mGmsts.addColumn (new RecordStateColumn<ESM::GameSetting>);
mGmsts.addColumn (new FixedRecordTypeColumn<ESM::GameSetting> (UniversalId::Type_Gmst));
mGmsts.addColumn (new FixedRecordTypeColumn<ESM::GameSetting> (UniversalId::Type_Gmst));
mGmsts.addColumn (new VarTypeColumn<ESM::GameSetting> (ColumnBase::Display_GmstVarType));
mGmsts.addColumn (new VarValueColumn<ESM::GameSetting>);
mSkills.addColumn (new StringIdColumn<ESM::Skill>);
mSkills.addColumn (new RecordStateColumn<ESM::Skill>);
mSkills.addColumn (new FixedRecordTypeColumn<ESM::Skill> (UniversalId::Type_Skill));
mSkills.addColumn (new AttributeColumn<ESM::Skill>);
mSkills.addColumn (new SpecialisationColumn<ESM::Skill>);
for (int i=0; i<4; ++i)
mSkills.addColumn (new UseValueColumn<ESM::Skill> (i));
mSkills.addColumn (new DescriptionColumn<ESM::Skill>);
mClasses.addColumn (new StringIdColumn<ESM::Class>);
mClasses.addColumn (new RecordStateColumn<ESM::Class>);
mClasses.addColumn (new FixedRecordTypeColumn<ESM::Class> (UniversalId::Type_Class));
mClasses.addColumn (new NameColumn<ESM::Class>);
mClasses.addColumn (new AttributesColumn<ESM::Class> (0));
mClasses.addColumn (new AttributesColumn<ESM::Class> (1));
mClasses.addColumn (new SpecialisationColumn<ESM::Class>);
for (int i=0; i<5; ++i)
mClasses.addColumn (new SkillsColumn<ESM::Class> (i, true, true));
for (int i=0; i<5; ++i)
mClasses.addColumn (new SkillsColumn<ESM::Class> (i, true, false));
mClasses.addColumn (new PlayableColumn<ESM::Class>);
mClasses.addColumn (new DescriptionColumn<ESM::Class>);
mFactions.addColumn (new StringIdColumn<ESM::Faction>);
mFactions.addColumn (new RecordStateColumn<ESM::Faction>);
mFactions.addColumn (new FixedRecordTypeColumn<ESM::Faction> (UniversalId::Type_Faction));
mFactions.addColumn (new NameColumn<ESM::Faction>);
mFactions.addColumn (new AttributesColumn<ESM::Faction> (0));
mFactions.addColumn (new AttributesColumn<ESM::Faction> (1));
mFactions.addColumn (new HiddenColumn<ESM::Faction>);
for (int i=0; i<6; ++i)
mFactions.addColumn (new SkillsColumn<ESM::Faction> (i));
mRaces.addColumn (new StringIdColumn<ESM::Race>);
mRaces.addColumn (new RecordStateColumn<ESM::Race>);
mRaces.addColumn (new FixedRecordTypeColumn<ESM::Race> (UniversalId::Type_Race));
mRaces.addColumn (new NameColumn<ESM::Race>);
mRaces.addColumn (new DescriptionColumn<ESM::Race>);
mRaces.addColumn (new FlagColumn<ESM::Race> (Columns::ColumnId_Playable, 0x1));
mRaces.addColumn (new FlagColumn<ESM::Race> (Columns::ColumnId_BeastRace, 0x2));
mRaces.addColumn (new WeightHeightColumn<ESM::Race> (true, true));
mRaces.addColumn (new WeightHeightColumn<ESM::Race> (true, false));
mRaces.addColumn (new WeightHeightColumn<ESM::Race> (false, true));
mRaces.addColumn (new WeightHeightColumn<ESM::Race> (false, false));
mSounds.addColumn (new StringIdColumn<ESM::Sound>);
mSounds.addColumn (new RecordStateColumn<ESM::Sound>);
mSounds.addColumn (new FixedRecordTypeColumn<ESM::Sound> (UniversalId::Type_Sound));
mSounds.addColumn (new SoundParamColumn<ESM::Sound> (SoundParamColumn<ESM::Sound>::Type_Volume));
mSounds.addColumn (new SoundParamColumn<ESM::Sound> (SoundParamColumn<ESM::Sound>::Type_MinRange));
mSounds.addColumn (new SoundParamColumn<ESM::Sound> (SoundParamColumn<ESM::Sound>::Type_MaxRange));
mSounds.addColumn (new SoundFileColumn<ESM::Sound>);
mScripts.addColumn (new StringIdColumn<ESM::Script>);
mScripts.addColumn (new RecordStateColumn<ESM::Script>);
mScripts.addColumn (new FixedRecordTypeColumn<ESM::Script> (UniversalId::Type_Script));
mScripts.addColumn (new ScriptColumn<ESM::Script>);
mRegions.addColumn (new StringIdColumn<ESM::Region>);
mRegions.addColumn (new RecordStateColumn<ESM::Region>);
mRegions.addColumn (new FixedRecordTypeColumn<ESM::Region> (UniversalId::Type_Region));
mRegions.addColumn (new NameColumn<ESM::Region>);
mRegions.addColumn (new MapColourColumn<ESM::Region>);
mRegions.addColumn (new SleepListColumn<ESM::Region>);
mBirthsigns.addColumn (new StringIdColumn<ESM::BirthSign>);
mBirthsigns.addColumn (new RecordStateColumn<ESM::BirthSign>);
mBirthsigns.addColumn (new FixedRecordTypeColumn<ESM::BirthSign> (UniversalId::Type_Birthsign));
mBirthsigns.addColumn (new NameColumn<ESM::BirthSign>);
mBirthsigns.addColumn (new TextureColumn<ESM::BirthSign>);
mBirthsigns.addColumn (new DescriptionColumn<ESM::BirthSign>);
mSpells.addColumn (new StringIdColumn<ESM::Spell>);
mSpells.addColumn (new RecordStateColumn<ESM::Spell>);
mSpells.addColumn (new FixedRecordTypeColumn<ESM::Spell> (UniversalId::Type_Spell));
mSpells.addColumn (new NameColumn<ESM::Spell>);
mSpells.addColumn (new SpellTypeColumn<ESM::Spell>);
mSpells.addColumn (new CostColumn<ESM::Spell>);
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_AlwaysSucceeds, 0x4));
mCells.addColumn (new StringIdColumn<Cell>);
mCells.addColumn (new RecordStateColumn<Cell>);
mCells.addColumn (new FixedRecordTypeColumn<Cell> (UniversalId::Type_Cell));
mCells.addColumn (new NameColumn<Cell>);
mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_SleepForbidden, ESM::Cell::NoSleep));
mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_InteriorWater, ESM::Cell::HasWater));
mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_InteriorSky, ESM::Cell::QuasiEx));
mCells.addColumn (new RegionColumn<Cell>);
mRefs.addColumn (new StringIdColumn<CellRef> (true));
mRefs.addColumn (new RecordStateColumn<CellRef>);
mRefs.addColumn (new CellColumn<CellRef>);
mRefs.addColumn (new IdColumn<CellRef>);
mRefs.addColumn (new ScaleColumn<CellRef>);
mRefs.addColumn (new OwnerColumn<CellRef>);
mRefs.addColumn (new SoulColumn<CellRef>);
mRefs.addColumn (new FactionColumn<CellRef>);
mRefs.addColumn (new FactionIndexColumn<CellRef>);
mRefs.addColumn (new ChargesColumn<CellRef>);
mRefs.addColumn (new EnchantmentChargesColumn<CellRef>);
mRefs.addColumn (new GoldValueColumn<CellRef>);
mRefs.addColumn (new TeleportColumn<CellRef>);
mRefs.addColumn (new TeleportCellColumn<CellRef>);
mRefs.addColumn (new LockLevelColumn<CellRef>);
mRefs.addColumn (new KeyColumn<CellRef>);
mRefs.addColumn (new TrapColumn<CellRef>);
mFilters.addColumn (new StringIdColumn<CSMFilter::Filter>);
mFilters.addColumn (new RecordStateColumn<CSMFilter::Filter>);
mFilters.addColumn (new FilterColumn<CSMFilter::Filter>);
mFilters.addColumn (new DescriptionColumn<CSMFilter::Filter>);
addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global);
addModel (new IdTable (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst);
addModel (new IdTable (&mSkills), UniversalId::Type_Skills, UniversalId::Type_Skill);
addModel (new IdTable (&mClasses), UniversalId::Type_Classes, UniversalId::Type_Class);
addModel (new IdTable (&mFactions), UniversalId::Type_Factions, UniversalId::Type_Faction);
addModel (new IdTable (&mRaces), UniversalId::Type_Races, UniversalId::Type_Race);
addModel (new IdTable (&mSounds), UniversalId::Type_Sounds, UniversalId::Type_Sound);
addModel (new IdTable (&mScripts), UniversalId::Type_Scripts, UniversalId::Type_Script);
addModel (new IdTable (&mRegions), UniversalId::Type_Regions, UniversalId::Type_Region);
addModel (new IdTable (&mBirthsigns), UniversalId::Type_Birthsigns, UniversalId::Type_Birthsign);
addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell);
addModel (new IdTable (&mCells), UniversalId::Type_Cells, UniversalId::Type_Cell);
addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables,
UniversalId::Type_Referenceable);
addModel (new IdTable (&mRefs), UniversalId::Type_References, UniversalId::Type_Reference);
addModel (new IdTable (&mFilters), UniversalId::Type_Filters, UniversalId::Type_Filter);
}
CSMWorld::Data::~Data()
{
for (std::vector<QAbstractItemModel *>::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter)
delete *iter;
}
const CSMWorld::IdCollection<ESM::Global>& CSMWorld::Data::getGlobals() const
{
return mGlobals;
}
CSMWorld::IdCollection<ESM::Global>& CSMWorld::Data::getGlobals()
{
return mGlobals;
}
const CSMWorld::IdCollection<ESM::GameSetting>& CSMWorld::Data::getGmsts() const
{
return mGmsts;
}
CSMWorld::IdCollection<ESM::GameSetting>& CSMWorld::Data::getGmsts()
{
return mGmsts;
}
const CSMWorld::IdCollection<ESM::Skill>& CSMWorld::Data::getSkills() const
{
return mSkills;
}
CSMWorld::IdCollection<ESM::Skill>& CSMWorld::Data::getSkills()
{
return mSkills;
}
const CSMWorld::IdCollection<ESM::Class>& CSMWorld::Data::getClasses() const
{
return mClasses;
}
CSMWorld::IdCollection<ESM::Class>& CSMWorld::Data::getClasses()
{
return mClasses;
}
const CSMWorld::IdCollection<ESM::Faction>& CSMWorld::Data::getFactions() const
{
return mFactions;
}
CSMWorld::IdCollection<ESM::Faction>& CSMWorld::Data::getFactions()
{
return mFactions;
}
const CSMWorld::IdCollection<ESM::Race>& CSMWorld::Data::getRaces() const
{
return mRaces;
}
CSMWorld::IdCollection<ESM::Race>& CSMWorld::Data::getRaces()
{
return mRaces;
}
const CSMWorld::IdCollection<ESM::Sound>& CSMWorld::Data::getSounds() const
{
return mSounds;
}
CSMWorld::IdCollection<ESM::Sound>& CSMWorld::Data::getSounds()
{
return mSounds;
}
const CSMWorld::IdCollection<ESM::Script>& CSMWorld::Data::getScripts() const
{
return mScripts;
}
CSMWorld::IdCollection<ESM::Script>& CSMWorld::Data::getScripts()
{
return mScripts;
}
const CSMWorld::IdCollection<ESM::Region>& CSMWorld::Data::getRegions() const
{
return mRegions;
}
CSMWorld::IdCollection<ESM::Region>& CSMWorld::Data::getRegions()
{
return mRegions;
}
const CSMWorld::IdCollection<ESM::BirthSign>& CSMWorld::Data::getBirthsigns() const
{
return mBirthsigns;
}
CSMWorld::IdCollection<ESM::BirthSign>& CSMWorld::Data::getBirthsigns()
{
return mBirthsigns;
}
const CSMWorld::IdCollection<ESM::Spell>& CSMWorld::Data::getSpells() const
{
return mSpells;
}
CSMWorld::IdCollection<ESM::Spell>& CSMWorld::Data::getSpells()
{
return mSpells;
}
const CSMWorld::IdCollection<CSMWorld::Cell>& CSMWorld::Data::getCells() const
{
return mCells;
}
CSMWorld::IdCollection<CSMWorld::Cell>& CSMWorld::Data::getCells()
{
return mCells;
}
const CSMWorld::RefIdCollection& CSMWorld::Data::getReferenceables() const
{
return mReferenceables;
}
CSMWorld::RefIdCollection& CSMWorld::Data::getReferenceables()
{
return mReferenceables;
}
const CSMWorld::RefCollection& CSMWorld::Data::getReferences() const
{
return mRefs;
}
CSMWorld::RefCollection& CSMWorld::Data::getReferences()
{
return mRefs;
}
const CSMWorld::IdCollection<CSMFilter::Filter>& CSMWorld::Data::getFilters() const
{
return mFilters;
}
CSMWorld::IdCollection<CSMFilter::Filter>& CSMWorld::Data::getFilters()
{
return mFilters;
}
QAbstractItemModel *CSMWorld::Data::getTableModel (const UniversalId& id)
{
std::map<UniversalId::Type, QAbstractItemModel *>::iterator iter = mModelIndex.find (id.getType());
if (iter==mModelIndex.end())
{
// try creating missing (secondary) tables on the fly
//
// Note: We create these tables here so we don't have to deal with them during load/initial
// construction of the ESX data where no update signals are available.
if (id.getType()==UniversalId::Type_RegionMap)
{
RegionMap *table = 0;
addModel (table = new RegionMap (*this), UniversalId::Type_RegionMap,
UniversalId::Type_None);
return table;
}
throw std::logic_error ("No table model available for " + id.toString());
}
return iter->second;
}
void CSMWorld::Data::merge()
{
mGlobals.merge();
}
void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base)
{
ESM::ESMReader reader;
/// \todo set encoding properly, once config implementation has been fixed.
ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding ("win1252"));
reader.setEncoder (&encoder);
reader.open (path.string());
// Note: We do not need to send update signals here, because at this point the model is not connected
// to any view.
while (reader.hasMoreRecs())
{
ESM::NAME n = reader.getRecName();
reader.getRecHeader();
switch (n.val)
{
case ESM::REC_GLOB: mGlobals.load (reader, base); break;
case ESM::REC_GMST: mGmsts.load (reader, base); break;
case ESM::REC_SKIL: mSkills.load (reader, base); break;
case ESM::REC_CLAS: mClasses.load (reader, base); break;
case ESM::REC_FACT: mFactions.load (reader, base); break;
case ESM::REC_RACE: mRaces.load (reader, base); break;
case ESM::REC_SOUN: mSounds.load (reader, base); break;
case ESM::REC_SCPT: mScripts.load (reader, base); break;
case ESM::REC_REGN: mRegions.load (reader, base); break;
case ESM::REC_BSGN: mBirthsigns.load (reader, base); break;
case ESM::REC_SPEL: mSpells.load (reader, base); break;
case ESM::REC_CELL:
mCells.load (reader, base);
mRefs.load (reader, mCells.getSize()-1, base);
break;
case ESM::REC_ACTI: mReferenceables.load (reader, base, UniversalId::Type_Activator); break;
case ESM::REC_ALCH: mReferenceables.load (reader, base, UniversalId::Type_Potion); break;
case ESM::REC_APPA: mReferenceables.load (reader, base, UniversalId::Type_Apparatus); break;
case ESM::REC_ARMO: mReferenceables.load (reader, base, UniversalId::Type_Armor); break;
case ESM::REC_BOOK: mReferenceables.load (reader, base, UniversalId::Type_Book); break;
case ESM::REC_CLOT: mReferenceables.load (reader, base, UniversalId::Type_Clothing); break;
case ESM::REC_CONT: mReferenceables.load (reader, base, UniversalId::Type_Container); break;
case ESM::REC_CREA: mReferenceables.load (reader, base, UniversalId::Type_Creature); break;
case ESM::REC_DOOR: mReferenceables.load (reader, base, UniversalId::Type_Door); break;
case ESM::REC_INGR: mReferenceables.load (reader, base, UniversalId::Type_Ingredient); break;
case ESM::REC_LEVC:
mReferenceables.load (reader, base, UniversalId::Type_CreatureLevelledList); break;
case ESM::REC_LEVI:
mReferenceables.load (reader, base, UniversalId::Type_ItemLevelledList); break;
case ESM::REC_LIGH: mReferenceables.load (reader, base, UniversalId::Type_Light); break;
case ESM::REC_LOCK: mReferenceables.load (reader, base, UniversalId::Type_Lockpick); break;
case ESM::REC_MISC:
mReferenceables.load (reader, base, UniversalId::Type_Miscellaneous); break;
case ESM::REC_NPC_: mReferenceables.load (reader, base, UniversalId::Type_Npc); break;
case ESM::REC_PROB: mReferenceables.load (reader, base, UniversalId::Type_Probe); break;
case ESM::REC_REPA: mReferenceables.load (reader, base, UniversalId::Type_Repair); break;
case ESM::REC_STAT: mReferenceables.load (reader, base, UniversalId::Type_Static); break;
case ESM::REC_WEAP: mReferenceables.load (reader, base, UniversalId::Type_Weapon); break;
default:
/// \todo throw an exception instead, once all records are implemented
reader.skipRecord();
}
}
}
bool CSMWorld::Data::hasId (const std::string& id) const
{
return
getGlobals().searchId (id)!=-1 ||
getGmsts().searchId (id)!=-1 ||
getSkills().searchId (id)!=-1 ||
getClasses().searchId (id)!=-1 ||
getFactions().searchId (id)!=-1 ||
getRaces().searchId (id)!=-1 ||
getSounds().searchId (id)!=-1 ||
getScripts().searchId (id)!=-1 ||
getRegions().searchId (id)!=-1 ||
getBirthsigns().searchId (id)!=-1 ||
getSpells().searchId (id)!=-1 ||
getCells().searchId (id)!=-1 ||
getReferenceables().searchId (id)!=-1;
}

View file

@ -0,0 +1,142 @@
#ifndef CSM_WOLRD_DATA_H
#define CSM_WOLRD_DATA_H
#include <map>
#include <vector>
#include <boost/filesystem/path.hpp>
#include <components/esm/loadglob.hpp>
#include <components/esm/loadgmst.hpp>
#include <components/esm/loadskil.hpp>
#include <components/esm/loadclas.hpp>
#include <components/esm/loadfact.hpp>
#include <components/esm/loadrace.hpp>
#include <components/esm/loadsoun.hpp>
#include <components/esm/loadscpt.hpp>
#include <components/esm/loadregn.hpp>
#include <components/esm/loadbsgn.hpp>
#include <components/esm/loadspel.hpp>
#include "../filter/filter.hpp"
#include "idcollection.hpp"
#include "universalid.hpp"
#include "cell.hpp"
#include "refidcollection.hpp"
#include "refcollection.hpp"
class QAbstractItemModel;
namespace CSMWorld
{
class Data
{
IdCollection<ESM::Global> mGlobals;
IdCollection<ESM::GameSetting> mGmsts;
IdCollection<ESM::Skill> mSkills;
IdCollection<ESM::Class> mClasses;
IdCollection<ESM::Faction> mFactions;
IdCollection<ESM::Race> mRaces;
IdCollection<ESM::Sound> mSounds;
IdCollection<ESM::Script> mScripts;
IdCollection<ESM::Region> mRegions;
IdCollection<ESM::BirthSign> mBirthsigns;
IdCollection<ESM::Spell> mSpells;
IdCollection<Cell> mCells;
RefIdCollection mReferenceables;
RefCollection mRefs;
IdCollection<CSMFilter::Filter> mFilters;
std::vector<QAbstractItemModel *> mModels;
std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex;
// not implemented
Data (const Data&);
Data& operator= (const Data&);
void addModel (QAbstractItemModel *model, UniversalId::Type type1,
UniversalId::Type type2 = UniversalId::Type_None);
public:
Data();
~Data();
const IdCollection<ESM::Global>& getGlobals() const;
IdCollection<ESM::Global>& getGlobals();
const IdCollection<ESM::GameSetting>& getGmsts() const;
IdCollection<ESM::GameSetting>& getGmsts();
const IdCollection<ESM::Skill>& getSkills() const;
IdCollection<ESM::Skill>& getSkills();
const IdCollection<ESM::Class>& getClasses() const;
IdCollection<ESM::Class>& getClasses();
const IdCollection<ESM::Faction>& getFactions() const;
IdCollection<ESM::Faction>& getFactions();
const IdCollection<ESM::Race>& getRaces() const;
IdCollection<ESM::Race>& getRaces();
const IdCollection<ESM::Sound>& getSounds() const;
IdCollection<ESM::Sound>& getSounds();
const IdCollection<ESM::Script>& getScripts() const;
IdCollection<ESM::Script>& getScripts();
const IdCollection<ESM::Region>& getRegions() const;
IdCollection<ESM::Region>& getRegions();
const IdCollection<ESM::BirthSign>& getBirthsigns() const;
IdCollection<ESM::BirthSign>& getBirthsigns();
const IdCollection<ESM::Spell>& getSpells() const;
IdCollection<ESM::Spell>& getSpells();
const IdCollection<Cell>& getCells() const;
IdCollection<Cell>& getCells();
const RefIdCollection& getReferenceables() const;
RefIdCollection& getReferenceables();
const RefCollection& getReferences() const;
RefCollection& getReferences();
const IdCollection<CSMFilter::Filter>& getFilters() const;
IdCollection<CSMFilter::Filter>& getFilters();
QAbstractItemModel *getTableModel (const UniversalId& id);
///< If no table model is available for \a id, an exception is thrown.
///
/// \note The returned table may either be the model for the ID itself or the model that
/// contains the record specified by the ID.
void merge();
///< Merge modified into base.
void loadFile (const boost::filesystem::path& path, bool base);
///< Merging content of a file into base or modified.
bool hasId (const std::string& id) const;
};
}
#endif

View file

@ -0,0 +1,86 @@
#ifndef CSM_WOLRD_IDCOLLECTION_H
#define CSM_WOLRD_IDCOLLECTION_H
#include <components/esm/esmreader.hpp>
#include "collection.hpp"
namespace CSMWorld
{
/// \brief Single type collection of top level records
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
class IdCollection : public Collection<ESXRecordT, IdAccessorT>
{
public:
void load (ESM::ESMReader& reader, bool base,
UniversalId::Type type = UniversalId::Type_None);
///< \param type Will be ignored, unless the collection supports multiple record types
};
template<typename ESXRecordT, typename IdAccessorT>
void IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base,
UniversalId::Type type)
{
std::string id = reader.getHNOString ("NAME");
if (reader.isNextSub ("DELE"))
{
int index = Collection<ESXRecordT, IdAccessorT>::searchId (id);
reader.skipRecord();
if (index==-1)
{
// deleting a record that does not exist
// ignore it for now
/// \todo report the problem to the user
}
else if (base)
{
Collection<ESXRecordT, IdAccessorT>::removeRows (index, 1);
}
else
{
Record<ESXRecordT> record = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
record.mState = RecordBase::State_Deleted;
this->setRecord (index, record);
}
}
else
{
ESXRecordT record;
IdAccessorT().getId (record) = id;
record.load (reader);
int index = this->searchId (IdAccessorT().getId (record));
if (index==-1)
{
// new record
Record<ESXRecordT> record2;
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
(base ? record2.mBase : record2.mModified) = record;
this->appendRecord (record2);
}
else
{
// old record
Record<ESXRecordT> record2 = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
if (base)
record2.mBase = record;
else
record2.setModified (record);
this->setRecord (index, record2);
}
}
}
}
#endif

View file

@ -0,0 +1,181 @@
#include "idtable.hpp"
#include "collectionbase.hpp"
#include "columnbase.hpp"
CSMWorld::IdTable::IdTable (CollectionBase *idCollection) : mIdCollection (idCollection)
{
}
CSMWorld::IdTable::~IdTable()
{
}
int CSMWorld::IdTable::rowCount (const QModelIndex & parent) const
{
if (parent.isValid())
return 0;
return mIdCollection->getSize();
}
int CSMWorld::IdTable::columnCount (const QModelIndex & parent) const
{
if (parent.isValid())
return 0;
return mIdCollection->getColumns();
}
QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const
{
if (role!=Qt::DisplayRole && role!=Qt::EditRole)
return QVariant();
if (role==Qt::EditRole && !mIdCollection->getColumn (index.column()).isEditable())
return QVariant();
return mIdCollection->getData (index.row(), index.column());
}
QVariant CSMWorld::IdTable::headerData (int section, Qt::Orientation orientation, int role) const
{
if (orientation==Qt::Vertical)
return QVariant();
if (role==Qt::DisplayRole)
return tr (mIdCollection->getColumn (section).getTitle().c_str());
if (role==ColumnBase::Role_Flags)
return mIdCollection->getColumn (section).mFlags;
if (role==ColumnBase::Role_Display)
return mIdCollection->getColumn (section).mDisplayType;
return QVariant();
}
bool CSMWorld::IdTable::setData ( const QModelIndex &index, const QVariant &value, int role)
{
if (mIdCollection->getColumn (index.column()).isEditable() && role==Qt::EditRole)
{
mIdCollection->setData (index.row(), index.column(), value);
emit dataChanged (CSMWorld::IdTable::index (index.row(), 0),
CSMWorld::IdTable::index (index.row(), mIdCollection->getColumns()-1));
return true;
}
return false;
}
Qt::ItemFlags CSMWorld::IdTable::flags (const QModelIndex & index) const
{
Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
if (mIdCollection->getColumn (index.column()).isUserEditable())
flags |= Qt::ItemIsEditable;
return flags;
}
bool CSMWorld::IdTable::removeRows (int row, int count, const QModelIndex& parent)
{
if (parent.isValid())
return false;
beginRemoveRows (parent, row, row+count-1);
mIdCollection->removeRows (row, count);
endRemoveRows();
return true;
}
QModelIndex CSMWorld::IdTable::index (int row, int column, const QModelIndex& parent) const
{
if (parent.isValid())
return QModelIndex();
if (row<0 || row>=mIdCollection->getSize())
return QModelIndex();
if (column<0 || column>=mIdCollection->getColumns())
return QModelIndex();
return createIndex (row, column);
}
QModelIndex CSMWorld::IdTable::parent (const QModelIndex& index) const
{
return QModelIndex();
}
void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type)
{
int index = mIdCollection->getAppendIndex();
beginInsertRows (QModelIndex(), index, index);
mIdCollection->appendBlankRecord (id, type);
endInsertRows();
}
QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const
{
return index (mIdCollection->getIndex (id), column);
}
void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& record)
{
int index = mIdCollection->searchId (id);
if (index==-1)
{
int index = mIdCollection->getAppendIndex();
beginInsertRows (QModelIndex(), index, index);
mIdCollection->appendRecord (record);
endInsertRows();
}
else
{
mIdCollection->replace (index, record);
emit dataChanged (CSMWorld::IdTable::index (index, 0),
CSMWorld::IdTable::index (index, mIdCollection->getColumns()-1));
}
}
const CSMWorld::RecordBase& CSMWorld::IdTable::getRecord (const std::string& id) const
{
return mIdCollection->getRecord (id);
}
int CSMWorld::IdTable::searchColumnIndex (Columns::ColumnId id) const
{
int columns = mIdCollection->getColumns();
for (int i=0; i<columns; ++i)
if (mIdCollection->getColumn (i).mColumnId==id)
return i;
return -1;
}
int CSMWorld::IdTable::findColumnIndex (Columns::ColumnId id) const
{
int index = searchColumnIndex (id);
if (index==-1)
throw std::logic_error ("invalid column index");
return index;
}

View file

@ -0,0 +1,69 @@
#ifndef CSM_WOLRD_IDTABLE_H
#define CSM_WOLRD_IDTABLE_H
#include <QAbstractItemModel>
#include "universalid.hpp"
#include "columns.hpp"
namespace CSMWorld
{
class CollectionBase;
class RecordBase;
class IdTable : public QAbstractItemModel
{
Q_OBJECT
CollectionBase *mIdCollection;
// not implemented
IdTable (const IdTable&);
IdTable& operator= (const IdTable&);
public:
IdTable (CollectionBase *idCollection);
///< The ownership of \a idCollection is not transferred.
virtual ~IdTable();
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 QVariant headerData (int section, Qt::Orientation orientation, 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;
void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None);
///< \param type Will be ignored, unless the collection supports multiple record types
QModelIndex getModelIndex (const std::string& id, int column) const;
void setRecord (const std::string& id, const RecordBase& record);
///< Add record or overwrite existing recrod.
const RecordBase& getRecord (const std::string& id) const;
int searchColumnIndex (Columns::ColumnId id) const;
///< Return index of column with the given \a id. If no such column exists, -1 is returned.
int findColumnIndex (Columns::ColumnId id) const;
///< Return index of column with the given \a id. If no such column exists, an exception is
/// thrown.
};
}
#endif

View file

@ -0,0 +1,48 @@
#include "idtableproxymodel.hpp"
#include <vector>
#include "idtable.hpp"
void CSMWorld::IdTableProxyModel::updateColumnMap()
{
mColumnMap.clear();
if (mFilter)
{
std::vector<int> columns = mFilter->getReferencedColumns();
const IdTable& table = dynamic_cast<const IdTable&> (*sourceModel());
for (std::vector<int>::const_iterator iter (columns.begin()); iter!=columns.end(); ++iter)
mColumnMap.insert (std::make_pair (*iter,
table.searchColumnIndex (static_cast<CSMWorld::Columns::ColumnId> (*iter))));
}
}
bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent)
const
{
if (!mFilter)
return true;
return mFilter->test (
dynamic_cast<IdTable&> (*sourceModel()), sourceRow, mColumnMap);
}
CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent)
: QSortFilterProxyModel (parent)
{}
QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const
{
return mapFromSource (dynamic_cast<IdTable&> (*sourceModel()).getModelIndex (id, column));
}
void CSMWorld::IdTableProxyModel::setFilter (const boost::shared_ptr<CSMFilter::Node>& filter)
{
mFilter = filter;
updateColumnMap();
invalidateFilter();
}

View file

@ -0,0 +1,39 @@
#ifndef CSM_WOLRD_IDTABLEPROXYMODEL_H
#define CSM_WOLRD_IDTABLEPROXYMODEL_H
#include <string>
#include <boost/shared_ptr.hpp>
#include <map>
#include <QSortFilterProxyModel>
#include "../filter/node.hpp"
namespace CSMWorld
{
class IdTableProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
boost::shared_ptr<CSMFilter::Node> mFilter;
std::map<int, int> mColumnMap; // column ID, column index in this model (or -1)
private:
void updateColumnMap();
bool filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const;
public:
IdTableProxyModel (QObject *parent = 0);
virtual QModelIndex getModelIndex (const std::string& id, int column) const;
void setFilter (const boost::shared_ptr<CSMFilter::Node>& filter);
};
}
#endif

View file

@ -0,0 +1,21 @@
#include "record.hpp"
CSMWorld::RecordBase::~RecordBase() {}
bool CSMWorld::RecordBase::isDeleted() const
{
return mState==State_Deleted || mState==State_Erased;
}
bool CSMWorld::RecordBase::isErased() const
{
return mState==State_Erased;
}
bool CSMWorld::RecordBase::isModified() const
{
return mState==State_Modified || mState==State_ModifiedOnly;
}

View file

@ -0,0 +1,127 @@
#ifndef CSM_WOLRD_RECORD_H
#define CSM_WOLRD_RECORD_H
#include <stdexcept>
namespace CSMWorld
{
struct RecordBase
{
enum State
{
State_BaseOnly = 0, // defined in base only
State_Modified = 1, // exists in base, but has been modified
State_ModifiedOnly = 2, // newly created in modified
State_Deleted = 3, // exists in base, but has been deleted
State_Erased = 4 // does not exist at all (we mostly treat that the same way as deleted)
};
State mState;
virtual ~RecordBase();
virtual RecordBase *clone() const = 0;
virtual void assign (const RecordBase& record) = 0;
///< Will throw an exception if the types don't match.
bool isDeleted() const;
bool isErased() const;
bool isModified() const;
};
template <typename ESXRecordT>
struct Record : public RecordBase
{
ESXRecordT mBase;
ESXRecordT mModified;
virtual RecordBase *clone() const;
virtual void assign (const RecordBase& record);
const ESXRecordT& get() const;
///< Throws an exception, if the record is deleted.
ESXRecordT& get();
///< Throws an exception, if the record is deleted.
const ESXRecordT& getBase() const;
///< Throws an exception, if the record is deleted. Returns modified, if there is no base.
void setModified (const ESXRecordT& modified);
///< Throws an exception, if the record is deleted.
void merge();
///< Merge modified into base.
};
template <typename ESXRecordT>
RecordBase *Record<ESXRecordT>::clone() const
{
return new Record<ESXRecordT> (*this);
}
template <typename ESXRecordT>
void Record<ESXRecordT>::assign (const RecordBase& record)
{
*this = dynamic_cast<const Record<ESXRecordT>& > (record);
}
template <typename ESXRecordT>
const ESXRecordT& Record<ESXRecordT>::get() const
{
if (mState==State_Erased)
throw std::logic_error ("attempt to access a deleted record");
return mState==State_BaseOnly || mState==State_Deleted ? mBase : mModified;
}
template <typename ESXRecordT>
ESXRecordT& Record<ESXRecordT>::get()
{
if (mState==State_Erased)
throw std::logic_error ("attempt to access a deleted record");
return mState==State_BaseOnly || mState==State_Deleted ? mBase : mModified;
}
template <typename ESXRecordT>
const ESXRecordT& Record<ESXRecordT>::getBase() const
{
if (mState==State_Erased)
throw std::logic_error ("attempt to access a deleted record");
return mState==State_ModifiedOnly ? mModified : mBase;
}
template <typename ESXRecordT>
void Record<ESXRecordT>::setModified (const ESXRecordT& modified)
{
if (mState==State_Erased)
throw std::logic_error ("attempt to modify a deleted record");
mModified = modified;
if (mState!=State_ModifiedOnly)
mState = State_Modified;
}
template <typename ESXRecordT>
void Record<ESXRecordT>::merge()
{
if (isModified())
{
mBase = mModified;
mState = State_BaseOnly;
}
else if (mState==State_Deleted)
{
mState = State_Erased;
}
}
}
#endif

View file

@ -0,0 +1,13 @@
#include "ref.hpp"
#include "cell.hpp"
void CSMWorld::CellRef::load (ESM::ESMReader &esm, Cell& cell, const std::string& id)
{
mId = id;
mCellId = cell.mId;
if (!mDeleted)
cell.addRef (mId);
}

View file

@ -0,0 +1,26 @@
#ifndef CSM_WOLRD_REF_H
#define CSM_WOLRD_REF_H
#include <components/esm/cellref.hpp>
namespace ESM
{
class ESMReader;
}
namespace CSMWorld
{
class Cell;
/// \brief Wrapper for CellRef sub record
struct CellRef : public ESM::CellRef
{
std::string mId;
std::string mCellId;
void load (ESM::ESMReader &esm, Cell& cell, const std::string& id);
///< Load cell ref and register it with \a cell.
};
}
#endif

View file

@ -0,0 +1,41 @@
#include "refcollection.hpp"
#include <sstream>
#include "ref.hpp"
#include "cell.hpp"
CSMWorld::RefCollection::RefCollection (Collection<Cell>& cells)
: mCells (cells), mNextId (0)
{}
void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base)
{
Record<Cell> cell = mCells.getRecord (cellIndex);
Cell& cell2 = base ? cell.mBase : cell.mModified;
CellRef ref;
while (cell2.getNextRef (reader, ref))
{
/// \todo handle deleted and moved references
ref.load (reader, cell2, getNewId());
Record<CellRef> record2;
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
(base ? record2.mBase : record2.mModified) = ref;
appendRecord (record2);
}
mCells.setRecord (cellIndex, cell);
}
std::string CSMWorld::RefCollection::getNewId()
{
std::ostringstream stream;
stream << "ref#" << mNextId++;
return stream.str();
}

View file

@ -0,0 +1,29 @@
#ifndef CSM_WOLRD_REFCOLLECTION_H
#define CSM_WOLRD_REFCOLLECTION_H
#include "collection.hpp"
#include "ref.hpp"
#include "record.hpp"
namespace CSMWorld
{
struct Cell;
/// \brief References in cells
class RefCollection : public Collection<CellRef>
{
Collection<Cell>& mCells;
int mNextId;
public:
RefCollection (Collection<Cell>& cells);
void load (ESM::ESMReader& reader, int cellIndex, bool base);
///< Load a sequence of references.
std::string getNewId();
};
}
#endif

View file

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

View file

@ -0,0 +1,37 @@
#ifndef CSM_WOLRD_REFIDADAPTER_H
#define CSM_WOLRD_REFIDADAPTER_H
#include <string>
class QVariant;
namespace CSMWorld
{
class RefIdColumn;
class RefIdData;
class RecordBase;
class RefIdAdapter
{
// not implemented
RefIdAdapter (const RefIdAdapter&);
RefIdAdapter& operator= (const RefIdAdapter&);
public:
RefIdAdapter();
virtual ~RefIdAdapter();
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int idnex)
const = 0;
virtual void setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const = 0;
///< If the data type does not match an exception is thrown.
virtual std::string getId (const RecordBase& record) const = 0;
};
}
#endif

View file

@ -0,0 +1,575 @@
#include "refidadapterimp.hpp"
CSMWorld::PotionRefIdAdapter::PotionRefIdAdapter (const InventoryColumns& columns,
const RefIdColumn *autoCalc)
: InventoryRefIdAdapter<ESM::Potion> (UniversalId::Type_Potion, columns),
mAutoCalc (autoCalc)
{}
QVariant CSMWorld::PotionRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data,
int index) const
{
const Record<ESM::Potion>& record = static_cast<const Record<ESM::Potion>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Potion)));
if (column==mAutoCalc)
return record.get().mData.mAutoCalc!=0;
return InventoryRefIdAdapter<ESM::Potion>::getData (column, data, index);
}
void CSMWorld::PotionRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const
{
Record<ESM::Potion>& record = static_cast<Record<ESM::Potion>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Potion)));
if (column==mAutoCalc)
record.get().mData.mAutoCalc = value.toInt();
else
InventoryRefIdAdapter<ESM::Potion>::setData (column, data, index, value);
}
CSMWorld::ApparatusRefIdAdapter::ApparatusRefIdAdapter (const InventoryColumns& columns,
const RefIdColumn *type, const RefIdColumn *quality)
: InventoryRefIdAdapter<ESM::Apparatus> (UniversalId::Type_Apparatus, columns),
mType (type), mQuality (quality)
{}
QVariant CSMWorld::ApparatusRefIdAdapter::getData (const RefIdColumn *column,
const RefIdData& data, int index) const
{
const Record<ESM::Apparatus>& record = static_cast<const Record<ESM::Apparatus>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Apparatus)));
if (column==mType)
return record.get().mData.mType;
if (column==mQuality)
return record.get().mData.mQuality;
return InventoryRefIdAdapter<ESM::Apparatus>::getData (column, data, index);
}
void CSMWorld::ApparatusRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const
{
Record<ESM::Apparatus>& record = static_cast<Record<ESM::Apparatus>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Apparatus)));
if (column==mType)
record.get().mData.mType = value.toInt();
else if (column==mQuality)
record.get().mData.mQuality = value.toFloat();
else
InventoryRefIdAdapter<ESM::Apparatus>::setData (column, data, index, value);
}
CSMWorld::ArmorRefIdAdapter::ArmorRefIdAdapter (const EnchantableColumns& columns,
const RefIdColumn *type, const RefIdColumn *health, const RefIdColumn *armor)
: EnchantableRefIdAdapter<ESM::Armor> (UniversalId::Type_Armor, columns),
mType (type), mHealth (health), mArmor (armor)
{}
QVariant CSMWorld::ArmorRefIdAdapter::getData (const RefIdColumn *column,
const RefIdData& data, int index) const
{
const Record<ESM::Armor>& record = static_cast<const Record<ESM::Armor>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Armor)));
if (column==mType)
return record.get().mData.mType;
if (column==mHealth)
return record.get().mData.mHealth;
if (column==mArmor)
return record.get().mData.mArmor;
return EnchantableRefIdAdapter<ESM::Armor>::getData (column, data, index);
}
void CSMWorld::ArmorRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const
{
Record<ESM::Armor>& record = static_cast<Record<ESM::Armor>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Armor)));
if (column==mType)
record.get().mData.mType = value.toInt();
else if (column==mHealth)
record.get().mData.mHealth = value.toInt();
else if (column==mArmor)
record.get().mData.mArmor = value.toInt();
else
EnchantableRefIdAdapter<ESM::Armor>::setData (column, data, index, value);
}
CSMWorld::BookRefIdAdapter::BookRefIdAdapter (const EnchantableColumns& columns,
const RefIdColumn *scroll, const RefIdColumn *skill)
: EnchantableRefIdAdapter<ESM::Book> (UniversalId::Type_Book, columns),
mScroll (scroll), mSkill (skill)
{}
QVariant CSMWorld::BookRefIdAdapter::getData (const RefIdColumn *column,
const RefIdData& data, int index) const
{
const Record<ESM::Book>& record = static_cast<const Record<ESM::Book>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Book)));
if (column==mScroll)
return record.get().mData.mIsScroll!=0;
if (column==mSkill)
return record.get().mData.mSkillID;
return EnchantableRefIdAdapter<ESM::Book>::getData (column, data, index);
}
void CSMWorld::BookRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const
{
Record<ESM::Book>& record = static_cast<Record<ESM::Book>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Book)));
if (column==mScroll)
record.get().mData.mIsScroll = value.toInt();
else if (column==mSkill)
record.get().mData.mSkillID = value.toInt();
else
EnchantableRefIdAdapter<ESM::Book>::setData (column, data, index, value);
}
CSMWorld::ClothingRefIdAdapter::ClothingRefIdAdapter (const EnchantableColumns& columns,
const RefIdColumn *type)
: EnchantableRefIdAdapter<ESM::Clothing> (UniversalId::Type_Clothing, columns), mType (type)
{}
QVariant CSMWorld::ClothingRefIdAdapter::getData (const RefIdColumn *column,
const RefIdData& data, int index) const
{
const Record<ESM::Clothing>& record = static_cast<const Record<ESM::Clothing>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Clothing)));
if (column==mType)
return record.get().mData.mType;
return EnchantableRefIdAdapter<ESM::Clothing>::getData (column, data, index);
}
void CSMWorld::ClothingRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const
{
Record<ESM::Clothing>& record = static_cast<Record<ESM::Clothing>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Clothing)));
if (column==mType)
record.get().mData.mType = value.toInt();
else
EnchantableRefIdAdapter<ESM::Clothing>::setData (column, data, index, value);
}
CSMWorld::ContainerRefIdAdapter::ContainerRefIdAdapter (const NameColumns& columns,
const RefIdColumn *weight, const RefIdColumn *organic, const RefIdColumn *respawn)
: NameRefIdAdapter<ESM::Container> (UniversalId::Type_Container, columns), mWeight (weight),
mOrganic (organic), mRespawn (respawn)
{}
QVariant CSMWorld::ContainerRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data,
int index) const
{
const Record<ESM::Container>& record = static_cast<const Record<ESM::Container>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Container)));
if (column==mWeight)
return record.get().mWeight;
if (column==mOrganic)
return (record.get().mFlags & ESM::Container::Organic)!=0;
if (column==mRespawn)
return (record.get().mFlags & ESM::Container::Respawn)!=0;
return NameRefIdAdapter<ESM::Container>::getData (column, data, index);
}
void CSMWorld::ContainerRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const
{
Record<ESM::Container>& record = static_cast<Record<ESM::Container>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Container)));
if (column==mWeight)
record.get().mWeight = value.toFloat();
else if (column==mOrganic)
{
if (value.toInt())
record.get().mFlags |= ESM::Container::Organic;
else
record.get().mFlags &= ~ESM::Container::Organic;
}
else if (column==mRespawn)
{
if (value.toInt())
record.get().mFlags |= ESM::Container::Respawn;
else
record.get().mFlags &= ~ESM::Container::Respawn;
}
else
NameRefIdAdapter<ESM::Container>::setData (column, data, index, value);
}
CSMWorld::CreatureColumns::CreatureColumns (const ActorColumns& actorColumns)
: ActorColumns (actorColumns)
{}
CSMWorld::CreatureRefIdAdapter::CreatureRefIdAdapter (const CreatureColumns& columns)
: ActorRefIdAdapter<ESM::Creature> (UniversalId::Type_Creature, columns), mColumns (columns)
{}
QVariant CSMWorld::CreatureRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data,
int index) const
{
const Record<ESM::Creature>& record = static_cast<const Record<ESM::Creature>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));
if (column==mColumns.mType)
return record.get().mData.mType;
if (column==mColumns.mSoul)
return record.get().mData.mSoul;
if (column==mColumns.mScale)
return record.get().mScale;
if (column==mColumns.mOriginal)
return QString::fromUtf8 (record.get().mOriginal.c_str());
std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
mColumns.mFlags.find (column);
if (iter!=mColumns.mFlags.end())
return (record.get().mFlags & iter->second)!=0;
return ActorRefIdAdapter<ESM::Creature>::getData (column, data, index);
}
void CSMWorld::CreatureRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const
{
Record<ESM::Creature>& record = static_cast<Record<ESM::Creature>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));
if (column==mColumns.mType)
record.get().mData.mType = value.toInt();
else if (column==mColumns.mSoul)
record.get().mData.mSoul = value.toInt();
else if (column==mColumns.mScale)
record.get().mScale = value.toFloat();
else if (column==mColumns.mOriginal)
record.get().mOriginal = value.toString().toUtf8().constData();
else
{
std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
mColumns.mFlags.find (column);
if (iter!=mColumns.mFlags.end())
{
if (value.toInt()!=0)
record.get().mFlags |= iter->second;
else
record.get().mFlags &= ~iter->second;
}
else
ActorRefIdAdapter<ESM::Creature>::setData (column, data, index, value);
}
}
CSMWorld::DoorRefIdAdapter::DoorRefIdAdapter (const NameColumns& columns,
const RefIdColumn *openSound, const RefIdColumn *closeSound)
: NameRefIdAdapter<ESM::Door> (UniversalId::Type_Door, columns), mOpenSound (openSound),
mCloseSound (closeSound)
{}
QVariant CSMWorld::DoorRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data,
int index) const
{
const Record<ESM::Door>& record = static_cast<const Record<ESM::Door>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Door)));
if (column==mOpenSound)
return QString::fromUtf8 (record.get().mOpenSound.c_str());
if (column==mCloseSound)
return QString::fromUtf8 (record.get().mCloseSound.c_str());
return NameRefIdAdapter<ESM::Door>::getData (column, data, index);
}
void CSMWorld::DoorRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const
{
Record<ESM::Door>& record = static_cast<Record<ESM::Door>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Door)));
if (column==mOpenSound)
record.get().mOpenSound = value.toString().toUtf8().constData();
else if (column==mCloseSound)
record.get().mCloseSound = value.toString().toUtf8().constData();
else
NameRefIdAdapter<ESM::Door>::setData (column, data, index, value);
}
CSMWorld::LightColumns::LightColumns (const InventoryColumns& columns)
: InventoryColumns (columns) {}
CSMWorld::LightRefIdAdapter::LightRefIdAdapter (const LightColumns& columns)
: InventoryRefIdAdapter<ESM::Light> (UniversalId::Type_Light, columns), mColumns (columns)
{}
QVariant CSMWorld::LightRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data,
int index) const
{
const Record<ESM::Light>& record = static_cast<const Record<ESM::Light>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Light)));
if (column==mColumns.mTime)
return record.get().mData.mTime;
if (column==mColumns.mRadius)
return record.get().mData.mRadius;
if (column==mColumns.mColor)
return record.get().mData.mColor;
if (column==mColumns.mSound)
return QString::fromUtf8 (record.get().mSound.c_str());
std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
mColumns.mFlags.find (column);
if (iter!=mColumns.mFlags.end())
return (record.get().mData.mFlags & iter->second)!=0;
return InventoryRefIdAdapter<ESM::Light>::getData (column, data, index);
}
void CSMWorld::LightRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const
{
Record<ESM::Light>& record = static_cast<Record<ESM::Light>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Light)));
if (column==mColumns.mTime)
record.get().mData.mTime = value.toInt();
else if (column==mColumns.mRadius)
record.get().mData.mRadius = value.toInt();
else if (column==mColumns.mColor)
record.get().mData.mColor = value.toInt();
else if (column==mColumns.mSound)
record.get().mSound = value.toString().toUtf8().constData();
else
{
std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
mColumns.mFlags.find (column);
if (iter!=mColumns.mFlags.end())
{
if (value.toInt()!=0)
record.get().mData.mFlags |= iter->second;
else
record.get().mData.mFlags &= ~iter->second;
}
else
InventoryRefIdAdapter<ESM::Light>::setData (column, data, index, value);
}
}
CSMWorld::MiscRefIdAdapter::MiscRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *key)
: InventoryRefIdAdapter<ESM::Miscellaneous> (UniversalId::Type_Miscellaneous, columns), mKey (key)
{}
QVariant CSMWorld::MiscRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data,
int index) const
{
const Record<ESM::Miscellaneous>& record = static_cast<const Record<ESM::Miscellaneous>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Miscellaneous)));
if (column==mKey)
return record.get().mData.mIsKey!=0;
return InventoryRefIdAdapter<ESM::Miscellaneous>::getData (column, data, index);
}
void CSMWorld::MiscRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const
{
Record<ESM::Miscellaneous>& record = static_cast<Record<ESM::Miscellaneous>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Miscellaneous)));
if (column==mKey)
record.get().mData.mIsKey = value.toInt();
else
InventoryRefIdAdapter<ESM::Miscellaneous>::setData (column, data, index, value);
}
CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns) : ActorColumns (actorColumns) {}
CSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns)
: ActorRefIdAdapter<ESM::NPC> (UniversalId::Type_Npc, columns), mColumns (columns)
{}
QVariant CSMWorld::NpcRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index)
const
{
const Record<ESM::NPC>& record = static_cast<const Record<ESM::NPC>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));
if (column==mColumns.mRace)
return QString::fromUtf8 (record.get().mRace.c_str());
if (column==mColumns.mClass)
return QString::fromUtf8 (record.get().mClass.c_str());
if (column==mColumns.mFaction)
return QString::fromUtf8 (record.get().mFaction.c_str());
if (column==mColumns.mHair)
return QString::fromUtf8 (record.get().mHair.c_str());
if (column==mColumns.mHead)
return QString::fromUtf8 (record.get().mHead.c_str());
std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
mColumns.mFlags.find (column);
if (iter!=mColumns.mFlags.end())
return (record.get().mFlags & iter->second)!=0;
return ActorRefIdAdapter<ESM::NPC>::getData (column, data, index);
}
void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const
{
Record<ESM::NPC>& record = static_cast<Record<ESM::NPC>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));
if (column==mColumns.mRace)
record.get().mRace = value.toString().toUtf8().constData();
else if (column==mColumns.mClass)
record.get().mClass = value.toString().toUtf8().constData();
else if (column==mColumns.mFaction)
record.get().mFaction = value.toString().toUtf8().constData();
else if (column==mColumns.mHair)
record.get().mHair = value.toString().toUtf8().constData();
else if (column==mColumns.mHead)
record.get().mHead = value.toString().toUtf8().constData();
else
{
std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
mColumns.mFlags.find (column);
if (iter!=mColumns.mFlags.end())
{
if (value.toInt()!=0)
record.get().mFlags |= iter->second;
else
record.get().mFlags &= ~iter->second;
}
else
ActorRefIdAdapter<ESM::NPC>::setData (column, data, index, value);
}
}
CSMWorld::WeaponColumns::WeaponColumns (const EnchantableColumns& columns)
: EnchantableColumns (columns) {}
CSMWorld::WeaponRefIdAdapter::WeaponRefIdAdapter (const WeaponColumns& columns)
: EnchantableRefIdAdapter<ESM::Weapon> (UniversalId::Type_Weapon, columns), mColumns (columns)
{}
QVariant CSMWorld::WeaponRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data,
int index) const
{
const Record<ESM::Weapon>& record = static_cast<const Record<ESM::Weapon>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Weapon)));
if (column==mColumns.mType)
return record.get().mData.mType;
if (column==mColumns.mHealth)
return record.get().mData.mHealth;
if (column==mColumns.mSpeed)
return record.get().mData.mSpeed;
if (column==mColumns.mReach)
return record.get().mData.mReach;
for (int i=0; i<2; ++i)
{
if (column==mColumns.mChop[i])
return record.get().mData.mChop[i];
if (column==mColumns.mSlash[i])
return record.get().mData.mSlash[i];
if (column==mColumns.mThrust[i])
return record.get().mData.mThrust[i];
}
std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
mColumns.mFlags.find (column);
if (iter!=mColumns.mFlags.end())
return (record.get().mData.mFlags & iter->second)!=0;
return EnchantableRefIdAdapter<ESM::Weapon>::getData (column, data, index);
}
void CSMWorld::WeaponRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const
{
Record<ESM::Weapon>& record = static_cast<Record<ESM::Weapon>&> (
data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Weapon)));
if (column==mColumns.mType)
record.get().mData.mType = value.toInt();
else if (column==mColumns.mHealth)
record.get().mData.mHealth = value.toInt();
else if (column==mColumns.mSpeed)
record.get().mData.mSpeed = value.toFloat();
else if (column==mColumns.mReach)
record.get().mData.mReach = value.toFloat();
else if (column==mColumns.mChop[0])
record.get().mData.mChop[0] = value.toInt();
else if (column==mColumns.mChop[1])
record.get().mData.mChop[1] = value.toInt();
else if (column==mColumns.mSlash[0])
record.get().mData.mSlash[0] = value.toInt();
else if (column==mColumns.mSlash[1])
record.get().mData.mSlash[1] = value.toInt();
else if (column==mColumns.mThrust[0])
record.get().mData.mThrust[0] = value.toInt();
else if (column==mColumns.mThrust[1])
record.get().mData.mThrust[1] = value.toInt();
else
{
std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
mColumns.mFlags.find (column);
if (iter!=mColumns.mFlags.end())
{
if (value.toInt()!=0)
record.get().mData.mFlags |= iter->second;
else
record.get().mData.mFlags &= ~iter->second;
}
else
EnchantableRefIdAdapter<ESM::Weapon>::setData (column, data, index, value);
}
}

View file

@ -0,0 +1,766 @@
#ifndef CSM_WOLRD_REFIDADAPTERIMP_H
#define CSM_WOLRD_REFIDADAPTERIMP_H
#include <map>
#include <QVariant>
#include <components/esm/loadalch.hpp>
#include <components/esm/loadappa.hpp>
#include "record.hpp"
#include "refiddata.hpp"
#include "universalid.hpp"
#include "refidadapter.hpp"
namespace CSMWorld
{
struct BaseColumns
{
const RefIdColumn *mId;
const RefIdColumn *mModified;
const RefIdColumn *mType;
};
/// \brief Base adapter for all refereceable record types
template<typename RecordT>
class BaseRefIdAdapter : public RefIdAdapter
{
UniversalId::Type mType;
BaseColumns mBase;
public:
BaseRefIdAdapter (UniversalId::Type type, const BaseColumns& base);
virtual std::string getId (const RecordBase& record) const;
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)
const;
virtual void setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const;
///< If the data type does not match an exception is thrown.
UniversalId::Type getType() const;
};
template<typename RecordT>
BaseRefIdAdapter<RecordT>::BaseRefIdAdapter (UniversalId::Type type, const BaseColumns& base)
: mType (type), mBase (base)
{}
template<typename RecordT>
std::string BaseRefIdAdapter<RecordT>::getId (const RecordBase& record) const
{
return dynamic_cast<const Record<RecordT>&> (record).get().mId;
}
template<typename RecordT>
QVariant BaseRefIdAdapter<RecordT>::getData (const RefIdColumn *column, const RefIdData& data,
int index) const
{
const Record<RecordT>& record = static_cast<const Record<RecordT>&> (
data.getRecord (RefIdData::LocalIndex (index, mType)));
if (column==mBase.mId)
return QString::fromUtf8 (record.get().mId.c_str());
if (column==mBase.mModified)
{
if (record.mState==Record<RecordT>::State_Erased)
return static_cast<int> (Record<RecordT>::State_Deleted);
return static_cast<int> (record.mState);
}
if (column==mBase.mType)
return static_cast<int> (mType);
return QVariant();
}
template<typename RecordT>
void BaseRefIdAdapter<RecordT>::setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const
{
Record<RecordT>& record = static_cast<Record<RecordT>&> (
data.getRecord (RefIdData::LocalIndex (index, mType)));
if (column==mBase.mModified)
record.mState = static_cast<RecordBase::State> (value.toInt());
}
template<typename RecordT>
UniversalId::Type BaseRefIdAdapter<RecordT>::getType() const
{
return mType;
}
struct ModelColumns : public BaseColumns
{
const RefIdColumn *mModel;
ModelColumns (const BaseColumns& base) : BaseColumns (base) {}
};
/// \brief Adapter for IDs with models (all but levelled lists)
template<typename RecordT>
class ModelRefIdAdapter : public BaseRefIdAdapter<RecordT>
{
ModelColumns mModel;
public:
ModelRefIdAdapter (UniversalId::Type type, const ModelColumns& columns);
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)
const;
virtual void setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const;
///< If the data type does not match an exception is thrown.
};
template<typename RecordT>
ModelRefIdAdapter<RecordT>::ModelRefIdAdapter (UniversalId::Type type, const ModelColumns& columns)
: BaseRefIdAdapter<RecordT> (type, columns), mModel (columns)
{}
template<typename RecordT>
QVariant ModelRefIdAdapter<RecordT>::getData (const RefIdColumn *column, const RefIdData& data,
int index) const
{
const Record<RecordT>& record = static_cast<const Record<RecordT>&> (
data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));
if (column==mModel.mModel)
return QString::fromUtf8 (record.get().mModel.c_str());
return BaseRefIdAdapter<RecordT>::getData (column, data, index);
}
template<typename RecordT>
void ModelRefIdAdapter<RecordT>::setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const
{
Record<RecordT>& record = static_cast<Record<RecordT>&> (
data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));
if (column==mModel.mModel)
record.get().mModel = value.toString().toUtf8().constData();
else
BaseRefIdAdapter<RecordT>::setData (column, data, index, value);
}
struct NameColumns : public ModelColumns
{
const RefIdColumn *mName;
const RefIdColumn *mScript;
NameColumns (const ModelColumns& base) : ModelColumns (base) {}
};
/// \brief Adapter for IDs with names (all but levelled lists and statics)
template<typename RecordT>
class NameRefIdAdapter : public ModelRefIdAdapter<RecordT>
{
NameColumns mName;
public:
NameRefIdAdapter (UniversalId::Type type, const NameColumns& columns);
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)
const;
virtual void setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const;
///< If the data type does not match an exception is thrown.
};
template<typename RecordT>
NameRefIdAdapter<RecordT>::NameRefIdAdapter (UniversalId::Type type, const NameColumns& columns)
: ModelRefIdAdapter<RecordT> (type, columns), mName (columns)
{}
template<typename RecordT>
QVariant NameRefIdAdapter<RecordT>::getData (const RefIdColumn *column, const RefIdData& data,
int index) const
{
const Record<RecordT>& record = static_cast<const Record<RecordT>&> (
data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));
if (column==mName.mName)
return QString::fromUtf8 (record.get().mName.c_str());
if (column==mName.mScript)
return QString::fromUtf8 (record.get().mScript.c_str());
return ModelRefIdAdapter<RecordT>::getData (column, data, index);
}
template<typename RecordT>
void NameRefIdAdapter<RecordT>::setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const
{
Record<RecordT>& record = static_cast<Record<RecordT>&> (
data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));
if (column==mName.mName)
record.get().mName = value.toString().toUtf8().constData();
else if (column==mName.mScript)
record.get().mScript = value.toString().toUtf8().constData();
else
ModelRefIdAdapter<RecordT>::setData (column, data, index, value);
}
struct InventoryColumns : public NameColumns
{
const RefIdColumn *mIcon;
const RefIdColumn *mWeight;
const RefIdColumn *mValue;
InventoryColumns (const NameColumns& base) : NameColumns (base) {}
};
/// \brief Adapter for IDs that can go into an inventory
template<typename RecordT>
class InventoryRefIdAdapter : public NameRefIdAdapter<RecordT>
{
InventoryColumns mInventory;
public:
InventoryRefIdAdapter (UniversalId::Type type, const InventoryColumns& columns);
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)
const;
virtual void setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const;
///< If the data type does not match an exception is thrown.
};
template<typename RecordT>
InventoryRefIdAdapter<RecordT>::InventoryRefIdAdapter (UniversalId::Type type,
const InventoryColumns& columns)
: NameRefIdAdapter<RecordT> (type, columns), mInventory (columns)
{}
template<typename RecordT>
QVariant InventoryRefIdAdapter<RecordT>::getData (const RefIdColumn *column, const RefIdData& data,
int index) const
{
const Record<RecordT>& record = static_cast<const Record<RecordT>&> (
data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));
if (column==mInventory.mIcon)
return QString::fromUtf8 (record.get().mIcon.c_str());
if (column==mInventory.mWeight)
return record.get().mData.mWeight;
if (column==mInventory.mValue)
return record.get().mData.mValue;
return NameRefIdAdapter<RecordT>::getData (column, data, index);
}
template<typename RecordT>
void InventoryRefIdAdapter<RecordT>::setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const
{
Record<RecordT>& record = static_cast<Record<RecordT>&> (
data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));
if (column==mInventory.mIcon)
record.get().mIcon = value.toString().toUtf8().constData();
else if (column==mInventory.mWeight)
record.get().mData.mWeight = value.toFloat();
else if (column==mInventory.mValue)
record.get().mData.mValue = value.toInt();
else
NameRefIdAdapter<RecordT>::setData (column, data, index, value);
}
class PotionRefIdAdapter : public InventoryRefIdAdapter<ESM::Potion>
{
const RefIdColumn *mAutoCalc;
public:
PotionRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *autoCalc);
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)
const;
virtual void setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const;
///< If the data type does not match an exception is thrown.
};
struct EnchantableColumns : public InventoryColumns
{
const RefIdColumn *mEnchantment;
const RefIdColumn *mEnchantmentPoints;
EnchantableColumns (const InventoryColumns& base) : InventoryColumns (base) {}
};
/// \brief Adapter for enchantable IDs
template<typename RecordT>
class EnchantableRefIdAdapter : public InventoryRefIdAdapter<RecordT>
{
EnchantableColumns mEnchantable;
public:
EnchantableRefIdAdapter (UniversalId::Type type, const EnchantableColumns& columns);
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)
const;
virtual void setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const;
///< If the data type does not match an exception is thrown.
};
template<typename RecordT>
EnchantableRefIdAdapter<RecordT>::EnchantableRefIdAdapter (UniversalId::Type type,
const EnchantableColumns& columns)
: InventoryRefIdAdapter<RecordT> (type, columns), mEnchantable (columns)
{}
template<typename RecordT>
QVariant EnchantableRefIdAdapter<RecordT>::getData (const RefIdColumn *column, const RefIdData& data,
int index) const
{
const Record<RecordT>& record = static_cast<const Record<RecordT>&> (
data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));
if (column==mEnchantable.mEnchantment)
return QString::fromUtf8 (record.get().mEnchant.c_str());
if (column==mEnchantable.mEnchantmentPoints)
return static_cast<int> (record.get().mData.mEnchant);
return InventoryRefIdAdapter<RecordT>::getData (column, data, index);
}
template<typename RecordT>
void EnchantableRefIdAdapter<RecordT>::setData (const RefIdColumn *column, RefIdData& data,
int index, const QVariant& value) const
{
Record<RecordT>& record = static_cast<Record<RecordT>&> (
data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));
if (column==mEnchantable.mEnchantment)
record.get().mEnchant = value.toString().toUtf8().constData();
else if (column==mEnchantable.mEnchantmentPoints)
record.get().mData.mEnchant = value.toInt();
else
InventoryRefIdAdapter<RecordT>::setData (column, data, index, value);
}
struct ToolColumns : public InventoryColumns
{
const RefIdColumn *mQuality;
const RefIdColumn *mUses;
ToolColumns (const InventoryColumns& base) : InventoryColumns (base) {}
};
/// \brief Adapter for tools with limited uses IDs (lockpick, repair, probes)
template<typename RecordT>
class ToolRefIdAdapter : public InventoryRefIdAdapter<RecordT>
{
ToolColumns mTools;
public:
ToolRefIdAdapter (UniversalId::Type type, const ToolColumns& columns);
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)
const;
virtual void setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const;
///< If the data type does not match an exception is thrown.
};
template<typename RecordT>
ToolRefIdAdapter<RecordT>::ToolRefIdAdapter (UniversalId::Type type, const ToolColumns& columns)
: InventoryRefIdAdapter<RecordT> (type, columns), mTools (columns)
{}
template<typename RecordT>
QVariant ToolRefIdAdapter<RecordT>::getData (const RefIdColumn *column, const RefIdData& data,
int index) const
{
const Record<RecordT>& record = static_cast<const Record<RecordT>&> (
data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));
if (column==mTools.mQuality)
return record.get().mData.mQuality;
if (column==mTools.mUses)
return record.get().mData.mUses;
return InventoryRefIdAdapter<RecordT>::getData (column, data, index);
}
template<typename RecordT>
void ToolRefIdAdapter<RecordT>::setData (const RefIdColumn *column, RefIdData& data,
int index, const QVariant& value) const
{
Record<RecordT>& record = static_cast<Record<RecordT>&> (
data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));
if (column==mTools.mQuality)
record.get().mData.mQuality = value.toFloat();
else if (column==mTools.mUses)
record.get().mData.mUses = value.toInt();
else
InventoryRefIdAdapter<RecordT>::setData (column, data, index, value);
}
struct ActorColumns : public NameColumns
{
const RefIdColumn *mHasAi;
const RefIdColumn *mHello;
const RefIdColumn *mFlee;
const RefIdColumn *mFight;
const RefIdColumn *mAlarm;
std::map<const RefIdColumn *, unsigned int> mServices;
ActorColumns (const NameColumns& base) : NameColumns (base) {}
};
/// \brief Adapter for actor IDs (handles common AI functionality)
template<typename RecordT>
class ActorRefIdAdapter : public NameRefIdAdapter<RecordT>
{
ActorColumns mActors;
public:
ActorRefIdAdapter (UniversalId::Type type, const ActorColumns& columns);
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)
const;
virtual void setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const;
///< If the data type does not match an exception is thrown.
};
template<typename RecordT>
ActorRefIdAdapter<RecordT>::ActorRefIdAdapter (UniversalId::Type type,
const ActorColumns& columns)
: NameRefIdAdapter<RecordT> (type, columns), mActors (columns)
{}
template<typename RecordT>
QVariant ActorRefIdAdapter<RecordT>::getData (const RefIdColumn *column, const RefIdData& data,
int index) const
{
const Record<RecordT>& record = static_cast<const Record<RecordT>&> (
data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));
if (column==mActors.mHasAi)
return record.get().mHasAI!=0;
if (column==mActors.mHello)
return record.get().mAiData.mHello;
if (column==mActors.mFlee)
return record.get().mAiData.mFlee;
if (column==mActors.mFight)
return record.get().mAiData.mFight;
if (column==mActors.mAlarm)
return record.get().mAiData.mAlarm;
std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
mActors.mServices.find (column);
if (iter!=mActors.mServices.end())
return (record.get().mAiData.mServices & iter->second)!=0;
return NameRefIdAdapter<RecordT>::getData (column, data, index);
}
template<typename RecordT>
void ActorRefIdAdapter<RecordT>::setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const
{
Record<RecordT>& record = static_cast<Record<RecordT>&> (
data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter<RecordT>::getType())));
if (column==mActors.mHasAi)
record.get().mHasAI = value.toInt();
else if (column==mActors.mHello)
record.get().mAiData.mHello = value.toInt();
else if (column==mActors.mFlee)
record.get().mAiData.mFlee = value.toInt();
else if (column==mActors.mFight)
record.get().mAiData.mFight = value.toInt();
else if (column==mActors.mAlarm)
record.get().mAiData.mAlarm = value.toInt();
else
{
typename std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
mActors.mServices.find (column);
if (iter!=mActors.mServices.end())
{
if (value.toInt()!=0)
record.get().mAiData.mServices |= iter->second;
else
record.get().mAiData.mServices &= ~iter->second;
}
else
NameRefIdAdapter<RecordT>::setData (column, data, index, value);
}
}
class ApparatusRefIdAdapter : public InventoryRefIdAdapter<ESM::Apparatus>
{
const RefIdColumn *mType;
const RefIdColumn *mQuality;
public:
ApparatusRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *type,
const RefIdColumn *quality);
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)
const;
virtual void setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const;
///< If the data type does not match an exception is thrown.
};
class ArmorRefIdAdapter : public EnchantableRefIdAdapter<ESM::Armor>
{
const RefIdColumn *mType;
const RefIdColumn *mHealth;
const RefIdColumn *mArmor;
public:
ArmorRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *type,
const RefIdColumn *health, const RefIdColumn *armor);
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)
const;
virtual void setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const;
///< If the data type does not match an exception is thrown.
};
class BookRefIdAdapter : public EnchantableRefIdAdapter<ESM::Book>
{
const RefIdColumn *mScroll;
const RefIdColumn *mSkill;
public:
BookRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *scroll,
const RefIdColumn *skill);
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)
const;
virtual void setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const;
///< If the data type does not match an exception is thrown.
};
class ClothingRefIdAdapter : public EnchantableRefIdAdapter<ESM::Clothing>
{
const RefIdColumn *mType;
public:
ClothingRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *type);
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)
const;
virtual void setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const;
///< If the data type does not match an exception is thrown.
};
class ContainerRefIdAdapter : public NameRefIdAdapter<ESM::Container>
{
const RefIdColumn *mWeight;
const RefIdColumn *mOrganic;
const RefIdColumn *mRespawn;
public:
ContainerRefIdAdapter (const NameColumns& columns, const RefIdColumn *weight,
const RefIdColumn *organic, const RefIdColumn *respawn);
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)
const;
virtual void setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const;
///< If the data type does not match an exception is thrown.
};
struct CreatureColumns : public ActorColumns
{
std::map<const RefIdColumn *, unsigned int> mFlags;
const RefIdColumn *mType;
const RefIdColumn *mSoul;
const RefIdColumn *mScale;
const RefIdColumn *mOriginal;
CreatureColumns (const ActorColumns& actorColumns);
};
class CreatureRefIdAdapter : public ActorRefIdAdapter<ESM::Creature>
{
CreatureColumns mColumns;
public:
CreatureRefIdAdapter (const CreatureColumns& columns);
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)
const;
virtual void setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const;
///< If the data type does not match an exception is thrown.
};
class DoorRefIdAdapter : public NameRefIdAdapter<ESM::Door>
{
const RefIdColumn *mOpenSound;
const RefIdColumn *mCloseSound;
public:
DoorRefIdAdapter (const NameColumns& columns, const RefIdColumn *openSound,
const RefIdColumn *closeSound);
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)
const;
virtual void setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const;
///< If the data type does not match an exception is thrown.
};
struct LightColumns : public InventoryColumns
{
const RefIdColumn *mTime;
const RefIdColumn *mRadius;
const RefIdColumn *mColor;
const RefIdColumn *mSound;
std::map<const RefIdColumn *, unsigned int> mFlags;
LightColumns (const InventoryColumns& columns);
};
class LightRefIdAdapter : public InventoryRefIdAdapter<ESM::Light>
{
LightColumns mColumns;
public:
LightRefIdAdapter (const LightColumns& columns);
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)
const;
virtual void setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const;
///< If the data type does not match an exception is thrown.
};
class MiscRefIdAdapter : public InventoryRefIdAdapter<ESM::Miscellaneous>
{
const RefIdColumn *mKey;
public:
MiscRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *key);
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)
const;
virtual void setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const;
///< If the data type does not match an exception is thrown.
};
struct NpcColumns : public ActorColumns
{
std::map<const RefIdColumn *, unsigned int> mFlags;
const RefIdColumn *mRace;
const RefIdColumn *mClass;
const RefIdColumn *mFaction;
const RefIdColumn *mHair;
const RefIdColumn *mHead;
NpcColumns (const ActorColumns& actorColumns);
};
class NpcRefIdAdapter : public ActorRefIdAdapter<ESM::NPC>
{
NpcColumns mColumns;
public:
NpcRefIdAdapter (const NpcColumns& columns);
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)
const;
virtual void setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const;
///< If the data type does not match an exception is thrown.
};
struct WeaponColumns : public EnchantableColumns
{
const RefIdColumn *mType;
const RefIdColumn *mHealth;
const RefIdColumn *mSpeed;
const RefIdColumn *mReach;
const RefIdColumn *mChop[2];
const RefIdColumn *mSlash[2];
const RefIdColumn *mThrust[2];
std::map<const RefIdColumn *, unsigned int> mFlags;
WeaponColumns (const EnchantableColumns& columns);
};
class WeaponRefIdAdapter : public EnchantableRefIdAdapter<ESM::Weapon>
{
WeaponColumns mColumns;
public:
WeaponRefIdAdapter (const WeaponColumns& columns);
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)
const;
virtual void setData (const RefIdColumn *column, RefIdData& data, int index,
const QVariant& value) const;
///< If the data type does not match an exception is thrown.
};
}
#endif

View file

@ -0,0 +1,536 @@
#include "refidcollection.hpp"
#include <stdexcept>
#include <components/esm/esmreader.hpp>
#include "refidadapter.hpp"
#include "refidadapterimp.hpp"
#include "columns.hpp"
CSMWorld::RefIdColumn::RefIdColumn (int columnId, Display displayType, int flag,
bool editable, bool userEditable)
: ColumnBase (columnId, displayType, flag), mEditable (editable), mUserEditable (userEditable)
{}
bool CSMWorld::RefIdColumn::isEditable() const
{
return mEditable;
}
bool CSMWorld::RefIdColumn::isUserEditable() const
{
return mUserEditable;
}
const CSMWorld::RefIdAdapter& CSMWorld::RefIdCollection::findAdaptor (UniversalId::Type type) const
{
std::map<UniversalId::Type, RefIdAdapter *>::const_iterator iter = mAdapters.find (type);
if (iter==mAdapters.end())
throw std::logic_error ("unsupported type in RefIdCollection");
return *iter->second;
}
CSMWorld::RefIdCollection::RefIdCollection()
{
BaseColumns baseColumns;
mColumns.push_back (RefIdColumn (Columns::ColumnId_Id, ColumnBase::Display_String,
ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false));
baseColumns.mId = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Modification, ColumnBase::Display_RecordState,
ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false));
baseColumns.mModified = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_RecordType, ColumnBase::Display_RefRecordType,
ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false));
baseColumns.mType = &mColumns.back();
ModelColumns modelColumns (baseColumns);
mColumns.push_back (RefIdColumn (Columns::ColumnId_Model, ColumnBase::Display_String));
modelColumns.mModel = &mColumns.back();
NameColumns nameColumns (modelColumns);
mColumns.push_back (RefIdColumn (Columns::ColumnId_Name, ColumnBase::Display_String));
nameColumns.mName = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Script, ColumnBase::Display_String));
nameColumns.mScript = &mColumns.back();
InventoryColumns inventoryColumns (nameColumns);
mColumns.push_back (RefIdColumn (Columns::ColumnId_Icon, ColumnBase::Display_String));
inventoryColumns.mIcon = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Weight, ColumnBase::Display_Float));
inventoryColumns.mWeight = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_CoinValue, ColumnBase::Display_Integer));
inventoryColumns.mValue = &mColumns.back();
EnchantableColumns enchantableColumns (inventoryColumns);
mColumns.push_back (RefIdColumn (Columns::ColumnId_Enchantment, ColumnBase::Display_String));
enchantableColumns.mEnchantment = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_EnchantmentPoints, ColumnBase::Display_Integer));
enchantableColumns.mEnchantmentPoints = &mColumns.back();
ToolColumns toolsColumns (inventoryColumns);
mColumns.push_back (RefIdColumn (Columns::ColumnId_Quality, ColumnBase::Display_Float));
toolsColumns.mQuality = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Charges, ColumnBase::Display_Integer));
toolsColumns.mUses = &mColumns.back();
ActorColumns actorsColumns (nameColumns);
mColumns.push_back (RefIdColumn (Columns::ColumnId_Ai, ColumnBase::Display_Boolean));
actorsColumns.mHasAi = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_AiHello, ColumnBase::Display_Integer));
actorsColumns.mHello = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_AiFlee, ColumnBase::Display_Integer));
actorsColumns.mFlee = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_AiFight, ColumnBase::Display_Integer));
actorsColumns.mFight = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_AiAlarm, ColumnBase::Display_Integer));
actorsColumns.mAlarm = &mColumns.back();
static const struct
{
int mName;
unsigned int mFlag;
} sServiceTable[] =
{
{ Columns::ColumnId_BuysWeapons, ESM::NPC::Weapon},
{ Columns::ColumnId_BuysArmor, ESM::NPC::Armor},
{ Columns::ColumnId_BuysClothing, ESM::NPC::Clothing},
{ Columns::ColumnId_BuysBooks, ESM::NPC::Books},
{ Columns::ColumnId_BuysIngredients, ESM::NPC::Ingredients},
{ Columns::ColumnId_BuysLockpicks, ESM::NPC::Picks},
{ Columns::ColumnId_BuysProbes, ESM::NPC::Probes},
{ Columns::ColumnId_BuysLights, ESM::NPC::Lights},
{ Columns::ColumnId_BuysApparati, ESM::NPC::Apparatus},
{ Columns::ColumnId_BuysRepairItems, ESM::NPC::RepairItem},
{ Columns::ColumnId_BuysMiscItems, ESM::NPC::Misc},
{ Columns::ColumnId_BuysPotions, ESM::NPC::Potions},
{ Columns::ColumnId_BuysMagicItems, ESM::NPC::MagicItems},
{ Columns::ColumnId_SellsSpells, ESM::NPC::Spells},
{ Columns::ColumnId_Trainer, ESM::NPC::Training},
{ Columns::ColumnId_Spellmaking, ESM::NPC::Spellmaking},
{ Columns::ColumnId_EnchantingService, ESM::NPC::Enchanting},
{ Columns::ColumnId_RepairService, ESM::NPC::Repair},
{ -1, 0 }
};
for (int i=0; sServiceTable[i].mName!=-1; ++i)
{
mColumns.push_back (RefIdColumn (sServiceTable[i].mName, ColumnBase::Display_Boolean));
actorsColumns.mServices.insert (std::make_pair (&mColumns.back(), sServiceTable[i].mFlag));
}
mColumns.push_back (RefIdColumn (Columns::ColumnId_AutoCalc, ColumnBase::Display_Boolean));
const RefIdColumn *autoCalc = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_ApparatusType,
ColumnBase::Display_ApparatusType));
const RefIdColumn *apparatusType = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_ArmorType, ColumnBase::Display_ArmorType));
const RefIdColumn *armorType = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Health, ColumnBase::Display_Integer));
const RefIdColumn *health = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_ArmorValue, ColumnBase::Display_Integer));
const RefIdColumn *armor = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Scroll, ColumnBase::Display_Boolean));
const RefIdColumn *scroll = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Attribute, ColumnBase::Display_Attribute));
const RefIdColumn *attribute = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_ClothingType, ColumnBase::Display_ClothingType));
const RefIdColumn *clothingType = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_WeightCapacity, ColumnBase::Display_Float));
const RefIdColumn *weightCapacity = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_OrganicContainer, ColumnBase::Display_Boolean));
const RefIdColumn *organic = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Respawn, ColumnBase::Display_Boolean));
const RefIdColumn *respawn = &mColumns.back();
CreatureColumns creatureColumns (actorsColumns);
mColumns.push_back (RefIdColumn (Columns::ColumnId_CreatureType, ColumnBase::Display_CreatureType));
creatureColumns.mType = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_SoulPoints, ColumnBase::Display_Integer));
creatureColumns.mSoul = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Scale, ColumnBase::Display_Float));
creatureColumns.mScale = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_OriginalCreature, ColumnBase::Display_String));
creatureColumns.mOriginal = &mColumns.back();
static const struct
{
int mName;
unsigned int mFlag;
} sCreatureFlagTable[] =
{
{ Columns::ColumnId_Biped, ESM::Creature::Biped },
{ Columns::ColumnId_HasWeapon, ESM::Creature::Weapon },
{ Columns::ColumnId_NoMovement, ESM::Creature::None },
{ Columns::ColumnId_Swims, ESM::Creature::Swims },
{ Columns::ColumnId_Flies, ESM::Creature::Flies },
{ Columns::ColumnId_Walks, ESM::Creature::Walks },
{ Columns::ColumnId_Essential, ESM::Creature::Essential },
{ Columns::ColumnId_SkeletonBlood, ESM::Creature::Skeleton },
{ Columns::ColumnId_MetalBlood, ESM::Creature::Metal },
{ -1, 0 }
};
// for re-use in NPC records
const RefIdColumn *essential = 0;
const RefIdColumn *skeletonBlood = 0;
const RefIdColumn *metalBlood = 0;
for (int i=0; sCreatureFlagTable[i].mName!=-1; ++i)
{
mColumns.push_back (RefIdColumn (sCreatureFlagTable[i].mName, ColumnBase::Display_Boolean));
creatureColumns.mFlags.insert (std::make_pair (&mColumns.back(), sCreatureFlagTable[i].mFlag));
switch (sCreatureFlagTable[i].mFlag)
{
case ESM::Creature::Essential: essential = &mColumns.back(); break;
case ESM::Creature::Skeleton: skeletonBlood = &mColumns.back(); break;
case ESM::Creature::Metal: metalBlood = &mColumns.back(); break;
}
}
creatureColumns.mFlags.insert (std::make_pair (respawn, ESM::Creature::Respawn));
mColumns.push_back (RefIdColumn (Columns::ColumnId_OpenSound, ColumnBase::Display_String));
const RefIdColumn *openSound = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_CloseSound, ColumnBase::Display_String));
const RefIdColumn *closeSound = &mColumns.back();
LightColumns lightColumns (inventoryColumns);
mColumns.push_back (RefIdColumn (Columns::ColumnId_Duration, ColumnBase::Display_Integer));
lightColumns.mTime = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Radius, ColumnBase::Display_Integer));
lightColumns.mRadius = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Colour, ColumnBase::Display_Integer));
lightColumns.mColor = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Sound, ColumnBase::Display_String));
lightColumns.mSound = &mColumns.back();
static const struct
{
int mName;
unsigned int mFlag;
} sLightFlagTable[] =
{
{ Columns::ColumnId_Dynamic, ESM::Light::Dynamic },
{ Columns::ColumnId_Portable, ESM::Light::Carry },
{ Columns::ColumnId_NegativeLight, ESM::Light::Negative },
{ Columns::ColumnId_Flickering, ESM::Light::Flicker },
{ Columns::ColumnId_SlowFlickering, ESM::Light::Flicker },
{ Columns::ColumnId_Pulsing, ESM::Light::Pulse },
{ Columns::ColumnId_SlowPulsing, ESM::Light::PulseSlow },
{ Columns::ColumnId_Fire, ESM::Light::Fire },
{ Columns::ColumnId_OffByDefault, ESM::Light::OffDefault },
{ -1, 0 }
};
for (int i=0; sLightFlagTable[i].mName!=-1; ++i)
{
mColumns.push_back (RefIdColumn (sLightFlagTable[i].mName, ColumnBase::Display_Boolean));
lightColumns.mFlags.insert (std::make_pair (&mColumns.back(), sLightFlagTable[i].mFlag));
}
mColumns.push_back (RefIdColumn (Columns::ColumnId_IsKey, ColumnBase::Display_Boolean));
const RefIdColumn *key = &mColumns.back();
NpcColumns npcColumns (actorsColumns);
mColumns.push_back (RefIdColumn (Columns::ColumnId_Race, ColumnBase::Display_String));
npcColumns.mRace = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Class, ColumnBase::Display_String));
npcColumns.mClass = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Faction, ColumnBase::Display_String));
npcColumns.mFaction = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::Columnid_Hair, ColumnBase::Display_String));
npcColumns.mHair = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Head, ColumnBase::Display_String));
npcColumns.mHead = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Female, ColumnBase::Display_Boolean));
npcColumns.mFlags.insert (std::make_pair (&mColumns.back(), ESM::NPC::Female));
npcColumns.mFlags.insert (std::make_pair (essential, ESM::NPC::Essential));
npcColumns.mFlags.insert (std::make_pair (respawn, ESM::NPC::Respawn));
npcColumns.mFlags.insert (std::make_pair (autoCalc, ESM::NPC::Autocalc));
npcColumns.mFlags.insert (std::make_pair (skeletonBlood, ESM::NPC::Skeleton));
npcColumns.mFlags.insert (std::make_pair (metalBlood, ESM::NPC::Metal));
WeaponColumns weaponColumns (enchantableColumns);
mColumns.push_back (RefIdColumn (Columns::ColumnId_WeaponType, ColumnBase::Display_WeaponType));
weaponColumns.mType = &mColumns.back();
weaponColumns.mHealth = health;
mColumns.push_back (RefIdColumn (Columns::ColumnId_WeaponSpeed, ColumnBase::Display_Float));
weaponColumns.mSpeed = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_WeaponReach, ColumnBase::Display_Float));
weaponColumns.mReach = &mColumns.back();
for (int i=0; i<6; ++i)
{
mColumns.push_back (RefIdColumn (Columns::ColumnId_MinChop + i, ColumnBase::Display_Integer));
weaponColumns.mChop[i] = &mColumns.back();
}
static const struct
{
int mName;
unsigned int mFlag;
} sWeaponFlagTable[] =
{
{ Columns::ColumnId_Magical, ESM::Weapon::Magical },
{ Columns::ColumnId_Silver, ESM::Weapon::Silver },
{ -1, 0 }
};
for (int i=0; sWeaponFlagTable[i].mName!=-1; ++i)
{
mColumns.push_back (RefIdColumn (sWeaponFlagTable[i].mName, ColumnBase::Display_Boolean));
weaponColumns.mFlags.insert (std::make_pair (&mColumns.back(), sWeaponFlagTable[i].mFlag));
}
mAdapters.insert (std::make_pair (UniversalId::Type_Activator,
new NameRefIdAdapter<ESM::Activator> (UniversalId::Type_Activator, nameColumns)));
mAdapters.insert (std::make_pair (UniversalId::Type_Potion,
new PotionRefIdAdapter (inventoryColumns, autoCalc)));
mAdapters.insert (std::make_pair (UniversalId::Type_Apparatus,
new ApparatusRefIdAdapter (inventoryColumns, apparatusType, toolsColumns.mQuality)));
mAdapters.insert (std::make_pair (UniversalId::Type_Armor,
new ArmorRefIdAdapter (enchantableColumns, armorType, health, armor)));
mAdapters.insert (std::make_pair (UniversalId::Type_Book,
new BookRefIdAdapter (enchantableColumns, scroll, attribute)));
mAdapters.insert (std::make_pair (UniversalId::Type_Clothing,
new ClothingRefIdAdapter (enchantableColumns, clothingType)));
mAdapters.insert (std::make_pair (UniversalId::Type_Container,
new ContainerRefIdAdapter (nameColumns, weightCapacity, organic, respawn)));
mAdapters.insert (std::make_pair (UniversalId::Type_Creature,
new CreatureRefIdAdapter (creatureColumns)));
mAdapters.insert (std::make_pair (UniversalId::Type_Door,
new DoorRefIdAdapter (nameColumns, openSound, closeSound)));
mAdapters.insert (std::make_pair (UniversalId::Type_Ingredient,
new InventoryRefIdAdapter<ESM::Ingredient> (UniversalId::Type_Ingredient, inventoryColumns)));
mAdapters.insert (std::make_pair (UniversalId::Type_CreatureLevelledList,
new BaseRefIdAdapter<ESM::CreatureLevList> (
UniversalId::Type_CreatureLevelledList, baseColumns)));
mAdapters.insert (std::make_pair (UniversalId::Type_ItemLevelledList,
new BaseRefIdAdapter<ESM::ItemLevList> (UniversalId::Type_ItemLevelledList, baseColumns)));
mAdapters.insert (std::make_pair (UniversalId::Type_Light,
new LightRefIdAdapter (lightColumns)));
mAdapters.insert (std::make_pair (UniversalId::Type_Lockpick,
new ToolRefIdAdapter<ESM::Lockpick> (UniversalId::Type_Lockpick, toolsColumns)));
mAdapters.insert (std::make_pair (UniversalId::Type_Miscellaneous,
new MiscRefIdAdapter (inventoryColumns, key)));
mAdapters.insert (std::make_pair (UniversalId::Type_Npc,
new NpcRefIdAdapter (npcColumns)));
mAdapters.insert (std::make_pair (UniversalId::Type_Probe,
new ToolRefIdAdapter<ESM::Probe> (UniversalId::Type_Probe, toolsColumns)));
mAdapters.insert (std::make_pair (UniversalId::Type_Repair,
new ToolRefIdAdapter<ESM::Repair> (UniversalId::Type_Repair, toolsColumns)));
mAdapters.insert (std::make_pair (UniversalId::Type_Static,
new ModelRefIdAdapter<ESM::Static> (UniversalId::Type_Static, modelColumns)));
mAdapters.insert (std::make_pair (UniversalId::Type_Weapon,
new WeaponRefIdAdapter (weaponColumns)));
}
CSMWorld::RefIdCollection::~RefIdCollection()
{
for (std::map<UniversalId::Type, RefIdAdapter *>::iterator iter (mAdapters.begin());
iter!=mAdapters.end(); ++iter)
delete iter->second;
}
int CSMWorld::RefIdCollection::getSize() const
{
return mData.getSize();
}
std::string CSMWorld::RefIdCollection::getId (int index) const
{
return getData (index, 0).toString().toUtf8().constData();
}
int CSMWorld::RefIdCollection::getIndex (const std::string& id) const
{
int index = searchId (id);
if (index==-1)
throw std::runtime_error ("invalid ID: " + id);
return index;
}
int CSMWorld::RefIdCollection::getColumns() const
{
return mColumns.size();
}
const CSMWorld::ColumnBase& CSMWorld::RefIdCollection::getColumn (int column) const
{
return mColumns.at (column);
}
QVariant CSMWorld::RefIdCollection::getData (int index, int column) const
{
RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index);
const RefIdAdapter& adaptor = findAdaptor (localIndex.second);
return adaptor.getData (&mColumns.at (column), mData, localIndex.first);
}
void CSMWorld::RefIdCollection::setData (int index, int column, const QVariant& data)
{
RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index);
const RefIdAdapter& adaptor = findAdaptor (localIndex.second);
adaptor.setData (&mColumns.at (column), mData, localIndex.first, data);
}
void CSMWorld::RefIdCollection::removeRows (int index, int count)
{
mData.erase (index, count);
}
void CSMWorld::RefIdCollection::appendBlankRecord (const std::string& id, UniversalId::Type type)
{
mData.appendRecord (type, id);
}
int CSMWorld::RefIdCollection::searchId (const std::string& id) const
{
RefIdData::LocalIndex localIndex = mData.searchId (id);
if (localIndex.first==-1)
return -1;
return mData.localToGlobalIndex (localIndex);
}
void CSMWorld::RefIdCollection::replace (int index, const RecordBase& record)
{
mData.getRecord (mData.globalToLocalIndex (index)).assign (record);
}
void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record,
UniversalId::Type type)
{
std::string id = findAdaptor (type).getId (record);
int index = mData.getAppendIndex (type);
mData.appendRecord (type, id);
mData.getRecord (mData.globalToLocalIndex (index)).assign (record);
}
const CSMWorld::RecordBase& CSMWorld::RefIdCollection::getRecord (const std::string& id) const
{
return mData.getRecord (mData.searchId (id));
}
const CSMWorld::RecordBase& CSMWorld::RefIdCollection::getRecord (int index) const
{
return mData.getRecord (mData.globalToLocalIndex (index));
}
void CSMWorld::RefIdCollection::load (ESM::ESMReader& reader, bool base, UniversalId::Type type)
{
std::string id = reader.getHNOString ("NAME");
int index = searchId (id);
if (reader.isNextSub ("DELE"))
{
reader.skipRecord();
if (index==-1)
{
// deleting a record that does not exist
// ignore it for now
/// \todo report the problem to the user
}
else if (base)
{
mData.erase (index, 1);
}
else
{
mData.getRecord (mData.globalToLocalIndex (index)).mState = RecordBase::State_Deleted;
}
}
else
{
if (index==-1)
{
// new record
int index = mData.getAppendIndex (type);
mData.appendRecord (type, id);
RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index);
mData.load (localIndex, reader, base);
mData.getRecord (localIndex).mState =
base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
}
else
{
// old record
RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index);
if (!base)
if (mData.getRecord (localIndex).mState==RecordBase::State_Erased)
throw std::logic_error ("attempt to access a deleted record");
mData.load (localIndex, reader, base);
if (!base)
mData.getRecord (localIndex).mState = RecordBase::State_Modified;
}
}
}
int CSMWorld::RefIdCollection::getAppendIndex (UniversalId::Type type) const
{
return mData.getAppendIndex (type);
}

View file

@ -0,0 +1,95 @@
#ifndef CSM_WOLRD_REFIDCOLLECTION_H
#define CSM_WOLRD_REFIDCOLLECTION_H
#include <vector>
#include <map>
#include <deque>
#include "columnbase.hpp"
#include "collectionbase.hpp"
#include "refiddata.hpp"
namespace CSMWorld
{
class RefIdAdapter;
class RefIdColumn : public ColumnBase
{
bool mEditable;
bool mUserEditable;
public:
RefIdColumn (int columnId, Display displayType,
int flag = Flag_Table | Flag_Dialogue, bool editable = true,
bool userEditable = true);
virtual bool isEditable() const;
virtual bool isUserEditable() const;
};
class RefIdCollection : public CollectionBase
{
private:
RefIdData mData;
std::deque<RefIdColumn> mColumns;
std::map<UniversalId::Type, RefIdAdapter *> mAdapters;
private:
const RefIdAdapter& findAdaptor (UniversalId::Type) const;
///< Throws an exception if no adaptor for \a Type can be found.
public:
RefIdCollection();
virtual ~RefIdCollection();
virtual int getSize() const;
virtual std::string getId (int index) const;
virtual int getIndex (const std::string& id) const;
virtual int getColumns() const;
virtual const ColumnBase& getColumn (int column) const;
virtual QVariant getData (int index, int column) const;
virtual void setData (int index, int column, const QVariant& data);
virtual void removeRows (int index, int count);
virtual void appendBlankRecord (const std::string& id, UniversalId::Type type);
///< \param type Will be ignored, unless the collection supports multiple record types
virtual int searchId (const std::string& id) const;
////< Search record with \a id.
/// \return index of record (if found) or -1 (not found)
virtual void replace (int index, const RecordBase& record);
///< If the record type does not match, an exception is thrown.
///
/// \attention \a record must not change the ID.
virtual void appendRecord (const RecordBase& record, UniversalId::Type type);
///< If the record type does not match, an exception is thrown.
///
///< \param type Will be ignored, unless the collection supports multiple record types
virtual const RecordBase& getRecord (const std::string& id) const;
virtual const RecordBase& getRecord (int index) const;
void load (ESM::ESMReader& reader, bool base, UniversalId::Type type);
virtual int getAppendIndex (UniversalId::Type type) const;
///< \param type Will be ignored, unless the collection supports multiple record types
};
}
#endif

View file

@ -0,0 +1,198 @@
#include "refiddata.hpp"
#include <cassert>
#include <components/misc/stringops.hpp>
CSMWorld::RefIdDataContainerBase::~RefIdDataContainerBase() {}
CSMWorld::RefIdData::RefIdData()
{
mRecordContainers.insert (std::make_pair (UniversalId::Type_Activator, &mActivators));
mRecordContainers.insert (std::make_pair (UniversalId::Type_Potion, &mPotions));
mRecordContainers.insert (std::make_pair (UniversalId::Type_Apparatus, &mApparati));
mRecordContainers.insert (std::make_pair (UniversalId::Type_Armor, &mArmors));
mRecordContainers.insert (std::make_pair (UniversalId::Type_Book, &mBooks));
mRecordContainers.insert (std::make_pair (UniversalId::Type_Clothing, &mClothing));
mRecordContainers.insert (std::make_pair (UniversalId::Type_Container, &mContainers));
mRecordContainers.insert (std::make_pair (UniversalId::Type_Creature, &mCreatures));
mRecordContainers.insert (std::make_pair (UniversalId::Type_Door, &mDoors));
mRecordContainers.insert (std::make_pair (UniversalId::Type_Ingredient, &mIngredients));
mRecordContainers.insert (std::make_pair (UniversalId::Type_CreatureLevelledList,
&mCreatureLevelledLists));
mRecordContainers.insert (std::make_pair (UniversalId::Type_ItemLevelledList, &mItemLevelledLists));
mRecordContainers.insert (std::make_pair (UniversalId::Type_Light, &mLights));
mRecordContainers.insert (std::make_pair (UniversalId::Type_Lockpick, &mLockpicks));
mRecordContainers.insert (std::make_pair (UniversalId::Type_Miscellaneous, &mMiscellaneous));
mRecordContainers.insert (std::make_pair (UniversalId::Type_Npc, &mNpcs));
mRecordContainers.insert (std::make_pair (UniversalId::Type_Probe, &mProbes));
mRecordContainers.insert (std::make_pair (UniversalId::Type_Repair, &mRepairs));
mRecordContainers.insert (std::make_pair (UniversalId::Type_Static, &mStatics));
mRecordContainers.insert (std::make_pair (UniversalId::Type_Weapon, &mWeapons));
}
CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::globalToLocalIndex (int index) const
{
for (std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator iter (
mRecordContainers.begin()); iter!=mRecordContainers.end(); ++iter)
{
if (index<iter->second->getSize())
return LocalIndex (index, iter->first);
index -= iter->second->getSize();
}
throw std::runtime_error ("RefIdData index out of range");
}
int CSMWorld::RefIdData::localToGlobalIndex (const LocalIndex& index)
const
{
std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator end =
mRecordContainers.find (index.second);
if (end==mRecordContainers.end())
throw std::logic_error ("invalid local index type");
int globalIndex = index.first;
for (std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator iter (
mRecordContainers.begin()); iter!=end; ++iter)
globalIndex += iter->second->getSize();
return globalIndex;
}
CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::searchId (
const std::string& id) const
{
std::string id2 = Misc::StringUtils::lowerCase (id);
std::map<std::string, std::pair<int, UniversalId::Type> >::const_iterator iter = mIndex.find (id2);
if (iter==mIndex.end())
return std::make_pair (-1, CSMWorld::UniversalId::Type_None);
return iter->second;
}
void CSMWorld::RefIdData::erase (int index, int count)
{
LocalIndex localIndex = globalToLocalIndex (index);
std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator iter =
mRecordContainers.find (localIndex.second);
while (count>0 && iter!=mRecordContainers.end())
{
int size = iter->second->getSize();
if (localIndex.first+count>size)
{
erase (localIndex, size-localIndex.first);
count -= size-localIndex.first;
++iter;
if (iter==mRecordContainers.end())
throw std::runtime_error ("invalid count value for erase operation");
localIndex.first = 0;
localIndex.second = iter->first;
}
else
{
erase (localIndex, count);
count = 0;
}
}
}
const CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord (const LocalIndex& index) const
{
std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator iter =
mRecordContainers.find (index.second);
if (iter==mRecordContainers.end())
throw std::logic_error ("invalid local index type");
return iter->second->getRecord (index.first);
}
CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord (const LocalIndex& index)
{
std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =
mRecordContainers.find (index.second);
if (iter==mRecordContainers.end())
throw std::logic_error ("invalid local index type");
return iter->second->getRecord (index.first);
}
void CSMWorld::RefIdData::appendRecord (UniversalId::Type type, const std::string& id)
{
std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =
mRecordContainers.find (type);
if (iter==mRecordContainers.end())
throw std::logic_error ("invalid local index type");
iter->second->appendRecord (id);
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id),
LocalIndex (iter->second->getSize()-1, type)));
}
int CSMWorld::RefIdData::getAppendIndex (UniversalId::Type type) const
{
int index = 0;
for (std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator iter (
mRecordContainers.begin()); iter!=mRecordContainers.end(); ++iter)
{
index += iter->second->getSize();
if (type==iter->first)
break;
}
return index;
}
void CSMWorld::RefIdData::load (const LocalIndex& index, ESM::ESMReader& reader, bool base)
{
std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =
mRecordContainers.find (index.second);
if (iter==mRecordContainers.end())
throw std::logic_error ("invalid local index type");
iter->second->load (index.first, reader, base);
}
void CSMWorld::RefIdData::erase (const LocalIndex& index, int count)
{
std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =
mRecordContainers.find (index.second);
if (iter==mRecordContainers.end())
throw std::logic_error ("invalid local index type");
for (int i=index.first; i<index.first+count; ++i)
{
std::map<std::string, LocalIndex>::iterator result =
mIndex.find (Misc::StringUtils::lowerCase (iter->second->getId (i)));
if (result!=mIndex.end())
mIndex.erase (result);
}
iter->second->erase (index.first, count);
}
int CSMWorld::RefIdData::getSize() const
{
return mIndex.size();
}

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