// **************************************************************************************************
//
// Unit Vcl.Styles.Hooks
// unit for the VCL Styles Utils
// https://github.com/RRUZ/vcl-styles-utils/
//
// 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 Vcl.Styles.Hooks.pas.
//
// The Initial Developer of the Original Code is Rodrigo Ruz V.
//
// Portions created by Rodrigo Ruz V. are Copyright (C) 2013-2023 Rodrigo Ruz V.
//
// Contributor(s): Mahdi Safsafi.
//
// All Rights Reserved.
//
// **************************************************************************************************
unit Vcl.Styles.Hooks;

interface

uses
  WinApi.Windows;

implementation

{$I VCL.Styles.Utils.inc}

uses
  DDetours, System.SyncObjs, System.SysUtils, System.Types, System.UITypes,
  System.Classes, System.Generics.Collections, System.StrUtils, WinApi.Messages,
  WinApi.UXTheme, Vcl.Graphics, Vcl.Styles.Utils.Graphics,
  {$IFDEF HOOK_UXTHEME}
  Vcl.Styles.UxTheme,
  {$ENDIF HOOK_UXTHEME}
  Vcl.Styles.Utils.SysControls, Vcl.Styles.FontAwesome, Vcl.Forms, Vcl.Controls,
  Vcl.StdCtrls, Vcl.ComCtrls, Vcl.Themes, Vcl.Styles.Utils.Misc;

type
  TListStyleBrush = class(TDictionary<Integer, HBRUSH>)
  protected
    procedure ValueNotify(const Value: HBRUSH; Action: TCollectionNotification); override;
  end;

  TSetStyle = procedure(Style: TCustomStyleServices) of object;

  TMonthCalendarClass = class(TMonthCalendar);

  TCommonCalendarClass = class(TCommonCalendar);

var
  VCLStylesBrush: TObjectDictionary<string, TListStyleBrush>;
  VCLStylesLock: TCriticalSection = nil;
  LSetStylePtr: TSetStyle;
  Trampoline_SetStyle: procedure(Self: TObject; Style: TCustomStyleServices);
  Trampoline_user32_FillRect: function(hDC: hDC; const lprc: TRect; hbr: HBRUSH): Integer; stdcall;
  Trampoline_user32_DrawEdge: function(hDC: hDC; var qrc: TRect; edge: UINT; grfFlags: UINT): BOOL; stdcall = nil;
  Trampoline_user32_DrawFrameControl: function(DC: HDC; Rect: PRect; uType, uState: UINT): BOOL; stdcall = nil;
  Trampoline_user32_LoadIconW: function(hInstance: HINST; lpIconName: PWideChar): HICON; stdcall = nil;
  Trampoline_user32_GetSysColorBrush: function(nIndex: Integer): HBRUSH; stdcall;
  {$IFDEF HOOK_UXTHEME}
  Trampoline_user32_LoadImageW: function(hInst: HINST; ImageName: LPCWSTR; ImageType: UINT; X, Y: Integer; Flags: UINT): THandle; stdcall = nil;
  {$ELSE}
  Trampoline_user32_GetSysColor: function(nIndex: Integer): DWORD; stdcall;
  {$ENDIF HOOK_UXTHEME}

{$IFDEF HOOK_TDateTimePicker}
  {$IF CompilerVersion>=29}
  Trampoline_SetWindowTheme: function(hwnd: HWND; pszSubAppName: LPCWSTR; pszSubIdList: LPCWSTR): HRESULT; stdcall;
  {$IFEND CompilerVersion}
{$ENDIF HOOK_TDateTimePicker}

function Detour_DrawEdge(hDC: hDC; var qrc: TRect; edge: UINT; grfFlags: UINT): BOOL; stdcall;
var
  CanDraw: Boolean;
  SaveIndex: Integer;
begin
  if not (ExecutingInMainThread) then
    Exit(Trampoline_user32_DrawEdge(hDC, qrc, edge, grfFlags));

  CanDraw := (not StyleServices.IsSystemStyle) and (TSysStyleManager.Enabled);
  if (CanDraw) and (edge <> BDR_OUTER) and (edge <> BDR_INNER) then
  begin
    SaveIndex := SaveDC(hDC);
    try
      DrawStyleEdge(hDC, qrc, TStyleElementEdges(edge), TStyleElementEdgeFlags(grfFlags));
    finally
      RestoreDC(hDC, SaveIndex);
    end;
    Exit(True);
  end;
  Exit(Trampoline_user32_DrawEdge(hDC, qrc, edge, grfFlags));
