Merge branch 'load'

deque
Marc Zinnschlag 11 years ago
commit 5b9e90744e

@ -5,11 +5,11 @@ opencs_units (. editor)
set (CMAKE_BUILD_TYPE DEBUG)
opencs_units (model/doc
document operation saving
document operation saving documentmanager loader
)
opencs_units_noqt (model/doc
documentmanager stage savingstate savingstages
stage savingstate savingstages
)
opencs_hdrs_noqt (model/doc
@ -44,7 +44,7 @@ opencs_units_noqt (model/tools
opencs_units (view/doc
viewmanager view operations operation subview startup filedialog newgame
filewidget adjusterwidget
filewidget adjusterwidget loader
)

@ -38,6 +38,11 @@ CS::Editor::Editor (OgreInit::OgreInit& ogreInit)
mNewGame.setLocalData (mLocal);
mFileDialog.setLocalData (mLocal);
connect (&mDocumentManager, SIGNAL (documentAdded (CSMDoc::Document *)),
this, SLOT (documentAdded (CSMDoc::Document *)));
connect (&mDocumentManager, SIGNAL (lastDocumentDeleted()),
this, SLOT (lastDocumentDeleted()));
connect (&mViewManager, SIGNAL (newGameRequest ()), this, SLOT (createGame ()));
connect (&mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ()));
connect (&mViewManager, SIGNAL (loadDocumentRequest ()), this, SLOT (loadDocument ()));
@ -158,9 +163,8 @@ void CS::Editor::openFiles (const boost::filesystem::path &savePath)
foreach (const QString &path, mFileDialog.selectedFilePaths())
files.push_back(path.toUtf8().constData());
CSMDoc::Document *document = mDocumentManager.addDocument (files, savePath, false);
mDocumentManager.addDocument (files, savePath, false);
mViewManager.addView (document);
mFileDialog.hide();
}
@ -174,9 +178,8 @@ void CS::Editor::createNewFile (const boost::filesystem::path &savePath)
files.push_back(mFileDialog.filename().toUtf8().constData());
CSMDoc::Document *document = mDocumentManager.addDocument (files, savePath, true);
mDocumentManager.addDocument (files, savePath, true);
mViewManager.addView (document);
mFileDialog.hide();
}
@ -186,9 +189,7 @@ void CS::Editor::createNewGame (const boost::filesystem::path& file)
files.push_back (file);
CSMDoc::Document *document = mDocumentManager.addDocument (files, file, true);
mViewManager.addView (document);
mDocumentManager.addDocument (files, file, true);
mNewGame.hide();
}
@ -295,3 +296,13 @@ std::auto_ptr<sh::Factory> CS::Editor::setupGraphics()
return factory;
}
void CS::Editor::documentAdded (CSMDoc::Document *document)
{
mViewManager.addView (document);
}
void CS::Editor::lastDocumentDeleted()
{
exit (0);
}

@ -85,6 +85,10 @@ namespace CS
void showSettings();
void documentAdded (CSMDoc::Document *document);
void lastDocumentDeleted();
private:
QString mIpcServerName;

@ -3,6 +3,7 @@
#include <exception>
#include <iostream>
#include <string>
#include <QApplication>
#include <QIcon>
@ -18,6 +19,8 @@
#include <QDir>
#endif
Q_DECLARE_METATYPE (std::string)
class Application : public QApplication
{
private:
@ -45,6 +48,7 @@ int main(int argc, char *argv[])
{
Q_INIT_RESOURCE (resources);
qRegisterMetaType<std::string> ("std::string");
qRegisterMetaType<CSMWorld::UniversalId> ("CSMWorld::UniversalId");
OgreInit::OgreInit ogreInit;

@ -8,23 +8,6 @@
#include <components/files/configurationmanager.hpp>
#endif
void CSMDoc::Document::load (const std::vector<boost::filesystem::path>::const_iterator& begin,
const std::vector<boost::filesystem::path>::const_iterator& end, bool lastAsModified)
{
assert (begin!=end);
std::vector<boost::filesystem::path>::const_iterator end2 (end);
if (lastAsModified)
--end2;
for (std::vector<boost::filesystem::path>::const_iterator iter (begin); iter!=end2; ++iter)
getData().loadFile (*iter, true, false);
if (lastAsModified)
getData().loadFile (*end2, false, false);
}
void CSMDoc::Document::addGmsts()
{
static const char *gmstFloats[] =
@ -2219,64 +2202,40 @@ void CSMDoc::Document::createBase()
}
}
CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_)
: mSavePath (savePath), mContentFiles (files), mTools (mData), mResDir(resDir),
mProjectPath ((configuration.getUserDataPath() / "projects") /
(savePath.filename().string() + ".project")),
mSaving (*this, mProjectPath)
CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
const std::vector< boost::filesystem::path >& files, bool new_,
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir)
: mSavePath (savePath), mContentFiles (files), mNew (new_), mTools (mData), mResDir(resDir),
mProjectPath ((configuration.getUserDataPath() / "projects") /
(savePath.filename().string() + ".project")),
mSaving (*this, mProjectPath)
{
if (files.empty())
if (mContentFiles.empty())
throw std::runtime_error ("Empty content file sequence");
if (new_ && files.size()==1)
createBase();
else
{
std::vector<boost::filesystem::path>::const_iterator end = files.end();
if (new_)
--end;
load (files.begin(), end, !new_);
}
if (new_)
{
mData.setDescription ("");
mData.setAuthor ("");
}
bool filtersFound = false;
if (boost::filesystem::exists (mProjectPath))
{
filtersFound = true;
}
else
if (!boost::filesystem::exists (mProjectPath))
{
boost::filesystem::path locCustomFiltersPath (configuration.getUserDataPath());
locCustomFiltersPath /= "defaultfilters";
if (boost::filesystem::exists(locCustomFiltersPath))
if (boost::filesystem::exists (locCustomFiltersPath))
{
boost::filesystem::copy_file (locCustomFiltersPath, mProjectPath);
filtersFound = true;
}
else
{
boost::filesystem::path filters(mResDir);
filters /= "defaultfilters";
if (boost::filesystem::exists(filters))
{
boost::filesystem::copy_file(filters, mProjectPath);
filtersFound = true;
}
boost::filesystem::copy_file (mResDir / "defaultfilters", mProjectPath);
}
}
if (filtersFound)
getData().loadFile (mProjectPath, false, true);
if (mNew)
{
mData.setDescription ("");
mData.setAuthor ("");
if (mContentFiles.size()==1)
createBase();
}
addOptionalGmsts();
addOptionalGlobals();
@ -2288,6 +2247,7 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, co
connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
connect (&mSaving, SIGNAL (done (int)), this, SLOT (operationDone (int)));
connect (
&mSaving, SIGNAL (reportMessage (const CSMWorld::UniversalId&, const std::string&, int)),
this, SLOT (reportMessage (const CSMWorld::UniversalId&, const std::string&, int)));
@ -2323,11 +2283,21 @@ const boost::filesystem::path& CSMDoc::Document::getSavePath() const
return mSavePath;
}
const boost::filesystem::path& CSMDoc::Document::getProjectPath() const
{
return mProjectPath;
}
const std::vector<boost::filesystem::path>& CSMDoc::Document::getContentFiles() const
{
return mContentFiles;
}
bool CSMDoc::Document::isNew() const
{
return mNew;
}
void CSMDoc::Document::save()
{
if (mSaving.isRunning())

@ -39,6 +39,7 @@ namespace CSMDoc
boost::filesystem::path mSavePath;
std::vector<boost::filesystem::path> mContentFiles;
bool mNew;
CSMWorld::Data mData;
CSMTools::Tools mTools;
boost::filesystem::path mProjectPath;
@ -53,10 +54,6 @@ namespace CSMDoc
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();
@ -72,9 +69,8 @@ namespace CSMDoc
public:
Document (const Files::ConfigurationManager& configuration,
const std::vector< boost::filesystem::path >& files,
const boost::filesystem::path& savePath,
const boost::filesystem::path& resDir, bool new_);
const std::vector< boost::filesystem::path >& files, bool new_,
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir);
~Document();
@ -84,10 +80,15 @@ namespace CSMDoc
const boost::filesystem::path& getSavePath() const;
const boost::filesystem::path& getProjectPath() const;
const std::vector<boost::filesystem::path>& getContentFiles() const;
///< \attention The last element in this collection is the file that is being edited,
/// but with its original path instead of the save path.
bool isNew() const;
///< Is this a newly created content file?
void save();
CSMWorld::UniversalId verify();

@ -19,38 +19,77 @@ CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& con
if (!boost::filesystem::is_directory (projectPath))
boost::filesystem::create_directories (projectPath);
mLoader.moveToThread (&mLoaderThread);
mLoaderThread.start();
connect (&mLoader, SIGNAL (documentLoaded (Document *)),
this, SLOT (documentLoaded (Document *)));
connect (&mLoader, SIGNAL (documentNotLoaded (Document *, const std::string&)),
this, SLOT (documentNotLoaded (Document *, const std::string&)));
connect (this, SIGNAL (loadRequest (CSMDoc::Document *)),
&mLoader, SLOT (loadDocument (CSMDoc::Document *)));
connect (&mLoader, SIGNAL (nextStage (CSMDoc::Document *, const std::string&, int)),
this, SIGNAL (nextStage (CSMDoc::Document *, const std::string&, int)));
connect (&mLoader, SIGNAL (nextRecord (CSMDoc::Document *)),
this, SIGNAL (nextRecord (CSMDoc::Document *)));
connect (this, SIGNAL (cancelLoading (CSMDoc::Document *)),
&mLoader, SLOT (abortLoading (CSMDoc::Document *)));
connect (&mLoader, SIGNAL (loadMessage (CSMDoc::Document *, const std::string&)),
this, SIGNAL (loadMessage (CSMDoc::Document *, const std::string&)));
}
CSMDoc::DocumentManager::~DocumentManager()
{
mLoaderThread.quit();
mLoader.hasThingsToDo().wakeAll();
mLoaderThread.wait();
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, const boost::filesystem::path& savePath,
void CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath,
bool new_)
{
Document *document = new Document (mConfiguration, files, savePath, mResDir, new_);
Document *document = new Document (mConfiguration, files, new_, savePath, mResDir);
mDocuments.push_back (document);
return document;
emit loadRequest (document);
mLoader.hasThingsToDo().wakeAll();
}
bool CSMDoc::DocumentManager::removeDocument (Document *document)
void CSMDoc::DocumentManager::removeDocument (CSMDoc::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");
throw std::runtime_error ("removing invalid document");
mDocuments.erase (iter);
delete document;
return mDocuments.empty();
if (mDocuments.empty())
emit lastDocumentDeleted();
}
void CSMDoc::DocumentManager::setResourceDir (const boost::filesystem::path& parResDir)
{
mResDir = boost::filesystem::system_complete(parResDir);
}
void CSMDoc::DocumentManager::documentLoaded (Document *document)
{
emit documentAdded (document);
emit loadingStopped (document, true, "");
}
void CSMDoc::DocumentManager::documentNotLoaded (Document *document, const std::string& error)
{
emit loadingStopped (document, false, error);
if (error.empty()) // do not remove the document yet, if we have an error
removeDocument (document);
}

@ -6,6 +6,11 @@
#include <boost/filesystem/path.hpp>
#include <QObject>
#include <QThread>
#include "loader.hpp"
namespace Files
{
class ConfigurationManager;
@ -15,10 +20,14 @@ namespace CSMDoc
{
class Document;
class DocumentManager
class DocumentManager : public QObject
{
Q_OBJECT
std::vector<Document *> mDocuments;
const Files::ConfigurationManager& mConfiguration;
QThread mLoaderThread;
Loader mLoader;
DocumentManager (const DocumentManager&);
DocumentManager& operator= (const DocumentManager&);
@ -29,20 +38,49 @@ namespace CSMDoc
~DocumentManager();
Document *addDocument (const std::vector< boost::filesystem::path >& files,
const boost::filesystem::path& savePath,
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
void addDocument (const std::vector< boost::filesystem::path >& files,
const boost::filesystem::path& savePath, bool new_);
///< \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?
void setResourceDir (const boost::filesystem::path& parResDir);
private:
private:
boost::filesystem::path mResDir;
private slots:
void documentLoaded (Document *document);
///< The ownership of \a document is not transferred.
void documentNotLoaded (Document *document, const std::string& error);
///< Document load has been interrupted either because of a call to abortLoading
/// or a problem during loading). In the former case error will be an empty string.
public slots:
void removeDocument (CSMDoc::Document *document);
///< Emits the lastDocumentDeleted signal, if applicable.
signals:
void documentAdded (CSMDoc::Document *document);
void loadRequest (CSMDoc::Document *document);
void lastDocumentDeleted();
void loadingStopped (CSMDoc::Document *document, bool completed,
const std::string& error);
void nextStage (CSMDoc::Document *document, const std::string& name, int steps);
void nextRecord (CSMDoc::Document *document);
void cancelLoading (CSMDoc::Document *document);
void loadMessage (CSMDoc::Document *document, const std::string& message);
};
}

@ -0,0 +1,130 @@
#include "loader.hpp"
#include <QTimer>
#include "../tools/reportmodel.hpp"
#include "document.hpp"
#include "state.hpp"
CSMDoc::Loader::Stage::Stage() : mFile (0), mRecordsLeft (false) {}
CSMDoc::Loader::Loader()
{
QTimer *timer = new QTimer (this);
connect (timer, SIGNAL (timeout()), this, SLOT (load()));
timer->start();
}
QWaitCondition& CSMDoc::Loader::hasThingsToDo()
{
return mThingsToDo;
}
void CSMDoc::Loader::load()
{
if (mDocuments.empty())
{
mMutex.lock();
mThingsToDo.wait (&mMutex);
mMutex.unlock();
return;
}
std::vector<std::pair<Document *, Stage> >::iterator iter = mDocuments.begin();
Document *document = iter->first;
int size = static_cast<int> (document->getContentFiles().size());
if (document->isNew())
--size;
bool done = false;
const int batchingSize = 100;
try
{
if (iter->second.mRecordsLeft)
{
CSMDoc::Stage::Messages messages;
for (int i=0; i<batchingSize; ++i) // do not flood the system with update signals
if (document->getData().continueLoading (messages))
{
iter->second.mRecordsLeft = false;
break;
}
CSMWorld::UniversalId log (CSMWorld::UniversalId::Type_LoadErrorLog, 0);
for (CSMDoc::Stage::Messages::const_iterator iter (messages.begin());
iter!=messages.end(); ++iter)
{
document->getReport (log)->add (iter->first, iter->second);
emit loadMessage (document, iter->second);
}
emit nextRecord (document);
return;
}
if (iter->second.mFile<size)
{
boost::filesystem::path path = document->getContentFiles()[iter->second.mFile];
int steps = document->getData().startLoading (path, iter->second.mFile<size-1, false);
iter->second.mRecordsLeft = true;
emit nextStage (document, path.filename().string(), steps/batchingSize);
}
else if (iter->second.mFile==size)
{
int steps = document->getData().startLoading (document->getProjectPath(), false, true);
iter->second.mRecordsLeft = true;
emit nextStage (document, "Project File", steps/batchingSize);
}
else
{
done = true;
}
++(iter->second.mFile);
}
catch (const std::exception& e)
{
mDocuments.erase (iter);
emit documentNotLoaded (document, e.what());
return;
}
if (done)
{
mDocuments.erase (iter);
emit documentLoaded (document);
}
}
void CSMDoc::Loader::loadDocument (CSMDoc::Document *document)
{
mDocuments.push_back (std::make_pair (document, Stage()));
}
void CSMDoc::Loader::abortLoading (CSMDoc::Document *document)
{
for (std::vector<std::pair<Document *, Stage> >::iterator iter = mDocuments.begin();
iter!=mDocuments.end(); ++iter)
{
if (iter->first==document)
{
mDocuments.erase (iter);
emit documentNotLoaded (document, "");
break;
}
}
}

@ -0,0 +1,71 @@
#ifndef CSM_DOC_LOADER_H
#define CSM_DOC_LOADER_H
#include <vector>
#include <QObject>
#include <QMutex>
#include <QWaitCondition>
namespace CSMDoc
{
class Document;
class Loader : public QObject
{
Q_OBJECT
struct Stage
{
int mFile;
bool mRecordsLeft;
Stage();
};
QMutex mMutex;
QWaitCondition mThingsToDo;
std::vector<std::pair<Document *, Stage> > mDocuments;
public:
Loader();
QWaitCondition& hasThingsToDo();
private slots:
void load();
public slots:
void loadDocument (CSMDoc::Document *document);
///< The ownership of \a document is not transferred.
void abortLoading (CSMDoc::Document *document);
///< Abort loading \a docuemnt (ignored if \a document has already finished being
/// loaded). Will result in a documentNotLoaded signal, once the Loader has finished
/// cleaning up.
signals:
void documentLoaded (Document *document);
///< The ownership of \a document is not transferred.
void documentNotLoaded (Document *document, const std::string& error);
///< Document load has been interrupted either because of a call to abortLoading
/// or a problem during loading). In the former case error will be an empty string.
void nextStage (CSMDoc::Document *document, const std::string& name, int steps);
void nextRecord (CSMDoc::Document *document);
///< \note This signal is only given once per group of records. The group size is
/// approximately the total number of records divided by the steps value of the
/// previous nextStage signal.
void loadMessage (CSMDoc::Document *document, const std::string& message);
///< Non-critical load error or warning
};
}
#endif

@ -3,17 +3,18 @@
namespace CSMDoc
{
enum State
{
State_Modified = 1,
State_Locked = 2,
State_Operation = 4,
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
};
State_Saving = 8,
State_Verifying = 16,
State_Compiling = 32, // not implemented yet
State_Searching = 64, // not implemented yet
State_Loading = 128 // pseudo-state; can not be encountered in a loaded document
};
}
#endif

@ -88,13 +88,17 @@ CSMDoc::Operation *CSMTools::Tools::getVerifier()
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;
// index 0: load error log
mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel));
mActiveReports.insert (std::make_pair (CSMDoc::State_Loading, 0));
}
CSMTools::Tools::~Tools()
{
delete mVerifier;
for (std::map<int, ReportModel *>::iterator iter (mReports.begin()); iter!=mReports.end(); ++iter)
delete iter->second;
}
CSMWorld::UniversalId CSMTools::Tools::runVerifier()
@ -133,7 +137,8 @@ int CSMTools::Tools::getRunningOperations() const
CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId& id)
{
if (id.getType()!=CSMWorld::UniversalId::Type_VerificationResults)
if (id.getType()!=CSMWorld::UniversalId::Type_VerificationResults &&
id.getType()!=CSMWorld::UniversalId::Type_LoadErrorLog)
throw std::logic_error ("invalid request for report model: " + id.toString());
return mReports.at (id.getIndex());

@ -55,7 +55,10 @@ int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collec
return number;
}
CSMWorld::Data::Data() : mRefs (mCells)
CSMWorld::Data::Data()
/// \todo set encoding properly, once config implementation has been fixed.
: mEncoder (ToUTF8::calculateEncoding ("win1252")),
mRefs (mCells), mReader (0), mDialogue (0)
{
mGlobals.addColumn (new StringIdColumn<ESM::Global>);
mGlobals.addColumn (new RecordStateColumn<ESM::Global>);
@ -260,6 +263,8 @@ CSMWorld::Data::~Data()
{
for (std::vector<QAbstractItemModel *>::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter)
delete *iter;
delete mReader;
}
const CSMWorld::IdCollection<ESM::Global>& CSMWorld::Data::getGlobals() const
@ -481,148 +486,166 @@ void CSMWorld::Data::merge()
mGlobals.merge();
}
void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base, bool project)
int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base, bool project)
{
ESM::ESMReader reader;
delete mReader;
mReader = 0;
mDialogue = 0;
mReader = new ESM::ESMReader;
mReader->setEncoder (&mEncoder);
mReader->open (path.string());
/// \todo set encoding properly, once config implementation has been fixed.
ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding ("win1252"));
reader.setEncoder (&encoder);
mBase = base;
mProject = project;
reader.open (path.string());
mAuthor = mReader->getAuthor();
mDescription = mReader->getDesc();
const ESM::Dialogue *dialogue = 0;
return mReader->getRecordCount();
}
mAuthor = reader.getAuthor();
mDescription = reader.getDesc();
bool CSMWorld::Data::continueLoading (CSMDoc::Stage::Messages& messages)
{
if (!mReader)
throw std::logic_error ("can't continue loading, because no load has been started");
// 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())
if (!mReader->hasMoreRecs())
{
ESM::NAME n = reader.getRecName();
reader.getRecHeader();
delete mReader;
mReader = 0;
mDialogue = 0;
return true;
}
ESM::NAME n = mReader->getRecName();
mReader->getRecHeader();
switch (n.val)
switch (n.val)
{
case ESM::REC_GLOB: mGlobals.load (*mReader, mBase); break;
case ESM::REC_GMST: mGmsts.load (*mReader, mBase); break;
case ESM::REC_SKIL: mSkills.load (*mReader, mBase); break;
case ESM::REC_CLAS: mClasses.load (*mReader, mBase); break;
case ESM::REC_FACT: mFactions.load (*mReader, mBase); break;
case ESM::REC_RACE: mRaces.load (*mReader, mBase); break;
case ESM::REC_SOUN: mSounds.load (*mReader, mBase); break;
case ESM::REC_SCPT: mScripts.load (*mReader, mBase); break;
case ESM::REC_REGN: mRegions.load (*mReader, mBase); break;
case ESM::REC_BSGN: mBirthsigns.load (*mReader, mBase); break;
case ESM::REC_SPEL: mSpells.load (*mReader, mBase); break;
case ESM::REC_CELL:
mCells.load (*mReader, mBase);
mRefs.load (*mReader, mCells.getSize()-1, mBase);
break;
case ESM::REC_ACTI: mReferenceables.load (*mReader, mBase, UniversalId::Type_Activator); break;
case ESM::REC_ALCH: mReferenceables.load (*mReader, mBase, UniversalId::Type_Potion); break;
case ESM::REC_APPA: mReferenceables.load (*mReader, mBase, UniversalId::Type_Apparatus); break;
case ESM::REC_ARMO: mReferenceables.load (*mReader, mBase, UniversalId::Type_Armor); break;
case ESM::REC_BOOK: mReferenceables.load (*mReader, mBase, UniversalId::Type_Book); break;
case ESM::REC_CLOT: mReferenceables.load (*mReader, mBase, UniversalId::Type_Clothing); break;
case ESM::REC_CONT: mReferenceables.load (*mReader, mBase, UniversalId::Type_Container); break;
case ESM::REC_CREA: mReferenceables.load (*mReader, mBase, UniversalId::Type_Creature); break;
case ESM::REC_DOOR: mReferenceables.load (*mReader, mBase, UniversalId::Type_Door); break;
case ESM::REC_INGR: mReferenceables.load (*mReader, mBase, UniversalId::Type_Ingredient); break;
case ESM::REC_LEVC:
mReferenceables.load (*mReader, mBase, UniversalId::Type_CreatureLevelledList); break;
case ESM::REC_LEVI:
mReferenceables.load (*mReader, mBase, UniversalId::Type_ItemLevelledList); break;
case ESM::REC_LIGH: mReferenceables.load (*mReader, mBase, UniversalId::Type_Light); break;
case ESM::REC_LOCK: mReferenceables.load (*mReader, mBase, UniversalId::Type_Lockpick); break;
case ESM::REC_MISC:
mReferenceables.load (*mReader, mBase, UniversalId::Type_Miscellaneous); break;
case ESM::REC_NPC_: mReferenceables.load (*mReader, mBase, UniversalId::Type_Npc); break;
case ESM::REC_PROB: mReferenceables.load (*mReader, mBase, UniversalId::Type_Probe); break;
case ESM::REC_REPA: mReferenceables.load (*mReader, mBase, UniversalId::Type_Repair); break;
case ESM::REC_STAT: mReferenceables.load (*mReader, mBase, UniversalId::Type_Static); break;
case ESM::REC_WEAP: mReferenceables.load (*mReader, mBase, UniversalId::Type_Weapon); break;
case ESM::REC_DIAL:
{
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;
std::string id = mReader->getHNOString ("NAME");
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;
case ESM::REC_DIAL:
{
std::string id = reader.getHNOString ("NAME");
ESM::Dialogue record;
record.mId = id;
record.load (*mReader);
ESM::Dialogue record;
record.mId = id;
record.load (reader);
if (record.mType==ESM::Dialogue::Journal)
{
mJournals.load (record, mBase);
mDialogue = &mJournals.getRecord (id).get();
}
else if (record.mType==ESM::Dialogue::Deleted)
{
mDialogue = 0; // record vector can be shuffled around which would make pointer
// to record invalid
if (record.mType==ESM::Dialogue::Journal)
if (mJournals.tryDelete (id))
{
mJournals.load (record, base);
dialogue = &mJournals.getRecord (id).get();
/// \todo handle info records
}
else if (record.mType==ESM::Dialogue::Deleted)
else if (mTopics.tryDelete (id))
{
dialogue = 0; // record vector can be shuffled around which would make pointer
// to record invalid
if (mJournals.tryDelete (id))
{
/// \todo handle info records
}
else if (mTopics.tryDelete (id))
{
/// \todo handle info records
}
else
{
/// \todo report deletion of non-existing record
}
/// \todo handle info records
}
else
{
mTopics.load (record, base);
dialogue = &mTopics.getRecord (id).get();
messages.push_back (std::make_pair (UniversalId::Type_None,
"Trying to delete dialogue record " + id + " which does not exist"));
}
}
else
{
mTopics.load (record, mBase);
mDialogue = &mTopics.getRecord (id).get();
}
break;
}
case ESM::REC_INFO:
{
if (!mDialogue)
{
messages.push_back (std::make_pair (UniversalId::Type_None,
"Found info record not following a dialogue record"));
mReader->skipRecord();
break;
}
case ESM::REC_INFO:
{
if (!dialogue)
{
/// \todo INFO record without matching DIAL record -> report to user
reader.skipRecord();
break;
}
if (mDialogue->mType==ESM::Dialogue::Journal)
mJournalInfos.load (*mReader, mBase, *mDialogue);
else
mTopicInfos.load (*mReader, mBase, *mDialogue);
if (dialogue->mType==ESM::Dialogue::Journal)
mJournalInfos.load (reader, base, *dialogue);
else
mTopicInfos.load (reader, base, *dialogue);
break;
}
case ESM::REC_FILT:
if (mProject)
{
mFilters.load (*mReader, mBase);
mFilters.setData (mFilters.getSize()-1,
mFilters.findColumnIndex (CSMWorld::Columns::ColumnId_Scope),
static_cast<int> (CSMFilter::Filter::Scope_Project));
break;
}
case ESM::REC_FILT:
if (project)
{
mFilters.load (reader, base);
mFilters.setData (mFilters.getSize()-1,
mFilters.findColumnIndex (CSMWorld::Columns::ColumnId_Scope),
static_cast<int> (CSMFilter::Filter::Scope_Project));
break;
}
// fall through (filter record in a content file is an error with format 0)
// fall through (filter record in a content file is an error with format 0)
default:
default:
messages.push_back (std::make_pair (UniversalId::Type_None,
"Unsupported record type: " + n.toString()));
/// \todo throw an exception instead, once all records are implemented
/// or maybe report error and continue?
reader.skipRecord();
}
mReader->skipRecord();
}
return false;
}
bool CSMWorld::Data::hasId (const std::string& id) const

@ -24,6 +24,8 @@
#include "../filter/filter.hpp"
#include "../doc/stage.hpp"
#include "idcollection.hpp"
#include "universalid.hpp"
#include "cell.hpp"
@ -33,12 +35,19 @@
class QAbstractItemModel;
namespace ESM
{
class ESMReader;
struct Dialogue;
}
namespace CSMWorld
{
class Data : public QObject
{
Q_OBJECT
ToUTF8::Utf8Encoder mEncoder;
IdCollection<ESM::Global> mGlobals;
IdCollection<ESM::GameSetting> mGmsts;
IdCollection<ESM::Skill> mSkills;
@ -62,6 +71,10 @@ namespace CSMWorld
std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex;
std::string mAuthor;
std::string mDescription;
ESM::ESMReader *mReader;
const ESM::Dialogue *mDialogue; // last loaded dialogue
bool mBase;
bool mProject;
// not implemented
Data (const Data&);
@ -167,10 +180,15 @@ namespace CSMWorld
void merge();
///< Merge modified into base.
void loadFile (const boost::filesystem::path& path, bool base, bool project);
///< Merging content of a file into base or modified.
int startLoading (const boost::filesystem::path& path, bool base, bool project);
///< Begin merging content of a file into base or modified.
///
/// \param project load project file instead of content file
///
///< \return estimated number of records
bool continueLoading (CSMDoc::Stage::Messages& messages);
///< \return Finished?
bool hasId (const std::string& id) const;

@ -18,7 +18,7 @@ namespace
static const TypeData sNoArg[] =
{
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "empty", 0 },
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "-", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills", 0 },
@ -101,6 +101,7 @@ namespace
static const TypeData sIndexArg[] =
{
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", 0 },
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_LoadErrorLog, "Load Error Log", 0 },
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker
};
}

@ -97,10 +97,11 @@ namespace CSMWorld
Type_JournalInfos,
Type_JournalInfo,
Type_Scene,
Type_Preview
Type_Preview,
Type_LoadErrorLog
};
enum { NumberOfTypes = Type_Scene+1 };
enum { NumberOfTypes = Type_LoadErrorLog+1 };
private:

@ -0,0 +1,193 @@
#include "loader.hpp"
#include <QVBoxLayout>
#include <QLabel>
#include <QProgressBar>
#include <QCursor>
#include <QDialogButtonBox>
#include <QCloseEvent>
#include <QListWidget>
#include "../../model/doc/document.hpp"
void CSVDoc::LoadingDocument::closeEvent (QCloseEvent *event)
{
event->ignore();
cancel();
}
CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document)
: mDocument (document), mAborted (false), mMessages (0)
{
setWindowTitle (("Opening " + document->getSavePath().filename().string()).c_str());
setMinimumWidth (400);
mLayout = new QVBoxLayout (this);
// file progress
mFile = new QLabel (this);
mLayout->addWidget (mFile);
mFileProgress = new QProgressBar (this);
mLayout->addWidget (mFileProgress);
int size = static_cast<int> (document->getContentFiles().size())+1;
if (document->isNew())
--size;
mFileProgress->setMinimum (0);
mFileProgress->setMaximum (size);
mFileProgress->setTextVisible (true);
mFileProgress->setValue (0);
// record progress
mLayout->addWidget (new QLabel ("Records", this));
mRecordProgress = new QProgressBar (this);
mLayout->addWidget (mRecordProgress);
mRecordProgress->setMinimum (0);
mRecordProgress->setTextVisible (true);
mRecordProgress->setValue (0);
// error message
mError = new QLabel (this);
mError->setWordWrap (true);
mLayout->addWidget (mError);
// buttons
mButtons = new QDialogButtonBox (QDialogButtonBox::Cancel, Qt::Horizontal, this);
mLayout->addWidget (mButtons);
setLayout (mLayout);
move (QCursor::pos());
show();
connect (mButtons, SIGNAL (rejected()), this, SLOT (cancel()));
}
void CSVDoc::LoadingDocument::nextStage (const std::string& name, int steps)
{
mFile->setText (QString::fromUtf8 (("Loading: " + name).c_str()));
mFileProgress->setValue (mFileProgress->value()+1);
mRecordProgress->setValue (0);
mRecordProgress->setMaximum (steps>0 ? steps : 1);
}
void CSVDoc::LoadingDocument::nextRecord()
{
int value = mRecordProgress->value()+1;
if (value<=mRecordProgress->maximum())
mRecordProgress->setValue (value);
}
void CSVDoc::LoadingDocument::abort (const std::string& error)
{
mAborted = true;
mError->setText (QString::fromUtf8 (("Loading failed: " + error).c_str()));
mButtons->setStandardButtons (QDialogButtonBox::Close);
}
void CSVDoc::LoadingDocument::addMessage (const std::string& message)
{
if (!mMessages)
{
mMessages = new QListWidget (this);
mLayout->insertWidget (4, mMessages);
}
new QListWidgetItem (QString::fromUtf8 (message.c_str()), mMessages);
}
void CSVDoc::LoadingDocument::cancel()
{
if (!mAborted)
emit cancel (mDocument);
else
{
emit close (mDocument);
deleteLater();
}
}
CSVDoc::Loader::Loader() {}
CSVDoc::Loader::~Loader()
{
for (std::map<CSMDoc::Document *, LoadingDocument *>::iterator iter (mDocuments.begin());
iter!=mDocuments.end(); ++iter)
delete iter->second;
}
void CSVDoc::Loader::add (CSMDoc::Document *document)
{
LoadingDocument *loading = new LoadingDocument (document);
mDocuments.insert (std::make_pair (document, loading));
connect (loading, SIGNAL (cancel (CSMDoc::Document *)),
this, SIGNAL (cancel (CSMDoc::Document *)));
connect (loading, SIGNAL (close (CSMDoc::Document *)),
this, SIGNAL (close (CSMDoc::Document *)));
}
void CSVDoc::Loader::loadingStopped (CSMDoc::Document *document, bool completed,
const std::string& error)
{
std::map<CSMDoc::Document *, LoadingDocument *>::iterator iter = mDocuments.begin();
for (; iter!=mDocuments.end(); ++iter)
if (iter->first==document)
break;
if (iter==mDocuments.end())
return;
if (completed || error.empty())
{
delete iter->second;
mDocuments.erase (iter);
}
else if (!completed && !error.empty())
{
iter->second->abort (error);
// Leave the window open for now (wait for the user to close it)
mDocuments.erase (iter);
}
}
void CSVDoc::Loader::nextStage (CSMDoc::Document *document, const std::string& name, int steps)
{
std::map<CSMDoc::Document *, LoadingDocument *>::iterator iter = mDocuments.find (document);
if (iter!=mDocuments.end())
iter->second->nextStage (name, steps);
}
void CSVDoc::Loader::nextRecord (CSMDoc::Document *document)
{
std::map<CSMDoc::Document *, LoadingDocument *>::iterator iter = mDocuments.find (document);
if (iter!=mDocuments.end())
iter->second->nextRecord();
}
void CSVDoc::Loader::loadMessage (CSMDoc::Document *document, const std::string& message)
{
std::map<CSMDoc::Document *, LoadingDocument *>::iterator iter = mDocuments.find (document);
if (iter!=mDocuments.end())
iter->second->addMessage (message);
}

@ -0,0 +1,99 @@
#ifndef CSV_DOC_LOADER_H
#define CSV_DOC_LOADER_H
#include <map>
#include <QObject>
#include <QWidget>
#include <QSignalMapper>
class QLabel;
class QProgressBar;
class QDialogButtonBox;
class QListWidget;
class QVBoxLayout;
namespace CSMDoc
{
class Document;
}
namespace CSVDoc
{
class LoadingDocument : public QWidget
{
Q_OBJECT
CSMDoc::Document *mDocument;
QLabel *mFile;
QProgressBar *mFileProgress;
QProgressBar *mRecordProgress;
bool mAborted;
QDialogButtonBox *mButtons;
QLabel *mError;
QListWidget *mMessages;
QVBoxLayout *mLayout;
private:
void closeEvent (QCloseEvent *event);
public:
LoadingDocument (CSMDoc::Document *document);
void nextStage (const std::string& name, int steps);
void nextRecord();
void abort (const std::string& error);
void addMessage (const std::string& message);
private slots:
void cancel();
signals:
void cancel (CSMDoc::Document *document);
///< Stop loading process.
void close (CSMDoc::Document *document);
///< Close stopped loading process.
};
class Loader : public QObject
{
Q_OBJECT
std::map<CSMDoc::Document *, LoadingDocument *> mDocuments;
public:
Loader();
virtual ~Loader();
signals:
void cancel (CSMDoc::Document *document);
void close (CSMDoc::Document *document);
public slots:
void add (CSMDoc::Document *document);
void loadingStopped (CSMDoc::Document *document, bool completed,
const std::string& error);
void nextStage (CSMDoc::Document *document, const std::string& name, int steps);
void nextRecord (CSMDoc::Document *document);
void loadMessage (CSMDoc::Document *document, const std::string& message);
};
}
#endif

