有关undo和redo(撤销重做)的问题!重奖!

awayso 2002-07-15 11:28:16
我做一个程序,需要有undo和redo功能,请教各位右经验的大虾们,该如何下手,使用剪贴板呢还是其他!请详细点!先给100分(我一次最多只能给100),不过零开贴给!
...全文
185 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
laosanr 2003-03-13
  • 打赏
  • 举报
回复
多步Undo/Redo的实现
应用软件中,Undo/Redo功能为用户提供了方便,而多步Undo/Redo则更是如此。
本文利用C++面对象的程序设计技术,研究了多步Undo/Redo的实现方法。

首先,我们建立一个基类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;
}
easyrock 2002-07-15
  • 打赏
  • 举报
回复
去www.codeproject.com看看吧
awayso 2002-07-15
  • 打赏
  • 举报
回复
听起来不错,我来试一试!
mty 2002-07-15
  • 打赏
  • 举报
回复
定义一个数据结构,设定可redo, undo的次数,每次CTRL+C的时候,
1).将剪贴板中内容取出保存
2).将当前选择内容置于剪贴板中
undo的时候,指针前移一次,取出其内容填充,直遇到HEAD,
redo的时候,指针后移一次,取出其内容填充,直遇到TAIL-->剪贴板
至于占用内存的问题,可以有很多办法解决,一种就是当数据超过一定容量的时候,
将其保存到文件中,你的数据结构中只保留着索引;另一种就是用内存映射文件等等都可以...
zzhcom 2002-07-15
  • 打赏
  • 举报
回复
可看看一个叫做"memomize"的模式
awayso 2002-07-15
  • 打赏
  • 举报
回复
我是操作的一个文件,我不能每次把它全部保存八,那样很占内存的!

to yhb4(胖子)
如何使用缓存机制,请详细好么?
shshsh_0510 2002-07-15
  • 打赏
  • 举报
回复
看看“模式”,如command等
yhb4 2002-07-15
  • 打赏
  • 举报
回复
使用缓存机制
dingkl 2002-07-15
  • 打赏
  • 举报
回复
定义一个结构,存放以前的操作,进行undo或redo时,利用保存的操作数据进行恢复或重新操作
yhb4 2002-07-15
  • 打赏
  • 举报
回复
使用缓存机制
zzhcom 2002-07-15
  • 打赏
  • 举报
回复
怎么会用到剪贴版呢,又不是拷贝粘贴。
具体的你可以看看一个叫memomize的模式!

16,472

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