Merge branch 'master' into HEAD
Conflicts: CMakeLists.txt apps/launcher/CMakeLists.txtloadfix
commit
4fa303d7c4
@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
|
||||
export CXX=g++
|
||||
export CC=gcc
|
||||
|
||||
echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse"
|
||||
echo "yes" | sudo apt-add-repository ppa:openmw/openmw
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -qq libgtest-dev google-mock
|
||||
sudo apt-get install -qq libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-thread-dev libboost-wave-dev
|
||||
sudo apt-get install -qq libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libavresample-dev
|
||||
sudo apt-get install -qq libbullet-dev libogre-1.9-dev libmygui-dev libsdl2-dev libunshield-dev libtinyxml-dev libopenal-dev libqt4-dev
|
||||
sudo mkdir /usr/src/gtest/build
|
||||
cd /usr/src/gtest/build
|
||||
sudo cmake .. -DBUILD_SHARED_LIBS=1
|
||||
sudo make -j4
|
||||
sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so
|
||||
sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so
|
@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
export CXX=clang++
|
||||
export CC=clang
|
||||
|
||||
brew tap openmw/openmw
|
||||
brew update
|
||||
brew unlink boost
|
||||
brew install cmake openmw-mygui openmw-bullet openmw-sdl2 openmw-ffmpeg pkg-config qt unshield
|
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DBUILD_WITH_CODE_COVERAGE=1 -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DUSE_SYSTEM_TINYXML=TRUE
|
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_FRAMEWORK_PATH="/usr/local/lib/macosx/Release" -DCMAKE_EXE_LINKER_FLAGS="-F/usr/local/lib/macosx/Release" -DCMAKE_CXX_FLAGS="-stdlib=libstdc++" -DCMAKE_BUILD_TYPE=Debug -DBUILD_MYGUI_PLUGIN=OFF -G"Unix Makefiles" ..
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,31 @@
|
||||
|
||||
#include "blacklist.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
bool CSMDoc::Blacklist::isBlacklisted (const CSMWorld::UniversalId& id) const
|
||||
{
|
||||
std::map<CSMWorld::UniversalId::Type, std::vector<std::string> >::const_iterator iter =
|
||||
mIds.find (id.getType());
|
||||
|
||||
if (iter==mIds.end())
|
||||
return false;
|
||||
|
||||
return std::binary_search (iter->second.begin(), iter->second.end(),
|
||||
Misc::StringUtils::lowerCase (id.getId()));
|
||||
}
|
||||
|
||||
void CSMDoc::Blacklist::add (CSMWorld::UniversalId::Type type,
|
||||
const std::vector<std::string>& ids)
|
||||
{
|
||||
std::vector<std::string>& list = mIds[type];
|
||||
|
||||
int size = list.size();
|
||||
|
||||
list.resize (size+ids.size());
|
||||
|
||||
std::transform (ids.begin(), ids.end(), list.begin()+size, Misc::StringUtils::lowerCase);
|
||||
std::sort (list.begin(), list.end());
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
#ifndef CSM_DOC_BLACKLIST_H
|
||||
#define CSM_DOC_BLACKLIST_H
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "../world/universalid.hpp"
|
||||
|
||||
namespace CSMDoc
|
||||
{
|
||||
/// \brief ID blacklist sorted by UniversalId type
|
||||
class Blacklist
|
||||
{
|
||||
std::map<CSMWorld::UniversalId::Type, std::vector<std::string> > mIds;
|
||||
|
||||
public:
|
||||
|
||||
bool isBlacklisted (const CSMWorld::UniversalId& id) const;
|
||||
|
||||
void add (CSMWorld::UniversalId::Type type, const std::vector<std::string>& ids);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,162 @@
|
||||
|
||||
#include "runner.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
#include <QTemporaryFile>
|
||||
#include <QTextStream>
|
||||
|
||||
#include "operation.hpp"
|
||||
|
||||
CSMDoc::Runner::Runner (const boost::filesystem::path& projectPath)
|
||||
: mRunning (false), mStartup (0), mProjectPath (projectPath)
|
||||
{
|
||||
connect (&mProcess, SIGNAL (finished (int, QProcess::ExitStatus)),
|
||||
this, SLOT (finished (int, QProcess::ExitStatus)));
|
||||
|
||||
connect (&mProcess, SIGNAL (readyReadStandardOutput()),
|
||||
this, SLOT (readyReadStandardOutput()));
|
||||
|
||||
mProcess.setProcessChannelMode (QProcess::MergedChannels);
|
||||
|
||||
mProfile.blank();
|
||||
}
|
||||
|
||||
CSMDoc::Runner::~Runner()
|
||||
{
|
||||
if (mRunning)
|
||||
{
|
||||
disconnect (&mProcess, 0, this, 0);
|
||||
mProcess.kill();
|
||||
mProcess.waitForFinished();
|
||||
}
|
||||
}
|
||||
|
||||
void CSMDoc::Runner::start (bool delayed)
|
||||
{
|
||||
if (mStartup)
|
||||
{
|
||||
delete mStartup;
|
||||
mStartup = 0;
|
||||
}
|
||||
|
||||
if (!delayed)
|
||||
{
|
||||
mLog.clear();
|
||||
|
||||
QString path = "openmw";
|
||||
#ifdef Q_OS_WIN
|
||||
path.append(QString(".exe"));
|
||||
#elif defined(Q_OS_MAC)
|
||||
QDir dir(QCoreApplication::applicationDirPath());
|
||||
dir.cdUp();
|
||||
dir.cdUp();
|
||||
dir.cdUp();
|
||||
path = dir.absoluteFilePath(path.prepend("OpenMW.app/Contents/MacOS/"));
|
||||
#else
|
||||
path.prepend(QString("./"));
|
||||
#endif
|
||||
|
||||
mStartup = new QTemporaryFile (this);
|
||||
mStartup->open();
|
||||
|
||||
{
|
||||
QTextStream stream (mStartup);
|
||||
|
||||
if (!mStartupInstruction.empty())
|
||||
stream << QString::fromUtf8 (mStartupInstruction.c_str()) << '\n';
|
||||
|
||||
stream << QString::fromUtf8 (mProfile.mScriptText.c_str());
|
||||
}
|
||||
|
||||
mStartup->close();
|
||||
|
||||
QStringList arguments;
|
||||
arguments << "--skip-menu";
|
||||
|
||||
if (mProfile.mFlags & ESM::DebugProfile::Flag_BypassNewGame)
|
||||
arguments << "--new-game=0";
|
||||
else
|
||||
arguments << "--new-game=1";
|
||||
|
||||
arguments << ("--script-run="+mStartup->fileName());;
|
||||
|
||||
arguments <<
|
||||
QString::fromUtf8 (("--data="+mProjectPath.parent_path().string()).c_str());
|
||||
|
||||
for (std::vector<std::string>::const_iterator iter (mContentFiles.begin());
|
||||
iter!=mContentFiles.end(); ++iter)
|
||||
{
|
||||
arguments << QString::fromUtf8 (("--content="+*iter).c_str());
|
||||
}
|
||||
|
||||
arguments
|
||||
<< QString::fromUtf8 (("--content="+mProjectPath.filename().string()).c_str());
|
||||
|
||||
mProcess.start (path, arguments);
|
||||
}
|
||||
|
||||
mRunning = true;
|
||||
emit runStateChanged();
|
||||
}
|
||||
|
||||
void CSMDoc::Runner::stop()
|
||||
{
|
||||
delete mStartup;
|
||||
mStartup = 0;
|
||||
|
||||
if (mProcess.state()==QProcess::NotRunning)
|
||||
{
|
||||
mRunning = false;
|
||||
emit runStateChanged();
|
||||
}
|
||||
else
|
||||
mProcess.kill();
|
||||
}
|
||||
|
||||
bool CSMDoc::Runner::isRunning() const
|
||||
{
|
||||
return mRunning;
|
||||
}
|
||||
|
||||
void CSMDoc::Runner::configure (const ESM::DebugProfile& profile,
|
||||
const std::vector<std::string>& contentFiles, const std::string& startupInstruction)
|
||||
{
|
||||
mProfile = profile;
|
||||
mContentFiles = contentFiles;
|
||||
mStartupInstruction = startupInstruction;
|
||||
}
|
||||
|
||||
void CSMDoc::Runner::finished (int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
mRunning = false;
|
||||
emit runStateChanged();
|
||||
}
|
||||
|
||||
QTextDocument *CSMDoc::Runner::getLog()
|
||||
{
|
||||
return &mLog;
|
||||
}
|
||||
|
||||
void CSMDoc::Runner::readyReadStandardOutput()
|
||||
{
|
||||
mLog.setPlainText (
|
||||
mLog.toPlainText() + QString::fromUtf8 (mProcess.readAllStandardOutput()));
|
||||
}
|
||||
|
||||
|
||||
CSMDoc::SaveWatcher::SaveWatcher (Runner *runner, Operation *operation)
|
||||
: QObject (runner), mRunner (runner)
|
||||
{
|
||||
connect (operation, SIGNAL (done (int, bool)), this, SLOT (saveDone (int, bool)));
|
||||
}
|
||||
|
||||
void CSMDoc::SaveWatcher::saveDone (int type, bool failed)
|
||||
{
|
||||
if (failed)
|
||||
mRunner->stop();
|
||||
else
|
||||
mRunner->start();
|
||||
|
||||
deleteLater();
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
#ifndef CSM_DOC_RUNNER_H
|
||||
#define CSM_DOC_RUNNER_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include <QObject>
|
||||
#include <QProcess>
|
||||
#include <QTextDocument>
|
||||
|
||||
#include <components/esm/debugprofile.hpp>
|
||||
|
||||
class QTemporaryFile;
|
||||
|
||||
namespace CSMDoc
|
||||
{
|
||||
class Runner : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QProcess mProcess;
|
||||
bool mRunning;
|
||||
ESM::DebugProfile mProfile;
|
||||
std::vector<std::string> mContentFiles;
|
||||
std::string mStartupInstruction;
|
||||
QTemporaryFile *mStartup;
|
||||
QTextDocument mLog;
|
||||
boost::filesystem::path mProjectPath;
|
||||
|
||||
public:
|
||||
|
||||
Runner (const boost::filesystem::path& projectPath);
|
||||
|
||||
~Runner();
|
||||
|
||||
/// \param delayed Flag as running but do not start the OpenMW process yet (the
|
||||
/// process must be started by another call of start with delayed==false)
|
||||
void start (bool delayed = false);
|
||||
|
||||
void stop();
|
||||
|
||||
/// \note Running state is entered when the start function is called. This
|
||||
/// is not necessarily identical to the moment the child process is started.
|
||||
bool isRunning() const;
|
||||
|
||||
void configure (const ESM::DebugProfile& profile,
|
||||
const std::vector<std::string>& contentFiles,
|
||||
const std::string& startupInstruction);
|
||||
|
||||
QTextDocument *getLog();
|
||||
|
||||
signals:
|
||||
|
||||
void runStateChanged();
|
||||
|
||||
private slots:
|
||||
|
||||
void finished (int exitCode, QProcess::ExitStatus exitStatus);
|
||||
|
||||
void readyReadStandardOutput();
|
||||
};
|
||||
|
||||
class Operation;
|
||||
|
||||
/// \brief Watch for end of save operation and restart or stop runner
|
||||
class SaveWatcher : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Runner *mRunner;
|
||||
|
||||
public:
|
||||
|
||||
/// *this attaches itself to runner
|
||||
SaveWatcher (Runner *runner, Operation *operation);
|
||||
|
||||
private slots:
|
||||
|
||||
void saveDone (int type, bool failed);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -1,25 +0,0 @@
|
||||
#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
|
@ -0,0 +1,51 @@
|
||||
#include "bodypartcheck.hpp"
|
||||
|
||||
CSMTools::BodyPartCheckStage::BodyPartCheckStage(
|
||||
const CSMWorld::IdCollection<ESM::BodyPart> &bodyParts,
|
||||
const CSMWorld::Resources &meshes,
|
||||
const CSMWorld::IdCollection<ESM::Race> &races ) :
|
||||
mBodyParts(bodyParts),
|
||||
mMeshes(meshes),
|
||||
mRaces(races)
|
||||
{ }
|
||||
|
||||
int CSMTools::BodyPartCheckStage::setup()
|
||||
{
|
||||
return mBodyParts.getSize();
|
||||
}
|
||||
|
||||
void CSMTools::BodyPartCheckStage::perform ( int stage, Messages &messages )
|
||||
{
|
||||
const CSMWorld::Record<ESM::BodyPart> &record = mBodyParts.getRecord(stage);
|
||||
|
||||
if ( record.isDeleted() )
|
||||
return;
|
||||
|
||||
const ESM::BodyPart &bodyPart = record.get();
|
||||
|
||||
CSMWorld::UniversalId id( CSMWorld::UniversalId::Type_BodyPart, bodyPart.mId );
|
||||
|
||||
// Check BYDT
|
||||
if (bodyPart.mData.mPart > 14 )
|
||||
messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range part value." ));
|
||||
|
||||
if (bodyPart.mData.mFlags > 3 )
|
||||
messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range flags value." ));
|
||||
|
||||
if (bodyPart.mData.mType > 2 )
|
||||
messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range type value." ));
|
||||
|
||||
// Check MODL
|
||||
|
||||
if ( bodyPart.mModel.empty() )
|
||||
messages.push_back(std::make_pair( id, bodyPart.mId + " has no model." ));
|
||||
else if ( mMeshes.searchId( bodyPart.mModel ) == -1 )
|
||||
messages.push_back(std::make_pair( id, bodyPart.mId + " has invalid model." ));
|
||||
|
||||
// Check FNAM
|
||||
|
||||
if ( bodyPart.mRace.empty() )
|
||||
messages.push_back(std::make_pair( id, bodyPart.mId + " has no race." ));
|
||||
else if ( mRaces.searchId( bodyPart.mRace ) == -1 )
|
||||
messages.push_back(std::make_pair( id, bodyPart.mId + " has invalid race." ));
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
#ifndef CSM_TOOLS_BODYPARTCHECK_H
|
||||
#define CSM_TOOLS_BODYPARTCHECK_H
|
||||
|
||||
#include <components/esm/loadbody.hpp>
|
||||
#include <components/esm/loadrace.hpp>
|
||||
|
||||
#include "../world/resources.hpp"
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that body part records are internally consistent
|
||||
class BodyPartCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::BodyPart> &mBodyParts;
|
||||
const CSMWorld::Resources &mMeshes;
|
||||
const CSMWorld::IdCollection<ESM::Race> &mRaces;
|
||||
|
||||
public:
|
||||
BodyPartCheckStage(
|
||||
const CSMWorld::IdCollection<ESM::BodyPart> &bodyParts,
|
||||
const CSMWorld::Resources &meshes,
|
||||
const CSMWorld::IdCollection<ESM::Race> &races );
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform( int stage, Messages &messages );
|
||||
///< Messages resulting from this tage will be appended to \a messages.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,267 @@
|
||||
|
||||
#include "commanddispatcher.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include "../doc/document.hpp"
|
||||
|
||||
#include "idtable.hpp"
|
||||
#include "record.hpp"
|
||||
#include "commands.hpp"
|
||||
|
||||
std::vector<std::string> CSMWorld::CommandDispatcher::getDeletableRecords() const
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
|
||||
IdTable& model = dynamic_cast<IdTable&> (*mDocument.getData().getTableModel (mId));
|
||||
|
||||
int stateColumnIndex = model.findColumnIndex (Columns::ColumnId_Modification);
|
||||
|
||||
for (std::vector<std::string>::const_iterator iter (mSelection.begin());
|
||||
iter!=mSelection.end(); ++iter)
|
||||
{
|
||||
int row = model.getModelIndex (*iter, 0).row();
|
||||
|
||||
// check record state
|
||||
RecordBase::State state = static_cast<RecordBase::State> (
|
||||
model.data (model.index (row, stateColumnIndex)).toInt());
|
||||
|
||||
if (state==RecordBase::State_Deleted)
|
||||
continue;
|
||||
|
||||
// check other columns (only relevant for a subset of the tables)
|
||||
int dialogueTypeIndex = model.searchColumnIndex (Columns::ColumnId_DialogueType);
|
||||
|
||||
if (dialogueTypeIndex!=-1)
|
||||
{
|
||||
int type = model.data (model.index (row, dialogueTypeIndex)).toInt();
|
||||
|
||||
if (type!=ESM::Dialogue::Topic && type!=ESM::Dialogue::Journal)
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push_back (*iter);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> CSMWorld::CommandDispatcher::getRevertableRecords() const
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
|
||||
IdTable& model = dynamic_cast<IdTable&> (*mDocument.getData().getTableModel (mId));
|
||||
|
||||
/// \todo Reverting temporarily disabled on tables that support reordering, because
|
||||
/// revert logic currently can not handle reordering.
|
||||
if (model.getFeatures() & IdTable::Feature_ReorderWithinTopic)
|
||||
return result;
|
||||
|
||||
int stateColumnIndex = model.findColumnIndex (Columns::ColumnId_Modification);
|
||||
|
||||
for (std::vector<std::string>::const_iterator iter (mSelection.begin());
|
||||
iter!=mSelection.end(); ++iter)
|
||||
{
|
||||
int row = model.getModelIndex (*iter, 0).row();
|
||||
|
||||
// check record state
|
||||
RecordBase::State state = static_cast<RecordBase::State> (
|
||||
model.data (model.index (row, stateColumnIndex)).toInt());
|
||||
|
||||
if (state==RecordBase::State_BaseOnly)
|
||||
continue;
|
||||
|
||||
result.push_back (*iter);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
CSMWorld::CommandDispatcher::CommandDispatcher (CSMDoc::Document& document,
|
||||
const CSMWorld::UniversalId& id, QObject *parent)
|
||||
: QObject (parent), mDocument (document), mId (id), mLocked (false)
|
||||
{}
|
||||
|
||||
void CSMWorld::CommandDispatcher::setEditLock (bool locked)
|
||||
{
|
||||
mLocked = locked;
|
||||
}
|
||||
|
||||
void CSMWorld::CommandDispatcher::setSelection (const std::vector<std::string>& selection)
|
||||
{
|
||||
mSelection = selection;
|
||||
std::for_each (mSelection.begin(), mSelection.end(), Misc::StringUtils::toLower);
|
||||
std::sort (mSelection.begin(), mSelection.end());
|
||||
}
|
||||
|
||||
void CSMWorld::CommandDispatcher::setExtendedTypes (const std::vector<UniversalId>& types)
|
||||
{
|
||||
mExtendedTypes = types;
|
||||
}
|
||||
|
||||
bool CSMWorld::CommandDispatcher::canDelete() const
|
||||
{
|
||||
if (mLocked)
|
||||
return false;
|
||||
|
||||
return getDeletableRecords().size()!=0;
|
||||
}
|
||||
|
||||
bool CSMWorld::CommandDispatcher::canRevert() const
|
||||
{
|
||||
if (mLocked)
|
||||
return false;
|
||||
|
||||
return getRevertableRecords().size()!=0;
|
||||
}
|
||||
|
||||
std::vector<CSMWorld::UniversalId> CSMWorld::CommandDispatcher::getExtendedTypes() const
|
||||
{
|
||||
std::vector<CSMWorld::UniversalId> tables;
|
||||
|
||||
if (mId==UniversalId::Type_Cells)
|
||||
{
|
||||
tables.push_back (mId);
|
||||
tables.push_back (UniversalId::Type_References);
|
||||
/// \todo add other cell-specific types
|
||||
}
|
||||
|
||||
return tables;
|
||||
}
|
||||
|
||||
void CSMWorld::CommandDispatcher::executeDelete()
|
||||
{
|
||||
if (mLocked)
|
||||
return;
|
||||
|
||||
std::vector<std::string> rows = getDeletableRecords();
|
||||
|
||||
if (rows.empty())
|
||||
return;
|
||||
|
||||
IdTable& model = dynamic_cast<IdTable&> (*mDocument.getData().getTableModel (mId));
|
||||
|
||||
int columnIndex = model.findColumnIndex (Columns::ColumnId_Id);
|
||||
|
||||
if (rows.size()>1)
|
||||
mDocument.getUndoStack().beginMacro (tr ("Delete multiple records"));
|
||||
|
||||
for (std::vector<std::string>::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter)
|
||||
{
|
||||
std::string id = model.data (model.getModelIndex (*iter, columnIndex)).
|
||||
toString().toUtf8().constData();
|
||||
|
||||
mDocument.getUndoStack().push (new CSMWorld::DeleteCommand (model, id));
|
||||
}
|
||||
|
||||
if (rows.size()>1)
|
||||
mDocument.getUndoStack().endMacro();
|
||||
}
|
||||
|
||||
void CSMWorld::CommandDispatcher::executeRevert()
|
||||
{
|
||||
if (mLocked)
|
||||
return;
|
||||
|
||||
std::vector<std::string> rows = getRevertableRecords();
|
||||
|
||||
if (rows.empty())
|
||||
return;
|
||||
|
||||
IdTable& model = dynamic_cast<IdTable&> (*mDocument.getData().getTableModel (mId));
|
||||
|
||||
int columnIndex = model.findColumnIndex (Columns::ColumnId_Id);
|
||||
|
||||
if (rows.size()>1)
|
||||
mDocument.getUndoStack().beginMacro (tr ("Revert multiple records"));
|
||||
|
||||
for (std::vector<std::string>::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter)
|
||||
{
|
||||
std::string id = model.data (model.getModelIndex (*iter, columnIndex)).
|
||||
toString().toUtf8().constData();
|
||||
|
||||
mDocument.getUndoStack().push (new CSMWorld::RevertCommand (model, id));
|
||||
}
|
||||
|
||||
if (rows.size()>1)
|
||||
mDocument.getUndoStack().endMacro();
|
||||
}
|
||||
|
||||
void CSMWorld::CommandDispatcher::executeExtendedDelete()
|
||||
{
|
||||
if (mExtendedTypes.size()>1)
|
||||
mDocument.getUndoStack().beginMacro (tr ("Extended delete of multiple records"));
|
||||
|
||||
for (std::vector<UniversalId>::const_iterator iter (mExtendedTypes.begin());
|
||||
iter!=mExtendedTypes.end(); ++iter)
|
||||
{
|
||||
if (*iter==mId)
|
||||
executeDelete();
|
||||
else if (*iter==UniversalId::Type_References)
|
||||
{
|
||||
IdTable& model = dynamic_cast<IdTable&> (
|
||||
*mDocument.getData().getTableModel (*iter));
|
||||
|
||||
const RefCollection& collection = mDocument.getData().getReferences();
|
||||
|
||||
int size = collection.getSize();
|
||||
|
||||
for (int i=size-1; i>=0; --i)
|
||||
{
|
||||
const Record<CellRef>& record = collection.getRecord (i);
|
||||
|
||||
if (record.mState==RecordBase::State_Deleted)
|
||||
continue;
|
||||
|
||||
if (!std::binary_search (mSelection.begin(), mSelection.end(),
|
||||
Misc::StringUtils::lowerCase (record.get().mCell)))
|
||||
continue;
|
||||
|
||||
mDocument.getUndoStack().push (
|
||||
new CSMWorld::DeleteCommand (model, record.get().mId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mExtendedTypes.size()>1)
|
||||
mDocument.getUndoStack().endMacro();
|
||||
}
|
||||
|
||||
void CSMWorld::CommandDispatcher::executeExtendedRevert()
|
||||
{
|
||||
if (mExtendedTypes.size()>1)
|
||||
mDocument.getUndoStack().beginMacro (tr ("Extended revert of multiple records"));
|
||||
|
||||
for (std::vector<UniversalId>::const_iterator iter (mExtendedTypes.begin());
|
||||
iter!=mExtendedTypes.end(); ++iter)
|
||||
{
|
||||
if (*iter==mId)
|
||||
executeRevert();
|
||||
else if (*iter==UniversalId::Type_References)
|
||||
{
|
||||
IdTable& model = dynamic_cast<IdTable&> (
|
||||
*mDocument.getData().getTableModel (*iter));
|
||||
|
||||
const RefCollection& collection = mDocument.getData().getReferences();
|
||||
|
||||
int size = collection.getSize();
|
||||
|
||||
for (int i=size-1; i>=0; --i)
|
||||
{
|
||||
const Record<CellRef>& record = collection.getRecord (i);
|
||||
|
||||
if (!std::binary_search (mSelection.begin(), mSelection.end(),
|
||||
Misc::StringUtils::lowerCase (record.get().mCell)))
|
||||
continue;
|
||||
|
||||
mDocument.getUndoStack().push (
|
||||
new CSMWorld::RevertCommand (model, record.get().mId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mExtendedTypes.size()>1)
|
||||
mDocument.getUndoStack().endMacro();
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
#ifndef CSM_WOLRD_COMMANDDISPATCHER_H
|
||||
#define CSM_WOLRD_COMMANDDISPATCHER_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "universalid.hpp"
|
||||
|
||||
namespace CSMDoc
|
||||
{
|
||||
class Document;
|
||||
}
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class CommandDispatcher : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
bool mLocked;
|
||||
CSMDoc::Document& mDocument;
|
||||
UniversalId mId;
|
||||
std::vector<std::string> mSelection;
|
||||
std::vector<UniversalId> mExtendedTypes;
|
||||
|
||||
std::vector<std::string> getDeletableRecords() const;
|
||||
|
||||
std::vector<std::string> getRevertableRecords() const;
|
||||
|
||||
public:
|
||||
|
||||
CommandDispatcher (CSMDoc::Document& document, const CSMWorld::UniversalId& id,
|
||||
QObject *parent = 0);
|
||||
///< \param id ID of the table the commands should operate on primarily.
|
||||
|
||||
void setEditLock (bool locked);
|
||||
|
||||
void setSelection (const std::vector<std::string>& selection);
|
||||
|
||||
void setExtendedTypes (const std::vector<UniversalId>& types);
|
||||
///< Set record lists selected by the user for extended operations.
|
||||
|
||||
bool canDelete() const;
|
||||
|
||||
bool canRevert() const;
|
||||
|
||||
/// Return IDs of the record collection that can also be affected when
|
||||
/// operating on the record collection this dispatcher is used for.
|
||||
///
|
||||
/// \note The returned collection contains the ID of the record collection this
|
||||
/// dispatcher is used for. However if that record collection does not support
|
||||
/// the extended mode, the returned vector will be empty instead.
|
||||
std::vector<UniversalId> getExtendedTypes() const;
|
||||
|
||||
public slots:
|
||||
|
||||
void executeDelete();
|
||||
|
||||
void executeRevert();
|
||||
|
||||
void executeExtendedDelete();
|
||||
|
||||
void executeExtendedRevert();
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,9 @@
|
||||
|
||||
#include "idtablebase.hpp"
|
||||
|
||||
CSMWorld::IdTableBase::IdTableBase (unsigned int features) : mFeatures (features) {}
|
||||
|
||||
unsigned int CSMWorld::IdTableBase::getFeatures() const
|
||||
{
|
||||
return mFeatures;
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
#ifndef CSM_WOLRD_IDTABLEBASE_H
|
||||
#define CSM_WOLRD_IDTABLEBASE_H
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
|
||||
#include "columns.hpp"
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class UniversalId;
|
||||
|
||||
class IdTableBase : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
enum Features
|
||||
{
|
||||
Feature_ReorderWithinTopic = 1,
|
||||
|
||||
/// Use ID column to generate view request (ID is transformed into
|
||||
/// worldspace and original ID is passed as hint with c: prefix).
|
||||
Feature_ViewId = 2,
|
||||
|
||||
/// Use cell column to generate view request (cell ID is transformed
|
||||
/// into worldspace and record ID is passed as hint with r: prefix).
|
||||
Feature_ViewCell = 4,
|
||||
|
||||
Feature_View = Feature_ViewId | Feature_ViewCell,
|
||||
|
||||
Feature_Preview = 8,
|
||||
|
||||
/// Table can not be modified through ordinary means.
|
||||
Feature_Constant = 16
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
unsigned int mFeatures;
|
||||
|
||||
public:
|
||||
|
||||
IdTableBase (unsigned int features);
|
||||
|
||||
virtual QModelIndex getModelIndex (const std::string& id, int column) const = 0;
|
||||
|
||||
/// Return index of column with the given \a id. If no such column exists, -1 is
|
||||
/// returned.
|
||||
virtual int searchColumnIndex (Columns::ColumnId id) const = 0;
|
||||
|
||||
/// Return index of column with the given \a id. If no such column exists, an
|
||||
/// exception is thrown.
|
||||
virtual int findColumnIndex (Columns::ColumnId id) const = 0;
|
||||
|
||||
/// Return the UniversalId and the hint for viewing \a row. If viewing is not
|
||||
/// supported by this table, return (UniversalId::Type_None, "").
|
||||
virtual std::pair<UniversalId, std::string> view (int row) const = 0;
|
||||
|
||||
/// Is \a id flagged as deleted?
|
||||
virtual bool isDeleted (const std::string& id) const = 0;
|
||||
|
||||
unsigned int getFeatures() const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,28 @@
|
||||
#include "land.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
|
||||
Land::Land()
|
||||
{
|
||||
mLand.reset(new ESM::Land());
|
||||
}
|
||||
|
||||
void Land::load(ESM::ESMReader &esm)
|
||||
{
|
||||
mLand->load(esm);
|
||||
|
||||
std::ostringstream stream;
|
||||
stream << "#" << mLand->mX << " " << mLand->mY;
|
||||
|
||||
mId = stream.str();
|
||||
}
|
||||
|
||||
void Land::blank()
|
||||
{
|
||||
/// \todo
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
#ifndef CSM_WORLD_LAND_H
|
||||
#define CSM_WORLD_LAND_H
|
||||
|
||||
#include <string>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <components/esm/loadland.hpp>
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
/// \brief Wrapper for Land record. Encodes X and Y cell index in the ID.
|
||||
///
|
||||
/// \todo Add worldspace support to the Land record.
|
||||
/// \todo Add a proper copy constructor (currently worked around using shared_ptr)
|
||||
struct Land
|
||||
{
|
||||
Land();
|
||||
|
||||
boost::shared_ptr<ESM::Land> mLand;
|
||||
|
||||
std::string mId;
|
||||
|
||||
/// Loads the metadata and ID
|
||||
void load (ESM::ESMReader &esm);
|
||||
|
||||
void blank();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,21 @@
|
||||
#include "landtexture.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
|
||||
void LandTexture::load(ESM::ESMReader &esm)
|
||||
{
|
||||
ESM::LandTexture::load(esm);
|
||||
|
||||
int plugin = esm.getIndex();
|
||||
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << mIndex << "_" << plugin;
|
||||
|
||||
mId = stream.str();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
#ifndef CSM_WORLD_LANDTEXTURE_H
|
||||
#define CSM_WORLD_LANDTEXTURE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <components/esm/loadltex.hpp>
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
/// \brief Wrapper for LandTexture record. Encodes mIndex and the plugin index (obtained from ESMReader)
|
||||
/// in the ID.
|
||||
///
|
||||
/// \attention The mId field of the ESM::LandTexture struct is not used.
|
||||
struct LandTexture : public ESM::LandTexture
|
||||
{
|
||||
std::string mId;
|
||||
|
||||
void load (ESM::ESMReader &esm);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,35 @@
|
||||
|
||||
#include "pathgrid.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
void CSMWorld::Pathgrid::load (ESM::ESMReader &esm, const IdCollection<Cell>& cells)
|
||||
{
|
||||
load (esm);
|
||||
|
||||
// correct ID
|
||||
if (!mId.empty() && mId[0]!='#' && cells.searchId (mId)==-1)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << "#" << mData.mX << " " << mData.mY;
|
||||
|
||||
mId = stream.str();
|
||||
}
|
||||
}
|
||||
|
||||
void CSMWorld::Pathgrid::load (ESM::ESMReader &esm)
|
||||
{
|
||||
ESM::Pathgrid::load (esm);
|
||||
|
||||
if (mCell.empty())
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << "#" << mData.mX << " " << mData.mY;
|
||||
|
||||
mId = stream.str();
|
||||
}
|
||||
else
|
||||
mId = mCell;
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
#ifndef CSM_WOLRD_PATHGRID_H
|
||||
#define CSM_WOLRD_PATHGRID_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <components/esm/loadpgrd.hpp>
|
||||
|
||||
#include "idcollection.hpp"
|
||||
#include "cell.hpp"
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
/// \brief Wrapper for Pathgrid record
|
||||
///
|
||||
/// \attention The mData.mX and mData.mY fields of the ESM::Pathgrid struct are not used.
|
||||
/// Exterior cell coordinates are encoded in the pathgrid ID.
|
||||
struct Pathgrid : public ESM::Pathgrid
|
||||
{
|
||||
std::string mId;
|
||||
|
||||
void load (ESM::ESMReader &esm, const IdCollection<Cell>& cells);
|
||||
|
||||
void load (ESM::ESMReader &esm);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -1,12 +1,8 @@
|
||||
|
||||
#include "ref.hpp"
|
||||
|
||||
#include "cell.hpp"
|
||||
|
||||
void CSMWorld::CellRef::load (ESM::ESMReader &esm, Cell& cell, const std::string& id)
|
||||
CSMWorld::CellRef::CellRef()
|
||||
{
|
||||
mId = id;
|
||||
mCell = cell.mId;
|
||||
|
||||
cell.addRef (mId);
|
||||
mRefNum.mIndex = 0;
|
||||
mRefNum.mContentFile = 0;
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
|
||||
#include "resources.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <OgreResourceGroupManager.h>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
CSMWorld::Resources::Resources (const std::string& baseDirectory, UniversalId::Type type,
|
||||
const char * const *extensions)
|
||||
: mBaseDirectory (baseDirectory), mType (type)
|
||||
{
|
||||
int baseSize = mBaseDirectory.size();
|
||||
|
||||
Ogre::StringVector resourcesGroups =
|
||||
Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
|
||||
|
||||
for (Ogre::StringVector::iterator iter (resourcesGroups.begin());
|
||||
iter!=resourcesGroups.end(); ++iter)
|
||||
{
|
||||
if (*iter=="General" || *iter=="Internal" || *iter=="Autodetect")
|
||||
continue;
|
||||
|
||||
Ogre::StringVectorPtr resources =
|
||||
Ogre::ResourceGroupManager::getSingleton().listResourceNames (*iter);
|
||||
|
||||
for (Ogre::StringVector::const_iterator iter (resources->begin());
|
||||
iter!=resources->end(); ++iter)
|
||||
{
|
||||
if (static_cast<int> (iter->size())<baseSize+1 ||
|
||||
iter->substr (0, baseSize)!=mBaseDirectory ||
|
||||
((*iter)[baseSize]!='/' && (*iter)[baseSize]!='\\'))
|
||||
continue;
|
||||
|
||||
if (extensions)
|
||||
{
|
||||
std::string::size_type index = iter->find_last_of ('.');
|
||||
|
||||
if (index==std::string::npos)
|
||||
continue;
|
||||
|
||||
std::string extension = iter->substr (index+1);
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (; extensions[i]; ++i)
|
||||
if (extensions[i]==extension)
|
||||
break;
|
||||
|
||||
if (!extensions[i])
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string file = iter->substr (baseSize+1);
|
||||
mFiles.push_back (file);
|
||||
mIndex.insert (std::make_pair (file, static_cast<int> (mFiles.size())-1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int CSMWorld::Resources::getSize() const
|
||||
{
|
||||
return mFiles.size();
|
||||
}
|
||||
|
||||
std::string CSMWorld::Resources::getId (int index) const
|
||||
{
|
||||
return mFiles.at (index);
|
||||
}
|
||||
|
||||
int CSMWorld::Resources::getIndex (const std::string& id) const
|
||||
{
|
||||
int index = searchId (id);
|
||||
|
||||
if (index==-1)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "Invalid resource: " << mBaseDirectory << '/' << id;
|
||||
|
||||
throw std::runtime_error (stream.str().c_str());
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
int CSMWorld::Resources::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;
|
||||
}
|
||||
|
||||
CSMWorld::UniversalId::Type CSMWorld::Resources::getType() const
|
||||
{
|
||||
return mType;
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
#ifndef CSM_WOLRD_RESOURCES_H
|
||||
#define CSM_WOLRD_RESOURCES_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "universalid.hpp"
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class Resources
|
||||
{
|
||||
std::map<std::string, int> mIndex;
|
||||
std::vector<std::string> mFiles;
|
||||
std::string mBaseDirectory;
|
||||
UniversalId::Type mType;
|
||||
|
||||
public:
|
||||
|
||||
/// \param type Type of resources in this table.
|
||||
Resources (const std::string& baseDirectory, UniversalId::Type type,
|
||||
const char * const *extensions = 0);
|
||||
|
||||
int getSize() const;
|
||||
|
||||
std::string getId (int index) const;
|
||||
|
||||
int getIndex (const std::string& id) const;
|
||||
|
||||
int searchId (const std::string& id) const;
|
||||
|
||||
UniversalId::Type getType() const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue