多步Undo/Redo的實現

發表于:2007-07-14來源:作者:點擊數: 標簽:
首先,建立一個基類CEditRecord,對于每一種操作,都從該基類上派生出與操作相對應的類,記載操作過程,供以后進行具體的Undo/Redo操作;基類CEditRecord中的純虛函數,為Undo、Redo操作提供接口。 然后,建立一個用于控制Undo/Redo的類:CRecordCtrl。 CReco
    首先,建立一個基類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;
}

原文轉自:http://www.anti-gravitydesign.com

国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97