end;

function Detour_FillRect(hDC: hDC; const lprc: TRect; hbr: HBRUSH): Integer; stdcall;
begin
  if not (ExecutingInMainThread) or StyleServices.IsSystemStyle or not (TSysStyleManager.Enabled) then
    Exit(Trampoline_user32_FillRect(hDC, lprc, hbr))
  else if (hbr > 0) and (hbr < COLOR_ENDCOLORS + 1) then
    Exit(Trampoline_user32_FillRect(hDC, lprc, GetSysColorBrush(hbr - 1)))
  else
    Exit(Trampoline_user32_FillRect(hDC, lprc, hbr));
end;

function Detour_GetSysColor(nIndex: Integer): DWORD; stdcall;
begin
  if not (ExecutingInMainThread) or StyleServices.IsSystemStyle or not (TSysStyleManager.Enabled) then
    Result := Trampoline_user32_GetSysColor(nIndex)
  else if nIndex = COLOR_HOTLIGHT then
    Result := DWORD(StyleServices.GetSystemColor(clHighlight))
  else
    Result := DWORD(StyleServices.GetSystemColor(TColor(nIndex or Integer($FF000000))));
end;

function Detour_GetSysColorBrush(nIndex: Integer): HBRUSH; stdcall;
var
  LCurrentStyleBrush: TListStyleBrush;
  LBrush: HBRUSH;
  LColor: TColor;
begin
  if not (ExecutingInMainThread) then
    Exit(Trampoline_user32_GetSysColorBrush(nIndex));

  {
    The reason to change the previous code implementation
    is that the win32 graphics may differ with the VCL graphics:
    Eg: TColor is signed in VCL and Unsigned in Win32Api.
    When hooking: keep always using the native way !
    Need Color ?
    Use GetObject with LOGBRUSH ! or use TBrushColorPair !
  }
  VCLStylesLock.Enter;
  try
    if StyleServices.IsSystemStyle or not TSysStyleManager.Enabled then
      Exit(Trampoline_user32_GetSysColorBrush(nIndex))
    else
    begin
      if VCLStylesBrush.ContainsKey(StyleServices.Name) then
        LCurrentStyleBrush := VCLStylesBrush.Items[StyleServices.Name]
      else
      begin
        VCLStylesBrush.Add(StyleServices.Name, TListStyleBrush.Create());
        LCurrentStyleBrush := VCLStylesBrush.Items[StyleServices.Name];
      end;
      if Assigned(LCurrentStyleBrush) then
      begin
        if LCurrentStyleBrush.ContainsKey(nIndex) then
          Exit(LCurrentStyleBrush[nIndex])
        else
        begin
          if nIndex = COLOR_HOTLIGHT then
            LColor := StyleServices.GetSystemColor(clHighlight)
          else
            LColor := StyleServices.GetSystemColor(TColor(nIndex or Integer($FF000000)));

          LBrush := CreateSolidBrush(LColor);
          LCurrentStyleBrush.Add(nIndex, LBrush);
          Exit(LBrush);
        end;
      end;
      Exit(Trampoline_user32_GetSysColorBrush(nIndex));
    end;
  finally
    VCLStylesLock.Leave;
  end;
end;

procedure Detour_SetStyle(Self: TObject; Style: TCustomStyleServices);
var
  I: Integer;
  LActiveStyle: TCustomStyleServices;
begin
  if not (ExecutingInMainThread) then
  begin
    Trampoline_SetStyle(Self, Style);
    exit;
  end;

  LActiveStyle := TStyleManager.ActiveStyle;
  Trampoline_SetStyle(Self, Style);
  if (Style <> LActiveStyle) then
  begin
    for I := 0 to Screen.FormCount - 1 do
      if Screen.Forms[I].HandleAllocated then
        SendMessage(Screen.Forms[I].Handle, WM_SYSCOLORCHANGE, 0, 0);
  end;
end;

//based on JvThemes.DrawThemedFrameControl
function Detour_WinApi_DrawFrameControl(DC: hDC; Rect: PRect; uType, uState: UINT): BOOL; stdcall;
const
  Mask = $00FF;
var
  LRect: TRect;
  LDetails: TThemedElementDetails;
  CanDraw: Boolean;
  LThemedButton: TThemedButton;
  LThemedComboBox: TThemedComboBox;
  LThemedScrollBar: TThemedScrollBar;
