(************************************************************************
*    This File is Part of the AHM Triton Tools                          *
*    for Borland Delphi and C++ Builder.                                *
*************************************************************************
* Copyright © 1995-1999 Alexander H. Mehlhorn (AHM) and Tritontools.com *
* All Rights reserved.                                                  *
*                                                                       *
*    For more Information contact us at                                 *
*    E-Mail : support@tritontools.com                                   *
*    URL : http://www.tritontools.com                                   *
*                                                                       *
*    AHM is copyright by Alexander H. Mehlhorn                          *
*    All Parts of this Collection were developed,                       *
*    designed and implemented by AHM                                    *
*                                                                       *
*    Under no Circumstances may you distribute this file                *
*    in this or any other format. This File is supplied                 *
*    as Evaluation of Source only. No Delphi Components may be developed*
*    using extracts or subsets of this code without expressed permission*
*    by AHM.                                                            *
*                                                                       *
*************************************************************************)
unit AHMTHotSpotManager;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  AHMSystemClass,AHMSystemConst,AHMTHookClass;
type
  TAHMHotSpotManager = class;
  TAHMHotSpots = class;
  TAHMHotSpot = class;
  TAHMHotSpotMove = procedure(Sender : TObject;HotSpot : TAHMHotSpot) of Object;
  TAHMHotSpot = class(TCollectionItem)
  private
    FAHMHotSpotManager : TAHMHotSpotManager;
    FActive: Boolean;
    FSpotTop: LongInt;
    FSpotRight: LongInt;
    FSpotLeft: LongInt;
    FSpotBottom: LongInt;
    FOnExit: TAHMHotSpotMove;
    FOnEnter: TAHMHotSpotMove;
    FOnMouseOver: TAHMHotSpotMove;
    FControl: TGraphicControl;
    FOnClick: TAHMHotSpotMove;
    FOnRightClick: TAHMHotSpotMove;
    FHotSpotName: String;
    procedure SetActive(const Value: Boolean);
    procedure SetControl(const Value: TGraphicControl);
    procedure SetSpotBottom(const Value: LongInt);
    procedure SetSpotLeft(const Value: LongInt);
    procedure SetSpotRight(const Value: LongInt);
    procedure SetSpotTop(const Value: LongInt);
    function GetHotSpotRect: TRect;
    procedure SetHotSpotName(const Value: String);
  protected
    function GetDisplayName: string; override;
    procedure OnEnterProc;
    procedure OnClickProc;
    procedure OnRightClickProc;
    procedure OnQuitProc;
  public
    constructor Create(Collection: TCollection); override;
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
    Function GetHotSpotID : Integer;
    property Owner : TAHMHotSpotManager read FAHMHotSpotManager;
    property HotSpotRect : TRect read GetHotSpotRect;
  published
    property GraphicControl : TGraphicControl read FControl write SetControl;
    property Active : Boolean read FActive write SetActive default True;
    property SpotLeft : LongInt read FSpotLeft write SetSpotLeft default 0;
    property SpotRight : LongInt read FSpotRight write SetSpotRight default 100;
    property SpotTop: LongInt read FSpotTop write SetSpotTop default 0;
    property SpotBottom : LongInt read FSpotBottom write SetSpotBottom default 100;
    property HotSpotName : String read FHotSpotName write SetHotSpotName;
    property OnClick : TAHMHotSpotMove read FOnClick write FOnClick;
    property OnRightClick : TAHMHotSpotMove read FOnRightClick write FOnRightClick;
    property OnEnter : TAHMHotSpotMove read FOnEnter write FOnEnter;
    property OnExit : TAHMHotSpotMove read FOnExit write FOnExit;
    property OnMouseOver : TAHMHotSpotMove read FOnMouseOver write FOnMouseOver;
  end;
  TAHMHotSpots = class(TCollection)
  private
    FAHMHotSpotManager : TAHMHotSpotManager;
    function GetAHMHotSpot(Index: Integer): TAHMHotSpot;
    procedure SetAHMHotSpot(Index: Integer; Value: TAHMHotSpot);
  protected
    function GetOwner : TPersistent; override;
  public
    constructor Create(AOwner: TAHMHotSpotManager);
    function Add : TAHMHotSpot;
   Function Delete(Index :Integer) :Boolean;
    property Owner : TAHMHotSpotManager read FAHMHotSpotManager;
    property Items[Index: Integer]: TAHMHotSpot
             read GetAHMHotSpot write SetAHMHotSpot; default;
  end;
  TAHMHotSpotManager = class(TAHMSystemsComponent)
  private
    FApplicationHandle,FOwnerhandle : Hwnd;
    FOwnerForm : TWinControl;
    FHookClass : TAHMHookClass;
    FAHMHotSpots : TAHMHotSpots;
    FLastHotSpotID: Integer;
    FCurrentHotSpotID: Integer;
    FOnEnter: TAHMHotSpotMove;
    FOnMouseOver: TAHMHotSpotMove;
    FOnExit: TAHMHotSpotMove;
    FOverNoHotSpots: TNotifyEvent;
    FOnClick: TAHMHotSpotMove;
    FOnRightClick: TAHMHotSpotMove;
    FBeforeSpotCalc: TNotifyevent;
    FAfterSpotCalc: TNotifyevent;
    procedure SetAHMHotSpots(Value: TAHMHotSpots);
    procedure SetOwnerForm(const Value: TWinControl);
    procedure Hooked(var Msg: TMessage;var Handled : Boolean);
  protected
    NewWndProc,OldWndProc : Pointer;
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    property OwnerForm : TWinControl read FOwnerForm;
    property OwnerHandle : HWnd read FOwnerHandle;
    property ApplicationHandle : HWnd read FApplicationHandle;
  public
    constructor Create(AOwner: TComponent); override;
    procedure Loaded; override;
    destructor Destroy; override;
    property LastHotSpotID : Integer read FLastHotSpotID;
    property CurrentHotSpotID : Integer read FCurrentHotSpotID;
  published
    property HotSpots: TAHMHotSpots read FAHMHotSpots write SetAHMHotSpots;
    property OnClick : TAHMHotSpotMove read FOnClick write FOnClick;
    property OnRightClick : TAHMHotSpotMove read FOnRightClick write FOnRightClick;
    property OnEnter : TAHMHotSpotMove read FOnEnter write FOnEnter;
    property OnExit : TAHMHotSpotMove read FOnExit write FOnExit;
    property OnMouseOver : TAHMHotSpotMove read FOnMouseOver write FOnMouseOver;
    property OverNoHotSpots : TNotifyEvent read FOverNoHotSpots write FOverNoHotSpots;
    property AfterSpotCalc : TNotifyevent read FAfterSpotCalc write FAfterSpotCalc;
    property BeforeSpotCalc : TNotifyevent read FBeforeSpotCalc write FBeforeSpotCalc;
    property WinControl : TWinControl read FOwnerForm write SetOwnerForm;
  end;

