unit MainForm;
{*******************************************************************************
  ShapesDemo
  Written by David Clegg, davidclegg@optusnet.com.au.

  This unit contains the base Shape class and its descendants, which are used
  to render onto a GDI+ drawing surface.
*******************************************************************************}

interface

uses
  System.Drawing, System.Collections, System.ComponentModel,
  System.Windows.Forms, System.Data, Shape, System.Drawing.Drawing2D;

type
  TMainForm = class(System.Windows.Forms.Form)
  {$REGION 'Designer Managed Code'}
  strict private
    /// <summary>
    /// Required designer variable.
    /// </summary>
    Components: System.ComponentModel.Container;
    pnMain: System.Windows.Forms.Panel;
    gbShape: System.Windows.Forms.GroupBox;
    rbEllipse: System.Windows.Forms.RadioButton;
    rbRectangle: System.Windows.Forms.RadioButton;
    rbTriangle: System.Windows.Forms.RadioButton;
    rbLine: System.Windows.Forms.RadioButton;
    gbBrush: System.Windows.Forms.GroupBox;
    pnBrush: System.Windows.Forms.Panel;
    btnChoose: System.Windows.Forms.Button;
    btnClose: System.Windows.Forms.Button;
    btnClear: System.Windows.Forms.Button;
    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    procedure InitializeComponent;
    procedure btnClose_Click(sender: System.Object; e: System.EventArgs);
    procedure TMainForm_MouseDown(sender: System.Object; e: System.Windows.Forms.MouseEventArgs);
    procedure TMainForm_MouseUp(sender: System.Object; e: System.Windows.Forms.MouseEventArgs);
    procedure TMainForm_MouseMove(sender: System.Object; e: System.Windows.Forms.MouseEventArgs);
    procedure btnClear_Click(sender: System.Object; e: System.EventArgs);
    procedure btnChoose_Click(sender: System.Object; e: System.EventArgs);
    procedure pnBrush_Paint(sender: System.Object; e: System.Windows.Forms.PaintEventArgs);
  {$ENDREGION}
  strict protected
    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    procedure Dispose(Disposing: Boolean); override;
  private
    FShape: TShape;
    FMouseDown: boolean;
    FCurrentBrush: Brush;
    function ResizeLinearGradientBrush(lRect: Rectangle; lBrush: LinearGradientBrush): LinearGradientBrush;
    function GetShape: TShape;
    function GetBrush: Brush;
    procedure ClearCanvas;
  public
    constructor Create;
  end;

implementation

uses
  System.Globalization, Triangle, Ellipse, Rectangle, Line, BrushForm;

const
  DEFAULT_SIZE = 50;

