I need to be able to make Undo and Redo operatons in my simple delphi paint. So I decided to make some container to save history (not full history, only few previous bitmap files).
unit HistoryQueue;
interface
uses
Graphics;
type
myHistory = class
constructor Create(Size:Integer);
public
procedure Push(Bmp:TBitmap);
function Pop():TBitmap;
procedure Clean();
procedure Offset();
function isEmpty():boolean;
function isFull():boolean;
function getLast():TBitmap;
protected
end;
var
historyQueueArray: array of TBitmap;
historyIndex, hSize:Integer;
implementation
procedure myHistory.Push(Bmp:TBitmap);
var tbmp:TBitmap;
begin
if(not isFull) then begin
Inc(historyIndex);
historyQueueArray[historyIndex]:=TBitmap.Create;
historyQueueArray[historyIndex].Assign(bmp);
end else begin
Offset();
historyQueueArray[historyIndex]:=TBitmap.Create;
historyQueueArray[historyIndex].Assign(bmp);
end;
end;
procedure myHistory.Clean;
var i:Integer;
begin
{ for i:=0 to hSize do begin
historyQueueArray[i].Free;
historyQueueArray[i].Destroy;
end; }
end;
constructor myHistory.Create(Size:Integer);
begin
hSize:=Size;
SetLength(historyQueueArray, hSize);
historyIndex:=-1;
end;
function myHistory.isEmpty: boolean;
begin
Result:=(historyIndex = -1);
end;
function myHistory.isFull: boolean;
begin
Result:=(historyIndex = hSize);
end;
procedure myHistory.Offset;
var i:integer;
begin
//historyQueueArray[0]:=nil;
for i:=0 to hSize-1 do begin
historyQueueArray[i]:=TBitmap.Create;
historyQueueArray[i].Assign(historyQueueArray[i+1]);
end;
end;
function myHistory.Pop: TBitmap;
var
popBmp:TBitmap;
begin
popBmp:= TBitmap.Create;
popBmp.Assign(historyQueueArray[historyIndex]);
Dec(historyIndex);
Result:=popBmp;
end;
function myHistory.getLast: TBitmap;
var
tBmp:TBitmap;
begin
tBmp:= TBitmap.Create;
tBmp.Assign(historyQueueArray[historyIndex]);
Result:=tBmp;
end;
end.
In my program I use it like that.
Saving in history:
procedure TMainForm.FormCreate(Sender: TObject);
begin
{...}
picHistory:=myHistory.Create(10); //FOR UNDO
tempHistory:=myHistory.Create(10); //FOR REDO
end;
//if mouse is up - that mean we finish to draw something on canvas, so we gonna save what we drew
procedure TMainForm.imgMainMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var bmp:TBitmap;
begin
mouseIsDown:=false;
bmp:=TBitmap.Create;
try
bmp.Assign(imgMain.Picture.Bitmap);
picHistory.Push(bmp);
finally
bmp.Free;
end;
end;
And making undo and redo.
procedure TMainForm.btnUndoClick(Sender: TObject);
var redBmp:TBitmap;
begin
if(not picHistory.isEmpty) then begin //if we draw something before
//prepare to save what've done into history for redo
redBmp:=TBitmap.Create;
redBmp.Assign(picHistory.getLast);
//showing what were done with image before on screen
MainForm.imgMain.Canvas.Draw(0,0, picHistory.Pop);
//but in case we want to be able get back our last changes we save it into redo history
tempHistory.Push(redBmp);
redBmp.Free;
end;
end;
{...}
procedure TMainForm.btnRedoClick(Sender: TObject);
begin
//if there were something into history for redo then show int on canvas
if(not tempHistory.isEmpty) then
MainForm.imgMain.Canvas.Draw(0,0, tempHistory.Pop);
end;
But there are strang thing happens - what I push on Undo
nothing changes. And when I push on Redo
- it works like Undo
.
And by the way when I declare history for redo and undo with different lenght like that
procedure TMainForm.FormCreate(Sender: TObject);
begin
{...}
picHistory:=myHistory.Create(6); //FOR UNDO
tempHistory:=myHistory.Create(12); //FOR REDO
end;
And then whatch by steps what happens in picHistory
it seems like lenght of it's array not 6, it's 12! So I think those two objects use one same array! Why does it happens and how to make it right?