implementation

constructor TAHMHotSpot.Create(Collection: TCollection);
begin
  inherited Create(Collection);
  FAHMHotSpotManager :=(((Collection as TAHMHotSpots).GetOwner) as TAHMHotSpotManager);
  FSpotLeft:=0; FSpotRight:=100;
  FSpotTop:=0; FSpotBottom:=100;
  FHotSpotName:='';
  FActive:=True;
end;

function TAHMHotSpot.GetDisplayName: string;
begin
  if FHotSpotName<>'' then Result:=FHotSpotName else
  Result := inherited GetDisplayName;
end;

function TAHMHotSpots.GetOwner: TPersistent;
begin
  Result := FAHMHotSpotManager;
end;

procedure TAHMHotSpot.Assign(Source: TPersistent);
begin
  if Source is TAHMHotSpot then
  begin
    if TAHMHotSpot(Source).GraphicControl<>nil then
      GraphicControl:=TAHMHotSpot(Source).GraphicControl
    else GraphicControl:=nil;
    Active :=TAHMHotSpot(Source).Active;
    SpotLeft :=TAHMHotSpot(Source).SpotLeft;
    SpotRight :=TAHMHotSpot(Source).SpotRight;
    SpotTop:=TAHMHotSpot(Source).SpotTop;
    SpotBottom :=TAHMHotSpot(Source).SpotBottom;
    HotSpotName :=TAHMHotSpot(Source).HotSpotName;
    OnClick :=TAHMHotSpot(Source).OnClick;
    OnRightClick :=TAHMHotSpot(Source).OnRightClick;
    OnEnter :=TAHMHotSpot(Source).OnEnter;
    OnExit :=TAHMHotSpot(Source).OnExit;
    OnMouseOver :=TAHMHotSpot(Source).OnMouseOver;
    Exit;
  end;
  inherited Assign(Source);