@ -10,9 +10,12 @@
#include <QtGui/QApplication>
#include "../../model/doc/document.hpp"
#include "../../model/settings/usersettings.hpp"
#include "../world/subviews.hpp"
#include "../tools/subviews.hpp"
#include "../../model/settings/usersettings.hpp"
#include "viewmanager.hpp"
#include "operations.hpp"
#include "subview.hpp"
@ -47,6 +50,10 @@ void CSVDoc::View::setupFileMenu()
connect (mVerify, SIGNAL (triggered()), this, SLOT (verify()));
file->addAction (mVerify);
QAction *loadErrors = new QAction (tr ("Load Error Log"), this);
connect (loadErrors, SIGNAL (triggered()), this, SLOT (loadErrorLog()));
file->addAction (loadErrors);
QAction *close = new QAction (tr ("&Close"), this);
connect (close, SIGNAL (triggered()), this, SLOT (close()));
file->addAction(close);
@ -502,3 +509,8 @@ void CSVDoc::View::toggleShowStatusBar (bool show)
subView->setStatusBar (show);
}
}
void CSVDoc::View::loadErrorLog()
{
addSubView (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_LoadErrorLog, 0));
}

@ -179,6 +179,8 @@ namespace CSVDoc
void addJournalInfosSubView();
void toggleShowStatusBar (bool show);
void loadErrorLog();
};
}

