// **************************************************************************************************
//
// Unit Vcl.Styles.OwnerDrawFix
// 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.OwnerDrawFix.pas.
//
// The Initial Developer of the Original Code is Rodrigo Ruz V.
// Portions created by Rodrigo Ruz V. are Copyright (C) 2012-2023 Rodrigo Ruz V.
// All Rights Reserved.
//
// **************************************************************************************************
unit Vcl.Styles.OwnerDrawFix;

interface

uses
  Winapi.Windows,
  Winapi.CommCtrl,
  Vcl.ComCtrls,
  Vcl.Graphics,
  Vcl.StdCtrls,
  Vcl.Controls,
  Vcl.Styles,
  Vcl.Themes,
  System.Classes;

type
  TVclStylesOwnerDrawFix = class
  public
    procedure ComboBoxDrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState);
    procedure ListBoxDrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState);
    procedure ListViewDrawItem(Sender: TCustomListView; Item: TListItem; Rect: TRect; State: TOwnerDrawState);
    procedure ListViewMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  end deprecated 'Use the Vcl.Styles.Hooks unit Instead';

var
  VclStylesOwnerDrawFix: TVclStylesOwnerDrawFix;

implementation

uses
  System.SysUtils;

type
  TCustomListViewClass = class(TCustomListView);

  { TVclStylesOwnerDrawFix }

procedure TVclStylesOwnerDrawFix.ComboBoxDrawItem(Control: TWinControl; Index: Integer; Rect: TRect;
  State: TOwnerDrawState);
const
  ColorStates: array [Boolean] of TStyleColor = (scComboBoxDisabled, scComboBox);
  FontColorStates: array [Boolean] of TStyleFont = (sfComboBoxItemDisabled, sfComboBoxItemNormal);
var
  LStyles: TCustomStyleServices;
begin
  LStyles := StyleServices;
  with Control as TComboBox do
  begin
    Canvas.Brush.Color := LStyles.GetStyleColor(ColorStates[Control.Enabled]);
    Canvas.Font.Color := LStyles.GetStyleFontColor(FontColorStates[Control.Enabled]);

    if odSelected in State then
    begin
      Canvas.Brush.Color := LStyles.GetSystemColor(clHighlight);
      Canvas.Font.Color := LStyles.GetSystemColor(clHighlightText);
    end;

    Canvas.FillRect(Rect);
    Canvas.TextOut(Rect.Left + 2, Rect.Top, Items[Index]);
  end;
end;

procedure TVclStylesOwnerDrawFix.ListBoxDrawItem(Control: TWinControl; Index: Integer; Rect: TRect;
  State: TOwnerDrawState);
Var
  LListBox: TListBox;
  LStyles: TCustomStyleServices;
  LDetails: TThemedElementDetails;
begin
  LListBox := TListBox(Control);
  LStyles := StyleServices;

  if odSelected in State then
    LListBox.Brush.Color := LStyles.GetSystemColor(clHighlight);

  LDetails := StyleServices.GetElementDetails(tlListItemNormal);

  LListBox.Canvas.FillRect(Rect);
  Rect.Left := Rect.Left + 2;
  LStyles.DrawText(LListBox.Canvas.Handle, LDetails, LListBox.Items[Index], Rect,
    [tfLeft, tfSingleLine, tfVerticalCenter]);

  if odFocused In State then
  begin
    LListBox.Canvas.Brush.Color := LStyles.GetSystemColor(clHighlight);
    LListBox.Canvas.DrawFocusRect(Rect);
  end;
end;

procedure TVclStylesOwnerDrawFix.ListViewDrawItem(Sender: TCustomListView; Item: TListItem; Rect: TRect;
  State: TOwnerDrawState);
const
  Spacing = 4;
var
  Dx: Integer;
  r: TRect;
  rc: TRect;
  ColIdx: Integer;
  s: string;
  LDetails: TThemedElementDetails;
  LStyles: TCustomStyleServices;
  BoxSize: TSize;
  LColor: TColor;
  ImageSize: Integer;
begin
  ImageSize := 0;
  LStyles := StyleServices;
  if not LStyles.GetElementColor(LStyles.GetElementDetails(ttItemNormal), ecTextColor, LColor) or (LColor = clNone) then
    LColor := LStyles.GetSystemColor(clWindowText);

  Sender.Canvas.Brush.Color := LStyles.GetStyleColor(scListView);
  Sender.Canvas.Font.Color := LColor;
  Sender.Canvas.FillRect(Rect);

  r := Rect;
  inc(r.Left, Spacing);
  for ColIdx := 0 to TListView(Sender).Columns.Count - 1 do
  begin
    Dx := 0;
    r.Right := r.Left + Sender.Column[ColIdx].Width;

    if (ColIdx > 0) and (Item.SubItems.Count >= ColIdx) then
      s := Item.SubItems[ColIdx - 1]
    else
    begin
      BoxSize.cx := GetSystemMetrics(SM_CXMENUCHECK);
      BoxSize.cy := GetSystemMetrics(SM_CYMENUCHECK);
      s := Item.Caption;
      if TListView(Sender).Checkboxes then
      begin
        inc(Dx, BoxSize.cx + 3);
        r.Left := r.Left + BoxSize.cx + 3;
      end;
    end;

    if ColIdx = 0 then
    begin
      if not IsWindowVisible(ListView_GetEditControl(Sender.Handle)) and ([odSelected, odHotLight] * State <> []) then
      begin
        if ([odSelected, odHotLight] * State <> []) then
        begin
          rc := Rect;
          if TListView(Sender).Checkboxes then
            rc.Left := rc.Left + BoxSize.cx + Spacing;

          if not TListView(Sender).RowSelect then
            rc.Right := Sender.Column[0].Width;

          Sender.Canvas.Brush.Color := LStyles.GetSystemColor(clHighlight);
          Sender.Canvas.FillRect(rc);
        end;
      end;
    end;

    if TListView(Sender).RowSelect then
      Sender.Canvas.Brush.Color := LStyles.GetSystemColor(clHighlight);

    if (ColIdx = 0) and (TCustomListViewClass(Sender).SmallImages <> nil) and
      (TCustomListViewClass(Sender).SmallImages.Handle <> 0) and (Item.ImageIndex >= 0) then
    begin
      ImageList_Draw(TCustomListViewClass(Sender).SmallImages.Handle, Item.ImageIndex, Sender.Canvas.Handle, r.Left - 2,
        r.Top, ILD_NORMAL);
      ImageSize := TCustomListViewClass(Sender).SmallImages.Width;
      inc(Dx, ImageSize);
      r.Left := r.Left + ImageSize;
    end;

    if ([odSelected, odHotLight] * State <> []) then
      LDetails := StyleServices.GetElementDetails(tlListItemSelected)
    else
      LDetails := StyleServices.GetElementDetails(tlListItemNormal);

    Sender.Canvas.Brush.Style := bsClear;
    LStyles.DrawText(Sender.Canvas.Handle, LDetails, s, r, [tfLeft, tfSingleLine, tfVerticalCenter, tfEndEllipsis]);

    if (ColIdx = 0) and TListView(Sender).Checkboxes then
    begin
      rc := Rect;
      rc.Top := Rect.Top + (Rect.Bottom - Rect.Top - BoxSize.cy) div 2;
      rc.Bottom := rc.Top + BoxSize.cy;
      rc.Left := rc.Left + Spacing;
      rc.Right := rc.Left + BoxSize.cx;

      if Item.Checked then
        LDetails := StyleServices.GetElementDetails(tbCheckBoxCheckedNormal)
      else
        LDetails := StyleServices.GetElementDetails(tbCheckBoxUncheckedNormal);

      LStyles.DrawElement(Sender.Canvas.Handle, LDetails, rc);
    end;

    if ColIdx = 0 then
      r.Left := r.Left - Dx;
    { else }
    inc(r.Left, Sender.Column[ColIdx].Width);
  end;

end;

procedure TVclStylesOwnerDrawFix.ListViewMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
const
  Spacing = 4;
var
  LDetails: TThemedElementDetails;
  Size: TSize;
begin
  if TListView(Sender).OwnerDraw and (TListView(Sender).Checkboxes) then
  begin
    LDetails := StyleServices.GetElementDetails(tbCheckBoxCheckedNormal);
    Size.cx := 0;
    Size.cy := 0;

    if StyleServices.GetElementSize(TListView(Sender).Canvas.Handle, LDetails, esMinimum, Size) and (X > Spacing) and
      (X <= Size.Width) then
      TListView(Sender).Selected.Checked := not TListView(Sender).Selected.Checked;

    // OutputDebugString(PChar(Format('X %d Size.Width %d',[X, Size.Width])));
  end;
end;

initialization

VclStylesOwnerDrawFix := TVclStylesOwnerDrawFix.Create;

finalization

VclStylesOwnerDrawFix.Free;

end.
