Merge remote-tracking branch 'cc9cii/moveref'

test
Marc Zinnschlag 10 years ago
commit f1c0847897

@ -231,8 +231,29 @@ void CSMDoc::CollectionReferencesStage::perform (int stage, Messages& messages)
record.mState==CSMWorld::RecordBase::State_Modified ||
record.mState==CSMWorld::RecordBase::State_ModifiedOnly)
{
mState.getSubRecords()[Misc::StringUtils::lowerCase (record.get().mCell)]
.push_back (i);
std::string cellId = record.get().mOriginalCell.empty() ?
record.get().mCell : record.get().mOriginalCell;
std::deque<int>& indices =
mState.getSubRecords()[Misc::StringUtils::lowerCase (cellId)];
// collect moved references at the end of the container
bool interior = cellId.substr (0, 1)!="#";
std::ostringstream stream;
if (!interior)
{
// recalculate the ref's cell location
std::pair<int, int> index = record.get().getCellIndex();
stream << "#" << index.first << " " << index.second;
}
// An empty mOriginalCell is meant to indicate that it is the same as
// the current cell. It is possible that a moved ref is moved again.
if ((record.get().mOriginalCell.empty() ?
record.get().mCell : record.get().mOriginalCell) != stream.str() && !interior)
indices.push_back (i);
else
indices.push_front (i);
}
}
}
@ -253,7 +274,7 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
const CSMWorld::Record<CSMWorld::Cell>& cell =
mDocument.getData().getCells().getRecord (stage);
std::map<std::string, std::vector<int> >::const_iterator references =
std::map<std::string, std::deque<int> >::const_iterator references =
mState.getSubRecords().find (Misc::StringUtils::lowerCase (cell.get().mId));
if (cell.mState==CSMWorld::RecordBase::State_Modified ||
@ -284,7 +305,7 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
// write references
if (references!=mState.getSubRecords().end())
{
for (std::vector<int>::const_iterator iter (references->second.begin());
for (std::deque<int>::const_iterator iter (references->second.begin());
iter!=references->second.end(); ++iter)
{
const CSMWorld::Record<CSMWorld::CellRef>& ref =
@ -293,6 +314,32 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
if (ref.mState==CSMWorld::RecordBase::State_Modified ||
ref.mState==CSMWorld::RecordBase::State_ModifiedOnly)
{
// recalculate the ref's cell location
std::ostringstream stream;
if (!interior)
{
std::pair<int, int> index = ref.get().getCellIndex();
stream << "#" << index.first << " " << index.second;
}
// An empty mOriginalCell is meant to indicate that it is the same as
// the current cell. It is possible that a moved ref is moved again.
if ((ref.get().mOriginalCell.empty() ? ref.get().mCell : ref.get().mOriginalCell)
!= stream.str() && !interior)
{
ESM::MovedCellRef moved;
moved.mRefNum = ref.get().mRefNum;
// Need to fill mTarget with the ref's new position.
std::istringstream istream (stream.str().c_str());
char ignore;
istream >> ignore >> moved.mTarget[0] >> moved.mTarget[1];
ref.get().mRefNum.save (mState.getWriter(), false, "MVRF");
mState.getWriter().writeHNT ("CNDT", moved.mTarget, 8);
}
ref.get().save (mState.getWriter());
}
else if (ref.mState==CSMWorld::RecordBase::State_Deleted)

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

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

@ -17,7 +17,8 @@ namespace CSMWorld
enum Roles
{
Role_Flags = Qt::UserRole,
Role_Display = Qt::UserRole+1
Role_Display = Qt::UserRole+1,
Role_ColumnId = Qt::UserRole+2
};
enum Flags

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

@ -222,6 +222,7 @@ namespace CSMWorld
{ ColumnId_HitSound, "Hit Sound" },
{ ColumnId_AreaSound, "Area Sound" },
{ ColumnId_BoltSound, "Bolt Sound" },
{ ColumnId_OriginalCell, "Original Cell" },
{ ColumnId_PathgridPoints, "Points" },
{ ColumnId_PathgridIndex, "Index" },

@ -264,6 +264,8 @@ namespace CSMWorld
ColumnId_InfoList = 240,
ColumnId_OriginalCell = 241,
// 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,

@ -2,6 +2,7 @@
#include "commanddispatcher.hpp"
#include <algorithm>
#include <memory>
#include <components/misc/stringops.hpp>
@ -10,6 +11,7 @@
#include "idtable.hpp"
#include "record.hpp"
#include "commands.hpp"
#include "idtableproxymodel.hpp"
std::vector<std::string> CSMWorld::CommandDispatcher::getDeletableRecords() const
{
@ -131,6 +133,54 @@ std::vector<CSMWorld::UniversalId> CSMWorld::CommandDispatcher::getExtendedTypes
return tables;
}
void CSMWorld::CommandDispatcher::executeModify (QAbstractItemModel *model, const QModelIndex& index, const QVariant& new_)
{
if (mLocked)
return;
std::auto_ptr<CSMWorld::UpdateCellCommand> modifyCell;
int columnId = model->data (index, ColumnBase::Role_ColumnId).toInt();
if (columnId==Columns::ColumnId_PositionXPos || columnId==Columns::ColumnId_PositionYPos)
{
IdTableProxyModel *proxy = dynamic_cast<IdTableProxyModel *> (model);
int row = proxy ? proxy->mapToSource (index).row() : index.row();
// This is not guaranteed to be the same as \a model, since a proxy could be used.
IdTable& model2 = dynamic_cast<IdTable&> (*mDocument.getData().getTableModel (mId));
int cellColumn = model2.searchColumnIndex (Columns::ColumnId_Cell);
if (cellColumn!=-1)
{
QModelIndex cellIndex = model2.index (row, cellColumn);
std::string cellId = model2.data (cellIndex).toString().toUtf8().data();
if (cellId.find ('#')!=std::string::npos)
{
// Need to recalculate the cell
modifyCell.reset (new UpdateCellCommand (model2, row));
}
}
}
std::auto_ptr<CSMWorld::ModifyCommand> modifyData (
new CSMWorld::ModifyCommand (*model, index, new_));
if (modifyCell.get())
{
mDocument.getUndoStack().beginMacro (modifyData->text());
mDocument.getUndoStack().push (modifyData.release());
mDocument.getUndoStack().push (modifyCell.release());
mDocument.getUndoStack().endMacro();
}
else
mDocument.getUndoStack().push (modifyData.release());
}
void CSMWorld::CommandDispatcher::executeDelete()
{
if (mLocked)

@ -7,6 +7,9 @@
#include "universalid.hpp"
class QModelIndex;
class QAbstractItemModel;
namespace CSMDoc
{
class Document;
@ -53,6 +56,12 @@ namespace CSMWorld
/// the extended mode, the returned vector will be empty instead.
std::vector<UniversalId> getExtendedTypes() const;
/// Add a modify command to the undo stack.
///
/// \attention model must either be a model for the table operated on by this
/// dispatcher or a proxy of it.
void executeModify (QAbstractItemModel *model, const QModelIndex& index, const QVariant& new_);
public slots:
void executeDelete();

@ -1,5 +1,8 @@
#include "commands.hpp"
#include <cmath>
#include <sstream>
#include <components/misc/stringops.hpp>
#include <QAbstractItemModel>
@ -183,6 +186,47 @@ void CSMWorld::CloneCommand::undo()
mModel.removeRow (mModel.getModelIndex (mId, 0).row());
}
CSMWorld::UpdateCellCommand::UpdateCellCommand (IdTable& model, int row, QUndoCommand *parent)
: QUndoCommand (parent), mModel (model), mRow (row)
{
setText ("Update cell ID");
}
void CSMWorld::UpdateCellCommand::redo()
{
if (!mNew.isValid())
{
int cellColumn = mModel.searchColumnIndex (Columns::ColumnId_Cell);
mIndex = mModel.index (mRow, cellColumn);
const int cellSize = 8192;
QModelIndex xIndex = mModel.index (
mRow, mModel.findColumnIndex (Columns::ColumnId_PositionXPos));
QModelIndex yIndex = mModel.index (
mRow, mModel.findColumnIndex (Columns::ColumnId_PositionYPos));
int x = std::floor (mModel.data (xIndex).toFloat() / cellSize);
int y = std::floor (mModel.data (yIndex).toFloat() / cellSize);
std::ostringstream stream;
stream << "#" << x << " " << y;
mNew = QString::fromUtf8 (stream.str().c_str());
}
mModel.setData (mIndex, mNew);
}
void CSMWorld::UpdateCellCommand::undo()
{
mModel.setData (mIndex, mOld);
}
CSMWorld::DeleteNestedCommand::DeleteNestedCommand (IdTree& model,
const std::string& id,
int nestedRow,

@ -144,6 +144,29 @@ namespace CSMWorld
virtual void undo();
};
/// \brief Update cell ID according to x/y-coordinates
///
/// \note The new value will be calculated in the first call to redo instead of the
/// constructor to accommodate multiple coordinate-affecting commands being executed
/// in a macro.
class UpdateCellCommand : public QUndoCommand
{
IdTable& mModel;
int mRow;
QModelIndex mIndex;
QVariant mNew; // invalid, if new cell ID has not been calculated yet
QVariant mOld;
public:
UpdateCellCommand (IdTable& model, int row, QUndoCommand *parent = 0);
virtual void redo();
virtual void undo();
};
class NestedTableStoring
{
NestedTableWrapperBase* mOld;

@ -368,7 +368,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mRefs.addColumn (new StringIdColumn<CellRef> (true));
mRefs.addColumn (new RecordStateColumn<CellRef>);
mRefs.addColumn (new FixedRecordTypeColumn<CellRef> (UniversalId::Type_Reference));
mRefs.addColumn (new CellColumn<CellRef>);
mRefs.addColumn (new CellColumn<CellRef> (true));
mRefs.addColumn (new OriginalCellColumn<CellRef>);
mRefs.addColumn (new IdColumn<CellRef>);
mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mPos, 0, false));
mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mPos, 1, false));
@ -457,6 +458,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
UniversalId::Type_Texture);
addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Videos)),
UniversalId::Type_Video);
mRefLoadCache.clear(); // clear here rather than startLoading() and continueLoading() for multiple content files
}
CSMWorld::Data::~Data()
@ -778,7 +781,6 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
mReader = 0;
mDialogue = 0;
mRefLoadCache.clear();
mReader = new ESM::ESMReader;
mReader->setEncoder (&mEncoder);
@ -815,7 +817,6 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
mReader = 0;
mDialogue = 0;
mRefLoadCache.clear();
return true;
}
@ -860,9 +861,16 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
case ESM::REC_CELL:
{
mCells.load (*mReader, mBase);
std::string cellId = Misc::StringUtils::lowerCase (mCells.getId (mCells.getSize()-1));
mRefs.load (*mReader, mCells.getSize()-1, mBase, mRefLoadCache[cellId], messages);
int index = mCells.load (*mReader, mBase);
if (index < 0 || index >= mCells.getSize())
{
// log an error and continue loading the refs to the last loaded cell
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_None);
messages.add (id, "Logic error: cell index out of bounds");
index = mCells.getSize()-1;
}
std::string cellId = Misc::StringUtils::lowerCase (mCells.getId (index));
mRefs.load (*mReader, index, mBase, mRefLoadCache[cellId], messages);
break;
}