{$REGION 'Windows Form Designer generated code'}
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
procedure TMainForm.InitializeComponent;
begin
  Self.pnMain := System.Windows.Forms.Panel.Create;
  Self.btnClear := System.Windows.Forms.Button.Create;
  Self.btnClose := System.Windows.Forms.Button.Create;
  Self.gbBrush := System.Windows.Forms.GroupBox.Create;
  Self.btnChoose := System.Windows.Forms.Button.Create;
  Self.pnBrush := System.Windows.Forms.Panel.Create;
  Self.gbShape := System.Windows.Forms.GroupBox.Create;
  Self.rbLine := System.Windows.Forms.RadioButton.Create;
  Self.rbTriangle := System.Windows.Forms.RadioButton.Create;
  Self.rbRectangle := System.Windows.Forms.RadioButton.Create;
  Self.rbEllipse := System.Windows.Forms.RadioButton.Create;
  Self.pnMain.SuspendLayout;
  Self.gbBrush.SuspendLayout;
  Self.gbShape.SuspendLayout;
  Self.SuspendLayout;
  // 
  // pnMain
  // 
  Self.pnMain.BackColor := System.Drawing.SystemColors.Control;
  Self.pnMain.Controls.Add(Self.btnClear);
  Self.pnMain.Controls.Add(Self.btnClose);
  Self.pnMain.Controls.Add(Self.gbBrush);
  Self.pnMain.Controls.Add(Self.gbShape);
  Self.pnMain.Dock := System.Windows.Forms.DockStyle.Left;
  Self.pnMain.Location := System.Drawing.Point.Create(0, 0);
  Self.pnMain.Name := 'pnMain';
  Self.pnMain.Size := System.Drawing.Size.Create(128, 554);
  Self.pnMain.TabIndex := 0;
  // 
  // btnClear
  // 
  Self.btnClear.Anchor := (System.Windows.Forms.AnchorStyles((System.Windows.Forms.AnchorStyles.Bottom 
      or System.Windows.Forms.AnchorStyles.Left)));
  Self.btnClear.Location := System.Drawing.Point.Create(27, 480);
  Self.btnClear.Name := 'btnClear';
  Self.btnClear.TabIndex := 3;
  Self.btnClear.Text := 'Clear';
  Include(Self.btnClear.Click, Self.btnClear_Click);
  // 
  // btnClose
  // 
  Self.btnClose.Anchor := (System.Windows.Forms.AnchorStyles((System.Windows.Forms.AnchorStyles.Bottom 
      or System.Windows.Forms.AnchorStyles.Left)));
  Self.btnClose.Location := System.Drawing.Point.Create(27, 512);
  Self.btnClose.Name := 'btnClose';
  Self.btnClose.TabIndex := 2;
  Self.btnClose.Text := 'Close';
  Include(Self.btnClose.Click, Self.btnClose_Click);
  // 
  // gbBrush
  // 
  Self.gbBrush.Controls.Add(Self.btnChoose);
  Self.gbBrush.Controls.Add(Self.pnBrush);
  Self.gbBrush.Location := System.Drawing.Point.Create(8, 144);
  Self.gbBrush.Name := 'gbBrush';
  Self.gbBrush.Size := System.Drawing.Size.Create(112, 48);
  Self.gbBrush.TabIndex := 1;
  Self.gbBrush.TabStop := False;
  // 
  // btnChoose
  // 
  Self.btnChoose.Location := System.Drawing.Point.Create(40, 16);
  Self.btnChoose.Name := 'btnChoose';
  Self.btnChoose.Size := System.Drawing.Size.Create(64, 23);
  Self.btnChoose.TabIndex := 2;
  Self.btnChoose.Text := 'Choose...';
  Include(Self.btnChoose.Click, Self.btnChoose_Click);
  // 
  // pnBrush
  // 
  Self.pnBrush.BackColor := System.Drawing.Color.Red;
  Self.pnBrush.BorderStyle := System.Windows.Forms.BorderStyle.Fixed3D;
  Self.pnBrush.Location := System.Drawing.Point.Create(8, 16);
  Self.pnBrush.Name := 'pnBrush';
  Self.pnBrush.Size := System.Drawing.Size.Create(24, 24);
  Self.pnBrush.TabIndex := 0;
  Include(Self.pnBrush.Paint, Self.pnBrush_Paint);
  // 
  // gbShape
  // 
  Self.gbShape.Controls.Add(Self.rbLine);
  Self.gbShape.Controls.Add(Self.rbTriangle);
  Self.gbShape.Controls.Add(Self.rbRectangle);
  Self.gbShape.Controls.Add(Self.rbEllipse);
  Self.gbShape.Location := System.Drawing.Point.Create(8, 8);
  Self.gbShape.Name := 'gbShape';
  Self.gbShape.Size := System.Drawing.Size.Create(112, 120);
  Self.gbShape.TabIndex := 0;
  Self.gbShape.TabStop := False;
  Self.gbShape.Text := 'Shape';
  // 
  // rbLine
  // 
  Self.rbLine.Location := System.Drawing.Point.Create(8, 88);
  Self.rbLine.Name := 'rbLine';
  Self.rbLine.Size := System.Drawing.Size.Create(80, 24);
  Self.rbLine.TabIndex := 3;
  Self.rbLine.Text := 'Line';
  // 
  // rbTriangle
  // 
  Self.rbTriangle.Location := System.Drawing.Point.Create(8, 64);
  Self.rbTriangle.Name := 'rbTriangle';
  Self.rbTriangle.Size := System.Drawing.Size.Create(80, 24);
  Self.rbTriangle.TabIndex := 2;
  Self.rbTriangle.Text := 'Triangle';
  // 
  // rbRectangle
  // 
  Self.rbRectangle.Checked := True;
  Self.rbRectangle.Location := System.Drawing.Point.Create(8, 16);
  Self.rbRectangle.Name := 'rbRectangle';
  Self.rbRectangle.Size := System.Drawing.Size.Create(80, 24);
  Self.rbRectangle.TabIndex := 1;
  Self.rbRectangle.TabStop := True;
  Self.rbRectangle.Text := 'Rectangle';
  // 
  // rbEllipse
  // 
  Self.rbEllipse.Location := System.Drawing.Point.Create(8, 40);
  Self.rbEllipse.Name := 'rbEllipse';
  Self.rbEllipse.Size := System.Drawing.Size.Create(80, 24);
  Self.rbEllipse.TabIndex := 0;
  Self.rbEllipse.Text := 'Ellipse';
  // 
  // TMainForm
  // 
  Self.AutoScaleBaseSize := System.Drawing.Size.Create(5, 13);
  Self.BackColor := System.Drawing.Color.White;
  Self.ClientSize := System.Drawing.Size.Create(696, 554);
  Self.Controls.Add(Self.pnMain);
  Self.Name := 'TMainForm';
  Self.StartPosition := System.Windows.Forms.FormStartPosition.CenterScreen;
  Self.Text := 'Shapes Demo';
  Include(Self.MouseDown, Self.TMainForm_MouseDown);
  Include(Self.MouseUp, Self.TMainForm_MouseUp);
  Include(Self.MouseMove, Self.TMainForm_MouseMove);
  Self.pnMain.ResumeLayout(False);
  Self.gbBrush.ResumeLayout(False);
  Self.gbShape.ResumeLayout(False);
  Self.ResumeLayout(False);
end;
{$ENDREGION}