end;

constructor TAHMHotSpots.Create(AOwner: TAHMHotSpotManager);
begin
  inherited Create(TAHMHotSpot);
  FAHMHotSpotManager := AOwner;
end;

function TAHMHotSpots.Add: TAHMHotSpot;
begin
  Result := TAHMHotSpot(inherited Add);
end;

function TAHMHotSpots.GetAHMHotSpot(Index: Integer): TAHMHotSpot;
begin
  Result := TAHMHotSpot(inherited GetItem(Index));
end;

procedure TAHMHotSpots.SetAHMHotSpot(Index: Integer; Value: TAHMHotSpot);
begin
  inherited SetItem(Index, Value);
end;

constructor TAHMHotSpotManager.Create(AOwner: TComponent);
begin
  inherited create(AOwner);
  FAHMHotSpots:=TAHMHotSpots.Create(self);
  if (AOwner is TWinControl) then
    FOwnerForm := (AOwner) as TWinControl else FOwnerForm:=nil;
  FLastHotSpotID:=-1;
  FCurrentHotSpotID:=-1;
  if (AOwner is TWinControl) then
  begin
    FHookClass:=TAHMHookClass(HookList.add(self,AOwner,true));
    FOwnerHandle := FOwnerForm.Handle;
    FApplicationHandle := Application.Handle;
    FHookClass.BeforeMessage:=Hooked;
  end else raise EAHMOwnerFormNeeded.Create(self.ClassName+ahmscownerform);
end;

procedure TAHMHotSpotManager.Loaded;
begin
  inherited Loaded;
  FHookClass.UpdateHooks;
end;

destructor TAHMHotSpotManager.Destroy;
begin
  HookList.Remove(Self);
  FAHMHotSpots.Free;
  inherited Destroy;
end;

procedure TAHMHotSpotManager.SetAHMHotSpots(Value: TAHMHotSpots);
begin
  FAHMHotSpots.Assign(Value);
end;

procedure TAHMHotSpotManager.Notification(AComponent: TComponent;
  Operation: TOperation);
Var I : Integer;
begin
  if HotSpots<>nil then
  For I:=0 to Hotspots.Count-1 do
  if (AComponent=HotSpots[I].FControl) and (Operation=opRemove) then HotSpots[I].FControl:=nil;
  inherited Notification(AComponent, Operation);
  if (AComponent=FOwnerForm) and (Operation=opRemove) then SetOwnerForm(Owner as TWinControl);
end;


procedure TAHMHotSpotManager.Hooked(var Msg:TMessage;var Handled : Boolean);
Var I : Integer;
function InClient(Pos : TPoint;Client : TRect): Boolean;
begin
  if (Pos.X>Client.Left) and
     (Pos.X<Client.Right) and
     (Pos.Y>Client.Top) and
     (Pos.Y<Client.Bottom) then result:=True else result:=False;
