(*
    History++ plugin for Miranda IM: the free IM client for Microsoft* Windows*

    Copyright (C) 2006-2009 theMIROn, 2003-2006 Art Fedorov.
    History+ parts (C) 2001 Christian Kastner

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*)

{-----------------------------------------------------------------------------
 hpp_mescatcher (historypp project)

 Version:   1.0
 Created:   09.12.2006
 Author:    theMIROn

 [ Description ]

 Hidden window, used for catching WM messages and hotkeys

 [ History ]

 1.0 (09.12.2006)
   First version

 [ Modifications ]
 none

 [ Known Issues ]
 none

 Contributors: theMIROn
-----------------------------------------------------------------------------}

unit hpp_mescatcher;

interface

{.$DEFINE USE_CUSTOMIDLEHOOK}

uses
  Windows, Messages, Classes, Controls;

const
  hppWindowClassName = 'History++ MainWindow';

var
  hppMainWindow: HWND = 0;

procedure hppWakeMainThread(Sender: TObject);
function hppRegisterMainWindow: Boolean;
function hppUnregisterMainWindow: Boolean;

implementation

uses Forms, Themes;

{$IFDEF USE_CUSTOMIDLEHOOK}
type
  THackApplication = class(TComponent)
  protected
    FxxxxxxxxxHandle: HWnd;
    FxxxxxxxxxBiDiMode: TBiDiMode;
    FxxxxxxxxxBiDiKeyboard: AnsiString;
    FxxxxxxxxxNonBiDiKeyboard: AnsiString;
    FxxxxxxxxxObjectInstance: Pointer;
    FxxxxxxxxxMainForm: TForm;
    FMouseControl: TControl;
  end;
{$ENDIF}

var
  SavedWakeMainThread: TNotifyEvent = nil;
  SavedCheckIniChange: function (var Message: TMessage): Boolean of object = nil;
  {$IFDEF USE_CUSTOMIDLEHOOK}
  ForegroundIdleHook: HHOOK;
  {$ENDIF}

function MainWindowWndProc(hwndDlg: HWND; uMsg: uint; wParam: WPARAM; lParam: LPARAM): lresult; stdcall;
var
  Message: TMessage;
begin
  Result := 0;
  if Assigned(SavedCheckIniChange) then
  begin
    Message.Msg := uMsg;
    SavedCheckIniChange(Message);
  end;
  case uMsg of
    //WM_HOTKEY:
    // place for global hotkeys :)
    //if wParam = Hotkey then
    //  CallService(MS_HPP_SHOWGLOBALSEARCH,0,0);
    CM_WINDOWHOOK: begin
      if (wParam = 0) and not Assigned(SavedCheckIniChange) then
        SavedCheckIniChange := TWindowHook(Pointer(LParam)^);
    end;
    WM_SETTINGCHANGE: begin
      // workaround to force vcl notice mouse setting changed
      if wParam = SPI_SETWHEELSCROLLLINES then
        Mouse.SettingChanged(SPI_GETWHEELSCROLLLINES)
      else
        Mouse.SettingChanged(wParam);
      Result := DefWindowProc(hwndDlg, uMsg, wParam, lParam);
    end;
    WM_FONTCHANGE: begin
      Screen.ResetFonts;
      Result := DefWindowProc(hwndDlg, uMsg, wParam, lParam);
    end;
    WM_THEMECHANGED:
      StyleServices.ApplyThemeChange;
    WM_NULL:
      CheckSynchronize;
    else
      Result := DefWindowProc(hwndDlg, uMsg, wParam, lParam);
  end;
end;

{$IFDEF USE_CUSTOMIDLEHOOK}
function IdleHookProc(code: Integer; wParam: WPARAM; lParam: LPARAM): Integer; stdcall;
var
  Control: TControl;
  MouseControl: TControl;
  CaptureControl: TControl;
  P: TPoint;
begin
  if code < 0 then
  begin
    Result := CallNextHookEx(ForegroundIdleHook,code,wParam,lParam);
    exit;
  end;
  GetCursorPos(P);
  Control := FindDragTarget(P, True);
  MouseControl := THackApplication(Application).FMouseControl;
  CaptureControl := GetCaptureControl;
  if MouseControl <> Control then
  begin
    if ((MouseControl <> nil) and (CaptureControl = nil)) or
       ((CaptureControl <> nil) and (MouseControl = CaptureControl)) then
      MouseControl.Perform(CM_MOUSELEAVE, 0, 0);
    MouseControl := Control;
    if ((MouseControl <> nil) and (CaptureControl = nil)) or
       ((CaptureControl <> nil) and (MouseControl = CaptureControl)) then
      MouseControl.Perform(CM_MOUSEENTER, 0, 0);
    THackApplication(Application).FMouseControl := MouseControl;
  end;
  if Application.ShowHint and (MouseControl = nil) then
    Application.CancelHint;
  Result := 1;
end;
{$ENDIF}

function hppRegisterMainWindow: Boolean;
var
  WndClass: TWNDCLASS;
begin
  Result := False;
  ZeroMemory(@WndClass,SizeOf(WndClass));
  WndClass.lpfnWndProc   := @MainWindowWndProc;
  WndClass.hInstance     := GetModuleHandle(nil);
  WndClass.lpszClassName := hppWindowClassName;
  if Windows.RegisterClass(WndClass) = 0 then exit;
  hppMainWindow := CreateWindow(hppWindowClassName,hppWindowClassName,WS_DISABLED,
                             0,0,0,0,0,0,WndClass.hInstance,nil);
  Result := (hppMainWindow <> 0);
  if Result then
  begin
    // assign Application.CheckIniChange function
    Application.Handle := hppMainWindow;
    Application.Handle := 0;
    SavedWakeMainThread := Classes.WakeMainThread;
    @Classes.WakeMainThread := @hppWakeMainThread;
  end;
  {$IFDEF USE_CUSTOMIDLEHOOK}
  ForegroundIdleHook := SetWindowsHookEx(WH_FOREGROUNDIDLE,
                             @IdleHookProc,0,GetCurrentThreadID);
  {$ENDIF}
end;

function hppUnregisterMainWindow: Boolean;
begin
  if hppMainWindow <> 0 then
  begin
    DestroyWindow(hppMainWindow);
    hppMainWindow := 0;
  end;
  Result := Boolean(Windows.UnregisterClass(hppWindowClassName,GetModuleHandle(nil)));
  Classes.WakeMainThread := SavedWakeMainThread;
  {$IFDEF USE_CUSTOMIDLEHOOK}
  if ForegroundIdleHook <> 0 then UnhookWindowsHookEx(ForegroundIdleHook);
  {$ENDIF}
end;

procedure hppWakeMainThread(Sender: TObject);
begin
  PostMessage(hppMainWindow, WM_NULL, 0, 0);
  if Assigned(SavedWakeMainThread) then
    SavedWakeMainThread(Sender);
end;

end.