procedure TMainForm.Dispose(Disposing: Boolean);
begin
  if Disposing then
  begin
    if Components <> nil then
      Components.Dispose();
  end;
  inherited Dispose(Disposing);
end;

constructor TMainForm.Create;
begin
  inherited Create;
  //
  // Required for Windows Form Designer support
  //
  InitializeComponent;

  FMouseDown := False;
end;

procedure TMainForm.pnBrush_Paint(sender: System.Object; e: System.Windows.Forms.PaintEventArgs);
var
  lGraphics: Graphics;
begin
  if Assigned(FCurrentBrush) then
  begin
    //Ensure that the gradient is properly resized if the brush is a
    //LinearGradientBrush
    if (FCurrentBrush is LinearGradientBrush) then
      FCurrentBrush := ResizeLinearGradientBrush(pnBrush.ClientRectangle,
                         LinearGradientBrush(FCurrentBrush));
    lGraphics := pnBrush.CreateGraphics();
    try
      lGraphics.FillRectangle(FCurrentBrush, pnBrush.ClientRectangle);
    finally
      //The Graphics reference must be disposed once we are
      //finished with it
      lGraphics.Dispose();
    end;
  end;
end;

procedure TMainForm.btnChoose_Click(sender: System.Object; e: System.EventArgs);
begin
  if TBrushForm.ShowBrushForm(FCurrentBrush) = System.Windows.Forms.DialogResult.OK then
    pnBrush.Invalidate;
end;

procedure TMainForm.btnClear_Click(sender: System.Object; e: System.EventArgs);
begin
  ClearCanvas;
end;

procedure TMainForm.TMainForm_MouseMove(sender: System.Object; e: System.Windows.Forms.MouseEventArgs);
var
  lEndPoint: Point;
begin
	if Assigned(FShape) and FMouseDown then
  begin
    lEndPoint := Point.Create(e.X, e.Y);
    FShape.DrawOutline(FShape.StartPoint, lEndPoint);
  end;
end;

procedure TMainForm.TMainForm_MouseUp(sender: System.Object; e: System.Windows.Forms.MouseEventArgs);
begin
  if Assigned(FShape) then
  begin
    FShape.EndPoint.X := e.X;
    FShape.EndPoint.Y := e.Y;
    if FShape.EndPoint.Equals(FShape.StartPoint) then
    begin
      FShape.EndPoint.X := FShape.EndPoint.X + DEFAULT_SIZE;
      FShape.EndPoint.Y := FShape.EndPoint.Y + DEFAULT_SIZE;
    end;
    FShape.DrawBrush := GetBrush;
    FShape.Draw;
  end;
  FMouseDown := False;
end;

procedure TMainForm.TMainForm_MouseDown(sender: System.Object; e: System.Windows.Forms.MouseEventArgs);
begin
  FShape := GetShape;
  FShape.StartPoint := Point.Create(e.X, e.Y);
  FMouseDown := True;
end;

procedure TMainForm.btnClose_Click(sender: System.Object; e: System.EventArgs);
begin
  Close;
end;

/// <summary>
/// Determine which shape we should be drawing.
/// </summary>
function TMainForm.GetShape: TShape;
begin
  if (rbEllipse.Checked) then
    Result := TEllipse.Create(Self.CreateGraphics)
  else if (rbTriangle.Checked) then
    Result := TTriangle.Create(Self.CreateGraphics)
  else if (rbRectangle.Checked) then
    Result := TRectangle.Create(Self.CreateGraphics)
  else if (rbLine.Checked) then
    Result := TLine.Create(Self.CreateGraphics)
  else
    Result := nil;
end;

function TMainForm.GetBrush: Brush;
begin
  if Assigned(FCurrentBrush) then
  begin
    if FCurrentBrush is LinearGradientBrush then
    begin
      //ReAssign the LinearGradientBrush to ensure that then gradient takes up
      //shapes entire area
      FCurrentBrush := ResizeLinearGradientBrush(
        System.Drawing.Rectangle.FromLTRB(FShape.StartPoint.X, FShape.StartPoint.Y, FShape.EndPoint.X, FShape.EndPoint.Y),
        LinearGradientBrush(FCurrentBrush));
    end;

    Result := FCurrentBrush;
  end
  else
    Result := SolidBrush.Create(pnBrush.BackColor);
end;

/// <summary>
/// Resizes the specified LinearGradientBrush to ensure the gradient
/// fits in the specified rectangle.
/// </summary>
function TMainForm.ResizeLinearGradientBrush(lRect: System.Drawing.Rectangle; lBrush: LinearGradientBrush): LinearGradientBrush;
begin
  Result := LinearGradientBrush.Create(lRect, lBrush.LinearColors[0], lBrush.LinearColors[1],
										   LinearGradientMode.Vertical);
end;


procedure TMainForm.ClearCanvas;
var
  lCanvas: Graphics;
begin
  lCanvas := Self.CreateGraphics;
  try
    lCanvas.Clear(Self.BackColor);
  finally
    lCanvas.Dispose;
  end;
end;

end.
