added revert command

This commit is contained in:
Marc Zinnschlag 2012-12-06 14:56:04 +01:00
parent f07b7d17cd
commit b41cc5e9e9
11 changed files with 275 additions and 34 deletions

View file

@ -5,7 +5,7 @@ set (OPENCS_SRC
model/doc/documentmanager.cpp model/doc/document.cpp 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/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 view/doc/viewmanager.cpp view/doc/view.cpp view/doc/operations.cpp view/doc/operation.cpp

View file

@ -57,9 +57,14 @@ namespace CSMWorld
return static_cast<int> (record.mState); 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 virtual bool isEditable() const
{ {
return false; return true;
} }
}; };
} }

View file

@ -4,6 +4,7 @@
#include <QAbstractTableModel> #include <QAbstractTableModel>
#include "idtableproxymodel.hpp" #include "idtableproxymodel.hpp"
#include "idtable.hpp"
CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
const QVariant& new_, QUndoCommand *parent) const QVariant& new_, QUndoCommand *parent)
@ -39,3 +40,36 @@ void CSMWorld::CreateCommand::undo()
{ {
mModel.removeRow (mModel.getModelIndex (mId, 0).row()); 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);
}

View file

@ -15,6 +15,8 @@ class QAbstractItemModel;
namespace CSMWorld namespace CSMWorld
{ {
class IdTableProxyModel; class IdTableProxyModel;
class IdTable;
class RecordBase;
class ModifyCommand : public QUndoCommand class ModifyCommand : public QUndoCommand
{ {
@ -46,6 +48,27 @@ namespace CSMWorld
virtual void undo(); 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 #endif

View file

@ -71,6 +71,25 @@ namespace CSMWorld
virtual void removeRows (int index, int count) = 0; virtual void removeRows (int index, int count) = 0;
virtual void appendBlankRecord (const std::string& id) = 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 ///< \brief Collection of ID-based records
@ -120,6 +139,25 @@ namespace CSMWorld
virtual void appendBlankRecord (const std::string& id); 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); void addColumn (Column<ESXRecordT> *column);
}; };
@ -174,12 +212,12 @@ namespace CSMWorld
template<typename ESXRecordT> template<typename ESXRecordT>
int IdCollection<ESXRecordT>::getIndex (const std::string& id) const 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); throw std::runtime_error ("invalid ID: " + id);
return iter->second; return index;
} }
template<typename ESXRecordT> template<typename ESXRecordT>
@ -268,6 +306,49 @@ namespace CSMWorld
record.blank(); record.blank();
add (record); 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 #endif

View file

@ -56,7 +56,10 @@ bool CSMWorld::IdTable::setData ( const QModelIndex &index, const QVariant &valu
if (mIdCollection->isEditable (index.column()) && role==Qt::EditRole) if (mIdCollection->isEditable (index.column()) && role==Qt::EditRole)
{ {
mIdCollection->setData (index.row(), index.column(), value); 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; return true;
} }
@ -102,3 +105,30 @@ QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column)
{ {
return index (mIdCollection->getIndex (id), 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);
}

View file

@ -6,6 +6,7 @@
namespace CSMWorld namespace CSMWorld
{ {
class IdCollectionBase; class IdCollectionBase;
class RecordBase;
class IdTable : public QAbstractTableModel class IdTable : public QAbstractTableModel
{ {
@ -41,6 +42,11 @@ namespace CSMWorld
void addRecord (const std::string& id); void addRecord (const std::string& id);
QModelIndex getModelIndex (const std::string& id, int column) const; 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;
}; };
} }

View 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;
}

View file

@ -15,6 +15,18 @@ namespace CSMWorld
State_Deleted = 3, // exists in base, but has been deleted 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_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> template <typename ESXRecordT>
@ -22,13 +34,8 @@ namespace CSMWorld
{ {
ESXRecordT mBase; ESXRecordT mBase;
ESXRecordT mModified; ESXRecordT mModified;
State mState;
bool isDeleted() const; virtual RecordBase *clone() const;
bool isErased() const;
bool isModified() const;
const ESXRecordT& get() const; const ESXRecordT& get() const;
///< Throws an exception, if the record is deleted. ///< Throws an exception, if the record is deleted.
@ -44,21 +51,9 @@ namespace CSMWorld
}; };
template <typename ESXRecordT> template <typename ESXRecordT>
bool Record<ESXRecordT>::isDeleted() const RecordBase *Record<ESXRecordT>::clone() const
{ {
return mState==State_Deleted || mState==State_Erased; return new Record<ESXRecordT> (*this);
}
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;
} }
template <typename ESXRecordT> template <typename ESXRecordT>

View file

@ -11,6 +11,8 @@
#include "../../model/world/data.hpp" #include "../../model/world/data.hpp"
#include "../../model/world/commands.hpp" #include "../../model/world/commands.hpp"
#include "../../model/world/idtableproxymodel.hpp" #include "../../model/world/idtableproxymodel.hpp"
#include "../../model/world/idtable.hpp"
#include "../../model/world/record.hpp"
namespace CSVWorld namespace CSVWorld
{ {
@ -108,11 +110,16 @@ void CSVWorld::CommandDelegate::setEditLock (bool locked)
void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
{ {
QMenu menu (this); QModelIndexList selectedRows = selectionModel()->selectedRows();
QMenu menu (this);
if (mCreateAction) if (mCreateAction)
menu.addAction (mCreateAction); menu.addAction (mCreateAction);
if (selectedRows.size()>0)
menu.addAction (mRevertAction);
menu.exec (event->globalPos()); menu.exec (event->globalPos());
} }
@ -120,9 +127,9 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q
bool createAndDelete) bool createAndDelete)
: mUndoStack (undoStack), mCreateAction (0) : 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) 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); setItemDelegateForColumn (i, delegate);
} }
mModel = new CSMWorld::IdTableProxyModel (this); mProxyModel = new CSMWorld::IdTableProxyModel (this);
mModel->setSourceModel (model); mProxyModel->setSourceModel (mModel);
setModel (mModel); setModel (mProxyModel);
horizontalHeader()->setResizeMode (QHeaderView::Interactive); horizontalHeader()->setResizeMode (QHeaderView::Interactive);
verticalHeader()->hide(); verticalHeader()->hide();
setSortingEnabled (true); setSortingEnabled (true);
@ -149,6 +156,10 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q
connect (mCreateAction, SIGNAL (triggered()), this, SLOT (createRecord())); connect (mCreateAction, SIGNAL (triggered()), this, SLOT (createRecord()));
addAction (mCreateAction); addAction (mCreateAction);
} }
mRevertAction = new QAction (tr ("Revert Record"), this);
connect (mRevertAction, SIGNAL (triggered()), this, SLOT (revertRecord()));
addAction (mRevertAction);
} }
void CSVWorld::Table::setEditLock (bool locked) void CSVWorld::Table::setEditLock (bool locked)
@ -166,5 +177,35 @@ void CSVWorld::Table::createRecord()
std::ostringstream stream; std::ostringstream stream;
stream << "id" << index++; 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();
}
} }

View file

@ -13,6 +13,7 @@ namespace CSMWorld
class Data; class Data;
class UniversalId; class UniversalId;
class IdTableProxyModel; class IdTableProxyModel;
class IdTable;
} }
namespace CSVWorld namespace CSVWorld
@ -27,7 +28,9 @@ namespace CSVWorld
std::vector<CommandDelegate *> mDelegates; std::vector<CommandDelegate *> mDelegates;
QUndoStack& mUndoStack; QUndoStack& mUndoStack;
QAction *mCreateAction; QAction *mCreateAction;
CSMWorld::IdTableProxyModel *mModel; QAction *mRevertAction;
CSMWorld::IdTableProxyModel *mProxyModel;
CSMWorld::IdTable *mModel;
private: private:
@ -43,6 +46,8 @@ namespace CSVWorld
private slots: private slots:
void createRecord(); void createRecord();
void revertRecord();
}; };
} }