unit VAShared.FreeNotifier;
///////////////////////////////////////////////////////////////////////////////
/// To use:
///   FFreeNotifier := TFreeNotifier.Create(nil);
///   FFreeNotifier.OnFreeNotification := MySpecialCode;
///   FFreeNotofier.RegisterComponent(ComponentToWatch);
///
/// When ComponentToWatch is freed, MySpecialCode will be executed.
///
/// Only one FreeNotifier is needed to watch any number of components.
/// There's no need to unregister components when you Free the FreeNotifier.
///
/// This is just a shell around the FreeNotification functionality in Delphi.
///////////////////////////////////////////////////////////////////////////////

interface

uses
  System.Classes,
  System.Generics.Collections,
  System.Contnrs;

type
  TFreeNotifier = class(TComponent)
  private
    FRegisteredComponents: TObjectList<TComponent>;
    FOnFreeNotification: TNotifyEvent;
  protected
    procedure Notification(AComponent: TComponent;
      Operation: TOperation); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    property OnFreeNotification: TNotifyEvent read FOnFreeNotification
      write FOnFreeNotification;
    procedure RegisterComponent(AComponent: TComponent);
    procedure RegisterComponents(AList: TObjectList); overload;
    procedure RegisterComponents<T: class>(AList: TList<T>); overload;
    procedure UnregisterComponent(AComponent: TComponent);
    procedure UnregisterComponents(AList: TObjectList); overload;
    procedure UnregisterComponents<T: class>(AList: TList<T>); overload;
  end;

implementation

uses
  System.SysUtils;

constructor TFreeNotifier.Create(AOwner: TComponent);
begin
  inherited;
  FRegisteredComponents := TObjectList<TComponent>.Create(False);
end;

destructor TFreeNotifier.Destroy;
var
  AComponent: TComponent;
begin
  for AComponent in FRegisteredComponents do
    AComponent.RemoveFreeNotification(Self);
  FreeAndNil(FRegisteredComponents);
  inherited;
end;

procedure TFreeNotifier.RegisterComponent(AComponent: TComponent);
var
  I: Integer;
begin
  if Assigned(AComponent) and
    (not FRegisteredComponents.BinarySearch(AComponent, I)) then
  begin
    AComponent.FreeNotification(Self);
    FRegisteredComponents.Add(AComponent);
    FRegisteredComponents.Sort; // keep list sorted for binary searches to work
  end;
end;

procedure TFreeNotifier.RegisterComponents(AList: TObjectList);
// Register all TComponent descendents in the list
var
  AObject: TObject;
begin
  for AObject in AList do
    if AObject is TComponent then RegisterComponent(TComponent(AObject));
end;

procedure TFreeNotifier.RegisterComponents<T>(AList: TList<T>);
// Register all TComponent descendents in the list
var
  AObject: TObject;
begin
  for AObject in AList do
    if AObject is TComponent then RegisterComponent(TComponent(AObject));
end;

procedure TFreeNotifier.UnregisterComponent(AComponent: TComponent);
var
  I: Integer;
begin
  if Assigned(AComponent) and
    FRegisteredComponents.BinarySearch(AComponent, I) then
  begin
    AComponent.RemoveFreeNotification(Self);
    FRegisteredComponents.Delete(I);
  end;
end;

procedure TFreeNotifier.UnregisterComponents(AList: TObjectList);
var
  AObject: TObject;
begin
  for AObject in AList do
    if AObject is TComponent then UnregisterComponent(TComponent(AObject));
end;

procedure TFreeNotifier.UnregisterComponents<T>(AList: TList<T>);
var
  AObject: TObject;
begin
  for AObject in AList do
    if AObject is TComponent then UnregisterComponent(TComponent(AObject));
end;

procedure TFreeNotifier.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited;
  UnregisterComponent(AComponent);
  if Assigned(FOnFreeNotification) and (Operation = opRemove) and
    Assigned(AComponent) then
    FOnFreeNotification(AComponent);
end;

end.