@ -74,6 +74,15 @@ namespace CSMWorld
{
ESXRecordT record;
// Sometimes id (i.e. NAME of the cell) may be different to the id we stored
// earlier. e.g. NAME == "Vivec, Arena" but id == "#-4 11". Sometime NAME is
// missing altogether for scripts or cells.
//
// In such cases the returned index will be -1. We then try updating the
// IdAccessor's id manually (e.g. set mId of the record to "Vivec, Arena")
// and try getting the index once more after loading the record. The mId of the
// record would have changed to "#-4 11" after the load, and searchId() should find
// it (if this is a modify)
int index = this->searchId (id);
if (index==-1)

@ -28,9 +28,15 @@ int CSMWorld::IdTable::columnCount (const QModelIndex & parent) const
return mIdCollection->getColumns();
}
QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const
QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const
{
if ((role!=Qt::DisplayRole && role!=Qt::EditRole) || index.row() < 0 || index.column() < 0)
if (index.row() < 0 || index.column() < 0)
return QVariant();
if (role==ColumnBase::Role_ColumnId)
return QVariant (getColumnId (index.column()));
if ((role!=Qt::DisplayRole && role!=Qt::EditRole))
return QVariant();
if (role==Qt::EditRole && !mIdCollection->getColumn (index.column()).isEditable())
@ -56,6 +62,9 @@ QVariant CSMWorld::IdTable::headerData (int section, Qt::Orientation orientation
if (role==ColumnBase::Role_Display)
return mIdCollection->getColumn (section).mDisplayType;
if (role==ColumnBase::Role_ColumnId)
return getColumnId (section);
return QVariant();
}

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

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

@ -2,8 +2,10 @@
#include "refcollection.hpp"
#include <sstream>
#include <iostream>
#include <components/misc/stringops.hpp>
#include <components/esm/loadcell.hpp>
#include "ref.hpp"
#include "cell.hpp"
@ -20,14 +22,75 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
CellRef ref;
bool deleted = false;
ESM::MovedCellRef mref;
while (ESM::Cell::getNextRef (reader, ref, deleted))
// hack to initialise mindex
while (!(mref.mRefNum.mIndex = 0) && ESM::Cell::getNextRef(reader, ref, deleted, true, &mref))
{
ref.mCell = cell2.mId;
// Keep mOriginalCell empty when in modified (as an indicator that the
// original cell will always be equal the current cell).
ref.mOriginalCell = base ? cell2.mId : "";
/// \todo handle moved references
if (cell.get().isExterior())
{
// ignoring moved references sub-record; instead calculate cell from coordinates
std::pair<int, int> index = ref.getCellIndex();
std::ostringstream stream;
stream << "#" << index.first << " " << index.second;
ref.mCell = stream.str();
if (!base && // don't try to update base records
mref.mRefNum.mIndex != 0) // MVRF tag found
{
// there is a requirement for a placeholder where the original object was
//
// see the forum discussions here for more details:
// https://forum.openmw.org/viewtopic.php?f=6&t=577&start=30
ref.mOriginalCell = cell2.mId;
if (deleted)
{
// FIXME: how to mark the record deleted?
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Cell,
mCells.getId (cellIndex));
messages.add (id, "Moved reference "+ref.mRefID+" is in DELE state");
continue;
}
// It is not always possibe to ignore moved references sub-record and
// calculate from coordinates. Some mods may place the ref in positions
// outside normal bounds, resulting in non sensical cell id's. This often
// happens if the moved ref was deleted.
//
// Use the target cell from the MVRF tag but if different output an error
// message
if (index.first != mref.mTarget[0] || index.second != mref.mTarget[1])
{
std::cerr << "The Position of moved ref "
<< ref.mRefID << " does not match the target cell" << std::endl;
std::cerr << "Position: #" << index.first << " " << index.second
<<", Target #"<< mref.mTarget[0] << " " << mref.mTarget[1] << std::endl;
std::ostringstream stream;
stream << "#" << mref.mTarget[0] << " " << mref.mTarget[1];
ref.mCell = stream.str(); // overwrite
}
}
}
else
ref.mCell = cell2.mId;
std::map<ESM::RefNum, std::string>::iterator iter = cache.find (ref.mRefNum);
// ignore content file number
std::map<ESM::RefNum, std::string>::iterator iter = cache.begin();
for (; iter != cache.end(); ++iter)
{
if (ref.mRefNum.mIndex == iter->first.mIndex)
break;
}
if (deleted)
{

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

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

@ -6,11 +6,12 @@
CSVWorld::DataDisplayDelegate::DataDisplayDelegate(const ValueList &values,
const IconList &icons,
CSMWorld::CommandDispatcher *dispatcher,
CSMDoc::Document& document,
const QString &pageName,
const QString &settingName,
QObject *parent)
: EnumDelegate (values, document, parent), mDisplayMode (Mode_TextOnly),
: EnumDelegate (values, dispatcher, document, parent), mDisplayMode (Mode_TextOnly),
mIcons (icons), mIconSize (QSize(16, 16)), mIconLeftOffset(3),
mTextLeftOffset(8), mSettingKey (pageName + '/' + settingName)
{
@ -136,9 +137,9 @@ void CSVWorld::DataDisplayDelegateFactory::add (int enumValue, QString enumName,
}
CSVWorld::CommandDelegate *CSVWorld::DataDisplayDelegateFactory::makeDelegate (
CSMDoc::Document& document, QObject *parent) const
CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const
{
return new DataDisplayDelegate (mValues, mIcons, document, "", "", parent);
return new DataDisplayDelegate (mValues, mIcons, dispatcher, document, "", "", parent);
}

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

@ -177,10 +177,11 @@ void CSVWorld::DialogueDelegateDispatcherProxy::tableMimeDataDropped(const std::
*/
CSVWorld::DialogueDelegateDispatcher::DialogueDelegateDispatcher(QObject* parent,
CSMWorld::IdTable* table, CSMDoc::Document& document, QAbstractItemModel *model) :
CSMWorld::IdTable* table, CSMWorld::CommandDispatcher& commandDispatcher,
CSMDoc::Document& document, QAbstractItemModel *model) :
mParent(parent),
mTable(model ? model : table),
mDocument (document),
mCommandDispatcher (commandDispatcher), mDocument (document),
mNotEditableDelegate(table, parent)
{
}
@ -192,7 +193,7 @@ CSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CS
if (delegateIt == mDelegates.end())
{
delegate = CommandDelegateFactoryCollection::get().makeDelegate (
display, mDocument, mParent);
display, &mCommandDispatcher, mDocument, mParent);
mDelegates.insert(std::make_pair(display, delegate));
} else
{
@ -352,13 +353,16 @@ CSVWorld::EditWidget::~EditWidget()
delete mNestedTableDispatcher;
}
CSVWorld::EditWidget::EditWidget(QWidget *parent, int row, CSMWorld::IdTable* table, CSMDoc::Document& document, bool createAndDelete) :
mDispatcher(this, table, document),
CSVWorld::EditWidget::EditWidget(QWidget *parent,
int row, CSMWorld::IdTable* table, CSMWorld::CommandDispatcher& commandDispatcher,
CSMDoc::Document& document, bool createAndDelete) :
mDispatcher(this, table, commandDispatcher, document),
mNestedTableDispatcher(NULL),
QScrollArea(parent),
mWidgetMapper(NULL),
mNestedTableMapper(NULL),
mMainWidget(NULL),
mCommandDispatcher (commandDispatcher),
mDocument (document),
mTable(table)
{
@ -442,7 +446,14 @@ void CSVWorld::EditWidget::remake(int row)
{
mNestedModels.push_back(new CSMWorld::NestedTableProxyModel (mTable->index(row, i), display, dynamic_cast<CSMWorld::IdTree*>(mTable)));
NestedTable* table = new NestedTable(mDocument, mNestedModels.back(), this);
int idColumn = mTable->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
int typeColumn = mTable->findColumnIndex (CSMWorld::Columns::ColumnId_RecordType);
CSMWorld::UniversalId id = CSMWorld::UniversalId(
static_cast<CSMWorld::UniversalId::Type> (mTable->data (mTable->index (row, typeColumn)).toInt()),
mTable->data (mTable->index (row, idColumn)).toString().toUtf8().constData());
NestedTable* table = new NestedTable(mDocument, id, mNestedModels.back(), this);
// FIXME: does not work well when enum delegates are used
//table->resizeColumnsToContents();
table->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::CurrentChanged);
@ -499,7 +510,7 @@ void CSVWorld::EditWidget::remake(int row)
mNestedTableMapper->setModel(mNestedModels.back());
// FIXME: lack MIME support?
mNestedTableDispatcher =
new DialogueDelegateDispatcher (this, mTable, mDocument, mNestedModels.back());
new DialogueDelegateDispatcher (this, mTable, mCommandDispatcher, mDocument, mNestedModels.back());
mNestedTableMapper->setItemDelegate(mNestedTableDispatcher);
int columnCount =
@ -626,7 +637,8 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM
mMainLayout = new QVBoxLayout(mainWidget);
mEditWidget = new EditWidget(mainWidget, mTable->getModelIndex(mCurrentId, 0).row(), mTable, document, false);
mEditWidget = new EditWidget(mainWidget,
mTable->getModelIndex(mCurrentId, 0).row(), mTable, mCommandDispatcher, document, false);
connect(mEditWidget, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)),
this, SLOT(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)));

@ -109,6 +109,7 @@ namespace CSVWorld
QAbstractItemModel* mTable;
CSMWorld::CommandDispatcher& mCommandDispatcher;
CSMDoc::Document& mDocument;
NotEditableSubDelegate mNotEditableDelegate;
@ -119,6 +120,7 @@ namespace CSVWorld
public:
DialogueDelegateDispatcher(QObject* parent,
CSMWorld::IdTable* table,
CSMWorld::CommandDispatcher& commandDispatcher,
CSMDoc::Document& document,
QAbstractItemModel* model = 0);
@ -167,12 +169,14 @@ namespace CSVWorld
DialogueDelegateDispatcher *mNestedTableDispatcher;
QWidget* mMainWidget;
CSMWorld::IdTable* mTable;
CSMWorld::CommandDispatcher& mCommandDispatcher;
CSMDoc::Document& mDocument;
std::vector<CSMWorld::NestedTableProxyModel*> mNestedModels; //Plain, raw C pointers, deleted in the dtor
public:
EditWidget (QWidget *parent, int row, CSMWorld::IdTable* table,
CSMWorld::CommandDispatcher& commandDispatcher,
CSMDoc::Document& document, bool createAndDelete = false);
virtual ~EditWidget();

@ -37,8 +37,8 @@ void CSVWorld::EnumDelegate::addCommands (QAbstractItemModel *model,
CSVWorld::EnumDelegate::EnumDelegate (const std::vector<std::pair<int, QString> >& values,
CSMDoc::Document& document, QObject *parent)
: CommandDelegate (document, parent), mValues (values)
CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent)
: CommandDelegate (dispatcher, document, parent), mValues (values)
{
}
@ -142,9 +142,9 @@ CSVWorld::EnumDelegateFactory::EnumDelegateFactory (const std::vector<std::strin
}
CSVWorld::CommandDelegate *CSVWorld::EnumDelegateFactory::makeDelegate (
CSMDoc::Document& document, QObject *parent) const
CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const
{
return new EnumDelegate (mValues, document, parent);
return new EnumDelegate (mValues, dispatcher, document, parent);
}
void CSVWorld::EnumDelegateFactory::add (int value, const QString& name)

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

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

@ -11,7 +11,7 @@ namespace CSVWorld
class IdTypeDelegate : public DataDisplayDelegate
{
public:
IdTypeDelegate (const ValueList &mValues, const IconList &icons, CSMDoc::Document& document, QObject *parent);
IdTypeDelegate (const ValueList &mValues, const IconList &icons, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent);
};
class IdTypeDelegateFactory : public DataDisplayDelegateFactory
@ -20,7 +20,7 @@ namespace CSVWorld
IdTypeDelegateFactory();
virtual CommandDelegate *makeDelegate (CSMDoc::Document& document, QObject *parent) const;
virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const;
///< The ownership of the returned CommandDelegate is transferred to the caller.
};
}

@ -2,6 +2,7 @@
#include "../../model/world/nestedtableproxymodel.hpp"
#include "../../model/world/universalid.hpp"
#include "../../model/world/commands.hpp"
#include "../../model/world/commanddispatcher.hpp"
#include "util.hpp"
#include <QHeaderView>
@ -10,12 +11,14 @@
#include <QDebug>
CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document,
CSMWorld::UniversalId id,
CSMWorld::NestedTableProxyModel* model,
QWidget* parent)
: QTableView(parent),
mUndoStack(document.getUndoStack()),
mModel(model)
{
mDispatcher = new CSMWorld::CommandDispatcher (document, id, this);
setSelectionBehavior (QAbstractItemView::SelectRows);
setSelectionMode (QAbstractItemView::ExtendedSelection);
@ -31,6 +34,7 @@ CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document,
model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt());
CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate(display,
mDispatcher,
document,
this);

