mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-19 21:23:52 +00:00
added revert command
This commit is contained in:
parent
f07b7d17cd
commit
b41cc5e9e9
11 changed files with 275 additions and 34 deletions
|
@ -5,7 +5,7 @@ set (OPENCS_SRC
|
|||
model/doc/documentmanager.cpp model/doc/document.cpp
|
||||
|
||||
model/world/universalid.cpp model/world/idcollection.cpp model/world/data.cpp model/world/idtable.cpp
|
||||
model/world/commands.cpp model/world/idtableproxymodel.cpp
|
||||
model/world/commands.cpp model/world/idtableproxymodel.cpp model/world/record.cpp
|
||||
|
||||
view/doc/viewmanager.cpp view/doc/view.cpp view/doc/operations.cpp view/doc/operation.cpp
|
||||
|
||||
|
|
|
@ -57,9 +57,14 @@ namespace CSMWorld
|
|||
return static_cast<int> (record.mState);
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
record.mState = static_cast<RecordBase::State> (data.toInt());
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <QAbstractTableModel>
|
||||
|
||||
#include "idtableproxymodel.hpp"
|
||||
#include "idtable.hpp"
|
||||
|
||||
CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
|
||||
const QVariant& new_, QUndoCommand *parent)
|
||||
|
@ -39,3 +40,36 @@ void CSMWorld::CreateCommand::undo()
|
|||
{
|
||||
mModel.removeRow (mModel.getModelIndex (mId, 0).row());
|
||||
}
|
||||
|
||||
CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand *parent)
|
||||
: QUndoCommand (parent), mModel (model), mId (id), mOld (0)
|
||||
{
|
||||
setText (("Revert record " + id).c_str());
|
||||
|
||||
mOld = model.getRecord (id).clone();
|
||||
}
|
||||
|
||||
CSMWorld::RevertCommand::~RevertCommand()
|
||||
{
|
||||
delete mOld;
|
||||
}
|
||||
|
||||
void CSMWorld::RevertCommand::redo()
|
||||
{
|
||||
QModelIndex index = mModel.getModelIndex (mId, 1);
|
||||
RecordBase::State state = static_cast<RecordBase::State> (mModel.data (index).toInt());
|
||||
|
||||
if (state==RecordBase::State_ModifiedOnly)
|
||||
{
|
||||
mModel.removeRows (index.row(), 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
mModel.setData (index, static_cast<int> (RecordBase::State_BaseOnly));
|
||||
}
|
||||
}
|
||||
|
||||
void CSMWorld::RevertCommand::undo()
|
||||
{
|
||||
mModel.setRecord (*mOld);
|
||||
}
|
|
@ -15,6 +15,8 @@ class QAbstractItemModel;
|
|||
namespace CSMWorld
|
||||
{
|
||||
class IdTableProxyModel;
|
||||
class IdTable;
|
||||
class RecordBase;
|
||||
|
||||
class ModifyCommand : public QUndoCommand
|
||||
{
|
||||
|
@ -46,6 +48,27 @@ namespace CSMWorld
|
|||
|
||||
virtual void undo();
|
||||
};
|
||||
|
||||
class RevertCommand : public QUndoCommand
|
||||
{
|
||||
IdTable& mModel;
|
||||
std::string mId;
|
||||
RecordBase *mOld;
|
||||
|
||||
// not implemented
|
||||
RevertCommand (const RevertCommand&);
|
||||
RevertCommand& operator= (const RevertCommand&);
|
||||
|
||||
public:
|
||||
|
||||
RevertCommand (IdTable& model, const std::string& id, QUndoCommand *parent = 0);
|
||||
|
||||
virtual ~RevertCommand();
|
||||
|
||||
virtual void redo();
|
||||
|
||||
virtual void undo();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -71,6 +71,25 @@ namespace CSMWorld
|
|||
virtual void removeRows (int index, int count) = 0;
|
||||
|
||||
virtual void appendBlankRecord (const std::string& id) = 0;
|
||||
|
||||
virtual int searchId (const std::string& id) const = 0;
|
||||
////< Search record with \a id.
|
||||
/// \return index of record (if found) or -1 (not found)
|
||||
|
||||
virtual void replace (int index, const RecordBase& record) = 0;
|
||||
///< If the record type does not match, an exception is thrown.
|
||||
///
|
||||
/// \attention \a record must not change the ID.
|
||||
|
||||
virtual void appendRecord (const RecordBase& record) = 0;
|
||||
///< If the record type does not match, an exception is thrown.
|
||||
|
||||
virtual std::string getId (const RecordBase& record) const = 0;
|
||||
///< Return ID for \a record.
|
||||
///
|
||||
/// \attention Throw san exception, if the type of \a record does not match.
|
||||
|
||||
virtual const RecordBase& getRecord (const std::string& id) const = 0;
|
||||
};
|
||||
|
||||
///< \brief Collection of ID-based records
|
||||
|
@ -120,6 +139,25 @@ namespace CSMWorld
|
|||
|
||||
virtual void appendBlankRecord (const std::string& id);
|
||||
|
||||
virtual int searchId (const std::string& id) const;
|
||||
////< Search record with \a id.
|
||||
/// \return index of record (if found) or -1 (not found)
|
||||
|
||||
virtual void replace (int index, const RecordBase& record);
|
||||
///< If the record type does not match, an exception is thrown.
|
||||
///
|
||||
/// \attention \a record must not change the ID.
|
||||
|
||||
virtual void appendRecord (const RecordBase& record);
|
||||
///< If the record type does not match, an exception is thrown.
|
||||
|
||||
virtual std::string getId (const RecordBase& record) const;
|
||||
///< Return ID for \a record.
|
||||
///
|
||||
/// \attention Throw san exception, if the type of \a record does not match.
|
||||
|
||||
virtual const RecordBase& getRecord (const std::string& id) const;
|
||||
|
||||
void addColumn (Column<ESXRecordT> *column);
|
||||
};
|
||||
|
||||
|
@ -174,12 +212,12 @@ namespace CSMWorld
|
|||
template<typename ESXRecordT>
|
||||
int IdCollection<ESXRecordT>::getIndex (const std::string& id) const
|
||||
{
|
||||
std::map<std::string, int>::const_iterator iter = mIndex.find (id);
|
||||
int index = searchId (id);
|
||||
|
||||
if (iter==mIndex.end())
|
||||
if (index==-1)
|
||||
throw std::runtime_error ("invalid ID: " + id);
|
||||
|
||||
return iter->second;
|
||||
return index;
|
||||
}
|
||||
|
||||
template<typename ESXRecordT>
|
||||
|
@ -268,6 +306,49 @@ namespace CSMWorld
|
|||
record.blank();
|
||||
add (record);
|
||||
}
|
||||
|
||||
template<typename ESXRecordT>
|
||||
int IdCollection<ESXRecordT>::searchId (const std::string& id) const
|
||||
{
|
||||
std::string id2;
|
||||
|
||||
std::transform (id.begin(), id.end(), std::back_inserter (id2),
|
||||
(int(*)(int)) std::tolower);
|
||||
|
||||
std::map<std::string, int>::const_iterator iter = mIndex.find (id2);
|
||||
|
||||
if (iter==mIndex.end())
|
||||
return -1;
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
template<typename ESXRecordT>
|
||||
void IdCollection<ESXRecordT>::replace (int index, const RecordBase& record)
|
||||
{
|
||||
mRecords.at (index) = dynamic_cast<const Record<ESXRecordT>&> (record);
|
||||
}
|
||||
|
||||
template<typename ESXRecordT>
|
||||
void IdCollection<ESXRecordT>::appendRecord (const RecordBase& record)
|
||||
{
|
||||
mRecords.push_back (dynamic_cast<const Record<ESXRecordT>&> (record));
|
||||
mIndex.insert (std::make_pair (getId (record), mRecords.size()-1));
|
||||
}
|
||||
|
||||
template<typename ESXRecordT>
|
||||
std::string IdCollection<ESXRecordT>::getId (const RecordBase& record) const
|
||||
{
|
||||
const Record<ESXRecordT>& record2 = dynamic_cast<const Record<ESXRecordT>&> (record);
|
||||
return (record2.isModified() ? record2.mModified : record2.mBase).mId;
|
||||
}
|
||||
|
||||
template<typename ESXRecordT>
|
||||
const RecordBase& IdCollection<ESXRecordT>::getRecord (const std::string& id) const
|
||||
{
|
||||
int index = getIndex (id);
|
||||
return mRecords.at (index);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -56,7 +56,10 @@ bool CSMWorld::IdTable::setData ( const QModelIndex &index, const QVariant &valu
|
|||
if (mIdCollection->isEditable (index.column()) && role==Qt::EditRole)
|
||||
{
|
||||
mIdCollection->setData (index.row(), index.column(), value);
|
||||
emit dataChanged (index, index);
|
||||
|
||||
emit dataChanged (CSMWorld::IdTable::index (index.row(), 0),
|
||||
CSMWorld::IdTable::index (index.row(), mIdCollection->getColumns()-1));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -102,3 +105,30 @@ QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column)
|
|||
{
|
||||
return index (mIdCollection->getIndex (id), column);
|
||||
}
|
||||
|
||||
void CSMWorld::IdTable::setRecord (const RecordBase& record)
|
||||
{
|
||||
int index = mIdCollection->searchId (mIdCollection->getId (record));
|
||||
|
||||
if (index==-1)
|
||||
{
|
||||
int index = mIdCollection->getSize();
|
||||
|
||||
beginInsertRows (QModelIndex(), index, index);
|
||||
|
||||
mIdCollection->appendRecord (record);
|
||||
|
||||
endInsertRows();
|
||||
}
|
||||
else
|
||||
{
|
||||
mIdCollection->replace (index, record);
|
||||
emit dataChanged (CSMWorld::IdTable::index (index, 0),
|
||||
CSMWorld::IdTable::index (index, mIdCollection->getColumns()-1));
|
||||
}
|
||||
}
|
||||
|
||||
const CSMWorld::RecordBase& CSMWorld::IdTable::getRecord (const std::string& id) const
|
||||
{
|
||||
return mIdCollection->getRecord (id);
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
namespace CSMWorld
|
||||
{
|
||||
class IdCollectionBase;
|
||||
class RecordBase;
|
||||
|
||||
class IdTable : public QAbstractTableModel
|
||||
{
|
||||
|
@ -41,6 +42,11 @@ namespace CSMWorld
|
|||
void addRecord (const std::string& id);
|
||||
|
||||
QModelIndex getModelIndex (const std::string& id, int column) const;
|
||||
|
||||
void setRecord (const RecordBase& record);
|
||||
///< Add record or overwrite existing recrod.
|
||||
|
||||
const RecordBase& getRecord (const std::string& id) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
21
apps/opencs/model/world/record.cpp
Normal file
21
apps/opencs/model/world/record.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
|
||||
#include "record.hpp"
|
||||
|
||||
CSMWorld::RecordBase::~RecordBase() {}
|
||||
|
||||
bool CSMWorld::RecordBase::RecordBase::isDeleted() const
|
||||
{
|
||||
return mState==State_Deleted || mState==State_Erased;
|
||||
}
|
||||
|
||||
|
||||
bool CSMWorld::RecordBase::RecordBase::isErased() const
|
||||
{
|
||||
return mState==State_Erased;
|
||||
}
|
||||
|
||||
|
||||
bool CSMWorld::RecordBase::RecordBase::isModified() const
|
||||
{
|
||||
return mState==State_Modified || mState==State_ModifiedOnly;
|
||||
}
|
|
@ -15,6 +15,18 @@ namespace CSMWorld
|
|||
State_Deleted = 3, // exists in base, but has been deleted
|
||||
State_Erased = 4 // does not exist at all (we mostly treat that the same way as deleted)
|
||||
};
|
||||
|
||||
State mState;
|
||||
|
||||
virtual ~RecordBase();
|
||||
|
||||
virtual RecordBase *clone() const = 0;
|
||||
|
||||
bool isDeleted() const;
|
||||
|
||||
bool isErased() const;
|
||||
|
||||
bool isModified() const;
|
||||
};
|
||||
|
||||
template <typename ESXRecordT>
|
||||
|
@ -22,13 +34,8 @@ namespace CSMWorld
|
|||
{
|
||||
ESXRecordT mBase;
|
||||
ESXRecordT mModified;
|
||||
State mState;
|
||||
|
||||
bool isDeleted() const;
|
||||
|
||||
bool isErased() const;
|
||||
|
||||
bool isModified() const;
|
||||
virtual RecordBase *clone() const;
|
||||
|
||||
const ESXRecordT& get() const;
|
||||
///< Throws an exception, if the record is deleted.
|
||||
|
@ -44,21 +51,9 @@ namespace CSMWorld
|
|||
};
|
||||
|
||||
template <typename ESXRecordT>
|
||||
bool Record<ESXRecordT>::isDeleted() const
|
||||
RecordBase *Record<ESXRecordT>::clone() const
|
||||
{
|
||||
return mState==State_Deleted || mState==State_Erased;
|
||||
}
|
||||
|
||||
template <typename ESXRecordT>
|
||||
bool Record<ESXRecordT>::isErased() const
|
||||
{
|
||||
return mState==State_Erased;
|
||||
}
|
||||
|
||||
template <typename ESXRecordT>
|
||||
bool Record<ESXRecordT>::isModified() const
|
||||
{
|
||||
return mState==State_Modified || mState==State_ModifiedOnly;
|
||||
return new Record<ESXRecordT> (*this);
|
||||
}
|
||||
|
||||
template <typename ESXRecordT>
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#include "../../model/world/data.hpp"
|
||||
#include "../../model/world/commands.hpp"
|
||||
#include "../../model/world/idtableproxymodel.hpp"
|
||||
#include "../../model/world/idtable.hpp"
|
||||
#include "../../model/world/record.hpp"
|
||||
|
||||
namespace CSVWorld
|
||||
{
|
||||
|
@ -108,11 +110,16 @@ void CSVWorld::CommandDelegate::setEditLock (bool locked)
|
|||
|
||||
void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
|
||||
{
|
||||
QModelIndexList selectedRows = selectionModel()->selectedRows();
|
||||
|
||||
QMenu menu (this);
|
||||
|
||||
if (mCreateAction)
|
||||
menu.addAction (mCreateAction);
|
||||
|
||||
if (selectedRows.size()>0)
|
||||
menu.addAction (mRevertAction);
|
||||
|
||||
menu.exec (event->globalPos());
|
||||
}
|
||||
|
||||
|
@ -120,9 +127,9 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q
|
|||
bool createAndDelete)
|
||||
: mUndoStack (undoStack), mCreateAction (0)
|
||||
{
|
||||
QAbstractTableModel *model = data.getTableModel (id);
|
||||
mModel = &dynamic_cast<CSMWorld::IdTable&> (*data.getTableModel (id));
|
||||
|
||||
int columns = model->columnCount();
|
||||
int columns = mModel->columnCount();
|
||||
|
||||
for (int i=0; i<columns; ++i)
|
||||
{
|
||||
|
@ -131,10 +138,10 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q
|
|||
setItemDelegateForColumn (i, delegate);
|
||||
}
|
||||
|
||||
mModel = new CSMWorld::IdTableProxyModel (this);
|
||||
mModel->setSourceModel (model);
|
||||
mProxyModel = new CSMWorld::IdTableProxyModel (this);
|
||||
mProxyModel->setSourceModel (mModel);
|
||||
|
||||
setModel (mModel);
|
||||
setModel (mProxyModel);
|
||||
horizontalHeader()->setResizeMode (QHeaderView::Interactive);
|
||||
verticalHeader()->hide();
|
||||
setSortingEnabled (true);
|
||||
|
@ -149,6 +156,10 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q
|
|||
connect (mCreateAction, SIGNAL (triggered()), this, SLOT (createRecord()));
|
||||
addAction (mCreateAction);
|
||||
}
|
||||
|
||||
mRevertAction = new QAction (tr ("Revert Record"), this);
|
||||
connect (mRevertAction, SIGNAL (triggered()), this, SLOT (revertRecord()));
|
||||
addAction (mRevertAction);
|
||||
}
|
||||
|
||||
void CSVWorld::Table::setEditLock (bool locked)
|
||||
|
@ -166,5 +177,35 @@ void CSVWorld::Table::createRecord()
|
|||
std::ostringstream stream;
|
||||
stream << "id" << index++;
|
||||
|
||||
mUndoStack.push (new CSMWorld::CreateCommand (*mModel, stream.str()));
|
||||
mUndoStack.push (new CSMWorld::CreateCommand (*mProxyModel, stream.str()));
|
||||
}
|
||||
|
||||
void CSVWorld::Table::revertRecord()
|
||||
{
|
||||
QModelIndexList selectedRows = selectionModel()->selectedRows();
|
||||
|
||||
std::vector<std::string> revertableIds;
|
||||
|
||||
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter)
|
||||
{
|
||||
std::string id = mProxyModel->data (*iter).toString().toStdString();
|
||||
|
||||
CSMWorld::RecordBase::State state =
|
||||
static_cast<CSMWorld::RecordBase::State> (mModel->data (mModel->getModelIndex (id, 1)).toInt());
|
||||
|
||||
if (state!=CSMWorld::RecordBase::State_BaseOnly)
|
||||
revertableIds.push_back (id);
|
||||
}
|
||||
|
||||
if (revertableIds.size()>0)
|
||||
{
|
||||
if (revertableIds.size()>1)
|
||||
mUndoStack.beginMacro (tr ("Revert multiple records"));
|
||||
|
||||
for (std::vector<std::string>::const_iterator iter (revertableIds.begin()); iter!=revertableIds.end(); ++iter)
|
||||
mUndoStack.push (new CSMWorld::RevertCommand (*mModel, *iter));
|
||||
|
||||
if (revertableIds.size()>1)
|
||||
mUndoStack.endMacro();
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ namespace CSMWorld
|
|||
class Data;
|
||||
class UniversalId;
|
||||
class IdTableProxyModel;
|
||||
class IdTable;
|
||||
}
|
||||
|
||||
namespace CSVWorld
|
||||
|
@ -27,7 +28,9 @@ namespace CSVWorld
|
|||
std::vector<CommandDelegate *> mDelegates;
|
||||
QUndoStack& mUndoStack;
|
||||
QAction *mCreateAction;
|
||||
CSMWorld::IdTableProxyModel *mModel;
|
||||
QAction *mRevertAction;
|
||||
CSMWorld::IdTableProxyModel *mProxyModel;
|
||||
CSMWorld::IdTable *mModel;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -43,6 +46,8 @@ namespace CSVWorld
|
|||
private slots:
|
||||
|
||||
void createRecord();
|
||||
|
||||
void revertRecord();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue