unit PngCheckListBox;

{$I ..\Include\Thany.inc}

interface

uses
  Windows, Messages, Graphics, Controls, Classes, StdCtrls, CheckLst, pngimage,
  PngFunctions, Math;

type
  TPngCheckListBox = class(TCheckListBox)
  private
    FPngUnchecked: TPNGObject;
    FPngChecked: TPNGObject;
    FPngOptions: TPngOptions;
    FPngGrayed: TPNGObject;
    procedure SetPngChecked(const Value: TPNGObject);
    procedure SetPngUnchecked(const Value: TPNGObject);
    procedure SetPngOptions(const Value: TPngOptions);
    procedure SetPngGrayed(const Value: TPNGObject);
  protected
    procedure DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState); override;
    function GetCheckWidth: Integer; reintroduce;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property PngChecked: TPNGObject read FPngChecked write SetPngChecked;
    property PngUnchecked: TPNGObject read FPngUnchecked write SetPngUnchecked;
    property PngGrayed: TPNGObject read FPngGrayed write SetPngGrayed;
    property PngOptions: TPngOptions read FPngOptions write SetPngOptions default [pngBlendOnDisabled];
  end;

implementation

{ TPngCheckListBox }

constructor TPngCheckListBox.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FPngChecked := TPNGObject.Create;
FPngUnchecked := TPNGObject.Create;
FPngGrayed := TPNGObject.Create;
end;

destructor TPngCheckListBox.Destroy;
begin
FPngChecked.Free;
FPngUnchecked.Free;
FPngGrayed.Free;
inherited Destroy;
end;

procedure TPngCheckListBox.DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState);

  procedure DrawCheck(R: TRect; AState: TCheckBoxState; AEnabled: Boolean);
  var
     Png: TPNGObject;
     OldColor: TColor;
  begin
  //Draws the check image, if it's a PNG, otherwise the inherited would have
  //been called
  OldColor := Canvas.Brush.Color;
  Canvas.Brush.Color := Color;
  Canvas.FillRect(R);
  Canvas.Brush.Color := OldColor;
  case AState of
       cbUnchecked: Png := FPngUnchecked;
       cbChecked:   Png := FPngChecked;
       else         Png := FPngGrayed;
       end;
  DrawPNG(Png, Canvas, Classes.Rect(R.Left, R.Top, R.Left + Png.Width, R.Top + Png.Height), FPngOptions);
  end;

  procedure DrawText;
  var
     Flags: Integer;
     Data: string;
  begin
  //Draws the text for an item
  if Assigned(OnDrawItem)
  then OnDrawItem(Self, Index, Rect, State)
  else begin
       Canvas.FillRect(Rect);
       if Index < Items.Count
       then begin
            Flags := DrawTextBiDiModeFlags(DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX);
            if not UseRightToLeftAlignment
            then Inc(Rect.Left, 2)
            else Dec(Rect.Right, 2);
            Data := '';
            {$IFDEF THANY_COMPILER_6_UP}
            if (Style in [lbVirtual, lbVirtualOwnerDraw])
            then Data := DoGetData(Index)
            else Data := Items[Index];
            {$ELSE}
            Data := Items[Index];
            {$ENDIF}
            Windows.DrawText(Canvas.Handle, PChar(Data), Length(Data), Rect, Flags);
            end;
       end;
  end;

var
   R: TRect;
   SaveEvent: TDrawItemEvent;
   ACheckWidth: Integer;
   Enable: Boolean;
begin
if FPngChecked.Empty and FPngUnchecked.Empty and FPngGrayed.Empty
then inherited DrawItem(Index, Rect, State)
else begin
     ACheckWidth := GetCheckWidth;
     if Index < Items.Count
     then begin
          R := Rect;
          Enable := Self.Enabled and ItemEnabled[Index];
          if {$IFDEF THANY_COMPILER_6_UP} not Header[Index] {$ELSE} True {$ENDIF}
          then begin
               if not UseRightToLeftAlignment
               then begin
                    R.Right := Rect.Left;
                    R.Left := R.Right - ACheckWidth;
                    end
               else begin
                    R.Left := Rect.Right;
                    R.Right := R.Left + ACheckWidth;
                    end;
               DrawCheck(R, Self.State[Index], Enable);
               end
          else begin
               {$IFDEF THANY_COMPILER_6_UP}
               Canvas.Font.Color := HeaderColor;
               Canvas.Brush.Color := HeaderBackgroundColor;
               {$ENDIF}
               end;
          if not Enable
          then Canvas.Font.Color := clGrayText;
          end;

     if (Style = lbStandard) and Assigned(OnDrawItem)
     then begin
          //Force lbStandard list to ignore OnDrawItem event.
          SaveEvent := OnDrawItem;
          OnDrawItem := nil;
          try
            DrawText;
          finally
            OnDrawItem := SaveEvent;
           end;
          end
     else DrawText;
     end;
end;

function TPngCheckListBox.GetCheckWidth: Integer;
begin
//CheckWidth is equal to the widest PNG
if not (FPngChecked.Empty and FPngUnchecked.Empty and FPngGrayed.Empty)
then Result := Max(FPngChecked.Width, Max(FPngUnchecked.Width, FPngGrayed.Width))
else Result := inherited GetCheckWidth;
end;

procedure TPngCheckListBox.SetPngChecked(const Value: TPNGObject);
begin
//This is all neccesary, because you can't assign a nil to a TPNGObject
if Value = nil
then begin
     FPngChecked.Free;
     FPngChecked := TPNGObject.Create;
     end
else FPngChecked.Assign(Value);
Repaint;
end;

procedure TPngCheckListBox.SetPngUnchecked(const Value: TPNGObject);
begin
//This is all neccesary, because you can't assign a nil to a TPNGObject
if Value = nil
then begin
     FPngUnchecked.Free;
     FPngUnchecked := TPNGObject.Create;
     end
else FPngUnchecked.Assign(Value);
Repaint;
end;

procedure TPngCheckListBox.SetPngGrayed(const Value: TPNGObject);
begin
//This is all neccesary, because you can't assign a nil to a TPNGObject
if Value = nil
then begin
     FPngGrayed.Free;
     FPngGrayed := TPNGObject.Create;
     end
else FPngGrayed.Assign(Value);
Repaint;
end;

procedure TPngCheckListBox.SetPngOptions(const Value: TPngOptions);
begin
if FPngOptions <> Value
then begin
     FPngOptions := Value;
     Repaint;
     end;
end;

end.