end;
begin
  try
  if (csDestroying in Componentstate) then exit;
  if not(csDesigning in Componentstate) then
  case Msg.Msg of
    WM_MOUSEMOVE :
    begin
      if assigned(FBeforeSpotCalc) then FBeforeSpotCalc(Self);
      if HotSpots<>nil then
      for I:=0 to HotSpots.Count-1 do
      if Hotspots[I].Active then
      if InClient(Point(LoWord(Msg.LParam),SmallInt(HiWord(Msg.LParam))),HotSpots[I].HotSpotRect) then
      begin
        if (FCurrentHotSpotID>-1) and (I<>FCurrentHotSpotID) then HotSpots[FCurrentHotSpotID].OnQuitProc;
        FLastHotSpotID:=FCurrentHotSpotID;
        FCurrentHotSpotID:=HotSpots[I].GetHotSpotID;
        if (FCurrentHotSpotID<>FLastHotSpotID) and (FCurrentHotSpotID=HotSpots[I].GetHotSpotID) then HotSpots[I].OnEnterProc else
        if (FCurrentHotSpotID=HotSpots[I].GetHotSpotID) then
        begin
          if assigned(HotSpots[I].FOnMouseOver) then HotSpots[I].FOnMouseOver(Self,HotSpots[I]);
          if assigned(FOnMouseOver) then FOnMouseOver(Self,HotSpots[I]);
        end;
      end else
      if (FCurrentHotSpotID=HotSpots[I].GetHotSpotID) then
      begin
        FLastHotSpotID:=FCurrentHotSpotID;
        FCurrentHotSpotID:=-1;
        if (FCurrentHotSpotID<>FLastHotSpotID) and (FLastHotSpotID=HotSpots[I].GetHotSpotID) then HotSpots[I].OnQuitProc;
      end;
      if FCurrentHotSpotID=-1 then
      if assigned(FOverNoHotSpots) then FOverNoHotSpots(Self);
      if assigned(FAfterSpotCalc) then FAfterSpotCalc(Self);
    end;
    WM_LBUTTONDOWN,WM_LBUTTONDBLCLK :
    begin
        if HotSpots<>nil then
        for I:=0 to HotSpots.Count-1 do
        if Hotspots[I].Active then
        begin
          if (FCurrentHotSpotID=HotSpots[I].GetHotSpotID) then
          begin
            HotSpots[I].OnClickProc;
            Handled:=True;
          end;
        end;
    end;
    WM_RBUTTONDOWN,WM_RBUTTONDBLCLK :
    begin
        if HotSpots<>nil then
        for I:=0 to HotSpots.Count-1 do
        if Hotspots[I].Active then
        begin
          if (FCurrentHotSpotID=HotSpots[I].GetHotSpotID) then
          begin
            HotSpots[I].OnRightClickProc;
            Handled:=True;
          end;
        end;
    end;
  end;
  except
    Application.HandleException(Self);
  end;
end;


function TAHMHotSpots.Delete(Index: Integer): Boolean;
begin
  result := true;
  Try
    Items[Index].free;
  Except
    result := false;
    Application.handleexception(self);
  End;
end;

procedure TAHMHotSpot.SetActive(const Value: Boolean);
begin
  FActive := Value;
end;

procedure TAHMHotSpot.SetControl(const Value: TGraphicControl);
begin
  if Value=nil then FControl:=nil else
  if (csLoading in FAHMHotSpotManager.Componentstate) then FControl:=Value else
  if (Value<>FControl) then
  if (Value.Parent as TWinControl)=(FAHMHotSpotManager.FOwnerForm as TWinControl) then
  begin
    FControl := Value;
    if Value<>nil then
    begin
      Value.FreeNotification(FAHMHotSpotManager);
      if (csDesigning in FAHMHotSpotManager.Componentstate) then
      begin
        SetSpotTop(0);
        SetSpotBottom(FControl.Top+FControl.Height);
        SetSpotLeft(0);
        SetSpotRight(FControl.Left+FControl.Width);
      end;
    end;
  end;
