撤销和重做
撤销和重做操作主要的两种实现方式:
- 记录每一步操作后的当前数据库,撤销时把保存的数据库恢复。这种方式比较消耗内存;
- 记录每一步操作前后的差异,撤销时对差异进行“减法”操作,重做时对差异进行“加法”操作。但是有时难以计算“差异”。
撤销类
RS_Undoable
RS_Undoable
继承了标志类RS_Flags
,添加了一些设置标志类中的撤销标志位的方法。并且预留了一个虚函数接口void undoStateChanged(bool undone);
,用于设置撤销标志位变换之后的操作。
继承自RS_Undoable
类是实体类RS_Entity
,这表明每一个实体类都是支持Undo/Redo
操作的。在RS_Entity
类中,undoStateChanged()
函数主要就是将实体的选中标志位设置为false
,然后调用update()
虚函数。RS_Entity::update()
只提供了一个空实现,而派生类中重写了update()
函数的类不多,例如基本的绘图实体(直线、圆、圆弧)都没有重写,所以一般实体在执行Undo
操作时,就是将自身的选中标志位设置为false
就完了。
void RS_Entity::undoStateChanged(bool undone)
{
Q_UNUSED( undone);
setSelected(false); // 为什么不使用传入参数undone??
update();
}
RS_UndoCycle
撤销循环类RS_UndoCycle
相当于执行一个特征对应的节点。RS_UndoCycle
的定义如下,类中有一个RS_Undoable*
的集合,这个集合里面存放的实际上是实体的指针。在实际操作过程中,每执行一个特征后,都会添加一个撤销循环对象,这里面记录了该特征实际产生了多少个可撤销的步骤。因为有些特征包含多个步骤,所以这里使用了集合来存放RS_Undoable
。例如添加一条直线段,撤销循环中就只会有一个RS_Undoable
;如果选中多个实体,然后删除掉它们,撤销循环中就会有多个RS_Undoable
,每个RS_Undoable
对应一个实体删除的记录。
class RS_UndoCycle {
public:
RS_UndoCycle(/*RS2::UndoType type*/)=default;
void addUndoable(RS_Undoable* u);
void removeUndoable(RS_Undoable* u);
size_t size(void);
void changeUndoState(); // 各自执行undo操作
// ...
private:
std::set<RS_Undoable*> undoables; // 实际存放的是实体指针
};
RS_Undo
RS_Undo
类是与撤销操作相关的管理类,所有的撤销操作都是从这里开始执行的。RS_Undo
类提供了undo()
和redo()
两个接口,分别用来执行撤销和重做的操作。RS_Undo
类内存储了一个撤销列表,撤销列表是vector
类型,表明撤销列表有顺序的要求;里面的元素是RS_UndoCycle
的共享指针,表明每一个撤销节点。提供了一个计数器undoPointer
用于表示当前撤销节点的位置,默认情况下执行最后一个撤销节点。
class RS_Undo {
public:
virtual ~RS_Undo() = default;
bool undo();
bool redo();
int countUndoCycles(); // 可以undo的步骤的数量
int countRedoCycles(); // 可以redo的步骤的数量
bool hasUndoable(); // 是否可以执行undo
void startUndoCycle(); // 开始添加新的Undo,将已经撤销过的节点删掉
void addUndoable(RS_Undoable* u);
virtual void endUndoCycle();
virtual void removeUndoable(RS_Undoable* u) = 0;
// ...
private:
std::vector<std::shared_ptr<RS_UndoCycle>> undoList; // 撤销列表
int undoPointer = -1; // 当前节点指针
// ...
};
从RS_Undo
类派生的类是RS_Document
文档类。文档类在执行撤销特征的时候,会调用undo()
函数。在undo()
函数中,在获取当前撤销节点的同时,更新当前撤销节点指针,然后更新界面上的撤销和重做图标,最后调用当前撤销节点的执行撤销操作的函数。redo()
函数的操作与该函数类似。
bool RS_Undo::undo() {
// ...
std::shared_ptr<RS_UndoCycle> uc = undoList[undoPointer--]; // 获取当前撤销节点,更新当前撤销节点指针
setGUIButtons(); /