分享
 
 
 

多步Undo/Redo的实现

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

首先,建立一个基类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;

}

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有