|
|
多步Undo/Redo的实现 |
|
|
作者:未知 来源:月光软件站 加入时间:2005-2-28 月光软件站 |
首先,建立一个基类CEditRecord,对于每一种操作,都从该基类上派生出与操作相对应的类,记载操作过程,供以后进行具体的Undo/Redo操作;基类CEditRecord中的纯虚函数,为Undo、Redo操作提供接口。
然后,建立一个用于控制Undo/Redo的类:CRecordCtrl。 CRecordCtrl类从基类CObArray上派生,用于记载已经进行过的操作,响应Undo/Redo命令等;其中nMaxStep变量表示允许Undo/Redo的次数,nCurrRecord变量表示当前的Undo的位置;函数Undo()和Redo()用于响应来自系统菜单、快捷键或者工具条的Undo和Redo命令;函数SetMaxStep()用于设置允许Undo/Redo的次数。
//////////////////////////////////////////////////////// // CEditRecord.h // Class CEditRecord、CEditCtrl Definition class CEditRecord : public CObject { public: CEditRecord(); public: virtual BOOL Undo( )=0; virtual BOOL Redo( )=0; };
class CRecordCtrl:public CObArray { public: CRecordCtrl( ); ~CRecordCtrl( ); private: int nCurrRecord; int nMaxStep; public: BOOL EnableUndo( ); BOOL EnableRedo( ); BOOL Undo( ); BOOL Redo( ); BOOL SetMaxStep(int n); };
extern CEditCtrl Records;
//////////////////////////////////////////////////// // CEditRecord.Cpp // Class CEdit、CEditCtrl Imeplemetion
#include "CEditRecord.h" CRecordCtrl Records;
CEditRecord::CEditRecord( ) { int mm=Records.GetSize( ); if(nCurrRecord==mm-1) { if(mm==nMaxStep) { //删除最早的记录 CEditRecord* pRec=(CEditRecord*)GetAt(0); delete pRec; Records.RemoveAt(0); nCurrRecord--; } } else { //删除所有Undo过的记录 for(int i=mm-1;i>nCurrRecord;i--) { CEditRecord* pRec=(CEditRecord*)GetAt(i); delete pRec; Records.RemoveAt(i); } }
nCurrRecord=Records.Add(this); }
CRecordCtrl::CRecordCtrl( ) { nCurrRecord=-1; nMaxStep=5; }
CRecordCtrl::~CRecordCtrl( ) { for(int i=GetSize()-1;i>=0;i--) { CEditRecord* pUndo=(CEditRecord*)GetAt(i); delete pUndo; } }
BOOL CRecordCtrl::EnableUndo( ) { return (nCurrRecord>=0); }
BOOL CRecordCtrl::EnableRedo( ) { return (nCurrRecord<GetSize( )-1); }
BOOL CRecordCtrl::Undo( ) { if(!EnableUndo( )) return FALSE;
CEditRecord* pRec=(CEditRecord*)GetAt(nCurrRecord--); if(pRec==NULL) return FALSE;
return pRec->Undo(); }
BOOL CRecordCtrl::Redo() { if(!EnableRedo( )) return FALSE;
CEditRecord* pRec=(CEditRecord*)GetAt(++nCurrRecord); if(pRec==NULL) return FALSE;
return pRec->Redo(); }
BOOL CRecordCtrl::SetMaxStep(int n) { if(n<1) return FALSE; int mm=GetSize( );
if(UndoStep<=n || mm<=n) { UndoStep=n; return TRUE; } else { //压缩用于Undo的记录 int nPack=mm-n; int u=min(nCurrRecord,nPack); for(int i=u-1;i>=0;i--) { CEditRecord* pRec=(CEditRecord*)GetAt(i); delete pRec; nCurrRecord--; }
//压缩用于Redo的记录 int v=nPack-u; for(int i=0;i<v;i++) { CEditRecord* pRec=(CEditRecord*)GetAt(mm-i-1); if(pRedo==NULL) delete pRec; } } return TRUE; } 在CEditRecord的生成函数中,首先判定是否达到允许的最大Undo次数; 如果未达到,直接把this指针加入到阵列中;如果超过,需要从阵列中,清除一些关于早期的操作的记录,然后把this指针加入到阵列中。
函数CRecordCtrl::SetMaxStep( )中,对于缩小Undo/Redo次数这种情况,特别是在阵列中已经记载了较多的操作,则需清除一些。
在CRecordCtrl类的析构函数中,清除阵列中的每一个CEditRecord对象。
下面给出一个例子说明如何建立CEditRecord对象,为方便计,假设进行的操作是从一个全局性的字符串pText中删除一段内容,我们用Pos表示所删内容在pText中的相对位置, 用Len表示所删内容的长度,并假设全局串pTetx的存储空间足够大。
1.从基类CEditRecord上派生出CEditCutString; 2.设置私有变量Pos、Len用于表示所删内容在pText中的相对位置、长度;设置私有变量pBuff用于分配存储空间,保存所删内容;设置私有变量Avialiable用于表示可否进行Undo/Redo。
///////////////////////////////////////////////////// // Example // #include "CEditRecord.h"
class CEditCutString:public CEditRecord { public: CEditCutString(int,int); ~CEditCutString(); private: int Pos; int Len; char* pBuff; BOOL Avialiable;
public: virtual BOOL Undo(); virtual BOOL Redo(); };
CEditCutString::CEditCutString(int p,int n) { Pos=p; Len=n; pBuff=new char[n];
if(pBuff==NULL) Avialiable=FALSE; esle { Avialiable=TRUE; memcpy(pBuff,pText+Pos,Len); } }
CEditCutString::~CEditCutString { if(Avialiable) delete []pBuff; }
BOOL CEditCutString::Undo() { if(!Avialiable) return FALSE;
int l=strlen(pText); for(int i=l;i>=Pos;i--) pText[i+Len]=pText[i];
for(int j=0;j<Len;j++) pText[Pos+j]=pBuff[j];
return TRUE; }
BOOL CEditCutString::Redo() { if(!Avialiable) return FALSE;
int l=strlen(pText);
for(int i=Pos;i<=l-Len;i++) pText[i]=pText[i+Len];
return TRUE; }

|
|
相关文章:相关软件: |
|