unit Tshapes; {

For best results, please download this file to your local machine and view it with an editor. This is Pascal, not HTML, and it may look wrong when displayed with a web browser.

 }

{ Author: Ann Lynnworth, 21 January 96.
  This was written to demonstrate some of the ideas of TComponentExtensions,
  a component originally published as part of TPack, by Michael Ax.

  To quickly get the idea: add this unit to your Delphi library, go to the
  Samples palette and then place a TFramedRedCircle on a form...

  Please see _Delphi In-Depth_, edited by Cary Jensen, for a complete
  description of this code.

  For updates & other news, please visit our web site at
  http://www.href.com/.

  This unit is distributed free of charge.  Please retain the credits.
}

interface

uses
  SysUtils
  {$IFDEF WIN32}
  , Windows
  {$ELSE}
  , WinTypes
  {$ENDIF}
  , Messages, Classes, Graphics, Controls, ExtCtrls
  , Xtension {from TPack; needed for TComponentExtensions}
  ;

const
  perfectFrameWidth=3;  {used by TFrame}

{We're only customizing TFrame so that we can make it 'clear' and have
 a perfect width... }
type
  TFrame = class(TShape)
  public
    { Public declarations }
    Constructor Create(aOwner:TComponent); Override;
  end;

{ TFramedRedCircle is the "real example." }
type
  TFramedRedCircle = class(TShape)
    {Add a variable, cx, to "graft on" the functionality.  See
     xtension.pas for source to TComponentExtensions. }
    cx: TComponentExtensions;
  private
    { Private declarations }
    fFrame:TFrame;
  public
    { Public declarations }
    Constructor Create(aOwner:TComponent); Override;
    Destructor  Destroy; Override;
    procedure   Notification(AComponent: TComponent; Operation: TOperation); Override;
    procedure   Loaded; Override;
    procedure   Paint; Override;
  published
    { Published declarations }
    { here is the pointer to the other-object that we'll create
      automatically. }
    property Frame:TFrame read fFrame write fFrame;
  end;

procedure Register;

{------------------------------------------------------------------------------}

implementation

Constructor TFrame.Create(aOwner:TComponent);
begin
  inherited Create(aOwner);
  Brush.Style:=bsClear; {it wouldn't be a frame if you couldn't see through}
  Pen.Width:=perfectFrameWidth; {anything > 1 is noticable}
end;

{------------------------------------------------------------------------------}

Constructor TFramedRedCircle.Create(aOwner:TComponent);
begin
  inherited Create(aOwner);

  cx:= TComponentExtensions.Create(Self);

  {We want to do some fancy tricks if and only if we are in the
   designer.}

  {Here's some code that does NOT work because Delphi hasn't set this yet:
   if csDesigning in ComponentState }

  {Here's an alternative that DOES work.  The tForm Name property is
   only set while you're in the designer to aviod duplicate names with
   multiple instances of the form. This lets us 'sneak in' some
   design-time only code into a component's constructor *before*
   Delphi gets around to setting the 'ComponentState' property.}
  if aOwner.Name<>'' then begin {aOwner is the Form}
    Shape:=stCircle;
    Brush.Color:=clRed;
    cx.SetIfFoundExactly(fFrame,TFrame);
    cx.MakeIfNil(fFrame,TFrame); {create TFrame if none found on form}

    { It is NOT POSSIBLE to set the location of fFrame here because
      in this moment, our circle is sitting at 0,0 and has not yet
      been moved to its real location, as determined by your click.
      Paint is used to accomplish this instead.}

    end;
end;

procedure TFramedRedCircle.Paint;
var
  goHereLeft, goHereTop:integer;
  thisDiameter:integer;
  offset:integer;
begin
  inherited Paint;
  if fFrame<>nil then begin
    {offset=how much displacement between frame and circle}
    offset:=FFrame.pen.width+1;
    goHereLeft:=Left-offset;
    goHereTop :=Top-offset;
    {base the frame size on circle width}
    thisDiameter:=width+(offset*2);
    with fFrame do begin
      left:=goHereLeft;
      top :=goHereTop;
      height:=thisDiameter;
      width :=thisDiameter;
      end;
    end;
end;

Destructor TFramedRedCircle.Destroy;
begin
  cx.free;  {what we create, we must destroy}
  inherited Destroy;
end;

procedure TFramedRedCircle.Notification(AComponent: TComponent; Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if Operation = opRemove then begin
    cx.NilIfSet(fFrame,AComponent);  {disconnect if fFrame is being removed}
    end;
end;

procedure TFramedRedCircle.Loaded;
begin
  inherited Loaded;
  {this is sort of cool; if you run the form, the frame moves into place.}
  {i.e. this works even if you get rid of the Paint method.  with paint
   overridden, there is no need for this piece.}
  {if fFrame<>nil then
    with fFrame do begin
      left:=self.Left-(pen.width+1);
      top :=self.Top -(pen.width+1);
      end;}
end;

{------------------------------------------------------------------------------}

procedure Register;
begin
  RegisterComponents('Samples', [TFramedRedCircle, TFrame]);
end;

end.

{ 
}