begin
  if not (ExecutingInMainThread) then
    Exit(Trampoline_user32_DrawFrameControl(DC, Rect, uType, uState));

  Result := False;
  CanDraw := (not StyleServices.IsSystemStyle) and (TSysStyleManager.Enabled) and (Rect <> nil);
  if CanDraw then
  begin
    LRect := Rect^;
    case uType of
      DFC_BUTTON:
        case uState and Mask of

          DFCS_BUTTONPUSH:
            begin
              if uState and (DFCS_TRANSPARENT or DFCS_FLAT) = 0 then
              begin
                if uState and DFCS_INACTIVE <> 0 then
                  LThemedButton := tbPushButtonDisabled
                else if uState and DFCS_PUSHED <> 0 then
                  LThemedButton := tbPushButtonPressed
                else if uState and DFCS_HOT <> 0 then
                  LThemedButton := tbPushButtonHot
                else if uState and DFCS_MONO <> 0 then
                  LThemedButton := tbPushButtonDefaulted
                else
                  LThemedButton := tbPushButtonNormal;

                LDetails := StyleServices.GetElementDetails(LThemedButton);
                DrawStyleElement(DC, LDetails, LRect);
                Result := True;
              end;
            end;

          DFCS_BUTTONCHECK:
            begin
              if uState and DFCS_CHECKED <> 0 then
              begin
                if uState and DFCS_INACTIVE <> 0 then
                  LThemedButton := tbCheckBoxCheckedDisabled
                else if uState and DFCS_PUSHED <> 0 then
                  LThemedButton := tbCheckBoxCheckedPressed
                else if uState and DFCS_HOT <> 0 then
                  LThemedButton := tbCheckBoxCheckedHot
                else
                  LThemedButton := tbCheckBoxCheckedNormal;
              end
              else if uState and DFCS_MONO <> 0 then
              begin
                if uState and DFCS_INACTIVE <> 0 then
                  LThemedButton := tbCheckBoxMixedDisabled
                else if uState and DFCS_PUSHED <> 0 then
                  LThemedButton := tbCheckBoxMixedPressed
                else if uState and DFCS_HOT <> 0 then
                  LThemedButton := tbCheckBoxMixedHot
                else
                  LThemedButton := tbCheckBoxMixedNormal;
              end
              else
              begin
                if uState and DFCS_INACTIVE <> 0 then
                  LThemedButton := tbCheckBoxUncheckedDisabled
                else if uState and DFCS_PUSHED <> 0 then
                  LThemedButton := tbCheckBoxUncheckedPressed
                else if uState and DFCS_HOT <> 0 then
                  LThemedButton := tbCheckBoxUncheckedHot
                else
                  LThemedButton := tbCheckBoxUncheckedNormal;
              end;
              LDetails := StyleServices.GetElementDetails(LThemedButton);
              DrawStyleElement(DC, LDetails, LRect);
              Result := True;
            end;

          DFCS_BUTTONRADIO:
            begin
              if uState and DFCS_CHECKED <> 0 then
              begin
                if uState and DFCS_INACTIVE <> 0 then
                  LThemedButton := tbRadioButtonCheckedDisabled
                else if uState and DFCS_PUSHED <> 0 then
                  LThemedButton := tbRadioButtonCheckedPressed
                else if uState and DFCS_HOT <> 0 then
                  LThemedButton := tbRadioButtonCheckedHot
                else
                  LThemedButton := tbRadioButtonCheckedNormal;
              end
              else
              begin
                if uState and DFCS_INACTIVE <> 0 then
                  LThemedButton := tbRadioButtonUncheckedDisabled
                else if uState and DFCS_PUSHED <> 0 then
                  LThemedButton := tbRadioButtonUncheckedPressed
                else if uState and DFCS_HOT <> 0 then
                  LThemedButton := tbRadioButtonUncheckedHot
                else
                  LThemedButton := tbRadioButtonUncheckedNormal;
              end;
              LDetails := StyleServices.GetElementDetails(LThemedButton);
              DrawStyleElement(DC, LDetails, LRect);
              Result := True;
            end;
        end;

      DFC_SCROLL:
        begin
          case uState and Mask of

            DFCS_SCROLLCOMBOBOX:
              begin
                if uState and DFCS_INACTIVE <> 0 then
                  LThemedComboBox := tcDropDownButtonDisabled
                else if uState and DFCS_PUSHED <> 0 then
                  LThemedComboBox := tcDropDownButtonPressed
                else if uState and DFCS_HOT <> 0 then
                  LThemedComboBox := tcDropDownButtonHot
                else
                  LThemedComboBox := tcDropDownButtonNormal;

                LDetails := StyleServices.GetElementDetails(LThemedComboBox);
                DrawStyleElement(DC, LDetails, LRect);
                Result := True;
              end;

            DFCS_SCROLLUP:
              if uState and (DFCS_TRANSPARENT {or DFCS_FLAT}) = 0 then
              begin
                if uState and DFCS_INACTIVE <> 0 then
                  LThemedScrollBar := tsArrowBtnUpDisabled
                else if uState and DFCS_PUSHED <> 0 then
                  LThemedScrollBar := tsArrowBtnUpPressed
                else if uState and DFCS_HOT <> 0 then
                  LThemedScrollBar := tsArrowBtnUpHot
                else
                  LThemedScrollBar := tsArrowBtnUpNormal;

                LDetails := StyleServices.GetElementDetails(LThemedScrollBar);
                DrawStyleElement(DC, LDetails, LRect);
                Result := True;
              end;

            DFCS_SCROLLDOWN:
              if uState and (DFCS_TRANSPARENT {or DFCS_FLAT}) = 0 then
              begin
                if uState and DFCS_INACTIVE <> 0 then
                  LThemedScrollBar := tsArrowBtnDownDisabled
                else if uState and DFCS_PUSHED <> 0 then
                  LThemedScrollBar := tsArrowBtnDownPressed
                else if uState and DFCS_HOT <> 0 then
                  LThemedScrollBar := tsArrowBtnDownHot
                else
                  LThemedScrollBar := tsArrowBtnDownNormal;

                LDetails := StyleServices.GetElementDetails(LThemedScrollBar);
                DrawStyleElement(DC, LDetails, LRect);
                Result := True;
              end;

            DFCS_SCROLLLEFT:
              if uState and (DFCS_TRANSPARENT {or DFCS_FLAT}) = 0 then
              begin
                if uState and DFCS_INACTIVE <> 0 then
                  LThemedScrollBar := tsArrowBtnLeftDisabled
                else if uState and DFCS_PUSHED <> 0 then
                  LThemedScrollBar := tsArrowBtnLeftPressed
                else if uState and DFCS_HOT <> 0 then
                  LThemedScrollBar := tsArrowBtnLeftHot
                else
                  LThemedScrollBar := tsArrowBtnLeftNormal;

                LDetails := StyleServices.GetElementDetails(LThemedScrollBar);
                DrawStyleElement(DC, LDetails, LRect);
                Result := True;
              end;

            DFCS_SCROLLRIGHT:
              if uState and (DFCS_TRANSPARENT {or DFCS_FLAT}) = 0 then
              begin
                if uState and DFCS_INACTIVE <> 0 then
                  LThemedScrollBar := tsArrowBtnRightDisabled
                else if uState and DFCS_PUSHED <> 0 then
                  LThemedScrollBar := tsArrowBtnRightPressed
                else if uState and DFCS_HOT <> 0 then
                  LThemedScrollBar := tsArrowBtnRightHot
                else
                  LThemedScrollBar := tsArrowBtnRightNormal;

                LDetails := StyleServices.GetElementDetails(LThemedScrollBar);
                DrawStyleElement(DC, LDetails, LRect);
                Result := True;
              end;

          end;
        end;
    end;
  end;

  if not Result then
    Exit(Trampoline_user32_DrawFrameControl(DC, Rect, uType, uState));
end;

function GetStyleHighLightColor: TColor;
begin
  if ColorIsBright(StyleServices.GetSystemColor(clBtnFace)) or not ColorIsBright(StyleServices.GetSystemColor(clHighlight)) then
    Result := StyleServices.GetSystemColor(clBtnText)
  else
    Result := StyleServices.GetSystemColor(clHighlight);
end;

function Detour_LoadIconW(_hInstance: HINST; lpIconName: PWideChar): HICON; stdcall;
var
  s: string;
  LIcon: TIcon;
  LHandle: THandle;
  MustRelease: Boolean;

  procedure DrawIcon(const ACode: Word);
  begin
     //DestroyIcon(LHandle);
    Result := FontAwesome.GetIcon(ACode, LIcon.Width, LIcon.Height, GetStyleHighLightColor, StyleServices.GetSystemColor(clBtnFace), 0);
    MustRelease := False;
  end;

begin
  if not (ExecutingInMainThread) or StyleServices.IsSystemStyle or not (TSysStyleManager.Enabled) or not (TSysStyleManager.HookDialogIcons) then
    Exit(Trampoline_user32_LoadIconW(_hInstance, lpIconName));

  if {(_hInstance>0) and (_hInstance<>HInstance) and} IS_INTRESOURCE(lpIconName) then
  begin
    LIcon := TIcon.Create;
    try
      MustRelease := True;
      LHandle := Trampoline_user32_LoadIconW(_hInstance, lpIconName);
      LIcon.Handle := LHandle;
      Result := LHandle;
      s := IntToStr(NativeUInt(lpIconName));

      //OutputDebugString(PChar('Detour_LoadIconW '+s+ ' Module Name '+GetModuleName(_hInstance)+' _hInstance '+IntToHex(_hInstance, 8) ));
      case NativeUInt(lpIconName) of
        78:
          DrawIcon(fa_shield);
        81:
          DrawIcon(fa_info_circle);
        84:
          DrawIcon(fa_warning);
        98:
          DrawIcon(fa_minus_circle);
        99:
          DrawIcon(fa_question_circle);
      end;

      if _hInstance = 0 then
        case NativeUInt(lpIconName) of
          32518:
            DrawIcon(fa_shield);
          32516:
            DrawIcon(fa_info_circle);
          32515:
            DrawIcon(fa_warning);
          32513:
            DrawIcon(fa_minus_circle);
          32514:
            DrawIcon(fa_question_circle);
          32517:
            DrawIcon(fa_windows);
        end;

    finally
      if MustRelease then
        LIcon.ReleaseHandle;
      LIcon.Free;
    end;
  end
  else
    Exit(Trampoline_user32_LoadIconW(_hInstance, lpIconName));
end;

{$IFDEF HOOK_UXTHEME}
function Detour_LoadImageW(hInst: hInst; ImageName: LPCWSTR; ImageType: UINT; X, Y: Integer; Flags: UINT): THandle; stdcall;
const
  ExplorerFrame = 'explorerframe.dll';
var
  hModule: WinApi.Windows.HMODULE;
  LBitmap: TBitmap;
  s: string;
  LRect, LRect2: TRect;
  LBackColor, LColor: TColor;
begin
  if not (ExecutingInMainThread) or StyleServices.IsSystemStyle or not (TSysStyleManager.Enabled) then
    Exit(Trampoline_user32_LoadImageW(hInst, ImageName, ImageType, X, Y, Flags));

                                                                                                                         //w8 - W10
  if (hInst > 0) and (hInst <> HInstance) and (ImageType = IMAGE_ICON) and (X = 16) and (Y = 16) and IS_INTRESOURCE(ImageName) and TOSVersion.Check(6, 2) then
  begin
    s := IntToStr(NativeUInt(ImageName));

    case NativeUInt(ImageName) of
      //W8, W10
      //comctl32.dll
      16817:
        begin
          Exit(FontAwesome.GetIcon(fa_arrow_up, X, Y, StyleServices.GetSystemColor(clBtnText), StyleServices.GetSystemColor(clBtnFace), 0));
        end;
      16818:
        begin
          Exit(FontAwesome.GetIcon(fa_arrow_up, X, Y, StyleServices.GetSystemColor(clGrayText), StyleServices.GetSystemColor(clBtnFace), 0));
        end;
      //W10
      //shell32.dll
      5100:
        begin
          //OutputDebugString(PChar('GetModuleName ' + GetModuleName(hInst)));
          Exit(FontAwesome.GetIcon(fa_thumb_tack, 16, 16, 12, 12, StyleServices.GetSystemColor(clGrayText), StyleServices.GetSystemColor(clWindow), -22, iaRight));
          //Exit(FontAwesome.GetIcon(fa_check_square_o, 16, 16, StyleServices.GetSystemColor(clWindowText), StyleServices.GetSystemColor(clWindow), 0));
        end;
    end;
    Exit(Trampoline_user32_LoadImageW(hInst, ImageName, ImageType, X, Y, Flags));
  end
  else if (hInst > 0) and (ImageType = IMAGE_BITMAP) and (X = 0) and (Y = 0) and IS_INTRESOURCE(ImageName) then
  begin
    hModule := GetModuleHandle(ExplorerFrame);
    if (hModule = hInst) then
    begin
      s := IntToStr(NativeUInt(ImageName));
      Result := Trampoline_user32_LoadImageW(hInst, ImageName, ImageType, X, Y, Flags);
      LBitmap := TBitmap.Create;
      try
        LBitmap.Handle := Result;
        //LBitmap.SaveToFile(IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0)))+s+'.bmp');//SaveToFile('C:\Users\Rodrigo\Desktop\vcl-styles-utils\Vcl Styles Utils New Dialogs (Demo App)\Win32\Debug\Images\'+s+'.bmp');

        //W8 - W10
        if TOSVersion.Check(6, 2) then
        begin
          LBackColor := StyleServices.GetSystemColor(clWindow);
          LRect := Rect(0, 0, LBitmap.Width, LBitmap.Height);
          case NativeUInt(ImageName) of
            // Right Arrow, cross button, refresh, down arrow
            288:
              begin
                LColor := StyleServices.GetSystemColor(clBtnText);
                Bitmap32_SetAlphaAndColor(LBitmap, 1, LBackColor);

                LRect := Rect(0, 0, 16, 16);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_arrow_right, LRect, LColor);

                OffsetRect(LRect, 16, 0);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_remove, LRect, LColor);

                OffsetRect(LRect, 16, 0);
                LRect2 := LRect;
                InflateRect(LRect2, -2, -2);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_refresh, LRect2, LColor);

                OffsetRect(LRect, 16 + 2, 0);
                LRect2 := LRect;
                InflateRect(LRect2, -2, -2);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_caret_down, LRect2, LColor);

                Bitmap32_SetAlphaExceptColor(LBitmap, 255, LBackColor);
              end;
          else
            begin
              Bitmap32_Grayscale(LBitmap);
              _ProcessBitmap32(LBitmap, StyleServices.GetSystemColor(clHighlight), _BlendBurn)
            end;
          end;
        end
        else
        //Windows Vista - W7
          if (TOSVersion.Major = 6) and ((TOSVersion.Minor = 0) or (TOSVersion.Minor = 1)) then
        begin
          LBackColor := StyleServices.GetSystemColor(clWindow);
          LRect := Rect(0, 0, LBitmap.Width, LBitmap.Height);
          case NativeUInt(ImageName) of
           //Magnifier
            34560..34562,  // Aero Enabled
            34563..34568:  // Classic Theme
              begin
                LColor := StyleServices.GetSystemColor(clHighlight);
                Bitmap32_SetAlphaAndColor(LBitmap, 1, LBackColor);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_search, LRect, LColor);
                Bitmap32_SetAlphaExceptColor(LBitmap, 255, LBackColor);
               //Bitmap32_SetAlphaByColor(LBitmap, 255, LColor);
              end;

           //cross button normal
              34569..34571,  // Aero Enabled
              34572..34574:   // Classic Theme
              begin
                LColor := StyleServices.GetSystemColor(clWindowText);
                Bitmap32_SetAlphaAndColor(LBitmap, 1, LBackColor);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_remove, LRect, LColor);
                Bitmap32_SetAlphaExceptColor(LBitmap, 255, LBackColor);
              end;

           //cross button hot
              34575..34577, // Aero Enabled
              34581..34583, // Aero Enabled
              34578..34580:  // Classic Theme
              begin
                LColor := StyleServices.GetSystemColor(clHighlight);
                Bitmap32_SetAlphaAndColor(LBitmap, 1, LBackColor);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_remove, LRect, LColor);
                Bitmap32_SetAlphaExceptColor(LBitmap, 255, LBackColor);
              end;

           // Aero Enabled
           // Right Arrow, cross button, refresh, down arrow
              288:
              begin
                LColor := StyleServices.GetSystemColor(clBtnText);
                Bitmap32_SetAlphaAndColor(LBitmap, 1, LBackColor);

                LRect := Rect(0, 0, 16, 16);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_arrow_right, LRect, LColor);

                OffsetRect(LRect, 16, 0);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_remove, LRect, LColor);

                OffsetRect(LRect, 16, 0);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_refresh, LRect, LColor);

                OffsetRect(LRect, 16, 0);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_caret_down, LRect, LColor);

                Bitmap32_SetAlphaExceptColor(LBitmap, 255, LBackColor);
              end;

            // Classic Theme
            // Right Arrow, cross button, refresh, down arrow
              289, 290:
              begin
                LColor := StyleServices.GetSystemColor(clBtnText);
                Bitmap32_SetAlphaAndColor(LBitmap, 1, LBackColor);

                LRect := Rect(0, 0, 21, 21);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_arrow_right, LRect, LColor);

                OffsetRect(LRect, 21, 0);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_remove, LRect, LColor);

                OffsetRect(LRect, 21, 0);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_refresh, LRect, LColor);

                OffsetRect(LRect, 21, 0);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_caret_down, LRect, LColor);

                Bitmap32_SetAlphaExceptColor(LBitmap, 255, LBackColor);
              end;

            // Aero Enabled
            // navigation buttons (arrows)
              577..579, 581:
              begin
                case NativeUInt(ImageName) of
                  577:
                    LColor := StyleServices.GetSystemColor(clBtnText);
                  578:
                    LColor := StyleServices.GetSystemColor(clHighlight);
                  579:
                    LColor := StyleServices.GetSystemColor(clGrayText);
                  581:
                    LColor := StyleServices.GetSystemColor(clBtnText);
                else
                  LColor := StyleServices.GetSystemColor(clBtnText);
                end;

                Bitmap32_SetAlphaAndColor(LBitmap, 1, LBackColor);

                LRect := Rect(0, 0, 27, 27);
                InflateRect(LRect, -4, -4);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_arrow_left, LRect, LColor);
                OffsetRect(LRect, 27 + 4, 0);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_arrow_right, LRect, LColor);

                Bitmap32_SetAlphaExceptColor(LBitmap, 255, LBackColor);
              end;

            //Classic Theme
            // navigation buttons (arrows)
              582..584:
              begin
                case NativeUInt(ImageName) of
                  582:
                    LColor := StyleServices.GetSystemColor(clBtnText);
                  583:
                    LColor := StyleServices.GetSystemColor(clHighlight);
                  584:
                    LColor := StyleServices.GetSystemColor(clGrayText);
                else
                  LColor := StyleServices.GetSystemColor(clBtnText);
                end;

                Bitmap32_SetAlphaAndColor(LBitmap, 1, LBackColor);

                //left arrow
                LRect := Rect(0, 0, 25, 25);
                InflateRect(LRect, -4, -4);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_arrow_left, LRect, LColor);

                //right arrow
                OffsetRect(LRect, 25 + 4, 0);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_arrow_right, LRect, LColor);

                //dropdown arrow
                LRect := Rect(60, 8, 72, 20);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_caret_down, LRect, LColor);

                Bitmap32_SetAlphaExceptColor(LBitmap, 255, LBackColor);
              end;

            //Aero Enabled
            //background navigation buttons
              280:
              begin
                Bitmap32_SetAlphaAndColor(LBitmap, 1, LBackColor);

                LRect := Rect(0, 8, 12, 20);
                LColor := StyleServices.GetSystemColor(clGrayText);
                OffsetRect(LRect, 58, 0);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_caret_down, LRect, LColor);

                LColor := StyleServices.GetSystemColor(clBtnText);
                OffsetRect(LRect, 70, 0);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_caret_down, LRect, LColor);

                LColor := StyleServices.GetSystemColor(clHighlight);
                OffsetRect(LRect, 70, 0);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_caret_down, LRect, LColor);

                LColor := StyleServices.GetSystemColor(clHighlight);
                OffsetRect(LRect, 70, 0);
                FontAwesome.DrawChar(LBitmap.Canvas.Handle, fa_caret_down, LRect, LColor);

                Bitmap32_SetAlphaExceptColor(LBitmap, 255, LBackColor);
              end;

            //Classic Theme
            //background navigation buttons
              281:
              begin
                Bitmap32_SetAlphaAndColor(LBitmap, 1, LBackColor);
              end;
          end;
        end;
        LBitmap.ReleaseHandle;
      finally
        LBitmap.Free;
      end;
      //OutputDebugString(PChar(Format('Detour_LoadImageW ImageName %s', [s])));
      Exit(Result);
    end;
  end;

  Exit(Trampoline_user32_LoadImageW(hInst, ImageName, ImageType, X, Y, Flags));
end;
{$ENDIF HOOK_UXTHEME}


{$IFDEF HOOK_TDateTimePicker}
  {$IF CompilerVersion>=29}

function Detour_SetWindowTheme(hwnd: hwnd; pszSubAppName: LPCWSTR; pszSubIdList: LPCWSTR): HRESULT; stdcall;
var
  LControl: TWinControl;
begin
  if not (ExecutingInMainThread) then
    Exit(Trampoline_SetWindowTheme(hwnd, pszSubAppName, pszSubIdList));

  LControl := FindControl(hwnd);
  if (pszSubAppName = '') and (pszSubIdList = '') and TStyleManager.IsCustomStyleActive and (LControl <> nil) and (LControl is TMonthCalendar) then
    Exit(S_OK)
  else
    Exit(Trampoline_SetWindowTheme(hwnd, pszSubAppName, pszSubIdList));
end;
  {$IFEND CompilerVersion}
{$ENDIF HOOK_TDateTimePicker}


//don't hook CreateSolidBrush, because is used internally by GetSysColorBrush
//don't hook CopyImage

{ TListStyleBrush }

//Delete brushes

procedure TListStyleBrush.ValueNotify(const Value: HBRUSH; Action: TCollectionNotification);
begin
  inherited;
  if Action = TCollectionNotification.cnRemoved then
    DeleteObject(Value);
