{****************************************************
 This file is part of the Eye Candy Controls (EC-C)
 
  Copyright (C) 2013 Vojtěch Čihák, Czech Republic
 
  This library is free software.

  See the file COPYING.LGPL.txt, 
  included in this distribution,
  for details about the license.
****************************************************}   

unit ECTypes;
{$mode objfpc}{$H+}  

//{$DEFINE DBGLINE}  {don't remove, just comment}

interface

uses
  Classes, SysUtils, Controls, Graphics, Messages, LMessages, Math, LCLIntf, LCLType,
  {$IFDEF DBGLINE} LCLProc, {$ENDIF} StdCtrls, StrUtils, Themes, Types;

type
  {$PACKENUM 2}
  TBasicPos = (ebpTopLeft, ebpBottomRight);
  TColorLayout = (eclRGBColor, eclBGRColor, eclARGBColor, eclABGRColor, eclCMYColor, 
                  eclYMCColor, eclACMYColor, eclAYMCColor, eclSystemBGR);
  TGlyphDesign = (egdNone, egdArrowDec, egdArrowInc,
                  egdArrowUp, egdArrowRight, egdArrowDown, egdArrowLeft,
                  egdArrowUR, egdArrowDR, egdArrowDL, egdArrowUL,
                  egdArrowsUU, egdArrowsRR, egdArrowsDD, egdArrowsLL,
                  egdArrowsUD, egdArrowsMiddle, egdArrowsLR, egdArrowsHMiddle,
                  egdArrowsMax, egdArrowMax, egdArrowMin, egdArrowsMin,
                  egdArrowsHMax, egdArrowHMax, egdArrowHMin, egdArrowsHMin,
                  egdArrowsUDHor, egdArrowsURDL_S, egdArrowsURDL_M, egdArrowURDL_L,
                  egdArrowURDL_XL, egdArrowsUL_DR, egdArrowsUR_DL,
                  egdArrsB_Min, egdArrB_Min, egdArrsB_DD, egdArrB_Down, 
                  egdArrsB_Middle, egdArrsB_UD, egdArrB_Up, egdArrsB_UU,
                  egdArrB_Max, egdArrsB_Max,
                  egdArrsB_HMin, egdArrB_HMin, egdArrsB_LL, egdArrB_Left,
                  egdArrsB_HMiddle, egdArrsB_LR, egdArrB_Right, egdArrsB_RR,
                  egdArrB_HMax, egdArrsB_HMax,
                  egdArrC_Min, egdArrC_DD, egdArrC_Down, egdArrC_Middle,
                  egdArrC_UD, egdArrC_LR, egdArrC_Up, egdArrC_UU,
                  egdArrC_Max, egdArrC_URDL,
                  egdPlayPause, egdPlayUpDown, egdPlayStop, egdPlayEject, egdPlayEjectD,
                  egdMathBigMinus, egdMathMinus, egdMathEqual, egdMathPlusMinus, 
                  egdMathPlus, egdMathBigPlus, 
                  egdCombo, egdList, egdFramedList, 
                  egdGrid,
                  egdSizeArrUp, egdSizeArrRight, egdSizeArrDown, egdSizeArrLeft,
                  egdRectBeveled, egdRectFramed,  { coloured with 3D/clBtnText frame }
                  egdWinRectClr, egdWinRoundClr,  { for color dialogs }
                  egdWindowRect, egdWindowRound);  { Total: egdNone + 84 glyphs }
  TIncrementalMode = (eimContinuous, eimDiscrete);
  TItemState = (eisDisabled, eisHighlighted, eisEnabled, 
                eisPushed, eisPushedHihlighted, eisPushedDisabled);	
  TItemStates = set of TItemState;
  TObjectOrientation = (eooHorizontal, eooVertical);
  TObjectPos = (eopTop, eopRight, eopBottom, eopLeft);
  TObjectStyle = (eosButton, eosPanel);                  
  TRedrawMode = (ermHoverKnob, ermMoveKnob, ermFreeRedraw, ermRedrawBkgnd, ermRecalcRedraw);
  TTickDesign = (etdSimple, etdThick, etd3DLowered, etd3DRaised);
  TTickStyle = (etsSolid, etsDotted);
  TValuesVisibility = (evvNone, evvBounds, evvValues, evvAll);
  { Events }
  TMouseMethod = procedure(Button: TMouseButton; Shift: TShiftState) of object;
  TObjectMethod = procedure of object;
  TOnPrepareValue = procedure(Sender: TObject; var AValue: Double) of object;
  
  { TCanvasHelper }
  TCanvasHelper = class helper for TCanvas
  public
    procedure DrawButtonBackground(ARect: TRect; AEnabled: Boolean);  
    procedure DrawButtonBackground(ARect: TRect; AItemState: TItemState); overload;
    procedure DrawGlyph(ARect: TRect; AGlyphDesign: TGlyphDesign);
    procedure DrawPanelBackground(ARect: TRect; ABevelInner, ABevelOuter: TBevelCut;
                ABevelWidth: Integer; AColor: TColor);      
    procedure DrawPanelBackground(ARect: TRect; ABevelInner, ABevelOuter: TBevelCut;
                ABevelSpace, ABevelWidth: Integer; AColor3DDark, AColor3DLight, AColor: TColor); 
    function GlyphExtent(AGlyphDesign: TGlyphDesign): TSize;
    procedure SetFontParams(AOrientation: Integer; ASize: Integer; AStyle: TFontStyles);
    procedure SetRealGlyphColor(AGlyphColor: TColor; AState: TItemState); overload;
  end;
  
  { TBitmapHelper }
  TBitmapHelper = class helper for TBitmap
  public
    procedure SetProperties(AWidth, AHeight: Integer);
    procedure TransparentClear;
  end;

  { TFontOptions }
  TFontOptions = class(TPersistent)
  private
    FFontColor: TColor;
    FFontSize: SmallInt;
    FFontStyles: TFontStyles;  
    procedure SetFontColor(const AValue: TColor);
    procedure SetFontSize(const AValue: SmallInt);
    procedure SetFontStyles(const AValue: TFontStyles);
  protected
    procedure RecalcRedraw;
    procedure Redraw;
  public
    OnRecalcRedraw: TObjectMethod;
    OnRedraw: TObjectMethod;
    Parent: TControl;
    constructor Create(AParent: TControl);
  published
    property FontColor: TColor read FFontColor write SetFontColor default clDefault;
    property FontSize: SmallInt read FFontSize write SetFontSize;
    property FontStyles: TFontStyles read FFontStyles write SetFontStyles;
  end;

  { TECBaseControl }
  TECBaseControl = class(TCustomControl)
  private
    procedure SetBevelInner(const AValue: TBevelCut);
    procedure SetBevelOuter(const AValue: TBevelCut);
    procedure SetBevelSpace(const AValue: SmallInt);
    procedure SetBevelWidth(const AValue: SmallInt); 
    procedure SetColor3DDark(const AValue: TColor);
    procedure SetColor3DLight(const AValue: TColor); 
    procedure SetOrientation(const AValue: TObjectOrientation);
    procedure SetStyle(AValue: TObjectStyle); 
  protected
    FBevelInner: TBevelCut;
    FBevelOuter: TBevelCut;
    FBevelSpace: SmallInt;
    FBevelWidth: SmallInt; 
    FColor3DDark: TColor;
    FColor3DLight: TColor;
    FInvalidRect: TRect;
    FOrientation: TObjectOrientation;
    FStyle: TObjectStyle;
    RedrawMode: TRedrawMode;
    function GetBorderWidth: Integer;  { Border = BevelInner + BevelOuter + BevelSpace }
    function HasCaption: Boolean; virtual;
    procedure InvalidateCustomRect(AMove: Boolean); virtual; abstract;
    procedure OrientationChanged({%H-}AValue: TObjectOrientation); virtual;
    procedure RecalcRedraw; virtual; abstract; 
    procedure Redraw3DColorAreas; virtual; abstract; 
    procedure SetAutoSize(Value: Boolean); override;
    procedure StyleChanged({%H-}AValue: TObjectStyle); virtual;
    procedure WMPaint(var Message: TLMPaint); message LM_PAINT;  { resolves vanishing }
  public
    UpdateCount: SmallInt;
    constructor Create(AOwner: TComponent); override;
    procedure BeginUpdate; virtual;
    procedure EndUpdate(Recalculate: Boolean = True); virtual;
    procedure InvalidateNonUpdated;  { Invalidates non-updated component (UpdateCount = 0) }
    procedure Redraw; virtual; abstract;
    property BevelInner: TBevelCut read FBevelInner write SetBevelInner default bvNone;
    property BevelOuter: TBevelCut read FBevelOuter write SetBevelOuter default bvRaised;
    property BevelSpace: SmallInt read FBevelSpace write SetBevelSpace default 0;
    property BevelWidth: SmallInt read FBevelWidth write SetBevelWidth default 1;   
    property Color3DDark: TColor read FColor3DDark write SetColor3DDark default clDefault;
    property Color3DLight: TColor read FColor3DLight write SetColor3DLight default clDefault;  
    property Orientation: TObjectOrientation read FOrientation write SetOrientation;
    property Style: TObjectStyle read FStyle write SetStyle;
  end;
  
  { TECCustomKnob }
  TECCustomKnob = class(TPersistent)
  private
    FBackgroundColor: TColor;
    FBevelWidth: SmallInt;
    FColor: TColor;
    FCursor: TCursor;
    FHeight: Integer;
    FStyle: TObjectStyle;
    FTickMarkCount: SmallInt;
    FTickMarkDesign: TTickDesign;
    FTickMarkSpacing: SmallInt;
    FTickMarkStyle: TTickStyle;
    FWidth: Integer;
    procedure SetBackgroundColor(AValue: TColor);
    procedure SetBevelWidth(const AValue: SmallInt);
    procedure SetColor(const AValue: TColor);
    procedure SetCursor(const AValue: TCursor);
    procedure SetHeight(const AValue: Integer);
    procedure SetStyle(const AValue: TObjectStyle);
    procedure SetTickMarkCount(const AValue: SmallInt);
    procedure SetTickMarkDesign(const AValue: TTickDesign);
    procedure SetTickMarkSpacing(const AValue: SmallInt);
    procedure SetTickMarkStyle(const AValue: TTickStyle);
    procedure SetWidth(const AValue: Integer);
  protected const
    cDefBevelWidth = 2;
    cDefKnobHeight = 32;
    cDefKnobWidth = 20;
    cDefTickDesign = etd3DLowered;
    cDefTickMarkCount = 5;
    cDefTickMarkSpacing = 2;
  protected
    Parent: TECBaseControl;
    procedure DrawKnobs;
  public
    UpdateCount: SmallInt;
    KnobDisabled: TBitmap;
    KnobNormal: TBitmap;
    KnobHighlighted: TBitmap;
    Left: Integer;
    MouseEntered: Boolean;
    Top: Integer;
    constructor Create(AParent: TECBaseControl);
    destructor Destroy; override;
    procedure BeginUpdate;
    procedure EndUpdate;
    procedure RecalcRedraw;
    procedure SetSize(AWidth, AHeight: Integer);
    property BackgroundColor: TColor read FBackgroundColor write SetBackgroundColor;
  public                            
    property BevelWidth: SmallInt read FBevelWidth write SetBevelWidth default cDefBevelWidth;
    property Color: TColor read FColor write SetColor default clDefault;
    property Cursor: TCursor read FCursor write SetCursor default crHandPoint;
    property Height: Integer read FHeight write SetHeight default cDefKnobHeight;
    property Style: TObjectStyle read FStyle write SetStyle default eosButton;
    property TickMarkCount: SmallInt read FTickMarkCount write SetTickMarkCount default cDefTickMarkCount;
    property TickMarkDesign: TTickDesign read FTickMarkDesign write SetTickMarkDesign default cDefTickDesign;
    property TickMarkSpacing: SmallInt read FTickMarkSpacing write SetTickMarkSpacing default cDefTickMarkSpacing;
    property TickMarkStyle: TTickStyle read FTickMarkStyle write SetTickMarkStyle default etsSolid;
    property Width: Integer read FWidth write SetWidth default cDefKnobWidth;
  end;   

  { TBaseScrollControl }                   
  TBaseScrollControl = class(TCustomControl)
  private
    FAreaHeight: Integer;
    FAreaWidth: Integer;    
    FIncrementX: SmallInt;
    FIncrementY: SmallInt;
    function GetFullAreaHeight: Integer;
    function GetFullAreaWidth: Integer;  
    procedure SetAreaHeight(AValue: Integer);
    procedure SetAreaWidth(AValue: Integer);  
    procedure SetClientAreaLeft(AValue: Integer);
    procedure SetClientAreaTop(AValue: Integer);  
    procedure SetScrollBars(AValue: TScrollStyle);
  protected
    FClientAreaLeft: Integer;
    FClientAreaTop: Integer;        
    FRequiredArea: TPoint;  { area where all devices can fit }
    FScrollBars: TScrollStyle;
    FScrollInfoHor, FScrollInfoVert: TScrollInfo;
    procedure CreateWnd; override;
    procedure SetDefaultScrollParams; virtual;
    procedure UpdateRequiredAreaHeight; virtual; abstract; 
    procedure UpdateRequiredAreaWidth; virtual; abstract;  
    procedure UpdateScrollBars(AValue: TScrollStyle);
    procedure UpdateScrollInfoHor;
    procedure UpdateScrollInfoVert;  
    procedure WMHScroll(var Msg: TWMScroll); message WM_HSCROLL;
    procedure WMSize(var Message: TLMSize); message LM_SIZE;
    procedure WMVScroll(var Msg: TWMScroll); message WM_VSCROLL;     
  public
    UpdateCount: SmallInt;
    constructor Create(AOwner: TComponent); override;
    procedure BeginUpdate;
    procedure EndUpdate;
    procedure InvalidateNonUpdated;
    property AreaHeight: Integer read FAreaHeight write SetAreaHeight default -1;
    property AreaWidth: Integer read FAreaWidth write SetAreaWidth default -1;  
    property ClientAreaLeft: Integer read FClientAreaLeft write SetClientAreaLeft;
    property ClientAreaTop: Integer read FClientAreaTop write SetClientAreaTop;
    property FullAreaHeight: Integer read GetFullAreaHeight;
    property FullAreaWidth: Integer read GetFullAreaWidth;
    property IncrementX: SmallInt read FIncrementX write FIncrementX default 1;
    property IncrementY: SmallInt read FIncrementY write FIncrementY default 1;
    property ScrollBars: TScrollStyle read FScrollBars write SetScrollBars default ssAutoBoth;
  end;

function ARGBToColor(A, R, G, B: Byte): TColor; inline;

procedure ColorToRGBA(AColor: TColor; out R, G, B, A: Integer);

function ColorToStrLayouted(AColor: TColor; AColorLayout: TColorLayout): string;

function GetColorResolvingDefault(ASourceColor, ADefColor: TColor): TColor; inline;

function GetColorResolvingDefAndEnabled(ASourceColor, ADefColor: TColor; AEnabled: Boolean): TColor; inline;

function GetMergedColor(AColor, BColor: TColor; AProportion: Single): TColor;

function GetMonochromaticColor(AColor: TColor): TColor;

procedure IncludeRectangle(var AResultRect: TRect; const AppendRect: TRect);

function IsInRange(AValue, ALimit, BLimit: Integer): Boolean;

function IsRectIntersect(ARect, BRect: TRect): Boolean;

function LinearToLogarithmic(AValue, AMin, AMax, ALogarithmBase: Double): Double;

function NormalizeRectangle(PointAX, PointAY, PointBX, PointBY: Integer): TRect;

function TrimColorString(AString: string): string;

function TryStrToColorLayouted(AString: string; ALayout: TColorLayout; out AColor: TColor): Boolean;

operator = (ARect, BRect: TRect): Boolean;

const pi_1800: Double = 0.00174532925199432958;  { = pi/1800 }
      caThemedContent: array[low(TItemState)..high(TItemState)] of TThemedButton = 
        (tbPushButtonDisabled, tbPushButtonHot, tbPushButtonNormal, 
         tbPushButtonPressed, tbPushButtonHot, tbPushButtonDisabled);
      caThemedItems: array[low(TItemState)..high(TItemState)] of TThemedButton =
        (tbPushButtonDisabled, tbPushButtonHot, tbPushButtonNormal, 
         tbPushButtonPressed, tbPushButtonPressed, tbPushButtonPressed);
      caItemState: array[False..True] of TItemState = (eisDisabled, eisEnabled);
      caDisabledStates = [eisDisabled, eisPushedDisabled];
      caEnabledStates = [eisHighlighted, eisEnabled, eisPushed, eisPushedHihlighted];
      
implementation

function ARGBToColor(A, R, G, B: Byte): TColor; inline;
begin
  Result := (A shl 24) or (B shl 16) or (G shl 8) or R;
end;

procedure ColorToRGBA(AColor: TColor; out R, G, B, A: Integer);
begin
  R := AColor and $000000FF;
  G := (AColor shr 8) and $000000FF;
  B := (AColor shr 16) and $000000FF;
  A := (AColor shr 24) and $000000FF;
end;

function ColorToStrLayouted(AColor: TColor; AColorLayout: TColorLayout): string;
var R, G, B, A: Integer;
begin
  ColorToRGBA(AColor, R, G, B, A);
  case AColorLayout of
    eclRGBColor: Result := inttohex(R, 2) + inttohex(G, 2) + inttohex(B, 2);
    eclBGRColor: Result := inttohex(B, 2) + inttohex(G, 2) + inttohex(R, 2);
    eclARGBColor: Result := inttohex(A, 2) + inttohex(R, 2) + inttohex(G, 2) + inttohex(B, 2);
    eclABGRColor: Result := inttohex(A, 2) + inttohex(B, 2) + inttohex(G, 2) + inttohex(R, 2);
    eclCMYColor: Result := inttohex(255 - R, 2) + inttohex(255 - G, 2)  + inttohex(255 - B, 2);
    eclYMCColor: Result := inttohex(255 - B, 2) + inttohex(255 - G, 2)  + inttohex(255 - R, 2);
    eclACMYColor: Result := inttohex(A, 2) + inttohex(255 - R, 2) + inttohex(255 - G, 2)  + inttohex(255 - B, 2);
    eclAYMCColor: Result := inttohex(A, 2) + inttohex(255 - B, 2) + inttohex(255 - G, 2)  + inttohex(255 - R, 2);
    eclSystemBGR: Result := ColorToString(AColor);
  end;   
end;     

function GetColorResolvingDefault(ASourceColor, ADefColor: TColor): TColor;
begin
  Result := ASourceColor;
  if Result = clDefault then Result := ADefColor;
end; 

function GetColorResolvingDefAndEnabled(ASourceColor, ADefColor: TColor; AEnabled: Boolean): TColor;
begin
  Result := ASourceColor;
  if Result = clDefault then Result := ADefColor;  
  if not AEnabled then Result := GetMonochromaticColor(Result);
end;

function GetMergedColor(AColor, BColor: TColor; AProportion: Single): TColor;
var aR, bR, aG, bG, aB, bB: Integer;
begin
  GetRGBIntValues(ColorToRGB(AColor), aR, aG, aB);
  GetRGBIntValues(ColorToRGB(BColor), bR, bG, bB);
  aR := bR + trunc((aR - bR)*AProportion);
  aG := bG + trunc((aG - bG)*AProportion);
  aB := bB + trunc((aB - bB)*AProportion);
  Result := RGBToColor(aR, aG, aB);
end;

function GetMonochromaticColor(AColor: TColor): TColor;
var r, g, b: Integer;
begin
  GetRGBIntValues(ColorToRGB(AColor), r, g, b);
  r := 341*(r + g + b) shr 10;
  Result := RGBToColor(r, r, r);
end;

procedure IncludeRectangle(var AResultRect: TRect; const AppendRect: TRect);
begin
  AResultRect.Left := Math.min(AResultRect.Left, AppendRect.Left);
  AResultRect.Top := Math.min(AResultRect.Top, AppendRect.Top);
  AResultRect.Right := Math.max(AResultRect.Right, AppendRect.Right);
  AResultRect.Bottom := Math.max(AResultRect.Bottom, AppendRect.Bottom);
end;

function IsInRange(AValue, ALimit, BLimit: Integer): Boolean;
begin
  Result := ((ALimit <= AValue) and (AValue <= BLimit)) or
            ((BLimit <= AValue) and (AValue <= ALimit));
end;

function IsRectIntersect(ARect, BRect: TRect): Boolean;
var aTL, aBR: TPoint;
begin
  aTL.X := Math.max(ARect.Left, BRect.Left);
  aTL.Y := Math.max(ARect.Top, BRect.Top);
  aBR.X := Math.min(ARect.Right, BRect.Right);
  aBR.Y := Math.min(ARect.Bottom, BRect.Bottom);
  Result := (aTL.X < aBR.X) and (aTL.Y < aBR.Y);
end;
           
function LinearToLogarithmic(AValue, AMin, AMax, ALogarithmBase: Double): Double;
var aProportion: Double;
begin
  aProportion := (AValue - AMin)/(AMax - AMin);
  if AMin > 0
    then AMin := logn(ALogarithmBase, AMin)
    else AMin := 0;
  if AMax > 0
    then AMax := logn(ALogarithmBase, AMax)
    else AMax := 0;
  Result := power(ALogarithmBase, AMin + (AMax - AMin)*aProportion);  
end;  

function NormalizeRectangle(PointAX, PointAY, PointBX, PointBY: Integer): TRect;
var i: Integer;
begin
  if PointAX > PointBX then
    begin
      i := PointAX;
      PointAX := PointBX;
      PointBX := i;
    end;
  if PointAY > PointBY then
    begin
      i := PointAY;
      PointAY := PointBY;
      PointBY := i;
    end;
  Result := Rect(PointAX, PointAY, PointBX, PointBY);
end;

function TrimColorString(AString: string): string;
var aChar: Char;
    i: SmallInt;
begin
  Result := '';
  for i := 1 to length(AString) do
    begin
      {$IFDEF DBGLINE} DebugLn('Character '+ AString[i]); {$ENDIF}
      aChar := aString[i];
      if (aChar in ['0'..'9']) or (aChar in ['A'..'F']) or (aChar in ['a'..'f']) 
        then Result:=Result + aChar;
    end;
  {$IFDEF DBGLINE} DebugLn('Result: '+ Result); {$ENDIF}
end;     

function TryStrToColorLayouted(AString: string; ALayout: TColorLayout; out AColor: TColor): Boolean;
var i, aLength: SmallInt;
    A, R, G, B: Byte;
begin
  if ALayout <> eclSystemBGR then
    begin      
      if ALayout in [eclARGBColor, eclABGRColor, eclACMYColor, eclAYMCColor]
        then aLength := 8
        else aLength := 6;
      AString := TrimColorString(AString);
      {$IFDEF DBGLINE} DebugLn('AColorString ' + AString); {$ENDIF}
      AString:= RightStr(AString, aLength);
      i := length(AString);
      if i < aLength then
        for i := 0 to aLength - 1 - i do
          AString := '0' + AString;
      {$IFDEF DBGLINE}  DebugLn('AColorString ' + AString); {$ENDIF}
      try
        B := Hex2Dec(RightStr(AString, 2));
        if aLength = 6 then
          begin
            G := Hex2Dec(MidStr(AString, 3, 2));
            R := Hex2Dec(LeftStr(AString, 2));
            A := 0;
          end else
          begin
            G := Hex2Dec(MidStr(AString, 5, 2));
            R := Hex2Dec(MidStr(AString, 3, 2));
            A := Hex2Dec(LeftStr(AString, 2));
          end;
        case ALayout of
          eclRGBColor, eclARGBColor: AColor := ARGBToColor(A, R, G, B);   
          eclBGRColor, eclABGRColor: AColor := ARGBToColor(A, B, G, R);
          eclCMYColor, eclACMYColor: AColor := ARGBToColor(A, 255 - R, 255 - G, 255 - B);
          eclYMCColor, eclAYMCColor: AColor := ARGBToColor(A, 255 - B, 255 - G, 255 - R);
        end;
        Result := True;
      except
        Result := False;
      end;
    end else
    begin
      try
        AString := trim(AString);
        if not IdentToColor(AString, AColor) then
          begin
            AString := TrimColorString(AString);
            if AString <> '' 
              then AColor := TColor(Hex2Dec(AString))
              else AColor := clBlack;
          end;
        Result := True;
      except
        Result := False;
      end;
    end;                    
end;

operator = (ARect, BRect: TRect): Boolean;
begin
  Result := (ARect.Left = BRect.Left) and (ARect.Right = BRect.Right)
        and (ARect.Top = BRect.Top) and (ARect.Bottom = BRect.Bottom);
end;

{ TCanvasHelper }

procedure TCanvasHelper.DrawButtonBackground(ARect: TRect; AEnabled: Boolean);
begin                                                            
  if AEnabled 
    then DrawButtonBackground(ARect, eisEnabled)
    else DrawButtonBackground(ARect, eisDisabled);
end;

procedure TCanvasHelper.DrawButtonBackground(ARect: TRect; AItemState: TItemState);
begin                
 ThemeServices.DrawElement(Handle, 
    ThemeServices.GetElementDetails(caThemedItems[AItemState]), ARect, nil);   
end;

{$I ecdrawglyph.inc}

procedure TCanvasHelper.DrawPanelBackground(ARect: TRect; ABevelInner, 
  ABevelOuter: TBevelCut; ABevelWidth: Integer; AColor: TColor);
begin             
  DrawPanelBackground(ARect, ABevelInner, ABevelOuter, 0, ABevelWidth, clDefault, clDefault, AColor);
end;

procedure TCanvasHelper.DrawPanelBackground(ARect: TRect; ABevelInner, ABevelOuter: TBevelCut;
  ABevelSpace, ABevelWidth: Integer; AColor3DDark, AColor3DLight, AColor: TColor);
var bDefault3D: Boolean;
begin
  { both Canvas.Frame3D methods deflate Rectangle }
  { bvSpace draw the frame using Canvas.Brush.Color }
  bDefault3D := ((AColor3DDark = clDefault) and (AColor3DLight = clDefault));
  if not bDefault3D then
    begin
      AColor3DDark := GetColorResolvingDefault(AColor3DDark, clBtnShadow);
      AColor3DLight := GetColorResolvingDefault(AColor3DLight, clBtnHilight);   
    end;
  Brush.Color := AColor;
  if bDefault3D then
    begin
      if ABevelOuter <> bvNone then Frame3D(ARect, ABevelWidth, ABevelOuter)
    end else
    case ABevelOuter of
      bvLowered: Frame3D(ARect, AColor3DDark, AColor3DLight, ABevelWidth);
      bvRaised: Frame3D(ARect, AColor3DLight, AColor3DDark, ABevelWidth);     
      bvSpace: Frame3D(ARect, ABevelWidth, bvSpace);
    end;
  Frame3D(ARect, ABevelSpace, bvSpace);
  if bDefault3D then
    begin
      if ABevelInner <> bvNone then Frame3D(ARect, ABevelWidth, ABevelInner);
    end else
    case ABevelInner of
      bvLowered: Frame3D(ARect, AColor3DDark, AColor3DLight, ABevelWidth);
      bvRaised: Frame3D(ARect, AColor3DLight, AColor3DDark, ABevelWidth); 
      bvSpace: Frame3D(ARect, ABevelWidth, bvSpace);
    end; 
  FillRect(ARect);
end;   

function TCanvasHelper.GlyphExtent(AGlyphDesign: TGlyphDesign): TSize;
const cLargeGlyph: SmallInt = 16;
      cSmallGlyph: SmallInt = 8;      
begin
  case AGlyphDesign of
    egdNone: Result := Size(0, 0);
    egdArrowDec  .. egdArrowsUDHor: Result := Size(cSmallGlyph, cSmallGlyph);
    egdArrowsURDL_S: Result := Size(10, 10); 
    egdArrowsURDL_M: Result := Size(10, 10);
    egdArrowURDL_L: Result := Size(11, 11);
    egdArrowURDL_XL: Result := Size(12, 12);  
    egdArrowsUL_DR .. egdArrC_Max: Result := Size(cSmallGlyph, cSmallGlyph);  
    egdArrC_URDL: Result := Size(cLargeGlyph, cLargeGlyph);  
    egdPlayPause .. egdFramedList: Result := Size(cSmallGlyph, cSmallGlyph);  
    egdGrid .. egdWindowRound: Result := Size(cLargeGlyph, cLargeGlyph);  
  end;
end;

procedure TCanvasHelper.SetFontParams(AOrientation: Integer; ASize: Integer; AStyle: TFontStyles);
begin
  with Font do
    begin
      Orientation:=AOrientation;
      Size:=ASize;
      Style:=AStyle;
    end;
end;

