unit Cmd2F;
//*******************************************************
// Example program demonstrating undo and redo using
// command objects.
//*******************************************************
// Copyright (C) 1998 John Wiley & Sons, Inc.
// All rights reserved. See additional copyright
// information in Readme.txt.
//*******************************************************

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Menus, ExtCtrls, StdCtrls,
  Cmd2Draw, Cmd2Rect, Cmd2Ell, Cmd2Line;

type
  TCmd2Form = class(TForm)
    MainMenu1: TMainMenu;
    File1: TMenuItem;
    mnuExit: TMenuItem;
    Help1: TMenuItem;
    mnuAbout: TMenuItem;
    Edit1: TMenuItem;
    mnuUndo: TMenuItem;
    mnuRedo: TMenuItem;
    mnuClear: TMenuItem;
    ShapeGroup: TRadioGroup;
    ColorGroup: TRadioGroup;
    FillStyleGroup: TRadioGroup;
    DrawingArea: TImage;
    procedure mnuExitClick(Sender: TObject);
    procedure mnuAboutClick(Sender: TObject);
    procedure DrawingAreaMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure DrawingAreaMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure DrawingAreaMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure CreateCommandObject;
    procedure DrawCommands;
    procedure FormCreate(Sender: TObject);
    procedure DrawShape(x1, y1, x2, y2 : Integer);
    procedure mnuClearClick(Sender: TObject);
    procedure mnuUndoClick(Sender: TObject);
    procedure mnuRedoClick(Sender: TObject);

  private
    { Private declarations }
    StartX, StartY : Integer;
    CurX, CurY     : Integer;
    NewCmd         : TDrawingCommand;
    Commands       : array [1..1000] of TDrawingCommand;
    NumCommands    : Integer; // The number allocated.
    LastCommand    : Integer; // Index of last command used.

  public
    { Public declarations }
  end;

var
  Cmd2Form: TCmd2Form;

implementation

{$R *.DFM}

procedure TCmd2Form.mnuExitClick(Sender: TObject);
begin
    Close;
end;

procedure TCmd2Form.mnuAboutClick(Sender: TObject);
const
    CRCR = #13#10#13#10;
    CR = #13#10;
begin

    MessageDlg(
        'Niniejszy program demonstruje funkcjonowanie obiektów poleceń typu "undo/redo".' + CRCR +
        'Po narysowaniu kilku figur (za pomoca przeciągania kursora myszki przy naciśniętym lewym przycisku) klikanie w opcję "Cofnij" spowoduje usuwanie figur ostatnio narysowanych.' + CR +
        'Klikanie w opcję "Powtórz" spowoduje ich sukcesywne przywracanie.'
        , mtInformation, [mbOK], 0);


end;

// Start a rubber band operation to draw something.
procedure TCmd2Form.DrawingAreaMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
    StartX := X;
    StartY := Y;
    CurX := X;
    CurY := Y;
    CreateCommandObject;
    DrawingArea.Canvas.Brush.Style := bsClear;
    DrawingArea.Canvas.Pen.Mode := pmNot;
end;

// Continue a rubber band operation.
procedure TCmd2Form.DrawingAreaMouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: Integer);
begin
    if (NewCmd = nil) then exit;

    DrawShape(StartX, StartY, CurX, CurY);
    CurX := X;
    CurY := Y;
    DrawShape(StartX, StartY, CurX, CurY);
end;

// Finsh a rubber band operation.
procedure TCmd2Form.DrawingAreaMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
    if (NewCmd = nil) then exit;

    DrawShape(StartX, StartY, CurX, CurY);
    DrawingArea.Canvas.Pen.Mode := pmCopy;

    CurX := X;
    CurY := Y;

    // If (StartX, StartY) = (CurX, CurY), the command
    // occupies no area.
    if ((StartX = CurX) and (StartY = CurY)) then
    begin
        // Destroy the new command object.
        NumCommands := NumCommands - 1;
        LastCommand := NumCommands;
        mnuUndo.Enabled := (NumCommands > 0);
        mnuRedo.Enabled := False;
        NewCmd.Free;
    end else begin
        // Save the command object's position.
        NewCmd.SetPosition(StartX, StartY, CurX, CurY);

        // Draw the new command.
        NewCmd.Draw(DrawingArea.Canvas, True);
    end;
    NewCmd := nil;