end;

procedure TAHMHotSpot.SetSpotBottom(const Value: LongInt);
begin
  FSpotBottom := Value;
  if FControl<>nil then
  if FSpotBottom>FControl.Clientrect.Bottom then FSpotBottom:=(FControl.Clientrect.Bottom);
end;

procedure TAHMHotSpot.SetSpotLeft(const Value: LongInt);
begin
  FSpotLeft := Value;
end;

procedure TAHMHotSpot.SetSpotRight(const Value: LongInt);
begin
  FSpotRight := Value;
  if FControl<>nil then
  if FSpotRight>FControl.Clientrect.Right then FSpotRight:=(FControl.clientrect.right);
end;

procedure TAHMHotSpot.SetSpotTop(const Value: LongInt);
begin
  FSpotTop := Value;
end;

function TAHMHotSpot.GetHotSpotRect: TRect;
Var rright,rbottom : Integer;
begin
  if FControl=nil then
  begin
    result:=Rect(FSpotLeft,FSpotTop,FSpotRight,FSpotBottom);
  end else
  begin
    if FSpotRight>FControl.clientrect.Right then
      rright:=(FControl.clientrect.right) else rright:=FSpotRight;
    if FSpotBottom>FControl.clientrect.Bottom then
      rBottom:=(FControl.clientrect.Bottom) else rBottom:=FSpotBottom;
    result:=Rect(FControl.Left+FSpotLeft,FControl.Top+FSpotTop,FControl.Left+rright,FControl.Top+rBottom);
  end;
end;

procedure TAHMHotSpot.OnEnterProc;
begin
  if assigned(FOnEnter) then FOnEnter(Self,self);
  if assigned(FAHMHotSpotManager.FOnEnter) then FAHMHotSpotManager.FOnEnter(FAHMHotSpotManager,self);
//  DrawAnimatedRects(TCustomForm(FAHMHotSpotManager.OwnerForm).Canvas.Handle,IDANI_OPEN,Rect(0,0,0,0),HotSpotRect);
end;

procedure TAHMHotSpot.OnQuitProc;
begin
  if assigned(FOnExit) then FOnExit(Self,Self);
  if assigned(FAHMHotSpotManager.FOnExit) then FAHMHotSpotManager.FOnExit(FAHMHotSpotManager,Self);
end;

destructor TAHMHotSpot.Destroy;
begin
  FControl:=nil;
  inherited Destroy;
end;

procedure TAHMHotSpot.OnClickProc;
begin
  if assigned(FOnClick) then FOnClick(Self,Self);
  if assigned(FAHMHotSpotManager.FOnClick) then FAHMHotSpotManager.FOnClick(Self,Self);
end;

procedure TAHMHotSpot.OnRightClickProc;
begin
  if assigned(FOnClick) then FOnClick(Self,Self);
  if assigned(FAHMHotSpotManager.FOnClick) then FAHMHotSpotManager.FOnClick(Self,Self);
end;


procedure TAHMHotSpot.SetHotSpotName(const Value: String);
begin
  FHotSpotName := Value;
end;

function TAHMHotSpot.GetHotSpotID: Integer;
begin
  result:=Index;
end;

procedure TAHMHotSpotManager.SetOwnerForm(const Value: TWinControl);
Var I : Integer;
begin
  if not(csDestroying in Componentstate) then
  if Value<>FOwnerForm then
  begin
    FHookClass.UnHookWin;
    if Value<>nil then FOwnerForm := Value else FOwnerForm:=(Owner as TWinControl);
    if not(csLoading in Componentstate) then
    if HotSpots<>nil then
    For I:=0 to HotSpots.Count-1 do
    if (HotSpots[I].FControl<>nil) then
    if (HotSpots[I].FControl.Parent<>FOwnerForm) then HotSpots[I].FControl:=nil;
    FHookClass.HookWin;
  end;
end;

end.
