去下载MiniHex的源代码看看,下面是我从里面抄的:)
{**********************************************************}
{ }
{ 功能:管理 Undo 和 Redo. }
{ 说明:采用循环队列记录Undo动作和Redo动作, }
{ Undo和Redo是两个不同的队列。 }
{ }
{**********************************************************}
unit ActsMgr;
interface
uses
Windows, Messages, SysUtils, Classes,
Controls, Forms, Dialogs;
type
TActType = ( //动作类型
atNone,
atModify,
atInsert,
atDelete
);
PAct = ^TAct;
TAct = record //动作记录
Enabled: Boolean; //该动作是否能用
ActType: TActType; //动作类型
Buf: string; //缓冲区
Offset: Integer; //偏移
Count: Integer; //字节数
CurPos: Integer; //光标位置
Prev: PAct; //Prev指针
Next: PAct; //Next指针
end;
TActs = array of TAct;
TActsMgr = class
private
FUndoActs: TActs; //Undo队列
FUndoHead: PAct; //队列头指针
FUndoTail: PAct; //队列尾指针
FRedoActs: TActs; //Redo队列
FRedoHead: PAct; //队列头指针
FRedoTail: PAct; //队列尾指针
FMaxUndo: Integer; //最大Undo次数
procedure SetMaxUndo(Value: Integer);
procedure InitAct(var Act: TAct);
public
constructor Create(AMaxUndo: Integer);
destructor Destroy; override;
property MaxUndo: Integer read FMaxUndo write SetMaxUndo;
function AddUndoItem: PAct;
function AddRedoItem: PAct;
function Undo: PAct;
function Redo: PAct;
function CanUndo: Boolean;
function CanRedo: Boolean;
end;
implementation
constructor TActsMgr.Create(AMaxUndo: Integer);
begin
SetMaxUndo(AMaxUndo);
end;
destructor TActsMgr.Destroy;
begin
SetLength(FUndoActs, 0);
SetLength(FRedoActs, 0);
end;
procedure TActsMgr.SetMaxUndo(Value: Integer);
var
i: Integer;
begin
FMaxUndo := Value;
SetLength(FUndoActs, FMaxUndo + 1);
for i := 0 to FMaxUndo do
begin
if i = FMaxUndo then
FUndoActs.Next := @FUndoActs[0]
else
FUndoActs.Next := @FUndoActs[i+1];
if i = 0 then
FUndoActs.Prev := @FUndoActs[FMaxUndo]
else
FUndoActs.Prev := @FUndoActs[i-1];
FUndoActs.Enabled := False;
end;
FUndoHead := @FUndoActs[0];
FUndoTail := @FUndoActs[0];
SetLength(FRedoActs, FMaxUndo + 1);
for i := 0 to FMaxUndo do
begin
if i = FMaxUndo then
FRedoActs.Next := @FRedoActs[0]
else
FRedoActs.Next := @FRedoActs[i+1];
if i = 0 then
FRedoActs.Prev := @FRedoActs[FMaxUndo]
else
FRedoActs.Prev := @FRedoActs[i-1];
FRedoActs.Enabled := False;
end;
FRedoHead := @FRedoActs[0];
FRedoTail := @FRedoActs[0];
end;
procedure TActsMgr.InitAct(var Act: TAct);
begin
with Act do
begin
Enabled := True;
Buf := '';
Offset := 0;
Count := 0;
CurPos := 0;
end;
end;
function TActsMgr.AddUndoItem: PAct;
begin
InitAct(FUndoHead^);
Result := FUndoHead;
if FUndoHead^.Next = FUndoTail then
FUndoTail := FUndoTail^.Next;
FUndoHead := FUndoHead^.Next;
FUndoHead^.Enabled := False;
end;
function TActsMgr.AddRedoItem: PAct;
begin
InitAct(FRedoHead^);
Result := FRedoHead;
if FRedoHead^.Next = FRedoTail then
FRedoTail := FRedoTail^.Next;
FRedoHead := FRedoHead^.Next;
FRedoHead^.Enabled := False;
end;
function TActsMgr.Undo: PAct;
begin
if not CanUndo then
begin
Result := nil;
Exit;
end;
FUndoHead := FUndoHead^.Prev;
FRedoHead := FRedoHead^.Prev;
Result := FUndoHead;
end;
function TActsMgr.Redo: PAct;
begin
if not CanRedo then
begin
Result := nil;
Exit;
end;
Result := FRedoHead;
FRedoHead := FRedoHead^.Next;
FUndoHead := FUndoHead^.Next;
end;
function TActsMgr.CanUndo: Boolean;
begin
Result := (FUndoHead <> FUndoTail);
end;
function TActsMgr.CanRedo: Boolean;
begin
Result := FRedoHead^.Enabled;
end;
end.