procedure TCanvasHelper.SetRealGlyphColor(AGlyphColor: TColor; AState: TItemState);
begin
  AGlyphColor := GetColorResolvingDefault(AGlyphColor, clBtnText);
  if AState in [eisHighlighted, eisPushedHihlighted] 
    then AGlyphColor := GetMergedColor(clWhite, AGlyphColor, 0.27)
    else if AState in [eisDisabled, eisPushedDisabled] then
           AGlyphColor := GetMergedColor(Pixels[Width div 2, Height div 2], AGlyphColor, 0.67);     
  Brush.Color := AGlyphColor;
  Pen.Color := AGlyphColor;
end;      

{ TBitmapHelper }

procedure TBitmapHelper.SetProperties(AWidth, AHeight: Integer);
begin
  Canvas.Brush.Style := bsSolid;
  Canvas.Pen.Mode := pmCopy;
  Canvas.Pen.Style := psSolid;
  Transparent := True;
  TransparentMode := tmFixed;
  SetSize(AWidth, AHeight);
end;

procedure TBitmapHelper.TransparentClear;
var aTransparentColor: TColor;
begin
  aTransparentColor := TransparentColor;
  TransparentColor := clSilver;      
  Canvas.Brush.Color := aTransparentColor;
  Canvas.FillRect(0, 0, Width, Height);
  TransparentColor := aTransparentColor;
end;

{ TFontOptions }

constructor TFontOptions.Create(AParent: TControl);
begin
  Parent := AParent;
  FFontColor := clDefault;
  if assigned(AParent) then
    with AParent do
      begin
        FFontStyles := Font.Style;
        FFontSize := Font.Size;
      end
end;

procedure TFontOptions.RecalcRedraw;
begin
  if assigned(OnRecalcRedraw) then OnRecalcRedraw;
end;

procedure TFontOptions.Redraw;
begin
  if assigned(OnRedraw) then OnRedraw;
end;

procedure TFontOptions.SetFontColor(const AValue: TColor);
begin
  if FFontColor = AValue then exit;
  FFontColor := AValue;
  Redraw;
end;

procedure TFontOptions.SetFontSize(const AValue: SmallInt);
begin
  if FFontSize = AValue then exit;
  FFontSize := AValue;
  RecalcRedraw;
end;

procedure TFontOptions.SetFontStyles(const AValue: TFontStyles);
begin
  if FFontStyles = AValue then exit;
  FFontStyles := AValue;
  RecalcRedraw;
end;

{ TECBaseControl }

constructor TECBaseControl.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ControlStyle := ControlStyle + [csParentBackground, csReplicatable] - [csOpaque];
  FBevelInner := bvNone;
  FBevelOuter := bvRaised;
  FBevelWidth := 1;           
  Color := clDefault;   
  FColor3DDark:=clDefault;
  FColor3DLight:=clDefault;
end;   

procedure TECBaseControl.BeginUpdate;
begin
  inc(UpdateCount);
end; 

procedure TECBaseControl.EndUpdate(Recalculate: Boolean = True);
begin
  dec(UpdateCount);
  if UpdateCount <= 0 then
    begin
      UpdateCount := 0;
      if Recalculate
        then RecalcRedraw
        else Redraw;
    end;
end;   

function TECBaseControl.GetBorderWidth: Integer;
var i: Integer;
begin
  i := 0;
  if BevelOuter <> bvNone then inc(i);
  if BevelInner <> bvNone then inc(i);
  Result := BevelSpace + i*BevelWidth;
end;   

function TECBaseControl.HasCaption: Boolean;
begin
  Result := (Caption <> ''); 
end;       

procedure TECBaseControl.InvalidateNonUpdated;
begin
  if UpdateCount = 0 then Invalidate;
end;    
   
procedure TECBaseControl.OrientationChanged(AValue: TObjectOrientation);
begin
  RecalcRedraw;
end;   

procedure TECBaseControl.SetAutoSize(Value: Boolean);
begin
  inherited SetAutoSize(Value);
  if Value then
    begin
      InvalidatePreferredSize;
      AdjustSize;
    end; 
end;   

procedure TECBaseControl.StyleChanged(AValue: TObjectStyle);
begin
  Redraw;
end;     

procedure TECBaseControl.WMPaint(var Message: TLMPaint);
begin
  if (RedrawMode < ermFreeRedraw) and 
    ((Message.PaintStruct^.rcPaint.Left < FInvalidRect.Left) 
    or (Message.PaintStruct^.rcPaint.Top < FInvalidRect.Top)
    or (Message.PaintStruct^.rcPaint.Right > FInvalidRect.Right) 
    or (Message.PaintStruct^.rcPaint.Bottom > FInvalidRect.Bottom))
    then RedrawMode := ermFreeRedraw;  
  inherited WMPaint(Message);
end;     

{ TECBaseControl.Setters }

procedure TECBaseControl.SetBevelInner(const AValue: TBevelCut);
begin
  if FBevelInner = AValue then exit;
  FBevelInner := AValue;
  RecalcRedraw;
end;

procedure TECBaseControl.SetBevelOuter(const AValue: TBevelCut);
begin
  if FBevelOuter = AValue then exit;
  FBevelOuter := AValue;
  RecalcRedraw;
end;

procedure TECBaseControl.SetBevelSpace(const AValue: SmallInt);
begin
  if FBevelSpace = AValue then exit;
  FBevelSpace := AValue;
  RecalcRedraw;
end;

procedure TECBaseControl.SetBevelWidth(const AValue: SmallInt);
begin
  if FBevelWidth = AValue then exit;
  FBevelWidth := AValue;
  RecalcRedraw;
end;             

procedure TECBaseControl.SetColor3DDark(const AValue: TColor);
begin
  if FColor3DDark = AValue then exit;
  FColor3DDark := AValue;
  Redraw3DColorAreas;
end;

procedure TECBaseControl.SetColor3DLight(const AValue: TColor);
begin
  if FColor3DLight = AValue then exit;
  FColor3DLight := AValue;
  Redraw3DColorAreas; 
end;

procedure TECBaseControl.SetOrientation(const AValue: TObjectOrientation);
begin
  if FOrientation = AValue then exit;
  FOrientation := AValue;
  OrientationChanged(AValue);
end;       

procedure TECBaseControl.SetStyle(AValue: TObjectStyle);
begin
  if FStyle = AValue then exit;
  FStyle := AValue;
  StyleChanged(AValue);
end;       

{ TECCustomKnob }

