Pergunta

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?

Foi útil?

Solução

Your two instances of the myHistory class share the same global data. You must move your data declarations into the class to make them per-instance data instead of global.

Try this:

type
myHistory = class
  private
    historyQueueArray: array of TBitmap;  //Now they are class members instead of global
    historyIndex, hSize:Integer;
  public
    constructor Create(Size:Integer);
    procedure Push(Bmp:TBitmap);
    function Pop():TBitmap;
    procedure Clean();
    procedure Offset();
    function isEmpty():boolean;
    function isFull():boolean;
    function getLast():TBitmap;
  protected
  end;
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top