end;

// Create an appropriate command object.
procedure TCmd2Form.CreateCommandObject;
var
    color : TColor;
    style : TBrushStyle;
begin
    case (ShapeGroup.ItemIndex) of
        0: NewCmd := TRectangleCmd.Create;
        1: NewCmd := TEllipseCmd.Create;
        2: NewCmd := TLineCmd.Create;
    end;

    case (ColorGroup.ItemIndex) of
        0: color := clRed;
        1: color := clBlack;
        2: color := clGreen;
        3: color := clBlue;
    else
           color := clBlack;
    end;

    case (FillStyleGroup.ItemIndex) of
        0: style := bsSolid;
        1: style := bsClear;
        2: style := bsCross;
        3: style := bsDiagCross;
    else
           style := bsClear;
    end;

    // Save the command color and style.
    NewCmd.SetColorAndStyle(color, style);

    // If we are in the command history,
    // remove any future commands.
    while (NumCommands > LastCommand) do
    begin
        Commands[NumCommands].Free;
        NumCommands := NumCommands - 1;
    end;

    // Add the command to the list of commands.
    NumCommands := NumCommands + 1;
    Commands[NumCommands] := NewCmd;
    LastCommand := NumCommands;
    mnuUndo.Enabled := True;
    mnuRedo.Enabled := False;
end;

// Execute all of the drawing commands.
procedure TCmd2Form.DrawCommands;
var
    i    : Integer;
    rect : TRect;
begin
    // Erase the canvas.
    rect.Left := 0;
    rect.Top := 0;
    rect.Right := DrawingArea.Width;
    rect.Bottom := DrawingArea.Height;
    DrawingArea.Canvas.Brush.Style := bsSolid;
    DrawingArea.Canvas.Brush.Color := clSilver;
    DrawingArea.Canvas.FillRect(rect);

    // Draw the commands.
    for i := 1 to LastCommand do
        Commands[i].Draw(DrawingArea.Canvas, True);
end;

procedure TCmd2Form.FormCreate(Sender: TObject);
begin
    NumCommands := 0;
    LastCommand := 0;
    NewCmd := nil;
    DrawCommands;
end;

// Draw the correct shape inside the bounding box.
procedure TCmd2Form.DrawShape(x1, y1, x2, y2 : Integer);
begin
    NewCmd.SetPosition(x1, y1, x2, y2);
    NewCmd.Draw(DrawingArea.Canvas, False);
end;

// Clear everything.
procedure TCmd2Form.mnuClearClick(Sender: TObject);
begin
    // Remove all commands.
    while (NumCommands > 0) do
    begin
        Commands[NumCommands].Free;
        NumCommands := NumCommands - 1;
    end;
    LastCommand := 0;

    mnuUndo.Enabled := False;
    mnuRedo.Enabled := False;

    DrawCommands;
end;

// Undo the last command.
procedure TCmd2Form.mnuUndoClick(Sender: TObject);
begin
    LastCommand := LastCommand - 1;
    if (LastCommand <= 0) then mnuUndo.Enabled := False;
    mnuRedo.Enabled := True;
    DrawCommands;
end;

// Redo the last command.
procedure TCmd2Form.mnuRedoClick(Sender: TObject);
begin
    LastCommand := LastCommand + 1;
    if (LastCommand >= NumCommands) then
        mnuRedo.Enabled := False;
    mnuUndo.Enabled := True;
    DrawCommands;
end;

end.
