1.基本实现思路
实现一个文件的撤销重做最简单的思想就是,在每个撤销重做节点处保存一份文件的内容,撤销重做时,分别替换对应节点处的文件内容即可。这种做法开销太大,每个节点处都需要保存一份完整的文档内容,每次撤销重做时都需要替换全部的文档内容。
如果每个撤销重做节点处只保存此节点相对于上一节点改变的内容,每次撤销重做时只修改两个节点间差异的内容,可以有效的降低撤销重做的开销。FreeCAD的撤销重做机制就是基于这种思想实现的。
节点和节点之间的修改可以总结为三种情况:增加、删除、改变。撤销重做机制的主要内容就是记录这三种情况下的修改,并复原这三种情况的修改。
2.FreeCAD撤销重做对象
FreeCAD撤销重做机制的对象是Document对象,也就是说,Document对象的修改可以被撤销重做,其他的不可以撤销重做。
3.FreeCAD撤销重做相关类
App::Document类中包含了一系列Transaction类的容器,可以看出,Transaction类其实是App::Document撤销重做节点的表示类。
分析Transaction类的成员:
他的成员变量只有三个,一个是表示其名称的Name,一个是表示其标识符的transID,一个是表示修改信息的_Objects。_Objects对象是一个多索引的容器,里面存储了一系列的TransactionalObject和TransactionObject的键值对。TransactionalObject表示的是Document中被修改的对象单元,App层Document的对象单元是以DocumentObject为基类的,Gui层的Document的对象单元是以ViewProviderDocumentObject为基类的。TransactionObject表示对象单元被修改的内容,当文档的对象单元被修改时,将修改前的内容记录在TransactionObject中。对应于Document的对象单元,TransactionObject也被分为两类,TransactionDocumentObject和TransactionViewProvider,分别保存App层和Gui层的对象单元的修改。_Objects对象则表示一系列对文档单元对象的修改。
Transaction类的有几个比较重要的方法:
apply方法时Transaction类执行撤销重做的借口,其流程比较简单,把所有的单元对象的修改依次执行撤销重做(这里之所以要将所有的修改都做一遍Del、New、Change是因为存在耦合的情况,New和Change可能存储在同一个修改记录中)。
以上这几个方法主要是添加单元对象的修改到该类的_Objects对象中。
再来分析TransactionObject类,其成员变量如下:
Status表征此次修改的类型:新建、删除或者修改;_PropChangeMap表示被修改的属性的容器,其key为属性指针,PropData为修改前属性的内容(这里还要说明一下,Document的单元对象其实都是一堆属性的容器,从其基类PropertyContainer可以看出,因此对文档的修改其实就是对其中的Property的修改,所以在此处存在一个容器保存所有被修改的属性);_NameInDocument表示文档中该对象单元的名字(注意这个单元对象在文档中名字与其Label属性不同,Label属性表示其对用户的名字,而这个名字表示这个对象在文档中存储的标识符)。
分析其成员函数:
这三个方法是执行撤销重做的接口,分析其函数内容:
Transaction中的applyDel和applyNew并没有实现任何内容,这是由于,这一层的TransactionObject并不知道到底是该对App层的Document操作还是Gui层的Document操作。
applyChn的主要功能则是将_PropChangeMap中的所有属性依次恢复到原来的状态。
再来分析TransactionDocumentObject类,如下所示,该类无其他成员变量,仅仅是重载了applyNew和applyDel方法。
分析其重载方法的实现:
在这两个方法中,分别将相应的DocumentObject对象移除和添加到App::Document中。
再来分析TransactionViewProvider类,
该类也仅仅是重载了applyNew和applyDel方法,分析其重载方法的实现
其applyNew方法将对应的ViewProviderDocumentObject添加到Gui::Document中,但applyDel方法中并未进行相应的删除工作,这是因为ViewProviderDocumentObject的删除是被DocumentObject触发实现的,并未提交到撤销重做系统中。
4.FreeCAD撤销重做的基本流程
创建撤销节点流程:
撤销执行流程:
重做执行流程: