ACAD中撤销恢复机制的浅析

作者:阿门

很早以前就想整理一份这样的资料,趁着这次公司要召开研发大会 需要交一篇论文,就写下来了。截了一部分,贴上来让大家指正指正,呵呵。为公司软件实现的撤销恢复机制就不贴出来了,呵呵。。

顺便贺下 公司的第一届研发大会。期待。。。

转载请注明。。

ACAD平台的撤销恢复机制主要就是以ACAD中执行的命令为单位的,下面将从使用这一套机制入手,来试着来分析这一套机制。
如果想使用和了解ACAD的撤销恢复机制,必然要从自定义实体入手,因为,ACAD中所有的操作其实就是对DWG中数据对象的一些操作,只不过是以命令为单位来组织一个操作中的操作对象,所以ACAD的撤销恢复机制的针对对象都是ACAD的数据库对象,而已经封装好的数据库对象我们是无法了解到其内部的运行机制的,但ARX给我们提供了扩展ACAD数据库对象的机会,那就是自定义对象和实体技术,并且也预留也一些接口,用来实现自定义对象和实体的信息的撤销与恢复。首先,稍微介绍一下ARX实现自动撤销恢复与部分撤销恢复的一些接口:

a).AcDbObject::assertWriteEnable(Adesk::Boolean autoUndo = true,Adesk::Boolean recordModified = true);
该函数是用来验证当前操作节点是否以写方式打开而进行操作的,其中每一个参数autoUndo就是标记调用这个接口的成员函数中,是不是会自动进行undo记录。

b).virtual Acad::ErrorStatus AcDbObject::dwgInFields(AcDbDwgFiler * pFiler);
该函数是用来将编档器中的备忘数据恢复到对象中。

c).virtual Acad::ErrorStatus AcDbObject:: dwgOutFields( AcDbDwgFiler* pFiler);
该函数是用来将对象的状态及数据备忘到编档器中。

d).void AcDbObject::disableUndoRecording(Adesk::Boolean disable);
该函数是用来禁用、启用某对象的UNDO操作的。

e).void AcDbDatabase::disableUndoRecording(bool disable)。
该函数是用来禁用、启用整个数据库的UNDO操作的。

f).virtual Acad::ErrorStatus AcDbObject::applyPartialUndo( AcDbDwgFiler* undoFiler, AcRxClass* classObj);
该函数是控制对象局部撤销恢复信息的接口。

g); AcDbDwgFiler * AcDbObject::undoFiler();
该函数是用来返回记录对象撤销恢复记录的编档器

如何实现自动撤销?

当在对象的成员函数中使用assertWriteEnable时参数autoUndo为true则是告诉系统需要自动的实现撤销恢复记录,自动撤销恢复记录中保存的状态信息是在dwgInFields、dwgOutFields接口中对撤销编档器写入的信息;当已写方式打开一个对象时,在第一个调用其对象的成员函数中对assertWriteEnable进行了一次autoUndo参数为true的函数时,系统的UNDO机制会调用一次该对象成员dwgOutFields以备份当前对象的信息(需要备份多少信息,由用户程序自己来决定);当执行UNDO命令时,系统的UNDO机制同样会先进行一次dwgOutFields调用以备份当前对象的信息,然后再调用一次dwgInFields将之前的撤销备份信息写入对象;系统的REDO机制就是利用了UNDO机制,当执行REDO命令时,系统的UNDO机制同样会先进行一次dwgOutFields调用以备份当前对象的信息,然后再调用一次dwgInFields将之前的重做备份信息写入对象;这样就完成了系统的自动撤销恢复;下面附上少许代码:

void AMenLine::SetStartPoint( const AcGePoint3d& pt )
{   
     assertWriteEnabled(true,true);       //自动撤销恢复
     m_ptStart = pt;        //设置新值
}

void AMenLine::SetEndPoint( const AcGePoint3d& pt )
{
     assertWriteEnabled(true,true);       //自动撤销恢复
     m_ptEnd = pt; //设置新值
}

Acad::ErrorStatus AMenLine::dwgOutFields (AcDbDwgFiler *pFiler) const 
{
     assertReadEnabled () ;
     //----- Save parent class information first.
     Acad::ErrorStatus es =AcDbEntity::dwgOutFields (pFiler) ;
     if ( es != Acad::eOk )
         return (es) ;

     //----- Object version number needs to be saved first
     if ( (es =pFiler->writeUInt32 (AMenLine::kCurrentVersionNumber)) != Acad::eOk )
         return (es) ;

     //撤销编档器
     if( pFiler->filerType() == AcDb::kUndoFiler )
     {
         pFiler->writePoint3d(m_ptStart);     
         pFiler->writePoint3d(m_ptEnd);
     }

     return (pFiler->filerStatus ()) ;
}

Acad::ErrorStatus AMenLine::dwgInFields (AcDbDwgFiler *pFiler) 
{
     assertWriteEnabled () ;
     //----- Read parent class information first.
     Acad::ErrorStatus es =AcDbEntity::dwgInFields (pFiler) ;
     if ( es != Acad::eOk )
         return (es) ;

     //----- Object version number needs to be read first
     Adesk::UInt32 version =0 ;
     if ( (es =pFiler->readUInt32 (&version)) != Acad::eOk )
         return (es) ;

     if ( version > AMenLine::kCurrentVersionNumber )
         return (Acad::eMakeMeProxy) ;

     //撤销编档器
     if( pFiler->filerType() == AcDb::kUndoFiler )
     {
         pFiler->readPoint3d(&m_ptStart);
         pFiler->readPoint3d(&m_ptEnd);
     }

     return (pFiler->filerStatus ()) ;

}

如何实现局部撤销?

当在对象的成员函数中使用assertWriteEnable时参数autoUndo为false则告诉系统将由用户程员自己来实现撤销恢复,而不使用系统的自动记录来实现,从而实现局部的撤销恢复,当用户程序想实现局部撤销的时候,需要在调用assertWriteEnable的成员函数中,调用AcDbDwgFiler * AcDbObject::undoFiler();得到编档器,然后将需要实现撤销恢复的信息,写入编档器中从而完成信息状态的记录,在文档上有介绍,编入的第一个信息,应该是对象所在的AcRxClass指针,用来作判断是否是属于对应实体的信息;当系统在执行UNDO操作的时候,会如若发现有局部信息的记录,会自动调用applyPartialUndo( AcDbDwgFiler* undoFiler, AcRxClass* classObj)接口,所以,用户程序想真正的完成局部撤销的时候,还需要在这个接口中实现局部信息状态的恢复,其中applyPartialUndo接口中的AcDbDwgFiler* undoFiler参数就是保存信息状态时使用AcDbDwgFiler * AcDbObject::undoFiler()得到的那个撤销编档器,AcRxClass* classObj就是你在保存息状态的时候编入的第一个信息值(AcRxClass指针),如果在对象的多个成员函数中,都使用和实现了局部撤消机制,而在当前命令中,有调用这些接口对对象进行修改,那么在系统执行UNDO操作时,会多次调用applyPartialUndo( AcDbDwgFiler* undoFiler, AcRxClass* classObj)接口,从而实现每一个成员函数保存的信息的局部撤销,那么在设计上,你就可以在编档的时候编一个DWORD型的值,用来区分多次调用applyPartialUndo(AcDbDwgFiler* undoFiler, AcRxClass* classObj)接口的一个标记,为不同的成员函数操作导致调用到的applyPartialUndo编号,从而正确的实现多个成员函数局部撤销。与局部撤销对应的就是局部的恢复,其实,在ACAD系统中,它们使用的是同一套机制,看ARX帮助说明,就可以发现,局部撤销是通过在设置属性的SET函数中,调用相对应的接口获取撤销编档器后,把需要撤消的信息,写入编档器备份,等系统执行UNDO命令时,会根据UNDO命令栈中的数据情况,来调用实体的applyPartialUndo,从而使用户程序有机会把备份的值还原,而恢复操作,也是一个样的,它要求用户要编写applyPartialUndo接口时,不能使用直接对对象成员属性赋值的方法,而必须采用也实现了UNDO机制的SET函数(一般情况下就可以利用同一个函数),来实现对应值的设置,因为,在这个设置的过程中,其实又会对现在撤销前的值进行一个备份,当使用系统REDO机制的时候,也同样会调用applyPartialUndo,使用户程序有机会把对应的值重做,这其实就是UNDO机制的一种巧妙应用而已。下面附上少许代码:

void AMenLine::SetStartPoint( const AcGePoint3d& pt )
{    
     assertWriteEnabled(false,true);      //局部撤销恢复
    
     AcDbDwgFiler* pFiler = NULL;
     if( (pFiler = AcDbObject::undoFiler()) != NULL )
     {
         pFiler->writeItem(this->desc());     //写入对象对应的运行时识别类
         pFiler->writeItem(1);                     //为起点所编的号1
         pFiler->writePoint3d(m_ptStart);     //将起点源值写入撤销编档器
     }

     m_ptStart = pt;        //设置新值
}

void AMenLine::SetEndPoint( const AcGePoint3d& pt )
{
     assertWriteEnabled(false,true);      //局部撤销恢复

     AcDbDwgFiler* pFiler = NULL;
     if( (pFiler = AcDbObject::undoFiler()) != NULL )
     {
         pFiler->writeItem(this->desc());     //写入对象对应的运行时识别类
         pFiler->writeItem(1);                     //为终点所编的号1
         pFiler->writePoint3d(m_ptStart);     //将终点源值写入撤销编档器
     }

     m_ptEnd = pt; //设置新值
}

Acad::ErrorStatus AMenLine::applyPartialUndo( AcDbDwgFiler* undoFiler, AcRxClass* classObj )
{
     if( this->desc() != classObj )//如果运行时信息不匹配,则调用父类的applyPartialUndo接口
         return AcDbEntity::applyPartialUndo(undoFiler,classObj);

     int iParam = 0;
     undoFiler->readItem(&iParam);

     if( 1 == iParam )      //起点的编号
     {
         AcGePoint3d ptStart;
         undoFiler->readPoint3d(&ptStart);
         SetStartPoint(ptStart);     //调用实现了UNDO机制的接口,用来局部恢复
         return Acad::eOk;
     }
     else if( 2 == iParam )      //终点的编号
     {
         AcGePoint3d ptEnd;
         undoFiler->readPoint3d(&ptEnd);
          SetEndPoint(ptEnd);              //调用实现了UNDO机制的接口,用来局部恢复
         return Acad::eOk;
     }
     return Acad::eNotImplementedYet;
}

事务机制与撤销恢复机制的关系?
相信细心的开发人员可以发现,事务机制其实就是对撤销恢复机制的灵活利用和扩展,回滚事务其实就是一种命令撤销,向事务添加一个加入了数据库的操作节点时,可以认为是向撤销恢复机制里添加了一个新建命令(顺便说一句,撤销恢复机制只能是对加入到了数据库中的节点有才效,这也是为什么向事务中添加新建节点时,这个节点必须已经加入到了数据库中),用事务打开节点,然后操作,可以看作是向撤销恢复机制添加了一个修改命令。事务机制不过就是对命令撤销恢复机制的一个批处理而已。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
从acdbmgd.dll导出的内容,方便参考,格式如下: 类:Autodesk.AutoCAD.DatabaseServices.TextStyleTableRecord:Autodesk.AutoCAD.DatabaseServices.SymbolTableRecord New() 方法: {dtor}() {dtor}() {dtor}() {dtor}() ApplyPaperOrientationTransform(Viewport) ApplyPartialUndo(DwgFiler,RXClass) Audit(AuditInfo) Cancel() Close() CloseAndPage(Boolean) CopyFrom(RXObject) CreateExtensionDictionary() DisableUndoRecording(Boolean) Dispose() DowngradeOpen() DowngradeToNotify(Boolean) DwgIn(DwgFiler) DwgOut(DwgFiler) DxfIn(DxfFiler) DxfOut(DxfFiler) Erase() Erase(Boolean) HandOverTo(DBObject,Boolean,Boolean) ReleaseExtensionDictionary() RemoveField(ObjectId) ResetScaleDependentProperties() SetObjectIdsInFlux() SetPaperOrientation(Boolean) SwapIdWith(ObjectId,Boolean,Boolean) SwapReferences(IdMapping) UpgradeOpen() ViewportDraw(ViewportDraw) XDataTransformBy(Matrix3d) 函数: Clone() AS System.Object CompareTo(Object) AS System.Int32 CreateObjRef(Type) AS System.Runtime.Remoting.ObjRef DecomposeForSave(DwgVersion) AS Autodesk.AutoCAD.DatabaseServices.DecomposeForSaveReplacementRecord DeepClone(DBObject,IdMapping,Boolean) AS Autodesk.AutoCAD.DatabaseServices.DBObject Equals(Object) AS System.Boolean GetHashCode() AS System.Int32 GetLifetimeService() AS System.Object GetObjectSaveVersion(DwgFiler) AS Autodesk.AutoCAD.DatabaseServices.FullDwgVersion GetObjectSaveVersion(DxfFiler) AS Autodesk.AutoCAD.DatabaseServices.FullDwgVersion GetPersistentReactorIds() AS Autodesk.AutoCAD.DatabaseServices.ObjectIdCollection GetReactors() AS System.Collections.Generic.List`1[[Autodesk.AutoCAD.Runtime.RXObject, acdbmgd, Version=17.1.0.0, Culture=neutral, PublicKeyToken=null]] GetRXClass() AS Autodesk.AutoCAD.Runtime.RXClass GetType() AS System.Type GetXDataForApplication(String) AS Autodesk.AutoCAD.DatabaseServices.ResultBuffer HasPersistentReactor(ObjectId) AS System.Boolean InitializeLifetimeService() AS System.Object QueryX(RXClass) AS System.IntPtr RemoveField() AS Autodesk.AutoCAD.DatabaseServices.ObjectId RemoveField(String) AS Autodesk.AutoCAD.DatabaseServices.ObjectId SetAttributes(DrawableTraits) AS System.Int32 SetField(Field) AS Autodesk.AutoCAD.DatabaseServices.ObjectId SetField(String,Field) AS Autodesk.AutoCAD.DatabaseServices.ObjectId SetFromStyle() AS System.Boolean ToString() AS System.String UpgradeFromNotify() AS System.Boolean ViewportDrawLogicalFlags(ViewportDraw) AS System.Int32 WblockClone(RXObject,IdMapping,Boolean) AS Autodesk.AutoCAD.DatabaseServices.DBObject WorldDraw(WorldDraw) AS System.Boolean X(RXClass) AS System.IntPtr 属性: AcadObject AS System.Object 可读不可写 Annotative AS Autodesk.AutoCAD.DatabaseServices.AnnotativeStates 可读可写 AutoDelete AS System.Boolean 可读可写 BigFontFileName AS System.String 可读可写 ClassID AS System.Guid 可读不可写 Database AS Autodesk.AutoCAD.DatabaseServices.Database 可读不可写 Drawable AS Autodesk.AutoCAD.GraphicsInterface.Drawable 可读不可写 DrawableType AS Autodesk.AutoCAD.GraphicsInterface.DrawableType 可读不可写 ExtensionDictionary AS Autodesk.AutoCAD.DatabaseServices.ObjectId 可读不可写 FileName AS System.String 可读可写 FlagBits AS System.Byte 可读可写 Font AS Autodesk.AutoCAD.GraphicsInterface.FontDescriptor 可读可写 Handle AS Autodesk.AutoCAD.DatabaseServices.Handle 可读不可写 HasFields AS System.Boolean 可读不可写 HasSaveVersionOverride AS System.Boolean 可读可写 Id AS Autodesk.AutoCAD.DatabaseServices.ObjectId 可读不可写 IsAProxy AS System.Boolean 可读不可写 IsCancelling AS System.Boolean 可读不可写 IsDependent AS System.Boolean 可读不可写 IsDisposed AS System.Boolean 可读不可写 IsErased AS System.Boolean 可读不可写 IsEraseStatusToggled AS System.Boolean 可读不可写 IsModified AS System.Boolean 可读不可写 IsModifiedGraphics AS System.Boolean 可读不可写 IsModifiedXData AS System.Boolean 可读不可写 IsNewObject AS System.Boolean 可读不可写 IsNotifyEnabled AS System.Boolean 可读不可写 IsNotifying AS System.Boolean 可读不可写 IsObjectIdsInFlux AS System.Boolean 可读不可写 IsPersistent AS System.Boolean 可读不可写 IsReadEnabled AS System.Boolean 可读不可写 IsReallyClosing AS System.Boolean 可读不可写 IsResolved AS System.Boolean 可读不可写 IsShapeFile AS System.Boolean 可读可写 IsTransactionResident AS System.Boolean 可读不可写 IsUndoing AS System.Boolean 可读不可写 IsVertical AS System.Boolean 可读可写 IsWriteEnabled AS System.Boolean 可读不可写 MergeStyle AS Autodesk.AutoCAD.DatabaseServices.DuplicateRecordCloning 可读可写 Name AS System.String 可读可写 ObjectBirthVersion AS Autodesk.AutoCAD.DatabaseServices.FullDwgVersion 可读不可写 ObjectId AS Autodesk.AutoCAD.DatabaseServices.ObjectId 可读不可写 ObliquingAngle AS System.Double 可读可写 OwnerId AS Autodesk.AutoCAD.DatabaseServices.ObjectId 可读可写 PaperOrientation AS Autodesk.AutoCAD.DatabaseServices.PaperOrientationStates 可读不可写 PriorSize AS System.Double 可读可写 TextSize AS System.Double 可读可写 UndoFiler AS Autodesk.AutoCAD.DatabaseServices.DwgFiler 可读不可写 UnmanagedObject AS System.IntPtr 可读不可写 XData AS Autodesk.AutoCAD.DatabaseServices.ResultBuffer 可读可写 XScale AS System.Double 可读可写 -------------------------------- 类:Autodesk.AutoCAD.DatabaseServices.TextVerticalMode:System.Enum 函数: CompareTo(Object) AS System.Int32 Equals(Object) AS System.Boolean GetHashCode() AS System.Int32 GetType() AS System.Type GetTypeCode() AS System.TypeCode ToString() AS System.String ToString(IFormatProvider) AS System.String ToString(String,IFormatProvider) AS System.String ToString(String) AS System.String 字段: TextBase AS TextVerticalMode TextBottom AS TextVerticalMode TextTop AS TextVerticalMode TextVerticalMid AS TextVerticalMode value__ AS Int32
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值