@ -12,6 +12,7 @@ namespace CSMWorld
{
class NestedTableProxyModel;
class UniversalId;
class CommandDispatcher;
}
namespace CSMDoc
@ -29,9 +30,11 @@ namespace CSVWorld
QAction *mRemoveRowAction;
QUndoStack& mUndoStack;
CSMWorld::NestedTableProxyModel* mModel;
CSMWorld::CommandDispatcher *mDispatcher;
public:
NestedTable(CSMDoc::Document& document,
CSMWorld::UniversalId id,
CSMWorld::NestedTableProxyModel* model,
QWidget* parent = NULL);

@ -9,16 +9,16 @@
CSVWorld::RecordStatusDelegate::RecordStatusDelegate(const ValueList& values,
const IconList & icons,
CSMDoc::Document& document, QObject *parent)
: DataDisplayDelegate (values, icons, document,
CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent)
: DataDisplayDelegate (values, icons, dispatcher, document,
"records", "status-format",
parent)
{}
CSVWorld::CommandDelegate *CSVWorld::RecordStatusDelegateFactory::makeDelegate (
CSMDoc::Document& document, QObject *parent) const
CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const
{
return new RecordStatusDelegate (mValues, mIcons, document, parent);
return new RecordStatusDelegate (mValues, mIcons, dispatcher, document, parent);
}
CSVWorld::RecordStatusDelegateFactory::RecordStatusDelegateFactory()

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

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

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

