mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-04-30 05:47:57 +03:00
Imported Upstream version 0.26.0
This commit is contained in:
commit
9a2b6c69b6
1398 changed files with 212217 additions and 0 deletions
158
apps/opencs/CMakeLists.txt
Normal file
158
apps/opencs/CMakeLists.txt
Normal 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
157
apps/opencs/editor.cpp
Normal 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
66
apps/opencs/editor.hpp
Normal 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
49
apps/opencs/main.cpp
Normal 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();
|
||||
}
|
2284
apps/opencs/model/doc/document.cpp
Normal file
2284
apps/opencs/model/doc/document.cpp
Normal file
File diff suppressed because it is too large
Load diff
111
apps/opencs/model/doc/document.hpp
Normal file
111
apps/opencs/model/doc/document.hpp
Normal 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
|
38
apps/opencs/model/doc/documentmanager.cpp
Normal file
38
apps/opencs/model/doc/documentmanager.cpp
Normal 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();
|
||||
}
|
37
apps/opencs/model/doc/documentmanager.hpp
Normal file
37
apps/opencs/model/doc/documentmanager.hpp
Normal 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
|
19
apps/opencs/model/doc/state.hpp
Normal file
19
apps/opencs/model/doc/state.hpp
Normal 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
|
20
apps/opencs/model/filter/andnode.cpp
Normal file
20
apps/opencs/model/filter/andnode.cpp
Normal 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;
|
||||
}
|
23
apps/opencs/model/filter/andnode.hpp
Normal file
23
apps/opencs/model/filter/andnode.hpp
Normal 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
|
15
apps/opencs/model/filter/booleannode.cpp
Normal file
15
apps/opencs/model/filter/booleannode.cpp
Normal 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";
|
||||
}
|
29
apps/opencs/model/filter/booleannode.hpp
Normal file
29
apps/opencs/model/filter/booleannode.hpp
Normal 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
|
25
apps/opencs/model/filter/filter.hpp
Normal file
25
apps/opencs/model/filter/filter.hpp
Normal 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
|
8
apps/opencs/model/filter/leafnode.cpp
Normal file
8
apps/opencs/model/filter/leafnode.cpp
Normal file
|
@ -0,0 +1,8 @@
|
|||
|
||||
#include "leafnode.hpp"
|
||||
|
||||
std::vector<int> CSMFilter::LeafNode::getReferencedColumns() const
|
||||
{
|
||||
return std::vector<int>();
|
||||
}
|
||||
|
20
apps/opencs/model/filter/leafnode.hpp
Normal file
20
apps/opencs/model/filter/leafnode.hpp
Normal 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
|
60
apps/opencs/model/filter/narynode.cpp
Normal file
60
apps/opencs/model/filter/narynode.cpp
Normal 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();
|
||||
}
|
||||
|
||||
|
37
apps/opencs/model/filter/narynode.hpp
Normal file
37
apps/opencs/model/filter/narynode.hpp
Normal 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
|
6
apps/opencs/model/filter/node.cpp
Normal file
6
apps/opencs/model/filter/node.cpp
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
#include "node.hpp"
|
||||
|
||||
CSMFilter::Node::Node() {}
|
||||
|
||||
CSMFilter::Node::~Node() {}
|
53
apps/opencs/model/filter/node.hpp
Normal file
53
apps/opencs/model/filter/node.hpp
Normal 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
|
10
apps/opencs/model/filter/notnode.cpp
Normal file
10
apps/opencs/model/filter/notnode.cpp
Normal 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);
|
||||
}
|
21
apps/opencs/model/filter/notnode.hpp
Normal file
21
apps/opencs/model/filter/notnode.hpp
Normal 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
|
20
apps/opencs/model/filter/ornode.cpp
Normal file
20
apps/opencs/model/filter/ornode.cpp
Normal 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;
|
||||
}
|
23
apps/opencs/model/filter/ornode.hpp
Normal file
23
apps/opencs/model/filter/ornode.hpp
Normal 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
|
595
apps/opencs/model/filter/parser.cpp
Normal file
595
apps/opencs/model/filter/parser.cpp
Normal 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;
|
||||
}
|
59
apps/opencs/model/filter/parser.hpp
Normal file
59
apps/opencs/model/filter/parser.hpp
Normal 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
|
62
apps/opencs/model/filter/textnode.cpp
Normal file
62
apps/opencs/model/filter/textnode.cpp
Normal 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();
|
||||
}
|
33
apps/opencs/model/filter/textnode.hpp
Normal file
33
apps/opencs/model/filter/textnode.hpp
Normal 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
|
26
apps/opencs/model/filter/unarynode.cpp
Normal file
26
apps/opencs/model/filter/unarynode.cpp
Normal 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);
|
||||
}
|
34
apps/opencs/model/filter/unarynode.hpp
Normal file
34
apps/opencs/model/filter/unarynode.hpp
Normal 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
|
71
apps/opencs/model/filter/valuenode.cpp
Normal file
71
apps/opencs/model/filter/valuenode.cpp
Normal 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();
|
||||
}
|
37
apps/opencs/model/filter/valuenode.hpp
Normal file
37
apps/opencs/model/filter/valuenode.hpp
Normal 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
|
82
apps/opencs/model/settings/settingcontainer.cpp
Normal file
82
apps/opencs/model/settings/settingcontainer.cpp
Normal 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;
|
||||
}
|
47
apps/opencs/model/settings/settingcontainer.hpp
Normal file
47
apps/opencs/model/settings/settingcontainer.hpp
Normal 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
|
104
apps/opencs/model/settings/settingsitem.cpp
Normal file
104
apps/opencs/model/settings/settingsitem.cpp
Normal 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;
|
||||
}
|
63
apps/opencs/model/settings/settingsitem.hpp
Normal file
63
apps/opencs/model/settings/settingsitem.hpp
Normal 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
|
||||
|
1
apps/opencs/model/settings/support.cpp
Normal file
1
apps/opencs/model/settings/support.cpp
Normal file
|
@ -0,0 +1 @@
|
|||
#include "support.hpp"
|
39
apps/opencs/model/settings/support.hpp
Normal file
39
apps/opencs/model/settings/support.hpp
Normal 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
|
355
apps/opencs/model/settings/usersettings.cpp
Normal file
355
apps/opencs/model/settings/usersettings.cpp
Normal 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 §ionName) 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 §ionSettings)
|
||||
{
|
||||
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 §ionName, 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 §ion, 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 §ionName) 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;
|
||||
}
|
94
apps/opencs/model/settings/usersettings.hpp
Normal file
94
apps/opencs/model/settings/usersettings.hpp
Normal 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 *> §ions);
|
||||
|
||||
/// 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 §ionName, 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 §ionName) const;
|
||||
|
||||
/// Retrieves the value as a QString of the specified setting in the specified section
|
||||
QString getSetting(const QString §ion, 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 §ionName) const;
|
||||
|
||||
signals:
|
||||
|
||||
void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue);
|
||||
|
||||
};
|
||||
}
|
||||
#endif // USERSETTINGS_HPP
|
39
apps/opencs/model/tools/birthsigncheck.cpp
Normal file
39
apps/opencs/model/tools/birthsigncheck.cpp
Normal 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
|
||||
}
|
29
apps/opencs/model/tools/birthsigncheck.hpp
Normal file
29
apps/opencs/model/tools/birthsigncheck.hpp
Normal 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
|
72
apps/opencs/model/tools/classcheck.cpp
Normal file
72
apps/opencs/model/tools/classcheck.cpp
Normal 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());
|
||||
}
|
||||
}
|
29
apps/opencs/model/tools/classcheck.hpp
Normal file
29
apps/opencs/model/tools/classcheck.hpp
Normal 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
|
61
apps/opencs/model/tools/factioncheck.cpp
Normal file
61
apps/opencs/model/tools/factioncheck.cpp
Normal 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
|
||||
}
|
29
apps/opencs/model/tools/factioncheck.hpp
Normal file
29
apps/opencs/model/tools/factioncheck.hpp
Normal 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
|
23
apps/opencs/model/tools/mandatoryid.cpp
Normal file
23
apps/opencs/model/tools/mandatoryid.cpp
Normal 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));
|
||||
}
|
38
apps/opencs/model/tools/mandatoryid.hpp
Normal file
38
apps/opencs/model/tools/mandatoryid.hpp
Normal 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
|
84
apps/opencs/model/tools/operation.cpp
Normal file
84
apps/opencs/model/tools/operation.cpp
Normal 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();
|
||||
}
|
54
apps/opencs/model/tools/operation.hpp
Normal file
54
apps/opencs/model/tools/operation.hpp
Normal 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
|
68
apps/opencs/model/tools/racecheck.cpp
Normal file
68
apps/opencs/model/tools/racecheck.cpp
Normal 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);
|
||||
}
|
34
apps/opencs/model/tools/racecheck.hpp
Normal file
34
apps/opencs/model/tools/racecheck.hpp
Normal 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
|
33
apps/opencs/model/tools/regioncheck.cpp
Normal file
33
apps/opencs/model/tools/regioncheck.cpp
Normal 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
|
||||
}
|
29
apps/opencs/model/tools/regioncheck.hpp
Normal file
29
apps/opencs/model/tools/regioncheck.hpp
Normal 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
|
71
apps/opencs/model/tools/reportmodel.cpp
Normal file
71
apps/opencs/model/tools/reportmodel.cpp
Normal 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;
|
||||
}
|
37
apps/opencs/model/tools/reportmodel.hpp
Normal file
37
apps/opencs/model/tools/reportmodel.hpp
Normal 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
|
37
apps/opencs/model/tools/skillcheck.cpp
Normal file
37
apps/opencs/model/tools/skillcheck.cpp
Normal 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");
|
||||
}
|
29
apps/opencs/model/tools/skillcheck.hpp
Normal file
29
apps/opencs/model/tools/skillcheck.hpp
Normal 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
|
29
apps/opencs/model/tools/soundcheck.cpp
Normal file
29
apps/opencs/model/tools/soundcheck.cpp
Normal 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
|
||||
}
|
29
apps/opencs/model/tools/soundcheck.hpp
Normal file
29
apps/opencs/model/tools/soundcheck.hpp
Normal 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
|
35
apps/opencs/model/tools/spellcheck.cpp
Normal file
35
apps/opencs/model/tools/spellcheck.cpp
Normal 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
|
||||
}
|
29
apps/opencs/model/tools/spellcheck.hpp
Normal file
29
apps/opencs/model/tools/spellcheck.hpp
Normal 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
|
4
apps/opencs/model/tools/stage.cpp
Normal file
4
apps/opencs/model/tools/stage.cpp
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
#include "stage.hpp"
|
||||
|
||||
CSMTools::Stage::~Stage() {}
|
24
apps/opencs/model/tools/stage.hpp
Normal file
24
apps/opencs/model/tools/stage.hpp
Normal 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
|
||||
|
147
apps/opencs/model/tools/tools.cpp
Normal file
147
apps/opencs/model/tools/tools.cpp
Normal 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());
|
||||
}
|
73
apps/opencs/model/tools/tools.hpp
Normal file
73
apps/opencs/model/tools/tools.hpp
Normal 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
|
7
apps/opencs/model/tools/verifier.cpp
Normal file
7
apps/opencs/model/tools/verifier.cpp
Normal file
|
@ -0,0 +1,7 @@
|
|||
|
||||
#include "verifier.hpp"
|
||||
|
||||
#include "../doc/state.hpp"
|
||||
|
||||
CSMTools::Verifier::Verifier() : Operation (CSMDoc::State_Verifying)
|
||||
{}
|
17
apps/opencs/model/tools/verifier.hpp
Normal file
17
apps/opencs/model/tools/verifier.hpp
Normal 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
|
25
apps/opencs/model/world/cell.cpp
Normal file
25
apps/opencs/model/world/cell.cpp
Normal 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));
|
||||
}
|
24
apps/opencs/model/world/cell.hpp
Normal file
24
apps/opencs/model/world/cell.hpp
Normal 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
|
319
apps/opencs/model/world/collection.hpp
Normal file
319
apps/opencs/model/world/collection.hpp
Normal 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
|
6
apps/opencs/model/world/collectionbase.cpp
Normal file
6
apps/opencs/model/world/collectionbase.cpp
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
#include "collectionbase.hpp"
|
||||
|
||||
CSMWorld::CollectionBase::CollectionBase() {}
|
||||
|
||||
CSMWorld::CollectionBase::~CollectionBase() {}
|
85
apps/opencs/model/world/collectionbase.hpp
Normal file
85
apps/opencs/model/world/collectionbase.hpp
Normal 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
|
20
apps/opencs/model/world/columnbase.cpp
Normal file
20
apps/opencs/model/world/columnbase.cpp
Normal 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));
|
||||
}
|
82
apps/opencs/model/world/columnbase.hpp
Normal file
82
apps/opencs/model/world/columnbase.hpp
Normal 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
|
1221
apps/opencs/model/world/columnimp.hpp
Normal file
1221
apps/opencs/model/world/columnimp.hpp
Normal file
File diff suppressed because it is too large
Load diff
199
apps/opencs/model/world/columns.cpp
Normal file
199
apps/opencs/model/world/columns.cpp
Normal 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;
|
||||
}
|
186
apps/opencs/model/world/columns.hpp
Normal file
186
apps/opencs/model/world/columns.hpp
Normal 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
|
125
apps/opencs/model/world/commands.cpp
Normal file
125
apps/opencs/model/world/commands.cpp
Normal 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);
|
||||
}
|
104
apps/opencs/model/world/commands.hpp
Normal file
104
apps/opencs/model/world/commands.hpp
Normal 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
|
442
apps/opencs/model/world/data.cpp
Normal file
442
apps/opencs/model/world/data.cpp
Normal 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;
|
||||
}
|
142
apps/opencs/model/world/data.hpp
Normal file
142
apps/opencs/model/world/data.hpp
Normal 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
|
86
apps/opencs/model/world/idcollection.hpp
Normal file
86
apps/opencs/model/world/idcollection.hpp
Normal 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
|
181
apps/opencs/model/world/idtable.cpp
Normal file
181
apps/opencs/model/world/idtable.cpp
Normal 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;
|
||||
}
|
69
apps/opencs/model/world/idtable.hpp
Normal file
69
apps/opencs/model/world/idtable.hpp
Normal 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
|
48
apps/opencs/model/world/idtableproxymodel.cpp
Normal file
48
apps/opencs/model/world/idtableproxymodel.cpp
Normal 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();
|
||||
}
|
39
apps/opencs/model/world/idtableproxymodel.hpp
Normal file
39
apps/opencs/model/world/idtableproxymodel.hpp
Normal 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
|
21
apps/opencs/model/world/record.cpp
Normal file
21
apps/opencs/model/world/record.cpp
Normal 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;
|
||||
}
|
127
apps/opencs/model/world/record.hpp
Normal file
127
apps/opencs/model/world/record.hpp
Normal 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
|
13
apps/opencs/model/world/ref.cpp
Normal file
13
apps/opencs/model/world/ref.cpp
Normal 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);
|
||||
}
|
26
apps/opencs/model/world/ref.hpp
Normal file
26
apps/opencs/model/world/ref.hpp
Normal 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
|
41
apps/opencs/model/world/refcollection.cpp
Normal file
41
apps/opencs/model/world/refcollection.cpp
Normal 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();
|
||||
}
|
29
apps/opencs/model/world/refcollection.hpp
Normal file
29
apps/opencs/model/world/refcollection.hpp
Normal 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
|
6
apps/opencs/model/world/refidadapter.cpp
Normal file
6
apps/opencs/model/world/refidadapter.cpp
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
#include "refidadapter.hpp"
|
||||
|
||||
CSMWorld::RefIdAdapter::RefIdAdapter() {}
|
||||
|
||||
CSMWorld::RefIdAdapter::~RefIdAdapter() {}
|
37
apps/opencs/model/world/refidadapter.hpp
Normal file
37
apps/opencs/model/world/refidadapter.hpp
Normal 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
|
575
apps/opencs/model/world/refidadapterimp.cpp
Normal file
575
apps/opencs/model/world/refidadapterimp.cpp
Normal 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);
|
||||
}
|
||||
}
|
766
apps/opencs/model/world/refidadapterimp.hpp
Normal file
766
apps/opencs/model/world/refidadapterimp.hpp
Normal 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
|
536
apps/opencs/model/world/refidcollection.cpp
Normal file
536
apps/opencs/model/world/refidcollection.cpp
Normal 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);
|
||||
}
|
95
apps/opencs/model/world/refidcollection.hpp
Normal file
95
apps/opencs/model/world/refidcollection.hpp
Normal 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
|
198
apps/opencs/model/world/refiddata.cpp
Normal file
198
apps/opencs/model/world/refiddata.cpp
Normal 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
Loading…
Add table
Add a link
Reference in a new issue