constructor TECCustomKnob.Create(AParent: TECBaseControl);
begin
  inherited Create;
  Parent := AParent;  { don't change order ! }
  FBevelWidth := cDefBevelWidth;
  FColor := clDefault;
  FCursor := crHandPoint;
  FHeight := cDefKnobHeight;
  FTickMarkCount := cDefTickMarkCount;
  FTickMarkDesign := cDefTickDesign;
  FTickMarkSpacing := cDefTickMarkSpacing;
  FWidth := cDefKnobWidth;
  KnobDisabled := TBitmap.Create;
  KnobDisabled.SetProperties(self.FWidth, self.FHeight);
  KnobNormal := TBitmap.Create;
  KnobNormal.SetProperties(self.FWidth, self.FHeight);
  KnobHighlighted := TBitmap.Create;
  KnobHighlighted.SetProperties(self.FWidth, self.FHeight);
end;

destructor TECCustomKnob.Destroy;
begin
  FreeAndNil(KnobDisabled);
  FreeAndNil(KnobNormal);
  FreeAndNil(KnobHighlighted);
  inherited Destroy;
end;

procedure TECCustomKnob.BeginUpdate;
begin
  inc(UpdateCount);
end;     

procedure TECCustomKnob.DrawKnobs;
var aColor: TColor;
    aHeight, aWidth, h, i, j, k: Integer;
    aLight, aVert: Boolean;
    aRect: TRect;

  procedure DrawPanelBevel(AKnob: TBitmap);
  var aColor3DDark, aColor3DLight: TColor;
  begin
    with AKnob.Canvas do
      begin
        aColor3DDark := Parent.FColor3DDark;
        aColor3DLight := Parent.FColor3DLight;
        if (aColor3DDark = clDefault) and (aColor3DLight = clDefault) then
          begin
            Frame3D(aRect, BevelWidth, bvRaised);
          end else
          begin
            aColor3DDark := GetColorResolvingDefault(aColor3DDark, clBtnShadow);
            aColor3DLight := GetColorResolvingDefault(aColor3DLight, clBtnHilight);
            Frame3D(aRect, aColor3DLight, aColor3DDark, BevelWidth);
          end;
      end;
  end;

  function ModifyBrightness(AColor: TColor; ABrighness: Single): TColor;
  var r, g, b: Integer;
  begin
    GetRGBIntValues(AColor, r, g, b);
    b := round(b*ABrighness);
    if b > 255 then b := 255;
    g := round(g*ABrighness);
    if g > 255 then g := 255;
    r:=round(r*ABrighness);
    if r > 255 then r := 255;
    Result:=RGBToColor(r, g, b);
  end;

  procedure DrawTick(AKnob: TBitmap; AEnabled: Boolean = True);
  var { aLight scheme, aEnabled, Normal Line=0, or Lowered=1, Raised=2 }
      aBrightness: array [False..True, False..True, 0..2] of Single =
        (((2, 0.8, 1.2), (3, 0.67, 1.4)), ((0.67, 0.8, 1.2), (0.4, 0.67, 1.4)));
         { Dark Disabled, Dark Enabled,     Light Disabled,   Light Enabled }

    procedure DrawLine(x1, y1, x2: Integer; ABrightness: Single);
    var l: Integer;
    begin
      with AKnob.Canvas do
        begin
          if TickMarkStyle = etsSolid then
            begin  { etsSolid }
              if aVert then  { Horizontal tick (vertical slider) }
                for l := x1 to x2 - 1 do
                  Pixels[l, y1] := ModifyBrightness(Pixels[l, y1], ABrightness)
                else         { Vertical tick (horizontal slider) }
                for l := x1 to x2 - 1 do
                  Pixels[y1, l] := ModifyBrightness(Pixels[y1, l], ABrightness);
            end else
            begin  { etsDotted }
              if aVert then  { Horizontal tick (vertical slider) }
                for l := x1 to x2 - 1 do
                  begin
                    if ((l - x1) mod 3) = 0 then Pixels[l, y1] := ModifyBrightness(Pixels[l, y1], ABrightness);
                  end
                else         { Vertical tick (horizontal slider) }
                for l := x1 to x2 - 1 do
                  if ((l - x1) mod 3) = 0 then Pixels[y1, l] := ModifyBrightness(Pixels[y1, l], ABrightness);
            end;
        end;
    end;

  begin
    with AKnob.Canvas do
      begin
        case FTickMarkDesign of
          etdSimple: DrawLine(i, h, j, aBrightness[aLight, AEnabled, 0]);
          etdThick:
            begin
              if TickMarkStyle = etsSolid then
                begin 
                  DrawLine(i, h-1, j, aBrightness[aLight, AEnabled, 0]);
                  DrawLine(i, h, j, aBrightness[aLight, AEnabled, 0]);
                end else
                begin
                  DrawLine(i, h, j, aBrightness[aLight, AEnabled, 1]);
                  DrawLine(i + 1, h, j + 1, aBrightness[aLight, AEnabled, 1]);
                  DrawLine(i, h + 1, j, aBrightness[aLight, AEnabled, 1]);
                end;
            end;
          etd3DLowered:
            begin
              if TickMarkStyle = etsSolid then
                begin
                  DrawLine(i, h, j, aBrightness[aLight, AEnabled, 1]);
                  DrawLine(i, h + 1, j, aBrightness[aLight, AEnabled, 2]);
                end else
                begin
                  DrawLine(i, h, j, aBrightness[aLight, AEnabled, 1]);
                  DrawLine(i + 1, h + 1, j + 1, aBrightness[aLight, AEnabled, 2]);  
                end;
            end;
          etd3DRaised:
            begin
              if TickMarkStyle = etsSolid then
                begin 
                  DrawLine(i, h, j, aBrightness[aLight, AEnabled, 1]);
                  DrawLine(i, h - 1, j, aBrightness[aLight, AEnabled, 2]);
                end else
                begin
                   DrawLine(i, h, j, aBrightness[aLight, AEnabled, 1]);
                   DrawLine(i - 1, h - 1, j - 1, aBrightness[aLight, AEnabled, 2]); 
                end;
            end;
        end;
      end;
  end;

begin
  {$IFDEF DBGLINE} DebugLn('DrawKnobs'); {$ENDIF}
  if (UpdateCount = 0) and assigned(Parent) and assigned(Parent.Parent) then
    begin
      KnobHighlighted.BeginUpdate(True);
      KnobNormal.BeginUpdate(True);
      aRect := Rect(0, 0, Width, Height);
      case FStyle of
        eosButton:
          begin
            KnobDisabled.TransparentClear;
            KnobDisabled.Canvas.DrawButtonBackground(aRect, eisDisabled);
            KnobNormal.TransparentClear;      
            KnobNormal.Canvas.DrawButtonBackground(aRect, eisEnabled);
            KnobHighlighted.TransparentClear;
            KnobHighlighted.Canvas.DrawButtonBackground(aRect, eisHighlighted);
          end;    
        eosPanel:
          begin
            DrawPanelBevel(KnobDisabled);
            aRect := Rect(0, 0, Width, Height);
            DrawPanelBevel(KnobNormal);
            aRect := Rect(0, 0, Width, Height);
            DrawPanelBevel(KnobHighlighted);               
            i := BevelWidth;
            aRect := Rect(i, i, Width - i, Height - i);
            KnobNormal.Canvas.Brush.Color := GetColorResolvingDefault(Color, BackgroundColor);
            KnobNormal.Canvas.FillRect(aRect);
            KnobDisabled.Canvas.Brush.Color := ModifyBrightness(ColorToRGB(KnobNormal.Canvas.Brush.Color), 0.97);
            KnobDisabled.Canvas.FillRect(aRect);
            KnobHighlighted.Canvas.Brush.Color := ModifyBrightness(ColorToRGB(KnobNormal.Canvas.Brush.Color), 1.07);
            KnobHighlighted.Canvas.FillRect(aRect);
          end;
      end;
      aVert := (Parent.FOrientation = eooVertical);
      if aVert then  { Parent is Vertical }
        begin
          aHeight := FHeight;
          aWidth := FWidth;
        end else
        begin
          aWidth := FHeight;
          aHeight := FWidth;
        end;
      if (TickMarkCount > 0) and (aWidth >= 10) then
        begin
          aColor := ColorToRGB(clBtnText);   { detect Light or Dark scheme }
          aLight := ((aColor and $FF) + ((aColor shr 8) and $FF) + ((aColor shr 16) and $FF)) < 384;    
          i := BevelWidth + TickMarkSpacing;
          j := aWidth - i;
          if TickMarkStyle = etsDotted then
            begin
              k := (aWidth - 2*i) mod 3;
              if k < 2 then i := i + 1;
              if k = 0 then j := j + 1;
            end;
          if (TickMarkCount mod 2) = 0 then
            for k := 1 to (TickMarkCount div 2) do
              begin
                h := (aHeight div 2) - 2 + 3*k;
                DrawTick(KnobDisabled, False);
                DrawTick(KnobNormal);
                DrawTick(KnobHighlighted);
                h := (aHeight div 2) + 1 - 3*k;
                DrawTick(KnobDisabled, False);
                DrawTick(KnobNormal);
                DrawTick(KnobHighlighted);
              end
            else
            begin
              h := aHeight div 2;
              DrawTick(KnobDisabled, False);
              DrawTick(KnobNormal);
              DrawTick(KnobHighlighted);
              for k := 1 to (FTickMarkCount div 2) do
                begin
                  h := (aHeight div 2) + 3*k;
                  DrawTick(KnobDisabled, False);
                  DrawTick(KnobNormal);
                  DrawTick(KnobHighlighted);
                  h := (aHeight div 2) - 3*k;
                  DrawTick(KnobDisabled, False);
                  DrawTick(KnobNormal);
                  DrawTick(KnobHighlighted);
                end;
            end;
        end;
      KnobDisabled.Mask(KnobDisabled.TransparentColor);
      KnobHighlighted.Mask(KnobHighlighted.TransparentColor);
      KnobNormal.Mask(KnobNormal.TransparentColor);  
      KnobHighlighted.EndUpdate(False);
      KnobNormal.EndUpdate(False);
    end;
end;

procedure TECCustomKnob.EndUpdate;
begin
  dec(UpdateCount);
  if UpdateCount = 0 then DrawKnobs;
end;

procedure TECCustomKnob.RecalcRedraw;
begin
  if assigned(Parent) then Parent.RecalcRedraw;
end;

procedure TECCustomKnob.SetSize(AWidth, AHeight: Integer);
begin
  if Height <> AHeight then 
    begin
      FWidth := AWidth;
      Height := AHeight;
    end else
    Width := AWidth;  
  if AWidth = AHeight then DrawKnobs;
end;

{ TECCustomKnob.Setters }

procedure TECCustomKnob.SetBackgroundColor(AValue: TColor);
var aTransColor: TColor;
begin
  if FBackgroundColor = AValue then exit;
  FBackgroundColor := AValue;
  aTransColor := ColorToRGB(AValue) and $FAFCFE + $020301;
  KnobDisabled.TransparentColor := aTransColor;
  KnobHighlighted.TransparentColor := aTransColor;
  KnobNormal.TransparentColor := aTransColor;
  DrawKnobs;
end;             
        
procedure TECCustomKnob.SetBevelWidth(const AValue: SmallInt);
begin
  if FBevelWidth = AValue then exit;
  FBevelWidth := AValue;
  DrawKnobs;
  if assigned(Parent) then Parent.InvalidateCustomRect(False);
end;

procedure TECCustomKnob.SetColor(const AValue: TColor);
begin
  if FColor = AValue then exit;
  FColor := AValue;
  DrawKnobs;
  if assigned(Parent) then Parent.InvalidateCustomRect(False);
end;

procedure TECCustomKnob.SetCursor(const AValue: TCursor);
begin
  if FCursor = AValue then exit;
  FCursor := AValue;
  if assigned(Parent) then Parent.InvalidateCustomRect(False);
end;

procedure TECCustomKnob.SetHeight(const AValue: Integer);
var aWidth: Integer;
begin
  if (AValue < 1) or (FHeight = AValue) then exit;
  FHeight := AValue;
  aWidth := FWidth;
  KnobDisabled.SetSize(aWidth, AValue);
  KnobNormal.SetSize(aWidth, AValue);
  KnobHighlighted.SetSize(aWidth, AValue);
  DrawKnobs;
  RecalcRedraw;
end;

procedure TECCustomKnob.SetStyle(const AValue: TObjectStyle);
begin
  if FStyle = AValue then exit;
  FStyle := AValue;
  DrawKnobs;
  if assigned(Parent) then Parent.InvalidateCustomRect(False);
end;  

procedure TECCustomKnob.SetTickMarkCount(const AValue: SmallInt);
begin
  if FTickMarkCount = AValue then exit;
  FTickMarkCount := AValue;
  DrawKnobs;
  if assigned(Parent) then Parent.InvalidateCustomRect(False);
end;

procedure TECCustomKnob.SetTickMarkDesign(const AValue: TTickDesign);
begin
  if FTickMarkDesign = AValue then exit;
  FTickMarkDesign := AValue;
  DrawKnobs;
  if assigned(Parent) then Parent.InvalidateCustomRect(False);
end;

procedure TECCustomKnob.SetTickMarkSpacing(const AValue: SmallInt);
begin
  if FTickMarkSpacing = AValue then exit;
  FTickMarkSpacing := AValue;
  DrawKnobs;
  if assigned(Parent) then Parent.InvalidateCustomRect(False);
end;

procedure TECCustomKnob.SetTickMarkStyle(const AValue: TTickStyle);
begin
  if FTickMarkStyle = AValue then exit;
  FTickMarkStyle := AValue;
  DrawKnobs;
  if assigned(Parent) then Parent.InvalidateCustomRect(False);
end;

procedure TECCustomKnob.SetWidth(const AValue: Integer);
var aHeight: Integer;
begin
  if (AValue < 1) or (FWidth = AValue) then exit;
  FWidth := AValue;
  aHeight := FHeight;
  KnobDisabled.SetSize(AValue, aHeight);
  KnobNormal.SetSize(AValue, aHeight);
  KnobHighlighted.SetSize(AValue, aHeight);  
  DrawKnobs;
  RecalcRedraw;
end; 

{ TBaseScrollControl }

constructor TBaseScrollControl.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FAreaHeight:=-1;
  FAreaWidth:=-1;
  { scrollbars }
  SetDefaultScrollParams;
end;   

procedure TBaseScrollControl.BeginUpdate;
begin
  inc(UpdateCount);
end; 

procedure TBaseScrollControl.CreateWnd;
begin
  inherited CreateWnd;
  FClientAreaLeft:=ClientRect.Left;
  FClientAreaTop:=ClientRect.Top;
  FScrollInfoHor.fMask:=SIF_RANGE or SIF_PAGE or SIF_POS or SIF_DISABLENOSCROLL;  
  FScrollInfoHor.nMax:=0;
  FScrollInfoHor.nMin:=0;
  FScrollInfoHor.nPos:=0;
  FScrollInfoVert:=FScrollInfoHor;  
  FScrollInfoHor.nPage:=ClientWidth;
  FScrollInfoVert.nPage:=ClientHeight;
end;   

procedure TBaseScrollControl.EndUpdate;
begin
  dec(UpdateCount);
  if UpdateCount=0 then Invalidate;
end;
        
procedure TBaseScrollControl.InvalidateNonUpdated;
begin
  if UpdateCount=0 then Invalidate;
end;

procedure TBaseScrollControl.SetDefaultScrollParams;
begin
  FIncrementX:=1;
  FIncrementY:=1;
  FScrollBars:=ssAutoBoth;
end;     
           
procedure TBaseScrollControl.UpdateScrollBars(AValue: TScrollStyle);
begin
  case AValue of
  ssNone: 
    begin
      ShowScrollBar(Handle, SB_HORZ, False);
      ShowScrollBar(Handle, SB_VERT, False);
    end; 
  ssHorizontal: 
    begin
      ShowScrollBar(Handle, SB_HORZ, True);
      ShowScrollBar(Handle, SB_VERT, False);
    end;    
  ssVertical: 
    begin
      ShowScrollBar(Handle, SB_HORZ, False);
      ShowScrollBar(Handle, SB_VERT, True);
    end;  
  ssBoth: 
    begin
      ShowScrollBar(Handle, SB_HORZ, True);
      ShowScrollBar(Handle, SB_VERT, True);
    end;
  ssAutoHorizontal: 
    begin
      ShowScrollBar(Handle, SB_HORZ, FRequiredArea.X>ClientWidth);
      ShowScrollBar(Handle, SB_VERT, False);
    end;     
  ssAutoVertical: 
    begin
      ShowScrollBar(Handle, SB_HORZ, False);
      ShowScrollBar(Handle, SB_VERT, FRequiredArea.Y>ClientHeight);
    end;   
  ssAutoBoth: 
    begin
      ShowScrollBar(Handle, SB_HORZ, FRequiredArea.X>ClientWidth);
      ShowScrollBar(Handle, SB_VERT, FRequiredArea.Y>ClientHeight);
    end;
  end;
  UpdateScrollInfoHor;
  UpdateScrollInfoVert;
end;  

procedure TBaseScrollControl.UpdateScrollInfoHor;
begin
  FScrollInfoHor.nPos:=ClientAreaLeft;
  FScrollInfoHor.nMax:=FullAreaWidth;
  SetScrollInfo(Handle, SB_Horz, FScrollInfoHor, False);
end;

procedure TBaseScrollControl.UpdateScrollInfoVert;
begin
  FScrollInfoVert.nPos:=ClientAreaTop;
  FScrollInfoVert.nMax:=FullAreaHeight;
  SetScrollInfo(Handle, SB_Vert, FScrollInfoVert, False);
end;             

procedure TBaseScrollControl.WMHScroll(var Msg: TWMScroll);
begin
  { modify ClientArea and its setter will adjust scrollbar }
  {$IFDEF DEBUG} DebugLn('TBaseECScheme.WMHScroll'); {$ENDIF}
  case Msg.ScrollCode of
    SB_LINELEFT: ClientAreaLeft:=ClientAreaLeft-IncrementX;
    SB_LINERIGHT: ClientAreaLeft:=ClientAreaLeft+IncrementX;  
    SB_PAGELEFT: ClientAreaLeft:=ClientAreaLeft-ClientWidth;  
    SB_PAGERIGHT: ClientAreaLeft:=ClientAreaLeft+ClientWidth;
    SB_THUMBPOSITION, SB_THUMBTRACK: ClientAreaLeft:=Msg.Pos;
    SB_LEFT: ClientAreaLeft:=0;
    SB_RIGHT: ClientAreaLeft:=FullAreaWidth;
  end;
end;

procedure TBaseScrollControl.WMSize(var Message: TLMSize);
var aCW, aCH: Integer;
begin
  {$IFDEF DEBUG} DebugLn('WMSize W: ', inttostr(Width), ', H: ', inttostr(Height)); {$ENDIF}
  inherited WMSize(Message); 
  aCW:=ClientWidth;
  aCH:=ClientHeight;
  FScrollInfoHor.nPage:=aCW;
  FScrollInfoVert.nPage:=aCH;
  if (AreaWidth>-1) and (AreaWidth<aCW) then AreaWidth:=aCW;
  if (AreaHeight>-1) and (AreaHeight<aCH) then AreaHeight:=aCH;
  ClientAreaLeft:=Math.min(ClientAreaLeft, FRequiredArea.X-aCW);
  ClientAreaTop:=Math.min(ClientAreaTop, FRequiredArea.Y-aCH);
  UpdateScrollBars(ScrollBars);      
  Invalidate;
end;

procedure TBaseScrollControl.WMVScroll(var Msg: TWMScroll); 
begin
  {$IFDEF DEBUG} DebugLn('TBaseECScheme.WMVScroll'); {$ENDIF}
  case Msg.ScrollCode of
    SB_LINEUP: ClientAreaTop:=ClientAreaTop-IncrementY;
    SB_LINEDOWN: ClientAreaTop:=ClientAreaTop+IncrementY;
    SB_PAGEUP: ClientAreaTop:=ClientAreaTop-ClientHeight;
    SB_PAGEDOWN: ClientAreaTop:=ClientAreaTop+ClientHeight;
    SB_THUMBPOSITION, SB_THUMBTRACK: ClientAreaTop:=Msg.Pos;
    SB_TOP: ClientAreaTop:=0;
    SB_BOTTOM: ClientAreaTop:=FullAreaHeight;
  end;    
end;   

{ TBaseScrollControl.Setters }

function TBaseScrollControl.GetFullAreaHeight: Integer;
begin
  Result:=Math.max(ClientHeight, FRequiredArea.Y);
end;

function TBaseScrollControl.GetFullAreaWidth: Integer;
begin
  Result:=Math.max(ClientWidth, FRequiredArea.X);
end; 
         
procedure TBaseScrollControl.SetAreaHeight(AValue: Integer);
begin
  if AValue>=0 then AValue:=Math.max(AValue, ClientHeight);
  if (FAreaHeight=AValue) or ((AValue<0) and (FAreaHeight<0)) then exit;
  FAreaHeight:=AValue;
  UpdateRequiredAreaHeight;
  UpdateScrollBars(ScrollBars);
  Invalidate;
end;

procedure TBaseScrollControl.SetAreaWidth(AValue: Integer);
begin
  if AValue>=0 then AValue:=Math.max(AValue, ClientWidth);
  if (FAreaWidth=AValue) or ((AValue<0) and (FAreaWidth<0)) then exit;
  FAreaWidth:=AValue;
  UpdateRequiredAreaWidth;
  UpdateScrollBars(ScrollBars);
  Invalidate;
end; 

procedure TBaseScrollControl.SetClientAreaLeft(AValue: Integer);
begin
  if AValue<0 
    then AValue:=0
    else if AValue>(FullAreaWidth-ClientWidth) then AValue:=FullAreaWidth-ClientWidth;     
  if FClientAreaLeft=AValue then exit;
  FClientAreaLeft:=AValue;
  UpdateScrollBars(ScrollBars);
  InvalidateNonUpdated;  
end;
 
procedure TBaseScrollControl.SetClientAreaTop(AValue: Integer);
begin
  if AValue<0 
    then AValue:=0
    else if AValue>(FullAreaHeight-ClientHeight) then AValue:=FullAreaHeight-ClientHeight;
  if FClientAreaTop=AValue then exit;
  FClientAreaTop:=AValue;
  UpdateScrollBars(ScrollBars);
  InvalidateNonUpdated;  
end;
 

procedure TBaseScrollControl.SetScrollBars(AValue: TScrollStyle);
begin
  if FScrollBars=AValue then exit;
  FScrollBars:=AValue;
  UpdateScrollBars(AValue);
end;     

end.