@ -16,6 +16,7 @@ namespace CSMWorld
{
class TableMimeData;
class UniversalId;
class CommandDispatcher;
}
namespace CSVWorld
@ -51,7 +52,8 @@ namespace CSVWorld
virtual ~CommandDelegateFactory();
virtual CommandDelegate *makeDelegate (CSMDoc::Document& document, QObject *parent)
virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher,
CSMDoc::Document& document, QObject *parent)
const = 0;
///< The ownership of the returned CommandDelegate is transferred to the caller.
};
@ -78,7 +80,8 @@ namespace CSVWorld
///
/// This function must not be called more than once per value of \a display.
CommandDelegate *makeDelegate (CSMWorld::ColumnBase::Display display, CSMDoc::Document& document,
CommandDelegate *makeDelegate (CSMWorld::ColumnBase::Display display,
CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document,
QObject *parent) const;
///< The ownership of the returned CommandDelegate is transferred to the caller.
///
@ -111,8 +114,9 @@ namespace CSVWorld
{
Q_OBJECT
CSMDoc::Document& mDocument;
bool mEditLock;
CSMWorld::CommandDispatcher *mCommandDispatcher;
CSMDoc::Document& mDocument;
protected:
@ -125,7 +129,9 @@ namespace CSVWorld
public:
CommandDelegate (CSMDoc::Document& document, QObject *parent);
/// \param commandDispatcher If CommandDelegate will be only be used on read-only
/// cells, a 0-pointer can be passed here.
CommandDelegate (CSMWorld::CommandDispatcher *commandDispatcher, CSMDoc::Document& document, QObject *parent);
virtual void setModelData (QWidget *editor, QAbstractItemModel *model,
const QModelIndex& index) const;

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

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

@ -4,13 +4,34 @@
#include "esmreader.hpp"
#include "esmwriter.hpp"
void ESM::CellRef::load (ESMReader& esm, bool wideRefNum)
void ESM::RefNum::load (ESMReader& esm, bool wide)
{
loadId(esm, wideRefNum);
if (wide)
esm.getHNT (*this, "FRMR", 8);
else
esm.getHNT (mIndex, "FRMR");
}
void ESM::RefNum::save (ESMWriter &esm, bool wide, const std::string& tag) const
{
if (wide)
esm.writeHNT (tag, *this, 8);
else
{
int refNum = (mIndex & 0xffffff) | ((hasContentFile() ? mContentFile : 0xff)<<24);
esm.writeHNT (tag, refNum, 4);
}
}
void ESM::CellRef::load (ESMReader& esm, bool wideRefNum, bool ignoreRefNum)
{
loadId(esm, wideRefNum, ignoreRefNum);
loadData(esm);
}
void ESM::CellRef::loadId(ESMReader &esm, bool wideRefNum)
void ESM::CellRef::loadId (ESMReader& esm, bool wideRefNum, bool ignoreRefNum)
{
// According to Hrnchamd, this does not belong to the actual ref. Instead, it is a marker indicating that
// the following refs are part of a "temp refs" section. A temp ref is not being tracked by the moved references system.
@ -19,10 +40,8 @@ void ESM::CellRef::loadId(ESMReader &esm, bool wideRefNum)
if (esm.isNextSub ("NAM0"))
esm.skipHSub();
if (wideRefNum)
esm.getHNT (mRefNum, "FRMR", 8);
else
esm.getHNT (mRefNum.mIndex, "FRMR");
if (!ignoreRefNum)
mRefNum.load (esm, wideRefNum);
mRefID = esm.getHNString ("NAME");
}
@ -83,10 +102,7 @@ void ESM::CellRef::loadData(ESMReader &esm)
void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) const
{
if (wideRefNum)
esm.writeHNT ("FRMR", mRefNum, 8);
else
esm.writeHNT ("FRMR", mRefNum.mIndex, 4);
mRefNum.save (esm, wideRefNum);
esm.writeHNCString("NAME", mRefID);

@ -16,6 +16,10 @@ namespace ESM
unsigned int mIndex;
int mContentFile;
void load (ESMReader& esm, bool wide = false);
void save (ESMWriter &esm, bool wide = false, const std::string& tag = "FRMR") const;
enum { RefNum_NoContentFile = -1 };
inline bool hasContentFile() const { return mContentFile != RefNum_NoContentFile; }
inline void unset() { mIndex = 0; mContentFile = RefNum_NoContentFile; }
@ -96,9 +100,9 @@ namespace ESM
Position mPos;
/// Calls loadId and loadData
void load (ESMReader& esm, bool wideRefNum = false);
void load (ESMReader& esm, bool wideRefNum = false, bool ignoreRefNum = false);
void loadId (ESMReader& esm, bool wideRefNum = false);
void loadId (ESMReader& esm, bool wideRefNum = false, bool ignoreRefNum = false);
/// Implicitly called by load
void loadData (ESMReader& esm);

@ -168,7 +168,7 @@ std::string Cell::getDescription() const
}
}
bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool& deleted)
bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool& deleted, bool ignoreMoves, MovedCellRef *mref)
{
// TODO: Try and document reference numbering, I don't think this has been done anywhere else.
if (!esm.hasMoreSubs())
@ -176,10 +176,20 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool& deleted)
// NOTE: We should not need this check. It is a safety check until we have checked
// more plugins, and how they treat these moved references.
if (esm.isNextSub("MVRF")) {
// skip rest of cell record (moved references), they are handled elsewhere
esm.skipRecord(); // skip MVRF, CNDT
return false;
if (esm.isNextSub("MVRF"))
{
if (ignoreMoves)
{
esm.getHT (mref->mRefNum.mIndex);
esm.getHNOT (mref->mTarget, "CNDT");
adjustRefNum (mref->mRefNum, esm);
}
else
{
// skip rest of cell record (moved references), they are handled elsewhere
esm.skipRecord(); // skip MVRF, CNDT
return false;
}
}
ref.load (esm);

@ -156,7 +156,9 @@ struct Cell
All fields of the CellRef struct are overwritten. You can safely
reuse one memory location without blanking it between calls.
*/
static bool getNextRef(ESMReader &esm, CellRef &ref, bool& deleted);
/// \param ignoreMoves ignore MVRF record and read reference like a regular CellRef.
static bool getNextRef(ESMReader &esm,
CellRef &ref, bool& deleted, bool ignoreMoves = false, MovedCellRef *mref = 0);
/* This fetches an MVRF record, which is used to track moved references.
* Since they are comparably rare, we use a separate method for this.

Loading…
Cancel
Save