end;

const
  themelib = 'uxtheme.dll';

var
  hnd: THandle;

initialization
  VCLStylesLock := TCriticalSection.Create;
  VCLStylesBrush := TObjectDictionary<string, TListStyleBrush>.Create([doOwnsValues]);

  if StyleServices.Available then
  begin

  {$IFDEF HOOK_TDateTimePicker}
    TCustomStyleEngine.RegisterStyleHook(TDateTimePicker, TStyleHook);
  {$ENDIF HOOK_TDateTimePicker}

  {$IFDEF HOOK_TProgressBar}
    TCustomStyleEngine.RegisterStyleHook(TProgressBar, TStyleHook);
  {$ENDIF HOOK_TProgressBar}
    LSetStylePtr := TStyleManager.SetStyle;

    hnd := BeginTransaction();
    @Trampoline_user32_GetSysColor := InterceptCreate(user32, 'GetSysColor', @Detour_GetSysColor);
    @Trampoline_user32_GetSysColorBrush := InterceptCreate(user32, 'GetSysColorBrush', @Detour_GetSysColorBrush);
    @Trampoline_user32_FillRect := InterceptCreate(user32, 'FillRect', @Detour_FillRect);
    @Trampoline_user32_DrawEdge := InterceptCreate(user32, 'DrawEdge', @Detour_DrawEdge);
    @Trampoline_user32_DrawFrameControl := InterceptCreate(user32, 'DrawFrameControl', @Detour_WinApi_DrawFrameControl);
    @Trampoline_user32_LoadIconW := InterceptCreate(user32, 'LoadIconW', @Detour_LoadIconW);
  {$IFDEF HOOK_UXTHEME}
    if TOSVersion.Check(6) then
      @Trampoline_user32_LoadImageW := InterceptCreate(user32, 'LoadImageW', @Detour_LoadImageW);

  {$ENDIF HOOK_UXTHEME}

    @Trampoline_SetStyle := InterceptCreate(@LSetStylePtr, @Detour_SetStyle);

  {$IFDEF HOOK_TDateTimePicker}
    {$IF CompilerVersion>=29}
    //@Trampoline_TMonthCalendar_CreateWnd := InterceptCreate(@TMonthCalendarClass.CreateWnd, @Detour_TMonthCalendar_CreateWnd);
    @Trampoline_SetWindowTheme := InterceptCreate(themelib, 'SetWindowTheme', @Detour_SetWindowTheme);
    {$IFEND CompilerVersion}
  {$ENDIF HOOK_TDateTimePicker}

    EndTransaction(hnd);
  end;


finalization
  hnd := BeginTransaction();
  InterceptRemove(@Trampoline_user32_GetSysColor);
  InterceptRemove(@Trampoline_user32_GetSysColorBrush);
  InterceptRemove(@Trampoline_user32_FillRect);
  InterceptRemove(@Trampoline_user32_DrawEdge);
  InterceptRemove(@Trampoline_user32_DrawFrameControl);
  InterceptRemove(@Trampoline_user32_LoadIconW);

{$IFDEF HOOK_UXTHEME}
  if TOSVersion.Check(6) then
    InterceptRemove(@Trampoline_user32_LoadImageW);
{$ENDIF HOOK_UXTHEME}
  InterceptRemove(@Trampoline_SetStyle);

{$IFDEF HOOK_TDateTimePicker}
  {$IF CompilerVersion>=29}
  InterceptRemove(@Trampoline_SetWindowTheme);
  {$IFEND CompilerVersion}
{$ENDIF HOOK_TDateTimePicker}

  EndTransaction(hnd);
  VCLStylesBrush.Free;
  VCLStylesLock.Free;
  VCLStylesLock := nil;

end.