@ -84,6 +84,33 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager)
for (std::size_t i=0; i<sizeof (sMapping)/sizeof (Mapping); ++i)
mDelegateFactories->add (sMapping[i].mDisplay, new CSVWorld::EnumDelegateFactory (
CSMWorld::Columns::getEnums (sMapping[i].mColumnId), sMapping[i].mAllowNone));
connect (&mDocumentManager, SIGNAL (loadRequest (CSMDoc::Document *)),
&mLoader, SLOT (add (CSMDoc::Document *)));
connect (
&mDocumentManager, SIGNAL (loadingStopped (CSMDoc::Document *, bool, const std::string&)),
&mLoader, SLOT (loadingStopped (CSMDoc::Document *, bool, const std::string&)));
connect (
&mDocumentManager, SIGNAL (nextStage (CSMDoc::Document *, const std::string&, int)),
&mLoader, SLOT (nextStage (CSMDoc::Document *, const std::string&, int)));
connect (
&mDocumentManager, SIGNAL (nextRecord (CSMDoc::Document *)),
&mLoader, SLOT (nextRecord (CSMDoc::Document *)));
connect (
&mDocumentManager, SIGNAL (loadMessage (CSMDoc::Document *, const std::string&)),
&mLoader, SLOT (loadMessage (CSMDoc::Document *, const std::string&)));
connect (
&mLoader, SIGNAL (cancel (CSMDoc::Document *)),
&mDocumentManager, SIGNAL (cancelLoading (CSMDoc::Document *)));
connect (
&mLoader, SIGNAL (close (CSMDoc::Document *)),
&mDocumentManager, SLOT (removeDocument (CSMDoc::Document *)));
}
CSVDoc::ViewManager::~ViewManager()

@ -5,6 +5,8 @@
#include <QObject>
#include "loader.hpp"
namespace CSMDoc
{
class Document;
@ -29,6 +31,7 @@ namespace CSVDoc
CSVWorld::CommandDelegateFactoryCollection *mDelegateFactories;
bool mExitOnSaveStateChange;
bool mUserWarned;
Loader mLoader;
// not implemented
ViewManager (const ViewManager&);

@ -9,4 +9,6 @@ void CSVTools::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
{
manager.add (CSMWorld::UniversalId::Type_VerificationResults,
new CSVDoc::SubViewFactory<ReportSubView>);
manager.add (CSMWorld::UniversalId::Type_LoadErrorLog,
new CSVDoc::SubViewFactory<ReportSubView>);
}
Loading…
Cancel
Save