unit uSpinEditWatch;
{$mode objfpc}{$H+}
interface
uses
Windows, Classes, SysUtils, Spin, Forms, Controls, ExtCtrls;
type
TButtonType = (btLower, btUpper);
TOnButtonClick = procedure(Sender: TObject; SpinEdit: TSpinEdit; AButtonType: TButtonType) of object;
{ TSpinEditWatch }
TSpinEditWatch = class
private
FNeedToBreak: Boolean;
FSpinEdit: TSpinEdit;
FButtonType: TButtonType;
FTimer: TTimer;
FOnButtonClick: TOnButtonClick;
function Start: Boolean;
function Stop: Boolean;
function IsStarted: Boolean;
function IsSpinEditButton(AHandle: THandle): Boolean;
function GetFormFromHandle(AHandle: THandle): TForm;
procedure DoTheMath(AP: TPoint);
procedure FindSpinEdit(const AControl: TControl; const AP: TPoint);
procedure OnTimer(Sender: TObject);
public
constructor Create;
destructor Destroy; override;
property OnButtonClick: TOnButtonClick read FOnButtonClick write FOnButtonClick;
end;
var
SpinEditWatch: TSpinEditWatch = nil;
implementation
const
WH_MOUSE_LL = 14;
var
llMouseHook: HHOOK = 0;
function LowLevelMouseHook(nCode: Integer; wParam: WPARAM; lParam: LPARAM): HRESULT; stdcall;
var
PMHS: PMOUSEHOOKSTRUCT;
begin
PMHS := PMOUSEHOOKSTRUCT({%H-}Pointer(lParam));
if nCode = HC_ACTION then
begin
if wParam = WM_LBUTTONUP then
SpinEditWatch.DoTheMath(PMHS^.pt);
end;
Result := CallNextHookEx(llMouseHook, nCode, wParam, lParam);
end;
function TSpinEditWatch.Start: Boolean;
begin
if llMouseHook = 0 then
llMouseHook := SetWindowsHookEx(WH_MOUSE_LL, @LowLevelMouseHook, HInstance, 0);
Result := (llMouseHook <> 0)
end;
function TSpinEditWatch.Stop: Boolean;
begin
Result := False;
if (llMouseHook <> 0) and UnhookWindowsHookEx(llMouseHook) then
begin
llMouseHook := 0;
Result := True;
end;
end;
function TSpinEditWatch.IsStarted: Boolean;
begin
Result := (llMouseHook <> 0)
end;
function TSpinEditWatch.IsSpinEditButton(AHandle: THandle): Boolean;
var
Buffer: array[0..MAX_PATH] of AnsiChar;
begin
GetClassName(AHandle, @Buffer, MAX_PATH);
Result := AnsiString(Buffer) = 'msctls_updown32'
end;
function TSpinEditWatch.GetFormFromHandle(AHandle: THandle): TForm;
var
I: Integer;
begin
Result := nil;
for I := 0 to Screen.FormCount - 1 do
begin
if Screen.Forms[I].Handle = AHandle then
begin
Result := Screen.Forms[I];
Break;
end;
end;
end;
procedure TSpinEditWatch.FindSpinEdit(const AControl: TControl; const AP: TPoint);
var
I: Integer;
begin
if (AControl = nil) or (FNeedToBreak) then
Exit;
if AControl is TWinControl then
begin
if (TWinControl(AControl) is TSpinEdit) then
begin
if PtInRect(TWinControl(AControl).BoundsRect, AP) then
begin
FSpinEdit := (TWinControl(AControl) as TSpinEdit);
if AP.Y > FSpinEdit.Top + (FSpinEdit.Height div 2) then
FButtonType := btLower
else
FButtonType := btUpper;
FNeedToBreak := True;
end;
end;
for I := 0 to TWinControl(AControl).ControlCount - 1 do
FindSpinEdit(TWinControl(AControl).Controls[I], AP);
end;
end;
procedure TSpinEditWatch.DoTheMath(AP: TPoint);
var
hWnd: THandle;
PID: DWord;
TheForm: TForm;
begin
hWnd := WindowFromPoint(AP);
if not IsSpinEditButton(hWnd) then
Exit;
PID := 0;
GetWindowThreadProcessId(hWnd, PID);
if not PID = GetCurrentProcessId then
Exit;
TheForm := GetFormFromHandle(GetForegroundWindow);
if TheForm = nil then
Exit;
FSpinEdit := nil;
FNeedToBreak := False;
AP := TheForm.ScreenToClient(AP);
FindSpinEdit(TheForm, AP);
if FSpinEdit <> nil then
if Assigned(FOnButtonClick) then
FTimer.Enabled := True;
end;
procedure TSpinEditWatch.OnTimer(Sender: TObject);
begin
FTimer.Enabled := False;
FOnButtonClick(Self, FSpinEdit, FButtonType);
end;
constructor TSpinEditWatch.Create;
begin
FTimer := TTimer.Create(nil);
FTimer.Enabled := False;
FTimer.OnTimer := @OnTimer;
FTimer.Interval := 10;
Start;
end;
destructor TSpinEditWatch.Destroy;
begin
if IsStarted then
Stop;
FTimer.Free;
inherited destroy;
end;
initialization
SpinEditWatch := TSpinEditWatch.Create;
finalization;
SpinEditWatch.Free;
end.