{ The contents of this file are subject to the Mozilla Public License  }
{ Version 1.1 (the "License"); you may not use this file except in     }
{ compliance with the License. You may obtain a copy of the License at }
{ http://www.mozilla.org/MPL/                                          }
{                                                                      }
{ Software distributed under the License is distributed on an "AS IS"  }
{ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See  }
{ the License for the specific language governing rights and           }
{ limitations under the License.                                       }
{                                                                      }
{ The Original Code is AxMouseMonitor.pas.                             }
{                                                                      }
{ The Initial Developer of the Original Code is Ashley Godfrey, all    }
{ Portions created by these individuals are Copyright (C) of Ashley    }
{ Godfrey.                                                             }
{                                                                      }
{**********************************************************************}
{                                                                      }
{ This unit contains code to enable active mouse monitoring for non-   }
{ windowed VCL controls from within an ActiveX control.                }
{                                                                      }
{ Unit owner: Ashley Godfrey.                                          }
{ Last modified: April 6, 2004.                                        }
{ Updates available from http://www.evocorp.com                        }
{                                                                      }
{**********************************************************************}

unit AxMouseMonitor;

interface
uses
  SysUtils, Windows, Classes, Controls;

type
  TMouseMonitorThread = class;

  // The TMouseMonitorThreadComponent class is a simple component
  // specifically designed to monitor controls being destroyed,
  // enabling us to determine whether the current "focused" control
  // is still available. 
  TMouseMonitorThreadComponent = class(TComponent)
  private
    FMessagesThread: TMouseMonitorThread;
  protected
    procedure Notification(AComponent: TComponent;
      Operation: TOperation); override;
  public
    property MessagesThread: TMouseMonitorThread
      read FMessagesThread write FMessagesThread;
  end;

  // TMouseMonitorThread is the class you need to initialize from
  // within your main ActiveX control.
  TMouseMonitorThread = class(TThread)
  private
    FMouseControl: TControl;
    FNotificationComponent: TMouseMonitorThreadComponent;

    procedure DoComponentNotification(
      AComponent: TComponent; Operation: TOperation);
    procedure DoMouseIdle;
  protected
    procedure Execute; override;
  public
    constructor Create;
    destructor Destroy; override;
  end;


implementation

{ TMouseMonitorThreadComponent }

procedure TMouseMonitorThreadComponent.Notification(
  AComponent: TComponent; Operation: TOperation);
begin
  inherited;
  if Assigned(FMessagesThread) then
    FMessagesThread.DoComponentNotification(AComponent, Operation);
end;

{ TMouseMonitorThread }

constructor TMouseMonitorThread.Create;
begin
  inherited Create(False);
  FMouseControl := nil;
  FreeOnTerminate := False;

  // Component destroy notifier
  FNotificationComponent := TMouseMonitorThreadComponent.Create(nil);
  FNotificationComponent.MessagesThread := Self;
end;

destructor TMouseMonitorThread.Destroy;
begin
  if Assigned(FNotificationComponent) then
    FreeAndNil(FNotificationComponent);
  inherited;
end;

procedure TMouseMonitorThread.DoComponentNotification(
  AComponent: TComponent; Operation: TOperation);
begin
  if (AComponent = FMouseControl) and (Operation = opRemove) then
    FMouseControl := nil;
end;

procedure TMouseMonitorThread.DoMouseIdle;
var CaptureControl: TControl;
    InterimControl: TControl;
    P: TPoint;
begin
  GetCursorPos(P);
  InterimControl := FindDragTarget(P, True);
  CaptureControl := GetCaptureControl;
  if FMouseControl <> InterimControl then
  begin
    if ((FMouseControl <> nil) and (CaptureControl = nil)) or
       ((CaptureControl <> nil) and (FMouseControl = CaptureControl)) then
      FMouseControl.Perform(CM_MOUSELEAVE, 0, 0);
    FMouseControl := InterimControl;
    if ((FMouseControl <> nil) and (CaptureControl = nil)) or
       ((CaptureControl <> nil) and (FMouseControl = CaptureControl)) and
       not (csDestroying in FMouseControl.ComponentState) then
    begin
      try
        FMouseControl.FreeNotification(FNotificationComponent);
      except
        // Assertion in FreeNotification call failed...
        // The application could be closing, or something
        // weird has happened so continue.
      end;
      FMouseControl.Perform(CM_MOUSEENTER, 0, 0);
    end;
  end;
end;

procedure TMouseMonitorThread.Execute;
begin
  while not Terminated do
  try
    DoMouseIdle;
    Sleep(1);     // Relinquish thread timeslice (don't use zero as this
                  // chews up CPU usage - 100% style).
  except
    // Continue thread
  end;
end;

end.
 