{%MainUnit ../controls.pp}

{******************************************************************************
                                  TWinControl
 ******************************************************************************

 *****************************************************************************
  This file is part of the Lazarus Component Library (LCL)

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

{$IFOPT C-}
// Uncomment for local trace
//  {$C+}
//  {$DEFINE ASSERT_IS_ON}
{$ENDIF}

{off $DEFINE VerboseAutoSizeCtrlData}
{off $DEFINE VerboseMouseBugfix}
{off $DEFINE VerboseCanAutoSize}
{off $DEFINE VerboseIntfSizing}
{off $DEFINE VerboseClientRectBugFix}
{$IFDEF VerboseClientRectBugFix}
const CheckClientRectName = 'LCLInterfaceRadioGroup';
{$ENDIF}

{off $DEFINE VerboseSizeMsg}
{off $DEFINE CHECK_POSITION}
{$IFDEF CHECK_POSITION}
const CheckPostionClassName = 'xxxTBreakPropertyDlg';
const CheckPostionName = 'FakeStatusBar';
const CheckPostionParentName = 'xxxEnvVarsPage';

function CheckPosition(AControl: TControl): boolean;
begin
  Result:=(CompareText(AControl.ClassName,CheckPostionClassName)=0)
           or (CompareText(AControl.Name,CheckPostionName)=0)
           or ((AControl.Parent<>nil)
               and (CompareText(AControl.Parent.Name,CheckPostionParentName)=0));
end;
{$ENDIF}

function IsNotAligned(AControl: TControl): boolean;
begin
  Result:=(AControl.Align=alNone)
      and (AControl.Anchors=[akLeft,akTop])
      and (AControl.AnchorSide[akLeft].Control=nil)
      and (AControl.AnchorSide[akTop].Control=nil);
end;

function IsNotAligned(AControl: TControl; ASide: TAnchorKind): boolean;
begin
  Result:=(AControl.Align=alNone);
  if not Result then Exit;
  if ASide in [akLeft, akRight] then
    Result:=(AControl.Anchors*[akLeft, akRight]=[akLeft])
        and (AControl.AnchorSide[akLeft].Control=nil)
  else
    Result:=(AControl.Anchors*[akTop, akBottom]=[akTop])
        and (AControl.AnchorSide[akBottom].Control=nil);
end;

{------------------------------------------------------------------------------
  Autosizing Helper classes
-------------------------------------------------------------------------------}
type
  TAutoSizeBoxOrientation = (asboHorizontal, asboVertical);

  PAutoSizeBox = ^TAutoSizeBox;

  { TAutoSizeBox
    A TAutoSizeBox is a node in a tree.
    A TAutoSizeBox can be a cell. Then it is a leaf in the tree and can have a
    Control.
    A TAutoSizeBox can be a row or column. Then it has only one Children array.
    A TAutoSizeBox can be a table. Then it has both Children arrays.
  }

  TAutoSizeBox = class
  public
    Control: TControl; // the Control of a leaf node
    MinimumSize: array[TAutoSizeBoxOrientation] of integer;
    MaximumSize: array[TAutoSizeBoxOrientation] of integer; // 0 means inifinte
    PreferredSize: array[TAutoSizeBoxOrientation] of integer;// without theme space
    LeftTop: array[TAutoSizeBoxOrientation] of integer;
    BorderLeftTop: array[TAutoSizeBoxOrientation] of integer;
    BorderRightBottom: array[TAutoSizeBoxOrientation] of integer;
    Parent: array[TAutoSizeBoxOrientation] of TAutoSizeBox;
    Index: array[TAutoSizeBoxOrientation] of Integer; // index in parent or grandparent
    ChildCount: array[TAutoSizeBoxOrientation] of Integer;
    Children: array[TAutoSizeBoxOrientation] of PAutoSizeBox;
    NewControlBounds: TRect;

    // for nodes
    destructor Destroy; override;
    procedure Clear;
    procedure SetControl(AControl: TControl);
    procedure ApplyChildSizingBorders(ChildSizing: TControlChildSizing);

    // for rows and columns
    procedure AllocateChildsArray(Orientation: TAutoSizeBoxOrientation;
                                  NewChildCount: Integer);
    procedure InitSums;
    procedure SumLine(Orientation: TAutoSizeBoxOrientation;
                      DoInit: boolean);
    procedure ResizeChildren(ChildSizing: TControlChildSizing;
                             Orientation: TAutoSizeBoxOrientation;
                             TargetSize: integer);
    procedure ComputeLeftTops(Orientation: TAutoSizeBoxOrientation);

    // for tables
    procedure AllocateTable(ColCount, RowCount: Integer);
    procedure SetTableControls(ListOfControls: TFPList;
                               ChildSizing: TControlChildSizing;
                               BiDiMode: TBiDiMode);
    procedure SumTable;
    procedure ResizeTable(ChildSizing: TControlChildSizing;
                          TargetWidth, TargetHeight: integer);
    procedure AlignToRight(TargetWidth: integer);
    procedure ComputeTableControlBounds(ChildSizing: TControlChildSizing;
                                        BiDiMode: TBiDiMode);
    function SetTableControlBounds(ChildSizing: TControlChildSizing
                                   ): boolean;// true if changed
    function AlignControlsInTable(ListOfControls: TFPList;
                                  ChildSizing: TControlChildSizing;
                                  BiDiMode: TBiDiMode;
                                  TargetWidth, TargetHeight: integer;
                                  Apply: boolean): boolean;// true if changed

    // debugging
    procedure WriteDebugReport(const Title: string);
  end;
  
  { TAutoSizeCtrlData
    This class is used by the auto size algorithm, to compute the preferred
      size of a control given the preferred sizes of its children.
    Hints about the algorithm:
      First it builds a graph of dependencies. That means, for every side
      (Left,Top,Right,Bottom) of each child control the anchor control and
      space is calculated. Anchor means here direct and indirect anchors.
      Indirect anchors are defined by the Align property.
      For example a control with Align=alTop is anchored left to the parent,
      right to the parent and top to either the parent or another alTop control.
      Then it searches for circles and other invalid combinations and repairs
      them.
    }
    
  TAutoSizeCtrlData = class;
  
  TAutoSizeSideDataState = (
    assdfInvalid,
    assdfComputing,
    assdfUncomputable,// e.g. if [akLeft,akRight]*Anchors = []
    assdfValid
    );
  TAutoSizeSideDistDirection = (
    assddLeftTop,
    assddRightBottom
    );

  TAutoSizeSideData = record
    CtrlData: TAutoSizeCtrlData;
    Side: TAnchorSideReference;
    Space: integer;
    Distance: array[TAutoSizeSideDistDirection] of integer;
    DistanceState: array[TAutoSizeSideDistDirection] of TAutoSizeSideDataState;
  end;
    
  TAutoSizeCtrlData = class
  private
    FChilds: TAvgLvlTree;// tree of TAutoSizeCtrlData
    function GetChildren(AControl: TControl): TAutoSizeCtrlData;
    procedure DoMoveNonAlignedChildren(Side: TAnchorKind;
                                   var MoveDiff: integer; FindMinimum: boolean);
    procedure SetupNonAlignedChildren(MoveNonAlignedChildrenLeft,
                                      MoveNonAlignedChildrenTop: boolean);
    procedure AlignChildren;
    procedure SetupSpace;
    function ComputePositions: boolean;// false if recomputation is needed (a property changed)
  public
    Control: TControl; // the Control of a leaf node
    WinControl: TWinControl;// the Control as TWinControl (else nil)
    ChildCount: integer;
    Visible: boolean;//= Control.IsControlVisible
    PreferredSize: array[TAutoSizeBoxOrientation] of integer;// without theme space
    Borders: array[TAnchorKind] of integer;
    AdjustedClientBorders: array[TAnchorKind] of integer;// the borderspace created by WinControl.AdjustClientRect
    Sides: array[TAnchorKind] of TAutoSizeSideData;
    BaseBounds: TRect;
    BaseParentClientSize: TSize;
    constructor Create(AControl: TControl; IsParent: boolean = true);
    destructor Destroy; override;
    procedure Clear;
    procedure ComputePreferredClientArea(MoveNonAlignedChildrenLeft,
                      MoveNonAlignedChildrenTop: boolean;
                      out MoveNonAlignedToLeft, MoveNonAlignedToTop,
                      PreferredClientWidth, PreferredClientHeight: integer);
    procedure FixControlProperties(Child: TControl);
    procedure ClearSides;
    procedure SetFixedLeftTop(ChildData: TAutoSizeCtrlData; Side: TAnchorKind;
                              NewLeftTop: integer);
    property Children[AControl: TControl]: TAutoSizeCtrlData read GetChildren; default;
    procedure WriteDebugReport(const Title, Prefix: string; OnlyVisible: boolean = true);
  end;

const
  SizeBoxOrthogonal: array[TAutoSizeBoxOrientation] of TAutoSizeBoxOrientation
    = (asboVertical,asboHorizontal);
  {AutoSizeSideDataStateNames: array[TAutoSizeSideDataState] of shortstring = (
    'assdfInvalid',
    'assdfComputing',
    'assdfUncomputable',
    'assdfValid'
    );}
  {$IFNDEF DisableChecks}
  AutoSizeSideDistDirectionNames: array[TAutoSizeSideDistDirection] of shortstring = (
    'assddLeftTop',
    'assddRightBottom'
    );
  {$ENDIF}

function CompareAutoSizeCtrlDatas(Data1, Data2: Pointer): integer;
var
  Control1: TControl;
  Control2: TControl;
begin
  Control1:=TAutoSizeCtrlData(Data1).Control;
  Control2:=TAutoSizeCtrlData(Data2).Control;
  if Pointer(Control1)>Pointer(Control2) then
    Result:=1
  else if Pointer(Control1)<Pointer(Control2) then
    Result:=-1
  else
    Result:=0;
end;

function CompareControlWithAutoSizeCtrlData(AControl, AData: Pointer): integer;
var
  Control1: TControl;
  Control2: TControl;
begin
  Control1:=TControl(AControl);
  Control2:=TAutoSizeCtrlData(AData).Control;
  if Pointer(Control1)>Pointer(Control2) then
    Result:=1
  else if Pointer(Control1)<Pointer(Control2) then
    Result:=-1
  else
    Result:=0;
end;


{ TAutoSizeCtrlData }

function TAutoSizeCtrlData.GetChildren(AControl: TControl): TAutoSizeCtrlData;
var
  AVLNode: TAvgLvlTreeNode;
begin
  if AControl=nil then exit(nil);
  if AControl=Control then RaiseGDBException('TAutoSizeCtrlData.GetChilds');
  if FChilds=nil then
    FChilds:=TAvgLvlTree.Create(@CompareAutoSizeCtrlDatas);
  AVLNode:=FChilds.FindKey(AControl,@CompareControlWithAutoSizeCtrlData);
  if AVLNode<>nil then
    Result:=TAutoSizeCtrlData(AVLNode.Data)
  else begin
    Result:=TAutoSizeCtrlData.Create(AControl,false);
    FChilds.Add(Result);
  end;
end;

procedure TAutoSizeCtrlData.AlignChildren;
var
  AlignList: TFPList;
  AlignBoundaryControls: array[TAnchorKind] of TAutoSizeCtrlData;

  procedure DoAlign(TheAlign: TAlign);
  var
    Child: TControl;
    i: Integer;
    ChildData: TAutoSizeCtrlData;
    a: TAnchorKind;
  begin
    WinControl.CreateControlAlignList(TheAlign, AlignList, nil);
    for i := 0 to AlignList.Count - 1 do
    begin
      Child := TControl(AlignList[i]);
      ChildData := Children[Child];
      //DebugLn('DoAlign ',DbgSName(Child),' ',dbgs(Child.Align));

      for a := Low(TAnchorKind) to High(TAnchorKind) do
        if a in AnchorAlign[TheAlign] then
        begin
          ChildData.Sides[a].CtrlData := AlignBoundaryControls[a];
          if (a in [akLeft, akTop]) = (ChildData.Sides[a].CtrlData = Self) then
            ChildData.Sides[a].Side := asrLeft
          else
            ChildData.Sides[a].Side := asrRight;
          //DebugLn('DoAlign ',DbgSName(Child),' ',dbgs(a),' ',dbgs(a,ChildData.Sides[a].Side));
        end;

      case TheAlign of
        alTop: AlignBoundaryControls[akTop] := ChildData;
        alBottom: AlignBoundaryControls[akBottom] := ChildData;
        alLeft: AlignBoundaryControls[akLeft] := ChildData;
        alRight: AlignBoundaryControls[akRight] := ChildData;
        alClient: ; // Delphi compatibility: multiple alClient controls overlap
      end;
      {DebugLn(['DoAlign AlignBoundaryControls:',
        ' Left=',DbgSName(AlignBoundaryControls[akLeft].Control),
        ' Top=',DbgSName(AlignBoundaryControls[akTop].Control),
        ' Right=',DbgSName(AlignBoundaryControls[akRight].Control),
        ' Bottom=',DbgSName(AlignBoundaryControls[akBottom].Control)  ]);}
    end;
  end;
  
var
  a: TAnchorKind;
begin
  if ChildCount = 0 then exit;
  AlignList := TFPList.Create;
  try
    // align and anchor child controls
    for a := Low(TAnchorKind) to High(TAnchorKind) do
      AlignBoundaryControls[a] := Self;
    DoAlign(alTop);
    DoAlign(alBottom);
    DoAlign(alLeft);
    DoAlign(alRight);
    DoAlign(alClient);
  finally
    AlignList.Free;
  end;
end;

procedure TAutoSizeCtrlData.SetupSpace;
var
  i: Integer;
  Child: TControl;
  ChildData: TAutoSizeCtrlData;
  a: TAnchorKind;
  SiblingData: TAutoSizeCtrlData;
  NewSpace: LongInt;
begin
  for i:=0 to ChildCount-1 do begin
    Child:=WinControl.Controls[i];
    ChildData:=Children[Child];
    for a:=Low(TAnchorKind) to High(TAnchorKind) do begin
      if ChildData.Sides[a].CtrlData=Self then begin
        // aligned or anchored to parent
        if a in [akLeft,akRight] then begin
          ChildData.Sides[a].Space:=Max(WinControl.ChildSizing.LeftRightSpacing,
                                        ChildData.Borders[a]);
        end else begin
          ChildData.Sides[a].Space:=Max(WinControl.ChildSizing.TopBottomSpacing,
                                        ChildData.Borders[a]);
        end;
        inc(ChildData.Sides[a].Space,AdjustedClientBorders[a]);
      end else if ChildData.Sides[a].CtrlData<>nil then begin
        SiblingData:=ChildData.Sides[a].CtrlData;
        // aligned or anchored to a sibling
        if a in [akLeft,akTop] then begin
          NewSpace:=ChildData.Borders[a];
          if ChildData.Sides[a].Side=asrRight then begin
            NewSpace:=Max(NewSpace,WinControl.ChildSizing.HorizontalSpacing);
            if a=akLeft then
              NewSpace:=Max(NewSpace,SiblingData.Borders[akRight])
            else
              NewSpace:=Max(NewSpace,SiblingData.Borders[akBottom]);
          end else if ChildData.Sides[a].Side=asrLeft then

          else if ChildData.Sides[a].Side=asrCenter then
            NewSpace:=0;
          ChildData.Sides[a].Space:=NewSpace;
        end else begin
          NewSpace:=ChildData.Borders[a];
          if ChildData.Sides[a].Side=asrTop then begin
            NewSpace:=Max(NewSpace,WinControl.ChildSizing.VerticalSpacing);
            if a=akRight then
              NewSpace:=Max(NewSpace,SiblingData.Borders[akLeft])
            else
              NewSpace:=Max(NewSpace,SiblingData.Borders[akTop]);
          end else if ChildData.Sides[a].Side=asrBottom then

          else if ChildData.Sides[a].Side=asrCenter then
            NewSpace:=0;
          ChildData.Sides[a].Space:=NewSpace;
        end;
      end else if a in Child.Anchors then begin
        // anchored to parent via BaseBounds
        if a in [akLeft,akTop] then begin
          ChildData.Sides[a].Side:=asrRight;
        end else begin
          ChildData.Sides[a].Side:=asrLeft;
        end;
        case a of
        akTop: ChildData.Sides[a].Space:=ChildData.BaseBounds.Top;
        akLeft: ChildData.Sides[a].Space:=ChildData.BaseBounds.Left;
        akRight: ChildData.Sides[a].Space:=
                   ChildData.BaseParentClientSize.cx-ChildData.BaseBounds.Right;
        akBottom: ChildData.Sides[a].Space:=
                  ChildData.BaseParentClientSize.cy-ChildData.BaseBounds.Bottom;
        end;
      end else begin
        // not anchored => use borderspacing
        if a in [akLeft,akTop] then
          ChildData.Sides[a].Side:=asrRight
        else
          ChildData.Sides[a].Side:=asrLeft;
        if a in [akLeft,akRight] then begin
          ChildData.Sides[a].Space:=
            Max(WinControl.ChildSizing.LeftRightSpacing,
                ChildData.Borders[a]);
        end else begin
          ChildData.Sides[a].Space:=
            Max(WinControl.ChildSizing.TopBottomSpacing,
                ChildData.Borders[a]);
        end;
        inc(ChildData.Sides[a].Space,AdjustedClientBorders[a]);
      end;
    end;
  end;
end;

function TAutoSizeCtrlData.ComputePositions: boolean;
type
  TComputeResult = (
    crSuccess,
    crCircle,
    crFixedCircled
    );

  function ComputePosition(ChildData: TAutoSizeCtrlData; Side: TAnchorKind;
    Direction: TAutoSizeSideDistDirection): TComputeResult;
  var
    OppositeSide: TAnchorKind;
    NewDist: LongInt;
    SiblingData: TAutoSizeCtrlData;
    NeededSiblingSides: TAnchors;
    a: TAnchorKind;
    Child: TControl;
    IsSideLeftTop, IsOutwards, IsParentInwards: boolean;
    CurAnchors: TAnchors;
    CurSize: LongInt;
    FoundSides: TAnchors;
    AddPreferredSize: Boolean;
  begin
    if ChildData.Sides[Side].DistanceState[Direction]
      in [assdfValid,assdfUncomputable]
    then
      exit(crSuccess); // already computed
    if ChildData.Sides[Side].DistanceState[Direction]=assdfComputing then begin
      {$IFNDEF DisableChecks}
      DebugLn(['TAutoSizeCtrlData.ComputePositions.ComputePosition CIRCLE detected ',DbgSName(ChildData.Control),' ',dbgs(Side),' ',AutoSizeSideDistDirectionNames[Direction]]);
      {$ENDIF}
      exit(crCircle); // there is a circle
    end;
    if ChildData.Sides[Side].DistanceState[Direction]<>assdfInvalid then
      raise Exception.Create('TAutoSizeCtrlData.ComputePositions.ComputePosition <>assdfInvalid');
      
    // mark as computing
    ChildData.Sides[Side].DistanceState[Direction]:=assdfComputing;
    OppositeSide:=OppositeAnchor[Side];
    
    // try to find good distances to the client area for this side
    Child:=ChildData.Control;
    CurAnchors:=Child.Anchors;
    if Child.Align in [alLeft,alTop,alRight,alBottom,alClient] then
      CurAnchors:=CurAnchors+AnchorAlign[Child.Align];
    if (Side in CurAnchors) then begin
      // this side is anchored
      SiblingData:=ChildData.Sides[Side].CtrlData;
      NewDist:=0;
      if (SiblingData=nil) or (SiblingData=Self) then begin
        // this side is anchored to parent
        // Note: SiblingData=nil can happen, if the reference control
        //       is not visible => use parent as default anchor
        case ChildData.Sides[Side].Side of
        asrLeft,asrRight: // asrTop=asrLeft,asrBottom=asrRight
          begin
            IsSideLeftTop:=(Side in [akLeft,akTop]);
            IsOutwards:=(Direction=assddLeftTop)=IsSideLeftTop;
            IsParentInwards:=(SiblingData=nil)
                          or ((ChildData.Sides[Side].Side=asrLeft)=IsSideLeftTop);
            if not IsParentInwards then begin
              // for example: left side is anchored to right side of parent
              //DebugLn(['ComputePosition ',DbgSName(Child),' Side=',dbgs(Side),' parent outside anchored, Direction=',AutoSizeSideDistDirectionNames[Direction],'  => assdfUncomputable']);
              ChildData.Sides[Side].DistanceState[Direction]:=assdfUncomputable;
            end else if IsOutwards then begin
              // for example: left side is anchored to left side of parent
              //              and left distance is needed
              ChildData.Sides[Side].Distance[Direction]:=ChildData.Sides[Side].Space;
              ChildData.Sides[Side].DistanceState[Direction]:=assdfValid;
            end else begin
              // for example: left side is anchored to left side of parent,
              //              right distance is needed
              AddPreferredSize:=true;
              if OppositeSide in CurAnchors then begin
                // compute opposite side first
                Result:=ComputePosition(ChildData,OppositeSide,Direction);
                if Result<>crSuccess then begin
                  {$IFNDEF DisableChecks}
                  DebugLn(['ComputePosition FAILED opposite side: ',DbgSName(Child),' ',dbgs(Side),' ',AutoSizeSideDistDirectionNames[Direction]]);
                  {$ENDIF}
                  exit;
                end;
                if ChildData.Sides[OppositeSide].DistanceState[Direction]<>assdfValid
                then begin
                  ChildData.Sides[Side].DistanceState[Direction]:=assdfUncomputable;
                  exit;
                end;
                NewDist:=ChildData.Sides[OppositeSide].Distance[Direction];
                if (ChildData.Sides[OppositeSide].CtrlData<>nil)
                and (ChildData.Sides[OppositeSide].CtrlData<>Self)
                then begin
                  // opposite side is anchored to a sibling
                  if ((OppositeSide in [akLeft,akTop])
                      and (ChildData.Sides[OppositeSide].Side<>asrRight))
                  or ((OppositeSide in [akRight,akBottom])
                      and (ChildData.Sides[OppositeSide].Side<>asrLeft))
                  then
                    AddPreferredSize:=false;
                end;
              end else begin
                NewDist:=ChildData.Sides[OppositeSide].Space;
              end;
              if AddPreferredSize then begin
                if Side in [akLeft,akRight] then
                  inc(NewDist,ChildData.PreferredSize[asboHorizontal])
                else
                  inc(NewDist,ChildData.PreferredSize[asboVertical]);
              end;
              ChildData.Sides[Side].Distance[Direction]:=NewDist;
              ChildData.Sides[Side].DistanceState[Direction]:=assdfValid;
            end;
          end;
        asrCenter:
          begin
            //DebugLn(['ComputePosition ',DbgSName(Child),' Side=',dbgs(Side),' parent anchored, Direction=',AutoSizeSideDistDirectionNames[Direction],'  => assdfUncomputable']);
            ChildData.Sides[Side].DistanceState[Direction]:=assdfUncomputable;
          end;
        else
          RaiseGDBException('');
        end;
      end else begin
        // this side is anchored to a sibling
        // first compute needed sides of sibling
        NeededSiblingSides:=[];
        case ChildData.Sides[Side].Side of
        asrLeft: // Note: asrLeft=asrTop
          if Side in [akLeft,akRight] then begin
            Include(NeededSiblingSides,akLeft);
          end else begin
            Include(NeededSiblingSides,akTop);
          end;
        asrRight: // Note: asrRight=asrBottom
          if Side in [akLeft,akRight] then begin
            Include(NeededSiblingSides,akRight);
          end else begin
            Include(NeededSiblingSides,akBottom);
          end;
        asrCenter:
          if Side in [akLeft,akRight] then begin
            NeededSiblingSides:=NeededSiblingSides+[akLeft,akRight];
          end else begin
            NeededSiblingSides:=NeededSiblingSides+[akTop,akBottom];
          end;
        end;
        FoundSides:=[];
        for a:=Low(TAnchorKind) to High(TAnchorKind) do begin
          if a in NeededSiblingSides then begin
            Result:=ComputePosition(SiblingData,a,Direction);
            if (Result=crCircle)
            and ((Child.Align in [alNone,alCustom])
              or (not (Side in AnchorAlign[Child.Align]))) then
            begin
              // there is a circle and it can be broken => break it
              {$IFNDEF DisableChecks}
              DebugLn(['ComputePosition breaking CIRCLE ',DbgSName(Child),' - ',DbgSName(SiblingData.Control),' ',dbgs(a),' ',AutoSizeSideDistDirectionNames[Direction]]);
              {$ENDIF}
              Child.Anchors:=Child.Anchors-[Side];
              Result:=crFixedCircled;
            end;
            if Result<>crSuccess then begin
              {$IFNDEF DisableChecks}
              DebugLn(['ComputePosition FAILED sibling dependency: ',DbgSName(Child),' - ',DbgSName(SiblingData.Control),' Side=',dbgs(Side),' a=',dbgs(a),' ',AutoSizeSideDistDirectionNames[Direction]]);
              {$ENDIF}
              exit;
            end;
            if SiblingData.Sides[a].DistanceState[Direction]=assdfValid then
              Include(FoundSides,a);
          end;
        end;
        if FoundSides=[] then begin
          ChildData.Sides[Side].DistanceState[Direction]:=assdfUncomputable;
          exit(crSuccess);
        end;

        // this side is anchored to a sibling and some needed sibling sides are valid
        case ChildData.Sides[Side].Side of
        asrLeft,asrRight: // asrLeft=asrTop,asrRight=asrBottom
          begin
            if ChildData.Sides[Side].Side=asrLeft then begin
              if Side in [akLeft,akRight] then
                NewDist:=SiblingData.Sides[akLeft].Distance[Direction]
              else
                NewDist:=SiblingData.Sides[akTop].Distance[Direction];
            end else begin
              if Side in [akLeft,akRight] then
                NewDist:=SiblingData.Sides[akRight].Distance[Direction]
              else
                NewDist:=SiblingData.Sides[akBottom].Distance[Direction];
            end;
            if (Direction=assddLeftTop)=(Side in [akLeft,akTop]) then
              inc(NewDist,ChildData.Sides[Side].Space)
            else
              dec(NewDist,ChildData.Sides[Side].Space);
            //DebugLn(['ComputePosition ',DbgSName(Child),' - ',DbgSName(SiblingData.Control),' Side=',dbgs(Side),' ',AutoSizeSideDistDirectionNames[Direction],' NewDist=',NewDist]);
          end;
        asrCenter:
          if Side in [akLeft,akRight] then begin
            if FoundSides=[akLeft,akRight] then begin
              NewDist:=((SiblingData.Sides[akLeft].Distance[Direction]
                        +SiblingData.Sides[akRight].Distance[Direction]) div 2);
            end else if (FoundSides=[akLeft]) then begin
              NewDist:=SiblingData.Sides[akLeft].Distance[Direction]
                     +(SiblingData.PreferredSize[asboHorizontal] div 2);
            end else begin
              NewDist:=SiblingData.Sides[akRight].Distance[Direction]
                     -(SiblingData.PreferredSize[asboHorizontal] div 2);
            end;
            //DebugLn(['ComputePosition BEFORE ',DbgSName(Child),' center to ',DbgSName(SiblingData.Control),' Side=',dbgs(Side),' FoundSides=',dbgs(FoundSides),' NewDist=',NewDist,' Direction=',AutoSizeSideDistDirectionNames[Direction],' PreferredSize=',ChildData.PreferredSize[asboHorizontal]]);
            dec(NewDist,ChildData.PreferredSize[asboHorizontal] div 2);
            // use at least the size of the child
            if (Side=akLeft)=(Direction=assddRightBottom) then
              NewDist:=Max(NewDist,ChildData.PreferredSize[asboHorizontal]);
            //DebugLn(['ComputePosition AFTER ',DbgSName(Child),' center to ',DbgSName(SiblingData.Control),' Side=',dbgs(Side),' FoundSides=',dbgs(FoundSides),' NewDist=',NewDist,' Direction=',AutoSizeSideDistDirectionNames[Direction],' PreferredSize=',ChildData.PreferredSize[asboHorizontal]]);
          end else begin
            if FoundSides=[akTop,akBottom] then begin
              NewDist:=((SiblingData.Sides[akTop].Distance[Direction]
                        +SiblingData.Sides[akBottom].Distance[Direction]) div 2);
            end else if (FoundSides=[akTop]) then begin
              NewDist:=SiblingData.Sides[akTop].Distance[Direction]
                     +(SiblingData.PreferredSize[asboVertical] div 2);
            end else begin
              NewDist:=SiblingData.Sides[akBottom].Distance[Direction]
                     -(SiblingData.PreferredSize[asboVertical] div 2);
            end;
            dec(NewDist,ChildData.PreferredSize[asboVertical] div 2);
            // use at least the size of the child
            if (Side=akTop)=(Direction=assddRightBottom) then
              NewDist:=Max(NewDist,ChildData.PreferredSize[asboVertical]);
          end;
        end;
        ChildData.Sides[Side].Distance[Direction]:=NewDist;
        ChildData.Sides[Side].DistanceState[Direction]:=assdfValid;

        if (OppositeSide in CurAnchors)
        and ((Direction=assddLeftTop) <> (Side in [akLeft,akTop])) then begin
          // the opposite side is anchored too
          // use the maximum of both anchors
          Result:=ComputePosition(ChildData,OppositeSide,Direction);
          if Result<>crSuccess then begin
            //DebugLn(['ComputePosition (side anchored) FAILED computing opposite side: ',DbgSName(Child),' - ',DbgSName(SiblingData.Control),' Side=',dbgs(Side),' ',AutoSizeSideDistDirectionNames[Direction]]);
            exit;
          end;
          case ChildData.Sides[OppositeSide].DistanceState[Direction] of
          assdfValid:
            begin
              // opposite side +- preferred size
              NewDist:=ChildData.Sides[OppositeSide].Distance[Direction];
              CurSize:=0;
              if ((OppositeSide in [akLeft,akTop])
                  and (ChildData.Sides[OppositeSide].Side=asrRight))
              or ((OppositeSide in [akRight,akBottom])
                  and (ChildData.Sides[OppositeSide].Side=asrLeft))
              then begin
                if Side in [akLeft,akRight] then
                  CurSize:=ChildData.PreferredSize[asboHorizontal]
                else
                  CurSize:=ChildData.PreferredSize[asboVertical];
              end;
              inc(NewDist,CurSize);
              // check if opposite side needs a bigger distance
              if ChildData.Sides[Side].Distance[Direction]<NewDist then
                ChildData.Sides[Side].Distance[Direction]:=NewDist;
            end;
          assdfUncomputable: ; // no problem, there is already a value
          else
            raise Exception.Create('TAutoSizeCtrlData.ComputePositions.ComputePosition assdfValid,assdfUncomputable');
          end;
        end;
      end;
    end else if (OppositeSide in CurAnchors)
    and ((Direction=assddLeftTop) <> (Side in [akLeft,akTop])) then begin
      // this side is not anchored, but the opposite is
      // e.g. control is anchored to the right
      // => compute the opposite side first
      Result:=ComputePosition(ChildData,OppositeSide,Direction);
      if Result<>crSuccess then begin
        //DebugLn(['ComputePosition (side not anchored) FAILED computing opposite side: ',DbgSName(Child),' - ',DbgSName(SiblingData.Control),' Side=',dbgs(Side),' ',AutoSizeSideDistDirectionNames[Direction]]);
        exit;
      end;
      case ChildData.Sides[OppositeSide].DistanceState[Direction] of
      assdfValid:
        begin
          // opposite side +- preferred size
          NewDist:=ChildData.Sides[OppositeSide].Distance[Direction];
          if Side in [akLeft,akRight] then
            CurSize:=ChildData.PreferredSize[asboHorizontal]
          else
            CurSize:=ChildData.PreferredSize[asboVertical];
          inc(NewDist,CurSize);
          ChildData.Sides[Side].Distance[Direction]:=NewDist;
          ChildData.Sides[Side].DistanceState[Direction]:=assdfValid;
        end;
      assdfUncomputable:
        ChildData.Sides[Side].DistanceState[Direction]:=assdfUncomputable;
      else
        raise Exception.Create('TAutoSizeCtrlData.ComputePositions.ComputePosition assdfValid,assdfUncomputable');
      end;
    end else begin
      // not anchored
      if (Direction=assddLeftTop) = (Side in [akLeft,akTop]) then begin
        NewDist:=ChildData.Sides[Side].Space;
        ChildData.Sides[Side].Distance[Direction]:=NewDist;
        ChildData.Sides[Side].DistanceState[Direction]:=assdfValid;
      end else begin
        //DebugLn(['ComputePosition ',DbgSName(Child),' Side=',dbgs(Side),' not anchored, Direction=',AutoSizeSideDistDirectionNames[Direction],'  => assdfUncomputable']);
        ChildData.Sides[Side].DistanceState[Direction]:=assdfUncomputable;
      end;
    end;
    if not (ChildData.Sides[Side].DistanceState[Direction]
            in [assdfUncomputable,assdfValid])
    then begin
      {$IFNDEF DisableChecks}
      DebugLn(['TAutoSizeCtrlData.ComputePositions.ComputePosition ',DbgSName(Child),' Side=',dbgs(Side),' Direction=',AutoSizeSideDistDirectionNames[Direction]]);
      {$ENDIF}
      raise Exception.Create('TAutoSizeCtrlData.ComputePositions.ComputePosition assdfValid,assdfUncomputable');
    end;
    Result:=crSuccess;
  end;

var
  i: Integer;
  Child: TControl;
  ChildData: TAutoSizeCtrlData;
  a: TAnchorKind;
begin
  Result:=false;
  // for every side try to find a good distance to the client area
  for i:=0 to ChildCount-1 do begin
    Child:=WinControl.Controls[i];
    ChildData:=Children[Child];
    if not ChildData.Visible then continue;
    for a:=Low(TAnchorKind) to High(TAnchorKind) do begin
      if ComputePosition(ChildData,a,assddLeftTop)<>crSuccess then begin
        {$IFNDEF DisableChecks}
        DebugLn(['TAutoSizeCtrlData.ComputePositions Failed to compute LeftTop ',DbgSName(Child),' ',dbgs(a)]);
        {$ENDIF}
        exit;
      end;
      if ComputePosition(ChildData,a,assddRightBottom)<>crSuccess then begin
        {$IFNDEF DisableChecks}
        DebugLn(['TAutoSizeCtrlData.ComputePositions Failed to compute RightBottom ',DbgSName(Child),' ',dbgs(a)]);
        {$ENDIF}
        exit;
      end;
    end;
  end;
  //WriteDebugReport('ComputePositons','  ');
  Result:=true;
end;

constructor TAutoSizeCtrlData.Create(AControl: TControl; IsParent: boolean);
const
  BigInteger = High(Integer) div 4;
var
  CurBorders: TRect;
  a: TAnchorKind;
  AdjustedClientRect: TRect;
  r: TRect;
begin
  //DebugLn(['TAutoSizeCtrlData.Create ',DbgSName(AControl)]);
  Control:=AControl;
  if Control is TWinControl then begin
    WinControl:=TWinControl(Control);
    ChildCount:=WinControl.ControlCount;
  end else
    ChildCount:=0;
  Visible:=Control.IsControlVisible;
  Control.BorderSpacing.GetSpaceAround(CurBorders);
  Borders[akLeft]:=CurBorders.Left;
  Borders[akTop]:=CurBorders.Top;
  Borders[akRight]:=CurBorders.Right;
  Borders[akBottom]:=CurBorders.Bottom;
  BaseBounds:=Control.BaseBounds;
  if (BaseBounds.Left=BaseBounds.Right)
  and (BaseBounds.Top=BaseBounds.Bottom) then
    BaseBounds:=Control.BoundsRect;
  BaseParentClientSize:=Control.BaseParentClientSize;

  if (WinControl<>nil) and IsParent then begin
    AdjustedClientRect:=Rect(0,0,BigInteger,BigInteger);
    WinControl.AdjustClientRect(AdjustedClientRect);
    AdjustedClientBorders[akLeft]:=AdjustedClientRect.Left;
    AdjustedClientBorders[akTop]:=AdjustedClientRect.Top;
    AdjustedClientBorders[akRight]:=BigInteger-AdjustedClientRect.Right;
    AdjustedClientBorders[akBottom]:=BigInteger-AdjustedClientRect.Bottom;
  end else begin
    for a:=low(TAnchorKind) to high(TAnchorKind) do
      AdjustedClientBorders[a]:=0;
    if (BaseParentClientSize.cx=0) and (BaseParentClientSize.cy=0) then begin
      r:=Control.Parent.GetLogicalClientRect;
      BaseParentClientSize.cx:=r.Right;
      BaseParentClientSize.cy:=r.Bottom;
    end;
  end;
end;

destructor TAutoSizeCtrlData.Destroy;
begin
  Clear;
  FreeAndNil(FChilds);
  inherited Destroy;
end;

procedure TAutoSizeCtrlData.Clear;
begin
  ClearSides;
  if FChilds<>nil then
    FChilds.FreeAndClear;
end;

procedure TAutoSizeCtrlData.DoMoveNonAlignedChildren(Side: TAnchorKind;
  var MoveDiff: integer; FindMinimum: boolean);
var
  i: Integer;
  Child: TControl;
  MoveDiffValid: Boolean;
  ChildData: TAutoSizeCtrlData;
  AddSpace: LongInt;
  Position: Integer;
begin
  MoveDiffValid:=false;
  for i:=0 to ChildCount-1 do begin
    Child:=WinControl.Controls[i];
    ChildData:=Children[Child];
    if not ChildData.Visible then continue;
    if IsNotAligned(Child, Side) then begin
      // this is a non aligned control
      //DebugLn(['TAutoSizeCtrlData.DoMoveNonAlignedChilds Child=',DbgSName(Child),' Side=',dbgs(Side)]);
      if FindMinimum then begin
        AddSpace:=Child.BorderSpacing.GetSideSpace(Side);
        if Side=akLeft then
          AddSpace:=Max(AddSpace,WinControl.ChildSizing.LeftRightSpacing)
        else
          AddSpace:=Max(AddSpace,WinControl.ChildSizing.TopBottomSpacing);
        Position:=Child.GetSidePosition(Side)
                  -AddSpace
                  -AdjustedClientBorders[Side];
        if (not MoveDiffValid) or (MoveDiff>Position) then
        begin
          MoveDiff:=Position;
          MoveDiffValid:=true;
        end;
      end else begin
        SetFixedLeftTop(ChildData,Side,Child.GetSidePosition(Side)-MoveDiff);
      end;
    end else if (Child.Align=alCustom)
    and (Side in AnchorAlign[alCustom]) then begin
      if FindMinimum then begin
        // no auto move
      end else begin
        // don't move alCustom, but use them for bounds computation
        SetFixedLeftTop(ChildData,Side,Child.GetSidePosition(Side));
      end;
    end;
  end;
end;

procedure TAutoSizeCtrlData.SetupNonAlignedChildren(MoveNonAlignedChildrenLeft,
  MoveNonAlignedChildrenTop: boolean);
var
  ChildSizing: TControlChildSizing;
  Box: TAutoSizeBox;
  y: Integer;
  RowBox: TAutoSizeBox;
  x: Integer;
  ControlBox: TAutoSizeBox;
  Child: TControl;
  NewBounds: TRect;
  ChildData: TAutoSizeCtrlData;
  MoveDiff: Integer;
  AlignList: TFPList;
  r: TRect;
  i: Integer;
begin
  if ChildCount=0 then exit;
  if WinControl.ChildSizing.Layout=cclNone then begin
    // move the non-aligned controls (i.e. not aligned or fixed anchored)
    // Find the leftmost and topmost of those controls
    MoveDiff:=0;
    DoMoveNonAlignedChildren(akLeft,MoveDiff,true);
    //DebugLn(['TAutoSizeCtrlData.ComputePreferredClientArea akLeft MoveDiff=',MoveDiff]);
    if not MoveNonAlignedChildrenLeft then MoveDiff:=0;
    DoMoveNonAlignedChildren(akLeft,MoveDiff,false);
    MoveDiff:=0;
    DoMoveNonAlignedChildren(akTop,MoveDiff,true);
    //DebugLn(['TAutoSizeCtrlData.ComputePreferredClientArea akTop MoveDiff=',MoveDiff]);
    if not MoveNonAlignedChildrenTop then MoveDiff:=0;
    DoMoveNonAlignedChildren(akTop,MoveDiff,false);
  end else begin
    // there is an automatic layout for non aligned children
    // use the layout engine, but with static values
    ChildSizing:=nil;
    Box:=nil;
    AlignList:=TFPList.Create;
    try
      for i:=0 to WinControl.ControlCount-1 do begin
        Child:=WinControl.Controls[i];
        if Child.IsControlVisible and IsNotAligned(Child) then
          AlignList.Add(Child);
      end;
      if AlignList.Count=0 then exit;
      ChildSizing:=TControlChildSizing.Create(nil);
      Box:=TAutoSizeBox.Create;
      // copy current ChildSizing ...
      ChildSizing.Assign(WinControl.ChildSizing);
      // ... and change it to static layout (i.e. independent of parent size)
      ChildSizing.ShrinkHorizontal:=crsAnchorAligning;
      ChildSizing.EnlargeHorizontal:=crsAnchorAligning;
      ChildSizing.ShrinkVertical:=crsAnchorAligning;
      ChildSizing.EnlargeVertical:=crsAnchorAligning;
      // compute static layout
      r:=WinControl.GetLogicalClientRect;
      Box.AlignControlsInTable(AlignList,ChildSizing,WinControl.BiDiMode,
                               r.Right,r.Bottom,false);
      //Box.WriteDebugReport('TAutoSizeCtrlData.SetupNonAlignedChilds');
      // transfer the coords of the layout
      for y:=0 to Box.ChildCount[asboVertical]-1 do begin
        RowBox:=Box.Children[asboVertical][y];
        for x:=0 to RowBox.ChildCount[asboHorizontal]-1 do begin
          ControlBox:=RowBox.Children[asboHorizontal][x];
          Child:=ControlBox.Control;
          if Child=nil then continue;
          NewBounds:=ControlBox.NewControlBounds;
          //DebugLn(['TAutoSizeCtrlData.SetupNonAlignedChilds ',DbgSName(Child),' ',dbgs(NewBounds)]);
          ChildData:=Children[Child];
          // set left
          SetFixedLeftTop(ChildData,akLeft,NewBounds.Left);
          // set width
          ChildData.PreferredSize[asboHorizontal]:=NewBounds.Right-NewBounds.Left;
          // set top
          SetFixedLeftTop(ChildData,akTop,NewBounds.Top);
          // set height
          ChildData.PreferredSize[asboVertical]:=NewBounds.Bottom-NewBounds.Top;
        end;
      end;
    finally
      ChildSizing.Free;
      Box.Free;
      AlignList.Free;
    end;
  end;
end;

procedure TAutoSizeCtrlData.ComputePreferredClientArea(
  MoveNonAlignedChildrenLeft, MoveNonAlignedChildrenTop: boolean; out
  MoveNonAlignedToLeft, MoveNonAlignedToTop, PreferredClientWidth,
  PreferredClientHeight: integer);
{ if MoveNonAlignedChilds=true then all non-aligned children will be moved in
  parallel, so that at least one child is positioned left most and one child
  is positioned top most.

  Type of controls:
  1. layout: the left and top side of the control has only designed position
      and Parent.ChildSizing.Layout <> cclNone.
      That means: Align=alNone, Anchors=[akLeft,akTop],
      AnchorSide[akLeft/akTop].Control=nil, Parent.ChildSizing.Layout <> cclNone
  2. non-aligned: the left+top side of the control has only a designed position.
      That means: Align=alNone, akLeft is set, AnchorSide[akLeft].Control=nil
      and Parent.ChildSizing.Layout=cclNone
      Same for akTop.
  3. Aligned: Align<>alNone
      These are put consecutively into the remaining space.
      BorderSpacing and AdjustClientRect defines the space.
      The aligned sides automatically set the Anchors and the AnchorSide.Control
      to nil.
      alLeft,alRight,alTop,alBottom have one free side, which can be anchored.
  4. centered: akLeft and akRight are not set
  5. one side anchored: akLeft is set and akRight is not
                     OR akRight is set and akLeft is not
  5.1 anchored to a side (asrLeft,asrRight)
  5.2 anchored to a center (asrCenter)
  6. both sides anchored: akLeft and akRight not
     Note: asrCenter is not allowed here

  Circles and invalid combinations will be automatically fixed.
}

  procedure InitPreferredSizes;
  var
    i: Integer;
    Child: TControl;
    ChildData: TAutoSizeCtrlData;
    CurAnchors: TAnchors;
    CurPreferredWidth: integer;
    CurPreferredHeight: integer;
    UseCurrentWidth: Boolean;
    UseCurrentHeight: Boolean;
    NewWidth: LongInt;
    NewHeight: LongInt;
  begin
    for i:=0 to ChildCount-1 do begin
      Child:=WinControl.Controls[i];
      ChildData:=Children[Child];
      if ChildData.Visible then begin
        CurAnchors:=Child.Anchors;
        if Child.Align in [alLeft,alRight,alTop,alBottom,alClient] then
          CurAnchors:=CurAnchors+AnchorAlign[Child.Align];
        // check if the current Width and/or Height of the Child control can be
        // used. For example: The current Width can be used, if it is independent
        // of the parent's width.
        UseCurrentWidth:=true;
        if Child.AutoSize
        or ([akLeft,akRight]*CurAnchors=[akLeft,akRight]) then
          UseCurrentWidth:=false;
        UseCurrentHeight:=true;
        if Child.AutoSize
        or ([akTop,akBottom]*CurAnchors=[akTop,akBottom]) then
          UseCurrentHeight:=false;

        if (not UseCurrentWidth) or (not UseCurrentHeight) then
          Child.GetPreferredSize(CurPreferredWidth,CurPreferredHeight,true,true);

        //if Child.Name='OtherInfoGroupBox' then debugln(['InitPreferredSizes ',DbgSName(Child),' Bounds=',dbgs(Child.BoundsRect),' Anchors=',dbgs(Child.Anchors),' CurAnchors=',dbgs(CurAnchors),' UseW=',UseCurrentWidth,' UseH=',UseCurrentHeight,' Pref=',CurPreferredWidth,'x',CurPreferredHeight]);
        if UseCurrentWidth then
          NewWidth:=Child.Width
        else if (CurPreferredWidth>0)
        or ((CurPreferredWidth=0) and (csAutoSize0x0 in Child.ControlStyle)) then
          NewWidth:=CurPreferredWidth
        else
          NewWidth:=Max(1,Child.GetDefaultWidth);
        NewWidth:=Child.Constraints.MinMaxWidth(NewWidth);

        if UseCurrentHeight then
          NewHeight:=Child.Height
        else if (CurPreferredHeight>0)
        or ((CurPreferredHeight=0) and (csAutoSize0x0 in Child.ControlStyle)) then
          NewHeight:=CurPreferredHeight
        else
          NewHeight:=Max(1,Child.GetDefaultHeight);
        NewHeight:=Child.Constraints.MinMaxHeight(NewHeight);
      end else begin
        NewWidth:=0;
        NewHeight:=0;
      end;
        
      ChildData.PreferredSize[asboHorizontal]:=NewWidth;
      ChildData.PreferredSize[asboVertical]:=NewHeight;
      //DebugLn(['InitPreferredSizes Child=',DbgSName(Child),' PrefSize=',NewWidth,',',NewHeight]);
    end;
  end;
  
  procedure GetSideAnchor(ChildData: TAutoSizeCtrlData; a: TAnchorKind);
  var
    Child: TControl;
    ReferenceControl: TControl;
    ReferenceSide: TAnchorSideReference;
    Position: Integer;
  begin
    Child:=ChildData.Control;
    Child.AnchorSide[a].GetSidePosition(ReferenceControl,ReferenceSide,Position);
    //DebugLn(['GetSideAnchor Child=',DbgSName(Child),', a=',dbgs(a),' ReferenceControl=',DbgSName(ReferenceControl),' ReferenceSide=',dbgs(a,ReferenceSide)]);
    if ReferenceControl=nil then begin
      // invalid anchor
      // => anchor to parent
      ChildData.Sides[a].CtrlData:=Self;
      if a in [akLeft,akTop] then
        ChildData.Sides[a].Side:=asrLeft
      else
        ChildData.Sides[a].Side:=asrRight;
      exit;
    end;
    if ReferenceControl=Control then
      ChildData.Sides[a].CtrlData:=Self
    else if (ReferenceControl<>nil) and (ReferenceControl.Parent=Control) then
      ChildData.Sides[a].CtrlData:=Children[ReferenceControl];
    ChildData.Sides[a].Side:=ReferenceSide;
    //if ChildData.Sides[a].CtrlData<>nil then DebugLn(['GetSideAnchor Child=',DbgSName(Child),', a=',dbgs(a),' ReferenceControl=',DbgSName(ChildData.Sides[a].CtrlData.Control),' ReferenceSide=',dbgs(a,ChildData.Sides[a].Side)]);
  end;

var
  i: Integer;
  VisibleCount: Integer;
  Child: TControl;
  ChildData: TAutoSizeCtrlData;
  a: TAnchorKind;
  CurNeededClientWH: Integer;
begin
  PreferredClientWidth:=0;
  PreferredClientHeight:=0;
  MoveNonAlignedToLeft:=0;
  MoveNonAlignedToTop:=0;

  if ChildCount=0 then exit;

  // fix control properties
  // check if there are visible children
  VisibleCount:=0;
  for i:=0 to ChildCount-1 do begin
    Child:=WinControl.Controls[i];
    FixControlProperties(Child);
    ChildData:=Children[Child];
    if ChildData.Visible then
      inc(VisibleCount);
  end;
  //DebugLn(['TAutoSizeCtrlData.ComputePreferredClientArea ',DbgSName(Control),' VisibleCount=',VisibleCount]);
  if VisibleCount=0 then begin
    // nothing to do
    exit;
  end;
  
  InitPreferredSizes;

  repeat
    // init dependencies
    for i:=0 to ChildCount-1 do begin
      Child:=WinControl.Controls[i];
      ChildData:=Children[Child];
      ChildData.ClearSides;
      if not ChildData.Visible then continue;
      for a:=Low(TAnchorKind) to High(TAnchorKind) do begin
        ChildData.Sides[a].Side:=asrLeft;
        if (Child.Align in [alLeft,alRight,alTop,alBottom,alClient])
        and (a in AnchorAlign[Child.Align]) then begin
          // this is an aligned side
          // => the dependencies will be setup later in AlignChilds
        end else if a in Child.Anchors then begin
          // this is an anchored side
          GetSideAnchor(ChildData,a);
        end else begin
          // this is a dangling side
        end;
      end;
    end;
    //WriteDebugReport('anchored','');

    SetupNonAlignedChildren(MoveNonAlignedChildrenLeft,MoveNonAlignedChildrenTop);
    //WriteDebugReport('nonaligned','');
    // setup the dependencies for Aligned controls
    AlignChildren;
    //WriteDebugReport('aligned','');

    // setup space for dependencies
    SetupSpace;
    {$IFDEF VerboseAutoSizeCtrlData}
    WriteDebugReport('Space completed','');
    {$ENDIF}

    // calculate the needed positions for all children
  until ComputePositions;
  
  {$IFDEF VerboseAutoSizeCtrlData}
  if WinControl.ClassName='TScrollBox' then
    WriteDebugReport('Positions completed','');
  {$ENDIF}
  
  // compute needed clientwidth/clientheight
  for i:=0 to ChildCount-1 do begin
    Child:=WinControl.Controls[i];
    ChildData:=Children[Child];
    if not ChildData.Visible then continue;
    for a:=Low(TAnchorKind) to High(TAnchorKind) do begin
      if (ChildData.Sides[a].DistanceState[assddLeftTop]=assdfValid)
      and (ChildData.Sides[a].DistanceState[assddRightBottom]=assdfValid)
      then begin
        CurNeededClientWH:=ChildData.Sides[a].Distance[assddLeftTop]
                          +ChildData.Sides[a].Distance[assddRightBottom];
        if a in [akLeft,akRight] then begin
          if PreferredClientWidth<CurNeededClientWH then
            PreferredClientWidth:=CurNeededClientWH;
        end else begin
          if PreferredClientHeight<CurNeededClientWH then
            PreferredClientHeight:=CurNeededClientWH;
        end;
      end;
    end;
  end;

  // compute needed MoveNonAlignedToLeft,MoveNonAlignedToTop
  if MoveNonAlignedChildrenLeft or MoveNonAlignedChildrenTop then
  begin
    MoveNonAlignedToLeft:=Low(integer);
    MoveNonAlignedToTop:=Low(integer);
    for i:=0 to ChildCount-1 do
    begin
      Child:=WinControl.Controls[i];
      ChildData:=Children[Child];
      if not Child.IsControlVisible then continue;
      if IsNotAligned(Child, akLeft) then
      begin
        if MoveNonAlignedChildrenLeft
        and (ChildData.Sides[akLeft].DistanceState[assddLeftTop]=assdfValid) then
          MoveNonAlignedToLeft:=Max(MoveNonAlignedToLeft,
                     Child.Left-ChildData.Sides[akLeft].Distance[assddLeftTop]);
        { the below is only correct, if PreferredClientWidth is realized.
        if (ChildData.Sides[akLeft].DistanceState[assddRightBottom]=assdfValid) then
          MoveNonAlignedToLeft:=Min(MoveNonAlignedToLeft,
            Child.Left
            -(PreferredClientWidth
              -ChildData.Sides[akLeft].Distance[assddRightBottom]));}
      end;
      if IsNotAligned(Child, akTop) then
      begin
        if MoveNonAlignedChildrenTop
        and (ChildData.Sides[akTop].DistanceState[assddLeftTop]=assdfValid) then
          MoveNonAlignedToTop:=Max(MoveNonAlignedToTop,
                       Child.Top-ChildData.Sides[akTop].Distance[assddLeftTop]);
        { the below is only correct, if PreferredClientWidth is realized.
        if (ChildData.Sides[akTop].DistanceState[assddRightBottom]=assdfValid) then
          MoveNonAlignedToTop:=Min(MoveNonAlignedToTop,
            Child.Top
            -(PreferredClientHeight
              -ChildData.Sides[akTop].Distance[assddRightBottom]));}
      end;
    end;
    if MoveNonAlignedToLeft=Low(integer) then MoveNonAlignedToLeft:=0;
    if MoveNonAlignedToTop=Low(integer) then MoveNonAlignedToTop:=0;
  end;

  {$IFDEF VerboseAutoSizeCtrlData}
  //if WinControl.ClassName='TProjectVersionInfoOptionsFrame' then
  DebugLn(['TAutoSizeCtrlData.ComputePreferredClientArea END ',DbgSName(Control),' PreferredClientWidth/height=',PreferredClientWidth,',',PreferredClientHeight]);
  {$ENDIF}
end;

procedure TAutoSizeCtrlData.FixControlProperties(Child: TControl);
var
  a: TAnchorKind;
begin
  // check that all anchor-controls are siblings or the parent
  for a:=Low(TAnchorKind) to High(TAnchorKind) do begin
    if Child.AnchorSide[a].Control=nil then continue;
    if Child.AnchorSide[a].Control=Control then continue;
    if (Child.AnchorSide[a].Control=Child)
    or (Child.AnchorSide[a].Control.Parent<>Control) then begin
      {$IFNDEF DisableChecks}
      DebugLn(['TAutoSizeCtrlData.FixControlProperties ',DbgSName(Child),' a=',dbgs(a),' old=',DbgSName(Child.AnchorSide[a].Control),' new=nil']);
      {$ENDIF}
      Child.AnchorSide[a].Control:=nil;
    end;
  end;

  if Child.Align in [alLeft,alRight,alTop,alBottom,alClient] then begin
    // the aligned sides must be anchored
    Child.Anchors:=Child.Anchors+AnchorAlign[Child.Align];
    for a:=Low(TAnchorKind) to High(TAnchorKind) do begin
      if a in AnchorAlign[Child.Align] then begin
        // the aligned sides can not be anchored to a control
        {$IFNDEF DisableChecks}
        if Child.AnchorSide[a].Control<>nil then
          DebugLn(['TAutoSizeCtrlData.FixControlProperties aligned sides can not be anchored ',DbgSName(Child),' a=',dbgs(a),' old=',DbgSName(Child.AnchorSide[a].Control),' new=nil']);
        {$ENDIF}
        Child.AnchorSide[a].Control:=nil;
      end;
      if Child.AnchorSide[a].Side=asrCenter then begin
        // an aligned control can not be centered
        {$IFNDEF DisableChecks}
        DebugLn(['TAutoSizeCtrlData.FixControlProperties aligned control can not be centered ',DbgSName(Child),' a=',dbgs(a)]);
        {$ENDIF}
        Child.AnchorSide[a].Side:=asrLeft;
        if not (a in AnchorAlign[Child.Align]) then begin
          Child.Anchors:=Child.Anchors-[a];
          Child.AnchorSide[a].Control:=nil;
        end;
      end;
    end;
  end else begin
    for a:=Low(TAnchorKind) to High(TAnchorKind) do begin
      if (a in Child.Anchors)
      and (Child.AnchorSide[a].Side=asrCenter) then begin
        if Child.AnchorSide[a].Control<>nil then begin
          // the control should be centered relative to another control
          if a in [akLeft,akTop] then begin
            // un-anchor the other side
            {$IFNDEF DisableChecks}
            if OppositeAnchor[a] in Child.Anchors then
              DebugLn(['TAutoSizeCtrlData.FixControlProperties control is center-anchored -> unanchor opposite side: ',DbgSName(Child),' a=',dbgs(a)]);
            {$ENDIF}
            Child.Anchors:=Child.Anchors-[OppositeAnchor[a]];
            Child.AnchorSide[OppositeAnchor[a]].Control:=nil;
          end else begin
            // the centering was setup via the right,bottom
            // => normalize it to center via the Left,Top
            DebugLn(['TAutoSizeCtrlData.FixControlProperties control is center-anchored -> normalize it to use Left,Top instead of Bottom,Right: ',DbgSName(Child),' a=',dbgs(a)]);
            Child.AnchorSide[OppositeAnchor[a]].Control:=Child.AnchorSide[a].Control;
            Child.AnchorSide[OppositeAnchor[a]].Side:=asrCenter;
            Child.AnchorSide[a].Control:=nil;
            Child.AnchorSide[a].Side:=asrLeft;
            Child.Anchors:=Child.Anchors+[OppositeAnchor[a]]-[a];
          end;
        end else begin
          // the asrCenter is not active => ok
        end;
      end;
    end;
  end;
end;

procedure TAutoSizeCtrlData.ClearSides;
var
  a: TAnchorKind;
  d: TAutoSizeSideDistDirection;
begin
  for a:=Low(TAnchorKind) to High(TAnchorKind) do begin
    FillChar(Sides[a],SizeOf(TAutoSizeSideData),0);
    for d:=Low(TAutoSizeSideDistDirection) to High(TAutoSizeSideDistDirection) do
      Sides[a].DistanceState[d]:=assdfInvalid;
  end;
end;

procedure TAutoSizeCtrlData.SetFixedLeftTop(ChildData: TAutoSizeCtrlData;
  Side: TAnchorKind; NewLeftTop: integer);
begin
  ChildData.Sides[Side].CtrlData:=Self;
  ChildData.Sides[Side].Side:=asrLeft;
  ChildData.Sides[Side].Space:=NewLeftTop;
  ChildData.Sides[Side].Distance[assddLeftTop]:=NewLeftTop;
  ChildData.Sides[Side].DistanceState[assddLeftTop]:=assdfValid;
end;

procedure TAutoSizeCtrlData.WriteDebugReport(const Title, Prefix: string;
  OnlyVisible: boolean);

  function GetDistance(a: TAnchorKind; d: TAutoSizeSideDistDirection): string;
  begin
    case Sides[a].DistanceState[d] of
    assdfInvalid: Result:='invalid';
    assdfComputing: Result:='computing';
    assdfUncomputable: Result:='uncomputable';
    assdfValid: Result:=dbgs(Sides[a].Distance[d]);
    else Result:='???';
    end;
  end;
  
  function GetSideControl(a: TAnchorKind): string;
  begin
    if Sides[a].CtrlData<>nil then
      Result:=DbgSName(Sides[a].CtrlData.Control)
    else
      Result:='nil';
  end;

var
  a: TAnchorKind;
  i: Integer;
begin
  if Title<>'' then
    DebugLn([Prefix,'TAutoSizeCtrlData.WriteDebugReport ',Title]);
  DebugLn([Prefix,'  Control=',DbgSName(Control),' ChildCount=',ChildCount,' Visible=',Visible,' Anchors=',dbgs(Control.Anchors),' Align=',dbgs(Control.Align)]);
  Debugln([Prefix,'  PreferredSize=',PreferredSize[asboHorizontal],',',PreferredSize[asboVertical]]);
  DebugLn([Prefix,'  Borders=l=',Borders[akLeft],',t=',Borders[akTop],',r=',Borders[akRight],',b=',Borders[akBottom]]);
  DebugLn([Prefix,'  AdjustedClientBorders=l=',AdjustedClientBorders[akLeft],',t=',AdjustedClientBorders[akTop],',r=',AdjustedClientBorders[akRight],',b=',AdjustedClientBorders[akBottom]]);
  for a:=Low(TAnchorKind) to High(TAnchorKind) do begin
    DebugLn([Prefix,'  Side ',dbgs(a),' Control=',GetSideControl(a),
      ' RefSide=',dbgs(a,Sides[a].Side),
      ' Space=',Sides[a].Space,
      ' DistLT=',GetDistance(a,assddLeftTop),
      ' DistBR=',GetDistance(a,assddRightBottom)]);
  end;
  for i:=0 to ChildCount-1 do
    if WinControl.Controls[i].Visible or (not OnlyVisible) then
      Children[WinControl.Controls[i]].WriteDebugReport('',Prefix+dbgs(i)+': ');
end;

{ TAutoSizeBox }

procedure TAutoSizeBox.SetControl(AControl: TControl);
var
  Border: TRect;
  AutoSize0x0: Boolean;
  IsPrefWidthValid: Boolean;
  IsPrefHeightValid: Boolean;
begin
  Control:=AControl;
  MinimumSize[asboHorizontal]:=Control.Constraints.EffectiveMinWidth;
  MinimumSize[asboVertical]:=Control.Constraints.EffectiveMinHeight;
  MaximumSize[asboHorizontal]:=Control.Constraints.EffectiveMaxWidth;
  MaximumSize[asboVertical]:=Control.Constraints.EffectiveMaxHeight;
  Control.GetPreferredSize(PreferredSize[asboHorizontal],
                           PreferredSize[asboVertical],
                           true, // without constraints
                           true  // with theme space
                           );
  //DebugLn(['TAutoSizeBox.SetControl ',DbgSName(Control),' ',PreferredSize[asboHorizontal]]);
  AutoSize0x0:=csAutoSize0x0 in Control.ControlStyle;
  IsPrefWidthValid:=(PreferredSize[asboHorizontal]>0)
                    or (AutoSize0x0 and (PreferredSize[asboHorizontal]=0));
  IsPrefHeightValid:=(PreferredSize[asboVertical]>0)
                    or (AutoSize0x0 and (PreferredSize[asboVertical]=0));

  // apply constraints
  if IsPrefWidthValid then
    PreferredSize[asboHorizontal]:=
      Control.Constraints.MinMaxWidth(PreferredSize[asboHorizontal]);
  if IsPrefHeightValid then
    PreferredSize[asboVertical]:=
      Control.Constraints.MinMaxHeight(PreferredSize[asboVertical]);

  if IsPrefWidthValid
  and (Control.AutoSize or (Control.BorderSpacing.CellAlignHorizontal<>ccaFill))
  then begin
    // the control.width is fixed to its preferred width
    MaximumSize[asboHorizontal]:=PreferredSize[asboHorizontal];
  end;
  if IsPrefHeightValid
  and (Control.AutoSize or (Control.BorderSpacing.CellAlignVertical<>ccaFill))
  then begin
    // the control.height is fixed to its preferred height
    MaximumSize[asboVertical]:=PreferredSize[asboVertical];
  end;

  // if no preferred size is valid use the class defaults
  if not IsPrefWidthValid then
    PreferredSize[asboHorizontal]:=
      Control.Constraints.MinMaxWidth(Control.GetControlClassDefaultSize.CX);
  if not IsPrefHeightValid then
    PreferredSize[asboVertical]:=
      Control.Constraints.MinMaxHeight(Control.GetControlClassDefaultSize.CX);

  //DebugLn(['TAutoSizeBox.SetControl ',DbgSName(Control),' ',PreferredSize[asboHorizontal]]);
  Control.BorderSpacing.GetSpaceAround(Border);
  BorderLeftTop[asboHorizontal]:=Border.Left;
  BorderLeftTop[asboVertical]:=Border.Top;
  BorderRightBottom[asboHorizontal]:=Border.Right;
  BorderRightBottom[asboVertical]:=Border.Bottom;
end;

procedure TAutoSizeBox.AllocateChildsArray(Orientation: TAutoSizeBoxOrientation;
  NewChildCount: Integer);
var
  Size: Integer;
begin
  Size:=NewChildCount*SizeOf(Pointer);
  ReallocMem(Children[Orientation],Size);
  if Size>0 then
    FillChar(Children[Orientation][0],Size,0);
  ChildCount[Orientation]:=NewChildCount;
end;

procedure TAutoSizeBox.AllocateTable(ColCount, RowCount: Integer);
{ This creates a ColCount x RowCount number of cells,
  and a Row of Columns and a Column of Rows.
  
  +-++-++-++-+  +----------+
  | || || || |  |          |
  | || || || |  +----------+
  | || || || |  +----------+
  | || || || |  |          |
  | || || || |  +----------+
  | || || || |  +----------+
  | || || || |  |          |
  +-++-++-++-+  +----------+

}
var
  x, y: Integer;
  RowBox: TAutoSizeBox;
  ColBox: TAutoSizeBox;
  CellBox: TAutoSizeBox;
begin
  AllocateChildsArray(asboHorizontal,ColCount);
  AllocateChildsArray(asboVertical,RowCount);
  // create columns
  for x:=0 to ColCount-1 do begin
    ColBox:=TAutoSizeBox.Create;
    Children[asboHorizontal][x]:=ColBox;
    ColBox.AllocateChildsArray(asboVertical,RowCount);
    ColBox.Parent[asboHorizontal]:=Self;
    ColBox.Index[asboHorizontal]:=x;
    ColBox.Index[asboVertical]:=-1;
  end;
  // create rows
  for y:=0 to RowCount-1 do begin
    RowBox:=TAutoSizeBox.Create;
    Children[asboVertical][y]:=RowBox;
    RowBox.AllocateChildsArray(asboHorizontal,ColCount);
    RowBox.Parent[asboVertical]:=Self;
    RowBox.Index[asboHorizontal]:=-1;
    RowBox.Index[asboVertical]:=y;
  end;
  // create cells
  for y:=0 to RowCount-1 do begin
    RowBox:=Children[asboVertical][y];
    for x:=0 to ColCount-1 do begin
      ColBox:=Children[asboHorizontal][x];
      CellBox:=TAutoSizeBox.Create;
      RowBox.Children[asboHorizontal][x]:=CellBox;
      ColBox.Children[asboVertical][y]:=CellBox;
      CellBox.Parent[asboHorizontal]:=RowBox;
      CellBox.Parent[asboVertical]:=ColBox;
      CellBox.Index[asboHorizontal]:=x;
      CellBox.Index[asboVertical]:=y;
    end;
  end;
end;

procedure TAutoSizeBox.SetTableControls(ListOfControls: TFPList;
  ChildSizing: TControlChildSizing; BiDiMode: TBiDiMode);
var
  i: Integer;
  Row: LongInt;
  Col: LongInt;
  ChildControl: TControl;
  ChildBox: TAutoSizeBox;
  RowCount: LongInt;
  ColCount: Integer;
  LineMax: LongInt;
begin
  // allocate table
  case ChildSizing.Layout of
  cclLeftToRightThenTopToBottom:
    begin
      ColCount:=Max(1,Min(ChildSizing.ControlsPerLine,ListOfControls.Count));
      RowCount:=((ListOfControls.Count-1) div ColCount)+1;
    end;
  cclTopToBottomThenLeftToRight:
    begin
      RowCount:=Max(1,min(ChildSizing.ControlsPerLine,ListOfControls.Count));
      ColCount:=((ListOfControls.Count-1) div RowCount)+1;
    end;
  else
    raise Exception.Create('TAutoSizeBox.SetTableControls TODO');
  end;
  AllocateTable(ColCount,RowCount);

  // set controls
  for i:=0 to ListOfControls.Count-1 do begin
    ChildControl:=TControl(ListOfControls[i]);
    case ChildSizing.Layout of
    cclLeftToRightThenTopToBottom:
      begin
        LineMax:=ChildCount[asboHorizontal];
        Row:=i div LineMax;
        Col:=i mod LineMax;
        if (BiDiMode=bdRightToLeft) then
          Col:=LineMax-Col-1;
        ChildBox:=Children[asboHorizontal][Col].Children[asboVertical][Row];
        ChildBox.SetControl(ChildControl);
        ChildBox.ApplyChildsizingBorders(ChildSizing);
      end;
    cclTopToBottomThenLeftToRight:
      begin
        LineMax:=ChildCount[asboVertical];
        Col:=i div LineMax;
        Row:=i mod LineMax;
        if (BiDiMode=bdRightToLeft) then
          Col:=LineMax-Col-1;
        ChildBox:=Children[asboVertical][Row].Children[asboHorizontal][Col];
        ChildBox.SetControl(ChildControl);
        ChildBox.ApplyChildsizingBorders(ChildSizing);
      end;
    end;
  end;
end;

procedure TAutoSizeBox.ApplyChildSizingBorders(ChildSizing: TControlChildSizing);
var
  MinBorder: LongInt;
begin
  // left border
  if (Parent[asboHorizontal]=nil) or (Index[asboHorizontal]=0) then
    MinBorder:=ChildSizing.LeftRightSpacing
  else
    MinBorder:=ChildSizing.HorizontalSpacing;
  BorderLeftTop[asboHorizontal]:=Max(BorderLeftTop[asboHorizontal],MinBorder);
  
  // right border
  if (Parent[asboHorizontal]=nil)
  or (Index[asboHorizontal]=Parent[asboHorizontal].ChildCount[asboHorizontal]-1)
  then
    MinBorder:=ChildSizing.LeftRightSpacing
  else
    MinBorder:=ChildSizing.HorizontalSpacing;
  BorderRightBottom[asboHorizontal]:=Max(BorderRightBottom[asboHorizontal],
                                         MinBorder);

  // top border
  if (Parent[asboVertical]=nil) or (Index[asboVertical]=0) then
    MinBorder:=ChildSizing.TopBottomSpacing
  else
    MinBorder:=ChildSizing.VerticalSpacing;
  BorderLeftTop[asboVertical]:=Max(BorderLeftTop[asboVertical],MinBorder);

  // bottom border
  if (Parent[asboVertical]=nil)
  or (Index[asboVertical]=Parent[asboVertical].ChildCount[asboVertical]-1)
  then
    MinBorder:=ChildSizing.TopBottomSpacing
  else
    MinBorder:=ChildSizing.VerticalSpacing;
  BorderRightBottom[asboVertical]:=Max(BorderRightBottom[asboVertical],
                                       MinBorder);
end;

procedure TAutoSizeBox.InitSums;

  procedure Init(o: TAutoSizeBoxOrientation);
  var
    FirstChild: TAutoSizeBox;
  begin
    if ChildCount[o]>0 then begin
      FirstChild:=Children[o][0];
      MaximumSize[o]:=FirstChild.MaximumSize[o];
      MinimumSize[o]:=FirstChild.MinimumSize[o];
      PreferredSize[o]:=FirstChild.PreferredSize[o];
      BorderLeftTop[o]:=FirstChild.BorderLeftTop[o];
      BorderRightBottom[o]:=FirstChild.BorderRightBottom[o];
    end else begin
      MaximumSize[o]:=0;
      MinimumSize[o]:=0;
      PreferredSize[o]:=0;
      BorderLeftTop[o]:=0;
      BorderRightBottom[o]:=0;
    end;
  end;

begin
  Init(asboHorizontal);
  Init(asboVertical);
end;

procedure TAutoSizeBox.SumLine(Orientation: TAutoSizeBoxOrientation;
  DoInit: boolean);
// total orientated minimum is the sum of all minimums plus borders
// total orientated maximum is the sum of all maximums plus borders
// total orientated preferred is the sum of all preferred plus borders
// total orthogonal minimum is the maximum of all minimums
// total orthogonal maximum is the minimum of all maximums
// total orthogonal preferred is the maximum of all preferred
var
  i: Integer;
  Orthogonal: TAutoSizeBoxOrientation;
  CurChild: TAutoSizeBox;
  CurBorder: integer;
  LastChild: TAutoSizeBox;
begin
  if DoInit then InitSums;
  Orthogonal:=SizeBoxOrthogonal[Orientation];
  if ChildCount[Orientation]>0 then begin
    for i:=0 to ChildCount[Orientation]-1 do begin
      CurChild:=Children[Orientation][i];
      
      // add border in Orientation
      CurBorder:=CurChild.BorderLeftTop[Orientation];
      if i>0 then
        CurBorder:=Max(Children[Orientation][i-1].BorderRightBottom[Orientation],
                       CurBorder);
      if MaximumSize[Orientation]>0 then begin
        inc(MaximumSize[Orientation],CurBorder);
      end;
      inc(MinimumSize[Orientation],CurBorder);
      inc(PreferredSize[Orientation],CurBorder);
      // add item size in Orientation
      if MaximumSize[Orientation]>0 then begin
        if CurChild.MaximumSize[Orientation]>0 then
          inc(MaximumSize[Orientation],CurChild.MaximumSize[Orientation])
        else
          MaximumSize[Orientation]:=0;
      end;
      inc(MinimumSize[Orientation],CurChild.MinimumSize[Orientation]);
      inc(PreferredSize[Orientation],CurChild.PreferredSize[Orientation]);
      
      // maximize in Orthogonal
      if MaximumSize[Orthogonal]>0 then begin
        if CurChild.MaximumSize[Orthogonal]>0 then
          MaximumSize[Orthogonal]:=Max(MaximumSize[Orthogonal],
                                       CurChild.MaximumSize[Orthogonal])
        else
          MaximumSize[Orthogonal]:=0;
      end;
      MinimumSize[Orthogonal]:=Max(MinimumSize[Orthogonal],
                                   CurChild.MinimumSize[Orthogonal]);
      PreferredSize[Orthogonal]:=Max(PreferredSize[Orthogonal],
                                     CurChild.PreferredSize[Orthogonal]);
      BorderLeftTop[Orthogonal]:=Max(BorderLeftTop[Orthogonal],
                                     CurChild.BorderLeftTop[Orthogonal]);
      BorderRightBottom[Orthogonal]:=Max(BorderRightBottom[Orthogonal],
                                        CurChild.BorderRightBottom[Orthogonal]);
    end;

    // last border
    LastChild:=Children[Orientation][ChildCount[Orientation]-1];
    BorderRightBottom[Orientation]:=LastChild.BorderRightBottom[Orientation];
  end;
end;

procedure TAutoSizeBox.SumTable;
var
  x: Integer;
  ColBox: TAutoSizeBox;
  y: Integer;
  RowBox: TAutoSizeBox;
begin
  // sum items in rows
  for y:=0 to ChildCount[asboVertical]-1 do begin
    RowBox:=Children[asboVertical][y];
    RowBox.SumLine(asboHorizontal,true);
  end;
  // sum items in columns
  for x:=0 to ChildCount[asboHorizontal]-1 do begin
    ColBox:=Children[asboHorizontal][x];
    ColBox.SumLine(asboVertical,true);
  end;
  // sum rows
  SumLine(asboVertical,true);
  // sum columns
  SumLine(asboHorizontal,false);
end;

procedure TAutoSizeBox.ComputeLeftTops(Orientation: TAutoSizeBoxOrientation);
var
  i: Integer;
  Child: TAutoSizeBox;
  CurLeftTop: Integer;
  s: LongInt;
begin
  CurLeftTop:=0;
  for i:=0 to ChildCount[Orientation]-1 do begin
    Child:=Children[Orientation][i];
    if i=0 then
      inc(CurLeftTop,Child.BorderLeftTop[Orientation]);
    Child.LeftTop[Orientation]:=CurLeftTop;
    inc(CurLeftTop,Child.PreferredSize[Orientation]);
    s:=Child.BorderRightBottom[Orientation];
    if i<ChildCount[Orientation]-1 then
      s:=Max(s,Children[Orientation][i+1].BorderLeftTop[Orientation]);
    inc(CurLeftTop,s);
  end;
end;

procedure TAutoSizeBox.ResizeChildren(ChildSizing: TControlChildSizing;
  Orientation: TAutoSizeBoxOrientation; TargetSize: integer);
type
  TResizeFactor = record
    Scale: double;
    Offset: integer;
  end;
var
  EnlargeStyle: TChildControlResizeStyle;
  ShrinkStyle: TChildControlResizeStyle;
  CurSize: LongInt;

  function GetChildTotalSize: integer;
  // computes the total preferred size of all children of this Orientation
  var
    i: Integer;
    Child: TAutoSizeBox;
    s: LongInt;
  begin
    Result:=0;
    for i:=0 to ChildCount[Orientation]-1 do begin
      Child:=Children[Orientation][i];
      if i=0 then
        inc(Result,Child.BorderLeftTop[Orientation]);
      if Child.PreferredSize[Orientation]<1 then
        Child.PreferredSize[Orientation]:=1;
      inc(Result,Child.PreferredSize[Orientation]);
      s:=Child.BorderRightBottom[Orientation];
      if i<ChildCount[Orientation]-1 then
        s:=Max(s,Children[Orientation][i+1].BorderLeftTop[Orientation]);
      inc(Result,s);
    end;
  end;
  
  procedure GetChildMaxResize(out Factor: TResizeFactor;
    out ResizeableCount: integer);
  // returns the number of children/gaps, that can grow (ResizeableCount)
  // and the maximum factor, by which the children/gaps can grow (TResizeFactor)
  var
    i: Integer;
    CurScale: Double;
    CurOffset: LongInt;
    Child: TAutoSizeBox;
  begin
    Factor.Scale:=0;
    Factor.Offset:=0;
    ResizeableCount:=0;
    case EnlargeStyle of
    
    crsAnchorAligning:
      exit; // no resizing

    crsScaleChilds,crsHomogenousChildResize:
    
      for i:=0 to ChildCount[Orientation]-1 do begin
        Child:=Children[Orientation][i];
        if (Child.MaximumSize[Orientation]>0)
        and (Child.PreferredSize[Orientation]>=Child.MaximumSize[Orientation])
        then begin
          // this child can not be further enlarged
          continue;
        end;
        inc(ResizeableCount);

        case EnlargeStyle of

        crsScaleChilds, crsHomogenousChildResize:
          begin
            if Child.MaximumSize[Orientation]=0 then begin
              CurScale:=double(TargetSize);
              CurOffset:=TargetSize;
            end else begin
              CurScale:=double(Child.MaximumSize[Orientation])
                          /Child.PreferredSize[Orientation];
              CurOffset:=Child.MaximumSize[Orientation]
                         -Child.PreferredSize[Orientation];
            end;
            if (Factor.Offset=0) or (Factor.Offset>CurOffset) then begin
              Factor.Scale:=CurScale;
              Factor.Offset:=CurOffset;
            end;
          end;

        end;
      end;
      
    crsHomogenousSpaceResize:
      if ChildCount[Orientation]>0 then begin
        Factor.Scale:=double(TargetSize);
        Factor.Offset:=TargetSize;
        ResizeableCount:=ChildCount[Orientation]+1;
      end;

    else
      raise Exception.Create('TAutoSizeBox.ResizeChilds');
      
    end;
  end;
  
  procedure EnlargeChilds(const Factor: TResizeFactor);
  var
    i: Integer;
    Child: TAutoSizeBox;
    DiffSize: Integer;
    NewSize: LongInt;
    OldSize: LongInt;
  begin
    for i:=0 to ChildCount[Orientation]-1 do begin
      if TargetSize=CurSize then break;
    
      Child:=Children[Orientation][i];
      if (Child.MaximumSize[Orientation]<0)
      and (Child.PreferredSize[Orientation]>=Child.MaximumSize[Orientation])
      then begin
        // this child can not be further enlarged
        continue;
      end;

      case EnlargeStyle of
      
      crsScaleChilds:
        begin
          // scale PreferredSize
          DiffSize:=TargetSize-CurSize;
          OldSize:=Child.PreferredSize[Orientation];
          NewSize:=round(double(OldSize)*Factor.Scale);
          NewSize:=Min(OldSize+DiffSize,Max(OldSize+1,NewSize));
          inc(CurSize,NewSize-OldSize);
          Child.PreferredSize[Orientation]:=NewSize;
        end;
        
      crsHomogenousChildResize:
        begin
          // add to PreferredSize
          DiffSize:=TargetSize-CurSize;
          OldSize:=Child.PreferredSize[Orientation];
          NewSize:=Min(OldSize+Factor.Offset,OldSize+DiffSize);
          inc(CurSize,NewSize-OldSize);
          Child.PreferredSize[Orientation]:=NewSize;
        end;
        
      crsHomogenousSpaceResize:
        begin
          if i=0 then begin
            // add to left/top border
            DiffSize:=TargetSize-CurSize;
            OldSize:=Child.BorderLeftTop[Orientation];
            NewSize:=Min(OldSize+Factor.Offset,OldSize+DiffSize);
            inc(CurSize,NewSize-OldSize);
            Child.BorderLeftTop[Orientation]:=NewSize;
          end;
          // add to right/bottom border
          DiffSize:=TargetSize-CurSize;
          OldSize:=Child.BorderRightBottom[Orientation];
          NewSize:=Min(OldSize+Factor.Offset,OldSize+DiffSize);
          inc(CurSize,NewSize-OldSize);
          Child.BorderRightBottom[Orientation]:=NewSize;
          if i<ChildCount[Orientation]-1 then
            Child.BorderLeftTop[Orientation]:=NewSize;
        end;
      
      end;
    end;
  end;

  procedure GetChildMinResize(out Factor: TResizeFactor;
    out ResizeableCount: integer);
  // returns the number of children/gaps, that can shrink (ResizeableCount)
  // and the maximum factor, by which the children/gaps can shrink (TResizeFactor)
  var
    i: Integer;
    CurScale: Double;
    CurOffset: LongInt;
    Child: TAutoSizeBox;
  begin
    Factor.Scale:=0;
    Factor.Offset:=0;
    ResizeableCount:=0;
    case ShrinkStyle of

    crsAnchorAligning:
      exit; // no resizing

    crsScaleChilds,crsHomogenousChildResize:
      for i:=0 to ChildCount[Orientation]-1 do begin
        Child:=Children[Orientation][i];
        if (Child.PreferredSize[Orientation]<=Child.MinimumSize[Orientation])
        or (Child.PreferredSize[Orientation]<=1)
        then begin
          // this child can not be further shrinked
          continue;
        end;
        inc(ResizeableCount);

        case ShrinkStyle of

        crsScaleChilds:
          begin
            CurScale:=double(Child.MinimumSize[Orientation])
                      /Child.PreferredSize[Orientation];
            CurOffset:=Child.PreferredSize[Orientation]
                       -Child.MinimumSize[Orientation];
            if (Factor.Offset=0) or (Factor.Scale<CurScale) then begin
              Factor.Scale:=CurScale;
              Factor.Offset:=CurOffset;
            end;
          end;

        crsHomogenousChildResize:
          begin
            CurScale:=double(Child.MinimumSize[Orientation])
                      /Child.PreferredSize[Orientation];
            CurOffset:=Child.PreferredSize[Orientation]
                       -Child.MinimumSize[Orientation];
            if (Factor.Offset=0) or (Factor.Offset>CurOffset) then begin
              Factor.Scale:=CurScale;
              Factor.Offset:=CurOffset;
            end;
          end;

        end;
      end;

    crsHomogenousSpaceResize:
      for i:=0 to ChildCount[Orientation]-1 do begin
        Child:=Children[Orientation][i];
        if i=0 then begin
          CurScale:=double(TargetSize);
          CurOffset:=Child.BorderLeftTop[Orientation];
          if CurOffset>0 then begin
            inc(ResizeableCount);
            if (Factor.Offset=0) or (Factor.Offset>CurOffset) then begin
              Factor.Scale:=CurScale;
              Factor.Offset:=CurOffset;
            end;
          end;
        end;
        CurScale:=double(TargetSize);
        CurOffset:=Child.BorderRightBottom[Orientation];
        if CurOffset>0 then begin
          inc(ResizeableCount);
          if (Factor.Offset=0) or (Factor.Offset>CurOffset) then begin
            Factor.Scale:=CurScale;
            Factor.Offset:=CurOffset;
          end;
        end;
      end;

    else
      raise Exception.Create('TAutoSizeBox.ResizeChilds');

    end;
  end;

  procedure ShrinkChilds(const Factor: TResizeFactor);
  var
    i: Integer;
    Child: TAutoSizeBox;
    DiffSize: Integer;
    NewSize: LongInt;
    OldSize: LongInt;
  begin
    for i:=0 to ChildCount[Orientation]-1 do begin
      Child:=Children[Orientation][i];
      if (Child.PreferredSize[Orientation]<=1)
      or (Child.PreferredSize[Orientation]<=Child.MinimumSize[Orientation])
      then begin
        // this child can not be further shrinked
        continue;
      end;

      case ShrinkStyle of

      crsScaleChilds:
        begin
          // scale PreferredSize
          DiffSize:=CurSize-TargetSize;
          OldSize:=Child.PreferredSize[Orientation];
          NewSize:=Min(round(OldSize*Factor.Scale),OldSize-1);
          NewSize:=Max(Max(1,NewSize),OldSize-DiffSize);
          dec(CurSize,OldSize-NewSize);
          Child.PreferredSize[Orientation]:=NewSize;
        end;

      crsHomogenousChildResize:
        begin
          // add to PreferredSize
          DiffSize:=CurSize-TargetSize;
          OldSize:=Child.PreferredSize[Orientation];
          NewSize:=OldSize-Factor.Offset;
          NewSize:=Max(Max(NewSize,1),OldSize-DiffSize);
          dec(CurSize,OldSize-NewSize);
          Child.PreferredSize[Orientation]:=NewSize;
        end;

      crsHomogenousSpaceResize:
        begin
          if i=0 then begin
            // add to left/top border
            DiffSize:=CurSize-TargetSize;
            OldSize:=Child.BorderLeftTop[Orientation];
            NewSize:=Max(Max(0,OldSize-Factor.Offset),OldSize-DiffSize);
            dec(CurSize,OldSize-NewSize);
            Child.BorderLeftTop[Orientation]:=NewSize;
          end;
          // add to right/bottom border
          DiffSize:=CurSize-TargetSize;
          OldSize:=Child.BorderRightBottom[Orientation];
          NewSize:=Max(Max(0,OldSize-Factor.Offset),OldSize-DiffSize);
          dec(CurSize,OldSize-NewSize);
          Child.BorderRightBottom[Orientation]:=NewSize;
          if i<ChildCount[Orientation]-1 then
            Child.BorderLeftTop[Orientation]:=NewSize;
        end;

      end;
    end;
  end;

var
  MaxResizeFactorPerItem, MinResizeFactorPerItem, CurScale: TResizeFactor;
  ResizeableCount: integer;
  i: Integer;
begin
  CurSize:=GetChildTotalSize;
  //DebugLn('TAutoSizeBox.ResizeChilds CurSize=',dbgs(CurSize),' TargetSize=',dbgs(TargetSize));
  EnlargeStyle:=crsAnchorAligning;
  ShrinkStyle:=crsAnchorAligning;
  i:=0;
  if TargetSize>CurSize then begin
    // enlarge
    if Orientation=asboHorizontal then
      EnlargeStyle:=ChildSizing.EnlargeHorizontal
    else
      EnlargeStyle:=ChildSizing.EnlargeVertical;
    while TargetSize>CurSize do begin
      // shrink children
      GetChildMaxResize(MaxResizeFactorPerItem,ResizeableCount);
      if (ResizeableCount=0) or (MaxResizeFactorPerItem.Offset=0) then break;
      
      CurScale.Scale:=(double(TargetSize)/CurSize);
      if (MaxResizeFactorPerItem.Scale>0)
      and (MaxResizeFactorPerItem.Scale<CurScale.Scale) then
        CurScale.Scale:=MaxResizeFactorPerItem.Scale;
        
      CurScale.Offset:=((TargetSize-CurSize-1) div ResizeableCount)+1;
      // note: the above formula makes sure, that Offset>0
      if (MaxResizeFactorPerItem.Offset>0)
      and (MaxResizeFactorPerItem.Offset<CurScale.Offset) then
        CurScale.Offset:=MaxResizeFactorPerItem.Offset;
      
      EnlargeChilds(CurScale);
      inc(i);
      if i>1000 then RaiseGDBException('TAutoSizeBox.ResizeChilds consistency error');
    end;
  end else if TargetSize<CurSize then begin
    // shrink
    if Orientation=asboHorizontal then
      ShrinkStyle:=ChildSizing.ShrinkHorizontal
    else
      ShrinkStyle:=ChildSizing.ShrinkVertical;
    while TargetSize<CurSize do begin
      GetChildMinResize(MinResizeFactorPerItem,ResizeableCount);
      if (ResizeableCount=0) or (MinResizeFactorPerItem.Offset=0) then break;

      CurScale.Scale:=(double(TargetSize)/CurSize);
      if (MinResizeFactorPerItem.Scale>0)
      and (MinResizeFactorPerItem.Scale>CurScale.Scale) then
        CurScale.Scale:=MinResizeFactorPerItem.Scale;

      CurScale.Offset:=((CurSize-TargetSize-1) div ResizeableCount)+1;
      // note: the above formula makes sure, that Offset>0
      if (MinResizeFactorPerItem.Offset>0)
      and (MinResizeFactorPerItem.Offset>CurScale.Offset) then
        CurScale.Offset:=MinResizeFactorPerItem.Offset;

      ShrinkChilds(CurScale);
      inc(i);
      if i>1000 then RaiseGDBException('TAutoSizeBox.ResizeChilds consistency error');
    end;
  end;
end;

procedure TAutoSizeBox.ResizeTable(ChildSizing: TControlChildSizing;
  TargetWidth, TargetHeight: integer);
begin
  // resize rows and columns
  ResizeChildren(ChildSizing,asboHorizontal,TargetWidth);
  ComputeLeftTops(asboHorizontal);
  ResizeChildren(ChildSizing,asboVertical,TargetHeight);
  ComputeLeftTops(asboVertical);
end;

procedure TAutoSizeBox.AlignToRight(TargetWidth: integer);

  function GetChildTotalSize(Orientation: TAutoSizeBoxOrientation): integer;
  // computes the total preferred size of all children of this Orientation
  var
    i: Integer;
    Child: TAutoSizeBox;
  begin
    Result:=0;
    for i:=0 to ChildCount[Orientation]-1 do begin
      Child:=Children[Orientation][i];
      if i=0 then
        inc(Result,Child.BorderLeftTop[Orientation]);
      if Child.PreferredSize[Orientation]<1 then
        Child.PreferredSize[Orientation]:=1;
      inc(Result,Child.PreferredSize[Orientation]);
      inc(Result,Child.BorderRightBottom[Orientation]);
    end;
  end;

var
  Orientation: TAutoSizeBoxOrientation;
  i: Integer;
  Child: TAutoSizeBox;
  dx: Integer;
begin
  Orientation:=asboHorizontal;
  dx:=TargetWidth-GetChildTotalSize(Orientation);

  for i:=ChildCount[Orientation]-1 downto 0 do begin
    Child:=Children[Orientation][i];
    inc(Child.LeftTop[Orientation],dx);
  end;
end;

procedure TAutoSizeBox.ComputeTableControlBounds(
  ChildSizing: TControlChildSizing; BiDiMode: TBiDiMode);
var
  y: Integer;
  RowBox: TAutoSizeBox;
  x: Integer;
  ColBox: TAutoSizeBox;
  ControlBox: TAutoSizeBox;
  CurControl: TControl;
  NewBounds: TRect;
  CellBounds: TRect;
  NewWidth: LongInt;
  NewHeight: LongInt;
begin
  //WriteDebugReport;
  for y:=0 to ChildCount[asboVertical]-1 do begin
    RowBox:=Children[asboVertical][y];
    for x:=0 to RowBox.ChildCount[asboHorizontal]-1 do begin
      ControlBox:=RowBox.Children[asboHorizontal][x];
      ColBox:=ControlBox.Parent[asboVertical];
      CurControl:=ControlBox.Control;
      if CurControl=nil then continue;
      CellBounds:=Bounds(ColBox.LeftTop[asboHorizontal],
                         RowBox.LeftTop[asboVertical],
                         ColBox.PreferredSize[asboHorizontal],
                         RowBox.PreferredSize[asboVertical]);
      NewBounds.Left:=CellBounds.Left;
      NewBounds.Top:=CellBounds.Top;
      NewWidth:=ControlBox.PreferredSize[asboHorizontal];
      NewHeight:=ControlBox.PreferredSize[asboVertical];
      if (NewWidth<ColBox.PreferredSize[asboHorizontal]) then begin
        // column is bigger than preferred width of the control
        //DebugLn('TAutoSizeBox.SetTableControlBounds ',DbgSName(CurControl),' ',dbgs(ord(CurControl.BorderSpacing.CellAlignHorizontal)));
        case CurControl.BorderSpacing.CellAlignHorizontal of
        ccaFill:        NewWidth:=CellBounds.Right-CellBounds.Left;
        ccaLeftTop,ccaRightBottom:
                   if (CurControl.BorderSpacing.CellAlignHorizontal=ccaRightBottom)
                     =(BidiMode=bdLeftToRight)
                   then
                     NewBounds.Left:=CellBounds.Right-NewWidth;
        ccaCenter:      NewBounds.Left:=NewBounds.Left
                             +(CellBounds.Right-CellBounds.Left-NewWidth) div 2;
        end;
      end else if (NewWidth>ColBox.PreferredSize[asboHorizontal]) then begin
        // column is smaller than preferred width of the control
        if ChildSizing.ShrinkHorizontal
        in [crsScaleChilds,crsHomogenousChildResize]
        then
          NewWidth:=CellBounds.Right-CellBounds.Left;
      end;
      if (NewHeight<ColBox.PreferredSize[asboVertical]) then begin
        // column is bigger than preferred height of the control
        case CurControl.BorderSpacing.CellAlignVertical of
        ccaFill:        NewHeight:=CellBounds.Bottom-CellBounds.Top;
        ccaLeftTop:     ;
        ccaRightBottom: NewBounds.Top:=CellBounds.Bottom-NewHeight;
        ccaCenter:      NewBounds.Top:=NewBounds.Top
                            +(CellBounds.Bottom-CellBounds.Top-NewHeight) div 2;
        end;
      end else if (NewHeight>ColBox.PreferredSize[asboVertical]) then begin
        // column is smaller than preferred height of the control
        if ChildSizing.ShrinkVertical
        in [crsScaleChilds,crsHomogenousChildResize]
        then
          NewHeight:=CellBounds.Bottom-CellBounds.Top;
      end;

      NewBounds.Right:=NewBounds.Left+NewWidth;
      NewBounds.Bottom:=NewBounds.Top+NewHeight;
      ControlBox.NewControlBounds:=NewBounds;
      {$IFDEF CHECK_POSITION}
      if CheckPosition(CurControl) then
        DebugLn(['TAutoSizeBox.ComputeTableControlBounds ',DbgSName(CurControl),
           ' CellBounds=',dbgs(CellBounds),
           ' Preferred=',ControlBox.PreferredSize[asboHorizontal],'x',ControlBox.PreferredSize[asboVertical],
           ' NewBounds=',dbgs(NewBounds)]);
      {$ENDIF}
    end;
  end;
end;

function TAutoSizeBox.SetTableControlBounds(ChildSizing: TControlChildSizing
  ): boolean;
var
  y: Integer;
  RowBox: TAutoSizeBox;
  x: Integer;
  ControlBox: TAutoSizeBox;
  CurControl: TControl;
  NewBounds: TRect;
  OldBounds: TRect;
begin
  Result:=false;
  //WriteDebugReport;
  for y:=0 to ChildCount[asboVertical]-1 do begin
    RowBox:=Children[asboVertical][y];
    for x:=0 to RowBox.ChildCount[asboHorizontal]-1 do begin
      ControlBox:=RowBox.Children[asboHorizontal][x];
      CurControl:=ControlBox.Control;
      if CurControl=nil then continue;
      NewBounds:=ControlBox.NewControlBounds;
      OldBounds:=CurControl.BoundsRect;
      if not CompareRect(@NewBounds,@OldBounds) then begin
        Result:=true;
        CurControl.SetBoundsKeepBase(NewBounds.Left,
                                     NewBounds.Top,
                                     NewBounds.Right-NewBounds.Left,
                                     NewBounds.Bottom-NewBounds.Top);
      end;
    end;
  end;
end;

function TAutoSizeBox.AlignControlsInTable(ListOfControls: TFPList;
  ChildSizing: TControlChildSizing; BiDiMode: TBiDiMode;
  TargetWidth, TargetHeight: integer;
  Apply: boolean): boolean;
// true if a control was modified
begin
  SetTableControls(ListOfControls,ChildSizing,BiDiMode);
  //WriteDebugReport('after SetTableControls');
  SumTable;
  //WriteDebugReport('after SumTable');
  ResizeTable(ChildSizing,TargetWidth,TargetHeight);
  //WriteDebugReport('after ResizeTable');
  if BiDiMode=bdRightToLeft then
    AlignToRight(TargetWidth);
  //WriteDebugReport('after AlignToRight');
  ComputeTableControlBounds(ChildSizing,BiDiMode);
  //WriteDebugReport('after ComputeTableControlBounds');
  Result:=Apply and SetTableControlBounds(ChildSizing);
end;

procedure TAutoSizeBox.WriteDebugReport(const Title: string);
var
  y: Integer;
  RowBox: TAutoSizeBox;
  x: Integer;
  CellBox: TAutoSizeBox;
  ColBox: TAutoSizeBox;
begin
  DebugLn('TAutoSizeBox.WriteDebugReport '+Title
    +' ChildCounts=',dbgs(ChildCount[asboHorizontal]),'x',dbgs(ChildCount[asboVertical]));
  for y:=0 to ChildCount[asboVertical]-1 do begin
    RowBox:=Children[asboVertical][y];
    DbgOut('  Row='+dbgs(y),
            ' MinY='+dbgs(RowBox.MinimumSize[asboVertical]),
            ' MaxY='+dbgs(RowBox.MaximumSize[asboVertical]),
            ' PrefY='+dbgs(RowBox.PreferredSize[asboVertical]),
            ' BorderTop=',dbgs(RowBox.BorderLeftTop[asboVertical]),
            ' #Col='+dbgs(RowBox.ChildCount[asboHorizontal]));
    for x:=0 to RowBox.ChildCount[asboHorizontal]-1 do begin
      CellBox:=RowBox.Children[asboHorizontal][x];
      DbgOut(' CellControl=',DbgSName(CellBox.Control),
             ' Min='+dbgs(CellBox.MinimumSize[asboHorizontal])+'x'+dbgs(CellBox.MinimumSize[asboVertical]),
             ' Max='+dbgs(CellBox.MaximumSize[asboHorizontal])+'x'+dbgs(CellBox.MaximumSize[asboVertical]),
             ' BorderLeft=',dbgs(CellBox.BorderLeftTop[asboHorizontal]),
             ' Pref='+dbgs(CellBox.PreferredSize[asboHorizontal])+'x'+dbgs(CellBox.PreferredSize[asboVertical]),
             '');
    end;
    DebugLn;
  end;
  DbgOut(' Columns: ');
  for x:=0 to ChildCount[asboHorizontal]-1 do begin
    ColBox:=Children[asboHorizontal][x];
    DbgOut(' Col='+dbgs(ColBox.Index[asboHorizontal]),
           ' Min='+dbgs(ColBox.MinimumSize[asboHorizontal]),
           ' Max='+dbgs(ColBox.MaximumSize[asboHorizontal]),
           ' Pref='+dbgs(ColBox.PreferredSize[asboHorizontal]),
           '');
  end;
  DebugLn;
end;

destructor TAutoSizeBox.Destroy;
var
  o: TAutoSizeBoxOrientation;
begin
  // unlink from parent
  for o:=Low(TAutoSizeBoxOrientation) to high(TAutoSizeBoxOrientation) do
    if Parent[o]<>nil then
      Parent[o].Children[o][Index[o]]:=nil;
  Clear;
  inherited Destroy;
end;

procedure TAutoSizeBox.Clear;
var
  o: TAutoSizeBoxOrientation;
  i: Integer;
begin
  // free all children
  for o:=Low(TAutoSizeBoxOrientation) to high(TAutoSizeBoxOrientation) do
    for i:=0 to ChildCount[o]-1 do
      Children[o][i].Free;
  // free children arrays
  for o:=Low(TAutoSizeBoxOrientation) to high(TAutoSizeBoxOrientation) do
    ReallocMem(Children[o],0);
end;

{------------------------------------------------------------------------------
  function TWinControl.AutoSizePhases: TControlAutoSizePhases;
------------------------------------------------------------------------------}
function TWinControl.AutoSizePhases: TControlAutoSizePhases;
begin
  if Parent<>nil then
    Result:=Parent.AutoSizePhases
  else begin
    Result:=[];
    if ([wcfCreatingHandle,wcfCreatingChildHandles]*FWinControlFlags<>[]) then
      Include(Result,caspCreatingHandles);
    if fAutoSizingAll then
      Include(Result,caspComputingBounds);
    if wcfRealizingBounds in FWinControlFlags then
      Include(Result,caspRealizingBounds);
    if wcfUpdateShowing in FWinControlFlags then
      Include(Result,caspShowing);
    if FAutoSizingLockCount>0 then
      Include(Result,caspChangingProperties);
  end;
end;

{------------------------------------------------------------------------------
  function TWinControl.AutoSizeDelayed: boolean;
------------------------------------------------------------------------------}
function TWinControl.AutoSizeDelayed: boolean;
begin
  Result:=(csDestroyingHandle in ControlState)
          or (inherited AutoSizeDelayed);
  //if Result then debugln('TWinControl.AutoSizeDelayed A ',DbgSName(Self),' wcfCreatingChildHandles=',dbgs(wcfCreatingChildHandles in FWinControlFlags),' csLoading=',dbgs(csLoading in ComponentState));
  {$IFDEF VerboseCanAutoSize}
  if Result {and AutoSize} then begin
    if not HandleAllocated then
      debugln('TWinControl.AutoSizeDelayed Self='+DbgSName(Self)+' not HandleAllocated');
  end;
  {$ENDIF}
end;

function TWinControl.AutoSizeDelayedReport: string;
begin
  if csDestroyingHandle in ControlState then
    Result:='csDestroyingHandle'
  else
    Result:=inherited AutoSizeDelayedReport;
end;

{------------------------------------------------------------------------------
  TWinControl AutoSizeDelayedHandle

  Returns true if AutoSize should be skipped / delayed because of its handle.
  A TWinControl needs a parent handle.
------------------------------------------------------------------------------}
function TWinControl.AutoSizeDelayedHandle: Boolean;
begin
  Result := (Parent = nil) and (ParentWindow = 0);
end;

{------------------------------------------------------------------------------
  TWinControl AdjustClientRect
------------------------------------------------------------------------------}
procedure TWinControl.AdjustClientRect(var ARect: TRect);
begin
  // Can be overriden.
  // It's called often, so don't put expensive code here, or cache the result
end;

procedure TWinControl.GetAdjustedLogicalClientRect(out ARect: TRect);
begin
  if not (wcfAdjustedLogicalClientRectValid in FWinControlFlags) then begin
    FAdjustClientRect:=GetLogicalClientRect;
    AdjustClientRect(FAdjustClientRect);
    Include(FWinControlFlags,wcfAdjustedLogicalClientRectValid);
  end;
  ARect:=FAdjustClientRect;
end;

{------------------------------------------------------------------------------
  TWinControl CreateControlAlignList
  
  Creates a list of controls that need to be aligned via TheAlign.
------------------------------------------------------------------------------}
procedure TWinControl.CreateControlAlignList(TheAlign: TAlign;
  AlignList: TFPList; StartControl: TControl);

  function InsertBefore(Control1, Control2: TControl; AAlign: TAlign): Boolean;
  begin
    case AAlign of
      alTop: begin
        Result := (Control1.Top < Control2.Top)
               or ( (Control1.Top = Control2.Top)
                and (Control1.FBaseBounds.Top < Control2.FBaseBounds.Top));
      end;
      alLeft: begin
        Result := (Control1.Left < Control2.Left)
               or ( (Control1.Left = Control2.Left)
                and (Control1.FBaseBounds.Left < Control2.FBaseBounds.Left));
      end;
      // contrary to VCL, LCL uses > for alBottom, alRight
      // Maybe it is a bug in the VCL.
      // This results in first control is put rightmost/bottommost
      alBottom: begin
        Result := ((Control1.Top + Control1.Height) > (Control2.Top + Control2.Height))
               or ( ((Control1.Top + Control1.Height) = (Control2.Top + Control2.Height))
                and (Control1.FBaseBounds.Bottom > Control2.FBaseBounds.Bottom));
      end;
      alRight: begin
        Result := ((Control1.Left + Control1.Width) > (Control2.Left + Control2.Width))
               or ( ((Control1.Left + Control1.Width) = (Control2.Left + Control2.Width))
                and (Control1.FBaseBounds.Right > Control2.FBaseBounds.Right));
      end;
      alCustom: begin
        // CustomAlignInsertBefore returns true when Control2 is inserted before Control1
        // We return true when Control1 is inserted before Control2
        // So swap controls
        Result := CustomAlignInsertBefore(Control2, Control1);
      end;
    else
      Result := False;
    end;
  end;

var
  I, X: Integer;
  Control: TControl;
begin
  AlignList.Clear;

  // first add the current control
  if (StartControl <> nil) and (StartControl.Align = TheAlign) and
     ((TheAlign = alNone) or StartControl.IsControlVisible) then
    AlignList.Add(StartControl);

  // then add all other
  for I := 0 to FAlignOrder.Count - 1 do
  begin
    Control := TControl(FAlignOrder[I]);

    if (Control.Align = TheAlign) and Control.IsControlVisible then
    begin
      if Control = StartControl then Continue;

      X := 0;
      while (X < AlignList.Count) and
            not InsertBefore(Control, TControl(AlignList[X]), TheAlign) do
        Inc(X);
      AlignList.Insert(X, Control);
    end;
  end;
end;

{------------------------------------------------------------------------------
  TWinControl AlignControls

  Align child controls
------------------------------------------------------------------------------}
procedure TWinControl.AlignControls(AControl: TControl;
  var RemainingClientRect: TRect);
{ $DEFINE CHECK_POSITION}
var
  AlignList: TFPList;
  BoundsMutated: boolean;
  LastBoundsMutated: TControl;
  LastBoundsMutatedOld: TRect;
  ParentClientWidth: integer;
  ParentClientHeight: integer;
  RemainingBorderSpace: TRect; // borderspace around RemainingClientRect
                               // e.g. Right=3 means borderspace of 3

  function NeedAlignWork: Boolean;
  var
    I: Integer;
    CurControl: TControl;
  begin
    Result := True;
    for I := ControlCount - 1 downto 0 do
    begin
      CurControl:=Controls[I];
      if (CurControl.Align <> alNone)
      or (CurControl.Anchors <> [akLeft, akTop])
      or (CurControl.AnchorSide[akLeft].Control<>nil)
      or (CurControl.AnchorSide[akTop].Control<>nil)
      or (ChildSizing.Layout<>cclNone)
      then Exit;
    end;
    Result := False;
  end;

  function Anchored(Align: TAlign; Anchors: TAnchors): Boolean;
  begin
    case Align of
      alLeft: Result := akLeft in Anchors;
      alTop: Result := akTop in Anchors;
      alRight: Result := akRight in Anchors;
      alBottom: Result := akBottom in Anchors;
      alClient: Result := Anchors = [akLeft, akTop, akRight, akBottom];
    else
      Result := False;
    end;
  end;
  
  procedure DoPosition(Control: TControl; AAlign: TAlign; AControlIndex: Integer);
  var
    NewLeft, NewTop, NewWidth, NewHeight: Integer;
    ParentBaseClientSize: TSize;
    CurBaseBounds: TRect;
    NewRight: Integer;// temp variable, not always valid, use with care !
    NewBottom: Integer;// temp variable, not always valid, use with care !

    MinWidth: Integer;
    MaxWidth: Integer;
    MinHeight: Integer;
    MaxHeight: Integer;
    CurRemainingClientRect: TRect;
    CurRemainingBorderSpace: TRect; // borderspace around RemainingClientRect
                                    // e.g. Right=3 means borderspace of 3
    ChildAroundSpace: TRect;
    AnchorSideCacheValid: array[TAnchorKind] of boolean;
    AnchorSideCache: array[TAnchorKind] of integer;
    CurAnchors: TAnchors;
    CurAlignAnchors: TAnchors;
    OldBounds: TRect;
    NewBounds: TRect;

    AlignInfo: TAlignInfo; // alCustom
    PrefWidth: integer;
    PrefHeight: integer;

    function ConstraintWidth(NewWidth: integer): Integer;
    begin
      Result:=NewWidth;
      if (MaxWidth>=MinWidth) and (Result>MaxWidth) and (MaxWidth>0) then
        Result:=MaxWidth;
      if Result<MinWidth then Result:=MinWidth;
    end;

    procedure ConstraintWidth(var NewLeft, NewWidth: integer);
    var
      ConWidth: LongInt;
    begin
      ConWidth:=ConstraintWidth(NewWidth);
      if ConWidth<>NewWidth then begin
        if [akLeft,akRight]*CurAnchors=[akRight] then
          // move left side, keep right
          inc(NewLeft,NewWidth-ConWidth);
        NewWidth:=ConWidth;
      end;
    end;

    function ConstraintHeight(NewHeight: integer): Integer;
    begin
      Result:=NewHeight;
      if (MaxHeight>=MinHeight) and (Result>MaxHeight) and (MaxHeight>0) then
        Result:=MaxHeight;
      if Result<MinHeight then Result:=MinHeight;
    end;

    procedure ConstraintHeight(var NewTop, NewHeight: integer);
    var
      ConHeight: LongInt;
    begin
      ConHeight:=ConstraintHeight(NewHeight);
      if ConHeight<>NewHeight then begin
        if [akTop,akBottom]*CurAnchors=[akBottom] then
          // move top side, keep bottom
          inc(NewTop,NewHeight-ConHeight);
        NewHeight:=ConHeight;
      end;
    end;
    
    procedure InitAnchorSideCache;
    var
      a: TAnchorKind;
    begin
      for a:=Low(TAnchorKind) to High(TAnchorKind) do
        AnchorSideCacheValid[a]:=false;
    end;
    
    function GetAnchorSidePosition(Kind: TAnchorKind;
      DefaultPosition: Integer): integer;
    // calculates the position in pixels of a side due to anchors
    // For example: if akLeft is set, it returns the coordinate for the left anchor
    var
      CurAnchorSide: TAnchorSide;
      ReferenceControl: TControl;
      ReferenceSide: TAnchorSideReference;
      Position: Integer;
    begin
      if AnchorSideCacheValid[Kind] then begin
        Result:=AnchorSideCache[Kind];
        exit;
      end;
      Result:=DefaultPosition;
      CurAnchorSide:=Control.AnchorSide[Kind];
      //if CheckPosition(Control) and (Kind=akLeft) then debugln(['GetAnchorSidePosition A Self=',DbgSName(Self),' Control=',DbgSName(Control),' CurAnchorSide.Control=',DbgSName(CurAnchorSide.Control),' Spacing=',Control.BorderSpacing.GetSpace(Kind)]);
      CurAnchorSide.GetSidePosition(ReferenceControl,ReferenceSide,Position);
      if ReferenceControl<>nil then begin
        //DebugLn(['GetAnchorSidePosition ',DbgSName(Control),' ReferenceControl=',DbgSName(ReferenceControl)]);
        Result:=Position;
      end;
      //if CheckPosition(Control) and (Kind=akRight) then begin
      //  debugln('GetAnchorSidePosition B Self=',DbgSName(Self),' Control=',DbgSName(Control),' Result=',dbgs(Result),' ReferenceControl=',dbgsName(ReferenceControl));
      //  if ReferenceControl<>nil then DebugLn(['GetAnchorSidePosition ReferenceControl.BoundsRect=',dbgs(ReferenceControl.BoundsRect)]);
      //end;
      AnchorSideCacheValid[Kind]:=true;
      AnchorSideCache[Kind]:=Result;
      if ReferenceSide=asrTop then ;
    end;
    
  begin
    {$IFDEF CHECK_POSITION}
    if CheckPosition(Control) then
    with Control do
      DebugLn('[TWinControl.AlignControls.DoPosition] A Control=',dbgsName(Control),' ',
              dbgs(Left),',',dbgs(Top),',',dbgs(Width),',',dbgs(Height),
              ' recalculate the anchors=',dbgs(Control.Anchors <> AnchorAlign[AAlign]),
              ' Align=',DbgS(AAlign));
    {$ENDIF}

    with Control do begin
      // get constraints
      MinWidth:=Constraints.EffectiveMinWidth;
      if MinWidth<0 then MinWidth:=0;
      MaxWidth:=Constraints.EffectiveMaxWidth;
      MinHeight:=Constraints.EffectiveMinHeight;
      if MinHeight<0 then MinHeight:=0;
      MaxHeight:=Constraints.EffectiveMaxHeight;
      
      // get anchors set by Align
      CurAlignAnchors:=[];
      if Align in [alLeft,alRight,alBottom,alTop,alClient] then
        CurAlignAnchors:=AnchorAlign[Align];
      CurAnchors:=Anchors+CurAlignAnchors;

      // get default bounds
      NewLeft:=Left;
      NewTop:=Top;
      NewWidth:=Width;
      NewHeight:=Height;
      if AutoSize then begin
        GetPreferredSize(PrefWidth,PrefHeight);
        if PrefWidth>0 then NewWidth:=PrefWidth;
        if PrefHeight>0 then NewHeight:=PrefHeight;
      end;
      ConstraintWidth(NewLeft,NewWidth);
      ConstraintHeight(NewTop,NewHeight);
    end;

    InitAnchorSideCache;
    
    { Recalculate the anchors

      Use Anchors to ensure that a control maintains its current position
      relative to an edge of its parent or another sibling.
      This is controlled with the AnchorSide properties.

      1. If AnchorSide[].Control is not set, the distance is kept relative to
      the edges of the client area of its parent.
      When its parent is resized, the control holds its position relative to the
      edges to which it is anchored.
      If a control is anchored to opposite edges of its parent, the control
      stretches when its parent is resized. For example, if a control has its
      Anchors property set to [akLeft,akRight], the control stretches when the
      width of its parent changes.
      Anchors is enforced only when the parent is resized. Thus, for example,
      if a control is anchored to opposite edges of a form at design time and
      the form is created in a maximized state, the control is not stretched
      because the form is not resized after the control is created.
      
      2. If AnchorSide[].Control is set, the BorderSpace properties defines the
      distance to another sibling (i.e. AnchorSide[].Control).
    }
    if (AAlign = alNone) or (Control.Anchors <> CurAlignAnchors)
    then begin
      // at least one side is anchored without align
    
      // Get the base bounds. The base bounds are the user defined bounds
      // without automatic aligning and/or anchoring

      // get base size of parents client area
      ParentBaseClientSize:=Control.FBaseParentClientSize;
      if (ParentBaseClientSize.cx=0)
      and (ParentBaseClientSize.cy=0) then
        ParentBaseClientSize:=Size(ParentClientWidth,ParentClientHeight);

      // get base bounds of Control
      CurBaseBounds:=Control.FBaseBounds;
      if not (cfBaseBoundsValid in FControlFlags) then
        CurBaseBounds:=Control.BoundsRect;

      {$IFDEF CHECK_POSITION}
      //if csDesigning in ComponentState then
      if CheckPosition(Control) then
       DebugLn('[TWinControl.AlignControls.DoPosition] Before Anchoring ',
        ' Self='+DbgSName(Self),' Control='+DbgSName(Control),
        ' CurBaseBounds='+dbgs(CurBaseBounds.Left)+','+dbgs(CurBaseBounds.Top)+','+dbgs(CurBaseBounds.Right-CurBaseBounds.Left)+','+dbgs(CurBaseBounds.Bottom-CurBaseBounds.Top),
        ' ParentBaseClientSize='+dbgs(ParentBaseClientSize.cx)+','+dbgs(ParentBaseClientSize.cy),
        ' ControlParent.Client='+dbgs(ParentClientWidth)+','+dbgs(ParentClientHeight),
        ' NewBounds='+dbgs(NewLeft)+','+dbgs(NewTop)+','+dbgs(NewWidth)+','+dbgs(NewHeight),
        '');
      {$ENDIF}

      if akLeft in CurAnchors then begin
        // keep distance to left side of parent or another sibling
        NewLeft:=GetAnchorSidePosition(akLeft,CurBaseBounds.Left);
        if akRight in CurAnchors then begin
          // keep distance to right side of parent or another sibling
          // -> change the width
          NewRight:=ParentClientWidth
                    -(ParentBaseClientSize.cx-CurBaseBounds.Right);
          if (not (akRight in CurAlignAnchors))
          and (akRight in Control.Anchors) then
            NewRight:=GetAnchorSidePosition(akRight,NewRight);
          NewWidth:=ConstraintWidth(NewRight-NewLeft);
        end else begin
          // do not anchor to the right
          // -> keep new width
        end;
      end else begin
        // do not anchor to the left
        if akRight in CurAnchors then begin
          // keep distance to right side of parent
          // and keep new width
          NewRight:=ParentClientWidth
                    -(ParentBaseClientSize.cx-CurBaseBounds.Right);
          if (not (akRight in CurAlignAnchors))
          and (akRight in Control.Anchors) then
            NewRight:=GetAnchorSidePosition(akRight,NewRight);
          NewLeft:=NewRight-NewWidth;
        end else begin
          // do not anchor to the right
          // -> keep new width and scale center position.
          NewLeft:=MulDiv(ParentClientWidth,
                          (CurBaseBounds.Left+CurBaseBounds.Right) div 2,
                          ParentBaseClientSize.cx)
                   -(NewWidth div 2);
        end;
      end;

      if akTop in CurAnchors then begin
        // keep distance to top side of parent
        NewTop:=GetAnchorSidePosition(akTop,CurBaseBounds.Top);
        if akBottom in CurAnchors then begin
          // keep distance to bottom side of parent
          // -> change the height
          NewBottom:=ParentClientHeight
                    -(ParentBaseClientSize.cy-CurBaseBounds.Bottom);
          if (not (akBottom in CurAlignAnchors))
          and (akBottom in Control.Anchors) then
            NewBottom:=GetAnchorSidePosition(akBottom,NewBottom);
          NewHeight:=ConstraintHeight(NewBottom-NewTop);
        end else begin
          // do not anchor to the bottom
          // -> keep new height
        end;
      end else begin
        // do not anchor to the top
        if akBottom in CurAnchors then begin
          // keep distance to bottom side of parent
          // and keep new height
          NewBottom:=ParentClientHeight
                    -(ParentBaseClientSize.cy-CurBaseBounds.Bottom);
          if (not (akBottom in CurAlignAnchors))
          and (akBottom in Control.Anchors) then
            NewBottom:=GetAnchorSidePosition(akBottom,NewBottom);
          NewTop:=NewBottom-NewHeight;
        end else begin
          // do not anchor to the bottom
          // -> keep new height and scale center position.
          NewTop:=MulDiv(ParentClientHeight,
                         (CurBaseBounds.Top+CurBaseBounds.Bottom) div 2,
                         ParentBaseClientSize.cy)
                  -(NewHeight div 2);
        end;
      end;
      {$IFDEF CHECK_POSITION}
      //if csDesigning in ComponentState then
      if CheckPosition(Control) then
      with Control do begin
        DebugLn(['[TWinControl.AlignControls.DoPosition] After Anchoring',
          ' Self=',DbgSName(Self),
          ' Align=',DbgS(AAlign),
          ' Control=',dbgsName(Control),
          ' Old= l=',Left,',t=',Top,',w=',Width,',h=',Height,
          ' New= l=',NewLeft,',t=',NewTop,',w=',NewWidth,',h=',NewHeight,
          '']);
        DebugLn(['DoPosition akRight=',akRight in CurAnchors,' ',GetAnchorSidePosition(akRight,NewLeft+NewWidth)]);
      end;
      {$ENDIF}
    end;

    // set min size to stop circling (this should not be needed. But if someone
    // plays/fixes the above code, new bugs can enter and there are far too many
    // combinations to test, and so the LCL can loop for some applications.
    // Prevent this, so users can at least report a bug.)
    if NewWidth<0 then NewWidth:=0;
    if NewHeight<0 then NewHeight:=0;

    case AAlign of
      alLeft,alTop,alRight,alBottom,alClient: begin
        { Realign

          Use Align to align a control to the top, bottom, left, right of a
          form or panel and have it remain there even if the size of the form,
          panel, or component that contains the control changes. When the parent
          is resized, an aligned control also resizes so that it continues to span
          the top, bottom, left, or right edge of the parent (more exact:
          span the remaining client area of its parent).
        }
        NewRight:=NewLeft+NewWidth;
        NewBottom:=NewTop+NewHeight;

        // calculate current RemainingClientRect for the current Control
        CurRemainingClientRect:=RemainingClientRect;
        CurRemainingBorderSpace:=RemainingBorderSpace;
        Control.BorderSpacing.GetSpaceAround(ChildAroundSpace);
        AdjustBorderSpace(CurRemainingClientRect,CurRemainingBorderSpace,
                          ChildAroundSpace);
        {$IFDEF CHECK_POSITION}
        if CheckPosition(Control) then
          DebugLn('DoPosition Before aligning ',dbgsName(Control),' akRight in AnchorAlign[AAlign]=',DbgS(akRight in AnchorAlign[AAlign]),
                  ' akLeft in Control.Anchors=',DbgS(akLeft in Control.Anchors),
                  //' ARect=',ARect.Left,',',ARect.Top,',',ARect.Right,',',ARect.Bottom,
                  ' New=',DbgS(NewLeft,NewTop,NewRight,NewBottom));
        {$ENDIF}

        if akLeft in AnchorAlign[AAlign]
        then begin
          if (akRight in CurAnchors)
          then begin
            // left align and keep right border
            NewLeft:=CurRemainingClientRect.Left;
            NewRight:=NewLeft+ConstraintWidth(NewRight-NewLeft);
          end
          else begin
            // left align and right border free to move (-> keep width)
            dec(NewRight,NewLeft-CurRemainingClientRect.Left);
            NewLeft:=CurRemainingClientRect.Left;
          end;
        end;

        if akTop in AnchorAlign[AAlign]
        then begin
          if (akBottom in CurAnchors)
          then begin
            // top align and keep bottom border
            NewTop:=CurRemainingClientRect.Top;
            NewBottom:=NewTop+ConstraintHeight(NewBottom-NewTop);
          end
          else begin
            // top align and bottom border is free to move (-> keep height)
            dec(NewBottom,NewTop-CurRemainingClientRect.Top);
            NewTop:=CurRemainingClientRect.Top;
          end;
        end;

        if akRight in AnchorAlign[AAlign]
        then begin
          if (akLeft in CurAnchors)
          then begin
            // right align and keep left border
            NewWidth:=ConstraintWidth(CurRemainingClientRect.Right-NewLeft);
            if Align=alRight
            then begin
              // align to right (this overrides the keeping of left border)
              NewRight:=CurRemainingClientRect.Right;
              NewLeft:=NewRight-NewWidth;
            end
            else begin
              // keep left border overrides keeping right border
              NewRight:=NewLeft+NewWidth;
            end;
          end
          else begin
            // right align and left border free to move (-> keep width)
            inc(NewLeft,CurRemainingClientRect.Right-NewRight);
            NewRight:=CurRemainingClientRect.Right;
          end;
        end;

        if akBottom in AnchorAlign[AAlign]
        then begin
          if (akTop in CurAnchors)
          then begin
            // bottom align and keep top border
            NewHeight:=ConstraintHeight(CurRemainingClientRect.Bottom-NewTop);
            if AAlign=alBottom
            then begin
              // align to bottom (this overrides the keeping of top border)
              NewBottom:=CurRemainingClientRect.Bottom;
              NewTop:=NewBottom-NewHeight;
            end
            else begin
              // keeping top border overrides keeping bottom border
              NewBottom:=NewTop+NewHeight;
            end;
          end
          else begin
            // bottom align and top border free to move (-> keep height)
            inc(NewTop,CurRemainingClientRect.Bottom-NewBottom);
            NewBottom:=CurRemainingClientRect.Bottom;
          end;
        end;

        NewWidth:=Max(0,NewRight-NewLeft);
        NewHeight:=Max(0,NewBottom-NewTop);

        {$IFDEF CHECK_POSITION}
        //if csDesigning in Control.ComponentState then
        if CheckPosition(Control) then
          with Control do
            DebugLn('[TWinControl.AlignControls.DoPosition] After Aligning',
            ' ',Name,':',ClassName,
            ' Align=',DbgS(AAlign),
            ' Control=',Name,':',ClassName,
            ' Old=',DbgS(Left,Top,Width,Height),
            ' New=',DbgS(NewLeft,NewTop,NewWidth,NewHeight),
            //' ARect=',ARect.Left,',',ARect.Top,',',ARect.Right-ARect.Left,',',ARect.Bottom-ARect.Top,
            '');
        {$ENDIF}
      end;
      alCustom: begin
        AlignInfo.AlignList := AlignList;
        AlignInfo.Align := alCustom;
        AlignInfo.ControlIndex := AControlIndex;
        CustomAlignPosition(Control, NewLeft, NewTop, NewWidth, NewHeight, RemainingClientRect, AlignInfo);
      end;
    end;
    
    // apply the constraints
    NewWidth:=ConstraintWidth(NewWidth);
    NewHeight:=ConstraintHeight(NewHeight);
    NewRight:=NewLeft+NewWidth;
    NewBottom:=NewTop+NewHeight;

    // set the new bounds
    if (Control.Left <> NewLeft) or (Control.Top <> NewTop)
    or (Control.Width <> NewWidth) or (Control.Height <> NewHeight) then begin
      {$IFDEF CHECK_POSITION}
      //if csDesigning in Control.ComponentState then
      if CheckPosition(Control) then
      with Control do
        DebugLn('[TWinControl.AlignControls.DoPosition] NEW BOUNDS Control=',DbgSName(Control),
                ' New=l=',dbgs(NewLeft)+',t='+dbgs(NewTop)+',w='+dbgs(NewWidth)+',h='+dbgs(NewHeight));
      {$ENDIF}
      // lock the base bounds, so that the new automatic bounds do not override
      // the user settings
      OldBounds:=Control.BoundsRect;
      Control.SetBoundsKeepBase(NewLeft, NewTop, NewWidth, NewHeight);
      //DebugLn(['DoPosition ',DbgSName(Control),' ',cfAutoSizeNeeded in Control.FControlFlags]);
      NewBounds:=Control.BoundsRect;
      BoundsMutated:=not CompareRect(@OldBounds,@NewBounds);
      if BoundsMutated then begin
        LastBoundsMutated:=Control;
        LastBoundsMutatedOld:=OldBounds;
      end;
      // Sometimes SetBounds change the bounds. For example due to constraints.
      // update the new bounds
      with Control do
      begin
        NewLeft:=Left;
        NewTop:=Top;
        NewWidth:=Width;
        NewHeight:=Height;
      end;
      {$IFDEF CHECK_POSITION}
      //if csDesigning in Control.ComponentState then
      if CheckPosition(Control) then
      with Control do
        DebugLn('[TWinControl.AlignControls.DoPosition] AFTER SETBOUND Control=',DbgSName(Control),' Bounds=',DbgS(Control.BoundsRect));
      {$ENDIF}
    end;

    // adjust the remaining client area
    case AAlign of
      alTop:
        begin
          RemainingClientRect.Top:=Min(NewTop+NewHeight,RemainingClientRect.Bottom);
          RemainingBorderSpace.Top:=0;
          AdjustBorderSpace(RemainingClientRect,RemainingBorderSpace,
            0,Max(ChildSizing.VerticalSpacing,ChildAroundSpace.Bottom),0,0);
        end;
      alBottom:
        begin
          RemainingClientRect.Bottom:=Max(NewTop,RemainingClientRect.Top);
          RemainingBorderSpace.Bottom:=0;
          AdjustBorderSpace(RemainingClientRect,RemainingBorderSpace,
            0,0,0,Max(ChildSizing.VerticalSpacing,ChildAroundSpace.Top));
        end;
      alLeft:
        begin
          RemainingClientRect.Left:=Min(NewLeft+NewWidth,RemainingClientRect.Right);
          RemainingBorderSpace.Left:=0;
          AdjustBorderSpace(RemainingClientRect,RemainingBorderSpace,
            Max(ChildSizing.HorizontalSpacing,ChildAroundSpace.Right),0,0,0);
        end;
      alRight:
        begin
          RemainingClientRect.Right:=Max(NewLeft,RemainingClientRect.Left);
          RemainingBorderSpace.Right:=0;
          AdjustBorderSpace(RemainingClientRect,RemainingBorderSpace,
            0,0,Max(ChildSizing.HorizontalSpacing,ChildAroundSpace.Left),0);
        end;
      alClient:
        begin
          // For VCL compatibility alClient should *not* reduce the free space,
          // so that several alClient controls can overlap. This can be used
          // for example to simulate a two page control and edit both pages
          // at designtime with SendToBack.
          // At runtime programs should use Visible instead of BringToFront to
          // reduce overhead.
          // See bug 10380.
        end;
    end;

    {$IFDEF CHECK_POSITION}
    if CheckPosition(Control) then
    with Control do
      DebugLn('[TWinControl.AlignControls.DoPosition] END Control=',
        Name,':',ClassName,
        ' ',DbgS(Left,Top,Width,Height),
        ' Align=',DbgS(AAlign),
        //' ARect=',ARect.Left,',',ARect.Top,',',ARect.Right-ARect.Left,',',ARect.Bottom-ARect.Top,
        '');
    {$ENDIF}
  end;

  procedure DoAlign(AAlign: TAlign);
  var
    I: Integer;
    Control: TControl;
  begin
    //DebugLn(['DoAlign ',DbgSName(Self),' ',dbgs(AALign),' ClientRect=',dbgs(ClientRect),' ControlCount=',ControlCount]);
    CreateControlAlignList(AAlign,AlignList,AControl);
    {$IFDEF CHECK_POSITION}
    if CheckPosition(Self) then
      if AlignList.Count>0 then
      begin
        DbgOut('[TWinControl.AlignControls.DoAlign] Self=',DbgSName(Self),' Control=',dbgsName(AControl),
          ' current align=',DbgS(AAlign),' AlignList=[');
        for i:=0 to AlignList.Count-1 do
        begin
          if i>0 then DbgOut(',');
          DbgOut(DbgSName(TObject(AlignList[i])));
        end;
        DebugLn(']');
      end;
    {$ENDIF}

    // let override handle them
    if DoAlignChildControls(AAlign, AControl, AlignList, RemainingClientRect) then
      exit;
    // remove controls that are positioned by other means
    if (AAlign = alNone) and (AutoSize or (ChildSizing.Layout <> cclNone)) then
      for I := AlignList.Count - 1 downto 0 do
      begin
        Control := TControl(AlignList[I]);
        if IsNotAligned(Control) then AlignList.Delete(I);
      end;
    // anchor/align control
    for I := 0 to AlignList.Count - 1 do
      DoPosition(TControl(AlignList[I]), AAlign, I);
  end;
  
  procedure DoAlignNotAligned;
  // All controls, not aligned by their own properties, can be auto aligned.
  var
    i: Integer;
    Control: TControl;
  begin
    // check if ChildSizing aligning is enabled
    if (ChildSizing.Layout = cclNone) then
      exit;

    /// collect all 'not aligned' controls
    AlignList.Clear;
    for i := 0 to ControlCount - 1 do
    begin
      Control := Controls[i];
      if IsNotAligned(Control) and Control.IsControlVisible then
        AlignList.Add(Control);
    end;
    //debugln('DoAlignNotAligned ',DbgSName(Self),' AlignList.Count=',dbgs(AlignList.Count));
    if AlignList.Count = 0 then exit;

    LastBoundsMutated := nil;
    AlignNonAlignedControls(AlignList, BoundsMutated);
  end;
  
var
  i: Integer;
  OldRemainingClientRect: TRect;
  OldRemainingBorderSpace: TRect;
  MaxTries: LongInt;
  r: TRect;
begin
  //DebugLn(['TWinControl.AlignControls ',DbgSName(Self),' ',not (wcfAligningControls in FWinControlFlags)]);
  if wcfAligningControls in FWinControlFlags then exit;
  Include(FWinControlFlags,wcfAligningControls);
  try
    //if csDesigning in ComponentState then begin
      //DebugLn('[TWinControl.AlignControls] ',Name,':',Classname,' ',Left,',',Top,',',Width,',',Height,' AlignWork=',NeedAlignWork,' ControlCount=',ControlCount);
      //if AControl<>nil then DebugLn('  AControl=',AControl.Name,':',AControl.ClassName);
    //end;
    // first let the DockManager align controls
    if DockSite and UseDockManager and (DockManager<>nil) then
      DockManager.ResetBounds(false);
    AdjustClientRect(RemainingClientRect);
    r:=GetLogicalClientRect;
    ParentClientWidth:=r.Right;
    ParentClientHeight:=r.Bottom;

    if NeedAlignWork then
    begin
      //DebugLn(['TWinControl.AlignControls  ',DbgSName(Self),' RemainingClientRect=',dbgs(RemainingClientRect),' ',dbgs(ClientRect)]);
      RemainingBorderSpace:=Rect(0,0,0,0);
      // adjust RemainingClientRect by ChildSizing properties
      AdjustBorderSpace(RemainingClientRect,RemainingBorderSpace,
                     ChildSizing.LeftRightSpacing,ChildSizing.TopBottomSpacing,
                     ChildSizing.LeftRightSpacing,ChildSizing.TopBottomSpacing);
      //DebugLn('[TWinControl.AlignControls] ',Name,':',Classname,' ',Left,',',Top,',',Width,',',Height,' ClientRect=',RemainingClientRect.Left,',',RemainingClientRect.Top,',',RemainingClientRect.Right,',',RemainingClientRect.Bottom);
      AlignList := TFPList.Create;
      try
        // Auto aligning/anchoring can be very interdependent.
        // In worst case the n-2 depends on the n-1, the n-3 depends on n-2
        // and so forth. This is allowed, so do up to n loop step.
        // Do not more, to avoid endless loops, if there are circle
        // dependencies.
        MaxTries:=ControlCount;
        {$IFDEF CHECK_POSITION}inc(MaxTries);{$ENDIF}
        for i:=1 to MaxTries do begin
          // align and anchor child controls
          BoundsMutated:=false;
          OldRemainingClientRect:=RemainingClientRect;
          OldRemainingBorderSpace:=RemainingBorderSpace;
          DoAlign(alTop);
          DoAlign(alBottom);
          DoAlign(alLeft);
          DoAlign(alRight);
          DoAlign(alClient);
          DoAlign(alCustom);
          DoAlign(alNone);
          DoAlignNotAligned;
          if not BoundsMutated then break;
          if (i=ControlCount+1) then begin
            DebugLn(['TWinControl.AlignControls ENDLESS LOOP STOPPED ',DbgSName(Self),' i=',i]);
            if LastBoundsMutated<>nil then
              DebugLn(['TWinControl.AlignControls LAST CHANGED: ',DbgSName(LastBoundsMutated),' Old=',dbgs(LastBoundsMutatedOld),' Now=',dbgs(LastBoundsMutated.BoundsRect)]);
          end;
          // update again
          RemainingClientRect:=OldRemainingClientRect;
          RemainingBorderSpace:=OldRemainingBorderSpace;
        end;
      finally
        AlignList.Free;
      end;
    end;
    ControlsAligned;
  finally
    Exclude(FWinControlFlags,wcfAligningControls);
  end;
end;

function TWinControl.CustomAlignInsertBefore(AControl1, AControl2: TControl): Boolean;
begin
  Result := Assigned(FOnAlignInsertBefore)
        and FOnAlignInsertBefore(Self, AControl1, AControl2);
end;

procedure TWinControl.CustomAlignPosition(AControl: TControl; var ANewLeft, ANewTop,
  ANewWidth, ANewHeight: Integer; var AlignRect: TRect; AlignInfo: TAlignInfo);
begin
  if Assigned(FOnAlignPosition)
  then FOnAlignPosition(Self, AControl, ANewLeft, ANewTop, ANewWidth, ANewHeight, AlignRect, AlignInfo);
end;

function TWinControl.DoAlignChildControls(TheAlign: TAlign; AControl: TControl;
  AControlList: TFPList; var ARect: TRect): Boolean;
begin
  Result:=false;
end;

procedure TWinControl.DoChildSizingChange(Sender: TObject);
begin
  //debugln('TWinControl.DoChildSizingChange ',DbgSName(Self));
  if ControlCount=0 then exit;
  InvalidatePreferredSize;
  ReAlign;
end;

procedure TWinControl.InvalidatePreferredChildSizes;
var
  AControl: TControl;
  i: Integer;
begin
  for i:=0 to ControlCount-1 do begin
    AControl:=Controls[i];
    Exclude(AControl.FControlFlags,cfPreferredSizeValid);
    Exclude(AControl.FControlFlags,cfPreferredMinSizeValid);
    if AControl is TWinControl then
      Exclude(TWinControl(AControl).FWinControlFlags,wcfAdjustedLogicalClientRectValid);
    if AControl is TWinControl then
      TWinControl(AControl).InvalidatePreferredChildSizes;
  end;
end;

{-------------------------------------------------------------------------------
  procedure TWinControl.DoAutoSize;

  Shrink or enlarge to fit children.
-------------------------------------------------------------------------------}
procedure TWinControl.DoAutoSize;
var
  HasVisibleChilds: boolean;

  procedure GetMoveDiffForNonAlignedChilds(const CurClientRect: TRect;
    out dx, dy: integer);
  // how much can non-aligned-children be moved up and left
  // non-aligned-children: no fixed anchoring or autosizing,
  //      (Align=alNone, visible, AnchorSide[].Control=nil)
  // borderspacing is used
  // e.g. dx=10 means all non-align-children should be moved 10 pixels to the left
  var
    NewClientWidth, NewClientHeight: integer;
    Layout: TAutoSizeCtrlData;
  begin
    if ChildSizing.Layout<>cclNone then begin
      dx:=0;
      dy:=0;
      exit;
    end;

    // get the move requirements for the child controls
    Layout:=nil;
    try
      Layout:=TAutoSizeCtrlData.Create(Self);
      Layout.ComputePreferredClientArea(
          not (csAutoSizeKeepChildLeft in ControlStyle),
          not (csAutoSizeKeepChildTop in ControlStyle),
          dx,dy,NewClientWidth,NewClientHeight);
      if (NewClientWidth<>0) or (NewClientHeight<>0) then ;
      //if (dx<>0) or (dy<>0) then DebugLn(['GetMoveDiffForNonAlignedChilds ',DbgSName(Self),' dx=',dx,' dy=',dy]);
    finally
      Layout.Free;
    end;
  end;
  
var
  I: Integer;
  AControl: TControl;
  PreferredWidth: LongInt;
  PreferredHeight: LongInt;
  CurClientRect: TRect;
  WidthIsFixed: boolean;
  HeightIsFixed: boolean;
  NewLeft: LongInt;
  NewTop: LongInt;
  CurAnchors: TAnchors;
  dx: Integer;
  dy: Integer;
  NewChildBounds: TRect;
  OldChildBounds: TRect;
begin
  {$IFDEF VerboseAllAutoSize}
  debugln('TWinControl.DoAutoSize ',DbgSName(Self));
  {$ENDIF}
  if not (caspComputingBounds in AutoSizePhases) then begin
    {$IFDEF VerboseAllAutoSize}
    DebugLn(['TWinControl.DoAutoSize DELAYED AutoSizePhases=',dbgs(AutoSizePhases)]);
    {$ENDIF}
    AdjustSize;
    exit;
  end;

  DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TWinControl.DoAutoSize'){$ENDIF};
  try
    // test if resizing is possible
    HasVisibleChilds:=false;
    for i:=0 to ControlCount-1 do
      if Controls[i].IsControlVisible then begin
        HasVisibleChilds:=true;
        break;
      end;

    CurAnchors:=Anchors;
    if Align<>alNone then CurAnchors:=CurAnchors+AnchorAlign[Align];
    WidthIsFixed:=WidthIsAnchored;
    HeightIsFixed:=HeightIsAnchored;

    // move free children as much as possible to left and top (all free children the same)
    if HasVisibleChilds then begin
      CurClientRect:=GetLogicalClientRect;
      AdjustClientRect(CurClientRect);
      // get minimum left, top of non aligned children
      GetMoveDiffForNonAlignedChilds(CurClientRect,dx,dy);
      //DebugLn(['TWinControl.DoAutoSize ',DbgSName(Self),' CurClientRect=',dbgs(CurClientRect)]);

      if (dx<>0) or (dy<>0) then begin
        // move all free children to left and top of client area
        //DebugLn(['TWinControl.DoAutoSize ',DbgSName(Self),' dx=',dbgs(dx),' dy=',dbgs(dy),' CurClientRect=',dbgs(CurClientRect),' CurAnchors=',dbgs(CurAnchors),' IsFixed: w=',WidthIsFixed,' h=',HeightIsFixed]);
        for I := 0 to ControlCount - 1 do begin
          AControl:=Controls[I];
          if not AControl.IsControlVisible then continue;
          if AControl.Align<>alNone then continue;
          //DebugLn(['TWinControl.DoAutoSize BEFORE ',DbgSName(AControl),' ',dbgs(AControl.BoundsRect)]);
          NewChildBounds:=AControl.BoundsRect;
          if (akLeft in AControl.Anchors)
          and (AControl.AnchorSide[akLeft].Control=nil) then begin
            dec(NewChildBounds.Left,dx);
            if not (akRight in AControl.Anchors) then
              dec(NewChildBounds.Right,dx);
          end;
          if (akTop in AControl.Anchors)
          and (AControl.AnchorSide[akTop].Control=nil) then begin
            dec(NewChildBounds.Top,dy);
            if not (akBottom in AControl.Anchors) then
              dec(NewChildBounds.Bottom,dy);
          end;
          // Important: change the BaseBounds too, otherwise the changes will be undone by AlignControls
          OldChildBounds:=AControl.BoundsRect;
          if not CompareRect(@OldChildBounds,@NewChildBounds) then begin
            //DebugLn(['TWinControl.DoAutoSize moving child: ',DbgSName(AControl),' Old=',dbgs(OldChildBounds),' New=',dbgs(NewChildBounds)]);
            AControl.BoundsRect:=NewChildBounds;
            //DebugLn(['TWinControl.DoAutoSize AFTER ',DbgSName(AControl),' ',dbgs(AControl.BoundsRect)]);
          end;
        end;
      end;
    end;

    // autosize control to preferred size
    if (not WidthIsFixed) or (not HeightIsFixed) then begin
      GetPreferredSize(PreferredWidth,PreferredHeight,
                       false,// with constraints
                       true // with theme space
                       );
      //if ControlCount>0 then DebugLn(['TWinControl.DoAutoSize ',DbgSName(Self),' PreferredWidth=',PreferredWidth,' PreferredHeight=',PreferredHeight,' ControlCount=',ControlCount]);
    end else begin
      PreferredWidth:=0;
      PreferredHeight:=0;
    end;
    if WidthIsFixed or (PreferredWidth<0)
    or ((PreferredWidth=0) and (not (csAutoSize0x0 in ControlStyle))) then
      PreferredWidth:=Constraints.MinMaxWidth(Width);
    if HeightIsFixed or (PreferredHeight<0)
    or ((PreferredHeight=0) and (not (csAutoSize0x0 in ControlStyle))) then
      PreferredHeight:=Constraints.MinMaxHeight(Height);

    // set new size
    {$IF defined(VerboseAutoSize) or defined(VerboseAllAutoSize)}
    debugln(['TWinControl.DoAutoSize A ',DbgSName(Self),' Cur=',Width,'x',Height,' Prefer=',PreferredWidth,'x',PreferredHeight,' WidgetClass=',WidgetSetClass.ClassName,' Fixed=',WidthIsFixed,'x',HeightIsFixed]);
    {$ENDIF}
    if (PreferredWidth<>Width) or (PreferredHeight<>Height) then begin
      // adjust Left/Top as well to reduce auto sizing overhead
      NewLeft:=Left;
      NewTop:=Top;
      if akRight in CurAnchors then
        inc(NewLeft,Width-PreferredWidth);
      if akBottom in CurAnchors then
        inc(NewTop,Height-PreferredHeight);
      //if CompareText(Name,'NewUnitOkButton')=0 then
        //debugln(['DoAutoSize Resize ',DbgSName(Self),' Old=',dbgs(BoundsRect),' New=',dbgs(Bounds(NewLeft,NewTop,PreferredWidth,PreferredHeight)),' WidthIsFixed=',WidthIsFixed,' HeightIsFixed=',HeightIsFixed,' Align=',dbgs(Align),' Anchors=',dbgs(Anchors)]);
      SetBoundsKeepBase(NewLeft,NewTop,PreferredWidth,PreferredHeight);
    end;
  finally
    EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TWinControl.DoAutoSize'){$ENDIF};
  end;
end;

procedure TWinControl.DoAllAutoSize;

  function CheckHandleAllocated(AWinControl: TWinControl): boolean;
  // true if a handle was missing
  var
    i: Integer;
    ChildWinControl: TWinControl;
  begin
    if AWinControl.HandleObjectShouldBeVisible and (not AWinControl.HandleAllocated) then
    begin
      {$IFDEF VerboseAllAutoSize}
      DebugLn(['TWinControl.DoAllAutoSize CREATE HANDLE ',DbgSName(AWinControl)]);
      {$ENDIF}
      AWinControl.HandleNeeded;
      Exit(True);
    end;
    Result := False;
    for i := 0 to AWinControl.ControlCount - 1 do
    begin
      ChildWinControl := TWinControl(AWinControl.Controls[i]);
      if (ChildWinControl is TWinControl) and CheckHandleAllocated(ChildWinControl) then
        Result:=true;
    end;
  end;

  procedure ClearRequests(AControl: TControl);
  var
    i: Integer;
  begin
    Exclude(AControl.FControlFlags,cfAutoSizeNeeded);
    if AControl is TWinControl then
      for i:=0 to TWinControl(AControl).ControlCount-1 do
        ClearRequests(TWinControl(AControl).Controls[i]);
  end;

  procedure UpdateShowingRecursive(AWinControl: TWinControl;
    OnlyChildren: boolean);
  var
    i: Integer;
  begin
    // first make the children visible
    if AWinControl.FControls<>nil then
      for i:=0 to AWinControl.FControls.Count-1 do
        if TObject(AWinControl.FControls[i]) is TWinControl then
          UpdateShowingRecursive(TWinControl(AWinControl.FControls[i]),false);
    // then make the control visible
    if not OnlyChildren and AWinControl.HandleObjectShouldBeVisible and not AWinControl.Showing then
      AWinControl.UpdateShowing;
  end;

var
  RealizeCounter: Integer;
  UpdateShowingCounter: Integer;
begin
  if wcfAllAutoSizing in FWinControlFlags then exit;
  if AutoSizeDelayed then exit;

  {$IFDEF VerboseAllAutoSize}
  DebugLn(['TWinControl.DoAllAutoSize START ',DbgSName(Self),' ',dbgs(BoundsRect)]);
  {$ENDIF}
  Include(FWinControlFlags,wcfAllAutoSizing);
  try
    // create needed handles
    if HandleObjectShouldBeVisible then begin
      if CheckHandleAllocated(Self) then begin
        // a new handle was created
      end;
    end else begin
      // no autosize possible => remove needed flags
      ClearRequests(Self);
      exit;
    end;

    RealizeCounter:=0;
    UpdateShowingCounter:=0;
    while (not AutoSizeDelayed) do
    begin
      // compute all sizes for LCL objects without touching the widgetset
      {$IFDEF VerboseAllAutoSize}
      DebugLn(['TWinControl.DoAllAutoSize COMPUTE BOUNDS ',DbgSName(Self),' old=',dbgs(BoundsRect)]);
      {$ENDIF}
      inherited DoAllAutoSize;
      if cfAutoSizeNeeded in FControlFlags then RaiseGDBException('');
      AllAutoSized;
      // send all new bounds to widgetset
      {$IFDEF VerboseAllAutoSize}
      DebugLn(['TWinControl.DoAllAutoSize REALIZE BOUNDS ',DbgSName(Self),' lclbounds=',dbgs(BoundsRect)]);
      {$ENDIF}
      inc(RealizeCounter);
      if RealizeCounter=100 then
        Include(FWinControlFlags,wcfKillIntfSetBounds);
      RealizeBoundsRecursive;
      if (cfAutoSizeNeeded in FControlFlags) then continue; // repeat computing bounds
      RealizeCounter:=0;
      inc(UpdateShowingCounter);
      // make child handles visible
      {$IFDEF VerboseAllAutoSize}
      DebugLn(['TWinControl.DoAllAutoSize UPDATESHOWING children ',DbgSName(Self),' lclbounds=',dbgs(BoundsRect)]);
      {$ENDIF}
      Include(FWinControlFlags,wcfUpdateShowing);
      try
        UpdateShowingRecursive(Self,true);
      finally
        Exclude(FWinControlFlags,wcfUpdateShowing);
      end;
      // check if another turn is needed
      if not (cfAutoSizeNeeded in FControlFlags) then break; // complete
    end;
    {$IFDEF VerboseAllAutoSize}
    DebugLn(['TWinControl.DoAllAutoSize END ',DbgSName(Self),' ',dbgs(BoundsRect)]);
    {$ENDIF}
  finally
    FWinControlFlags:=FWinControlFlags-[wcfAllAutoSizing,wcfKillIntfSetBounds];
  end;
  // make handle visible => this can trigger events like Form.OnShow where
  // application does arbitrary stuff
  {$IFDEF VerboseAllAutoSize}
  DebugLn(['TWinControl.DoAllAutoSize UPDATESHOWING self ',DbgSName(Self),' lclbounds=',dbgs(BoundsRect)]);
  {$ENDIF}
  if not (wcfUpdateShowing in FWinControlFlags) then
  begin
    Include(FWinControlFlags, wcfUpdateShowing);
    try
      if HandleObjectShouldBeVisible and not Showing then
        UpdateShowing
      else begin
        {$IFDEF VerboseAllAutoSize}
        DebugLn(['TWinControl.DoAllAutoSize not UPDATESHOWING self ',DbgSName(Self),' because HandleObjectShouldBeVisible=',HandleObjectShouldBeVisible,' Showing=',Showing]);
        {$ENDIF}
      end;
    finally
      Exclude(FWinControlFlags, wcfUpdateShowing);
    end;
  end;
end;

procedure TWinControl.AllAutoSized;
begin
  // see TCustomForm.AllAutoSized
end;

{------------------------------------------------------------------------------
  TWinControl BroadCast
------------------------------------------------------------------------------}
procedure TWinControl.BroadCast(var ToAllMessage);
var
  I: Integer;
begin
  for I := 0 to ControlCount - 1 do
  begin
    Controls[I].WindowProc(TLMessage(ToAllMessage));
    if TLMessage(ToAllMessage).Result <> 0 then Exit;
  end;
end;

procedure TWinControl.NotifyControls(Msg: Word);
var
  ToAllMessage: TLMessage;
begin
  ToAllMessage.Msg := Msg;
  ToAllMessage.WParam := 0;
  ToAllMessage.LParam := 0;
  ToAllMessage.Result := 0;
  Broadcast(ToAllMessage);
end;

procedure TWinControl.DefaultHandler(var AMessage);
begin
  TWSWinControlClass(WidgetSetClass).DefaultWndHandler(Self, AMessage);
end;


{------------------------------------------------------------------------------
  TWinControl CanFocus


------------------------------------------------------------------------------}
function TWinControl.CanFocus: Boolean;
var
  Control: TWinControl;
  Form: TCustomForm;
begin
  Result := False;
  //Verify that every parent is enabled and visible before returning true.
  Form := GetParentForm(Self);
  if Form <> nil then
  begin
    Control := Self;
    repeat
      if Control = Form then break;
      // test all except the Form if it is visible and enabled
      if not (Control.IsControlVisible and Control.Enabled) then Exit;
      Control := Control.Parent;
    until False;
    Result := True;
  end;
end;

{------------------------------------------------------------------------------
  TWinControl CanSetFocus

  CanSetFocus should be prefered over CanFocus if used in CanSetFocus/SetFocus
  combination

  if MyControl.CanSetFocus then
    MyControl.SetFocus;

  because it checks also if the parent form can receive focus and thus prevents
  the "cannot focus an invisible window" LCL exception.
------------------------------------------------------------------------------}
function TWinControl.CanSetFocus: Boolean;
var
  Control: TWinControl;
begin
  Control := Self;
  while True do
  begin
    // test if all are visible and enabled
    if not (Control.IsControlVisible and Control.Enabled) then
      Exit(False);
    if not Assigned(Control.Parent) then
      Break;
    Control := Control.Parent;
  end;
  Result := Control is TCustomForm;//the very top parent must be a form
end;

{------------------------------------------------------------------------------
  TWinControl CreateSubClass
------------------------------------------------------------------------------}
procedure TWinControl.CreateSubClass(var Params: TCreateParams;
  ControlClassName: PChar);
begin
  // TODO: Check if we need this method
end;

{------------------------------------------------------------------------------
  TWinControl DisableAlign
------------------------------------------------------------------------------}
procedure TWinControl.DisableAlign;
begin
  DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TWinControl.DisableAlign'){$ENDIF};
end;

{-------------------------------------------------------------------------------
  TWinControl DoAdjustClientRectChange

  Asks the interface if clientrect has changed since last AlignControl
  and calls AdjustSize on change.
-------------------------------------------------------------------------------}
procedure TWinControl.DoAdjustClientRectChange(const InvalidateRect: Boolean = True);
var
  R: TRect;
begin
  if InvalidateRect then
    InvalidateClientRectCache(False);
  R := GetClientRect;
  AdjustClientRect(R);
  //if CheckPosition(Self) then
    //DebugLn(['TWinControl.DoAdjustClientRectChange ',DbgSName(Self),' new=',dbgs(r),' old=',dbgs(FAdjustClientRectRealized),' ',CompareRect(@r,@FAdjustClientRectRealized)]);
  if not CompareRect(@R, @FAdjustClientRectRealized) then
  begin
    // client rect changed since last AlignControl
    {$IF defined(VerboseAllAutoSize) or defined(VerboseClientRectBugFix) or defined(VerboseIntfSizing) or defined(VerboseOnResize)}
    DebugLn(['TWinControl.DoAdjustClientRectChange ClientRect changed ',DbgSName(Self),
      ' Old=',Dbgs(FAdjustClientRectRealized),' New=',DbgS(R)]);
    {$ENDIF}
    FAdjustClientRectRealized := R;
    AdjustSize;
    Resize;
  end;
end;

{-------------------------------------------------------------------------------
  TWinControl DoConstraintsChange
  Params: Sender : TObject

  Call inherited, then send the constraints to the interface
-------------------------------------------------------------------------------}
procedure TWinControl.DoConstraintsChange(Sender : TObject);
begin
  inherited DoConstraintsChange(Sender);
  //debugln('TWinControl.DoConstraintsChange ',DbgSName(Self),' HandleAllocated=',dbgs(HandleAllocated));
  if HandleAllocated then
    TWSWinControlClass(WidgetSetClass).ConstraintsChange(Self);
end;

{-------------------------------------------------------------------------------
  TWinControl InvalidateClientRectCache(WithChildControls: boolean)

  The clientrect is cached. Call this procedure to invalidate the cache, so that
  next time the clientrect is fetched from the interface.
-------------------------------------------------------------------------------}
procedure TWinControl.InvalidateClientRectCache(WithChildControls: boolean);
var
  I: Integer;
begin
  {$IFDEF VerboseClientRectBugFix}
  if Name=CheckClientRectName then begin
    DebugLn(['TWinControl.InvalidateClientRectCache ',DbgSName(Self)]);
    //DumpStack;
  end;
  {$ENDIF}
  Exclude(FWinControlFlags,wcfAdjustedLogicalClientRectValid);
  Include(FWinControlFlags,wcfClientRectNeedsUpdate);

  if WithChildControls then begin
    // invalidate clients too
    if Assigned(FControls) then
      for I := 0 to FControls.Count - 1 do
        if TObject(FControls.Items[I]) is TWinControl then
          TWinControl(FControls.Items[I]).InvalidateClientRectCache(true);
  end;
  InvalidatePreferredSize;
end;

{-------------------------------------------------------------------------------
  TWinControl ClientRectNeedsInterfaceUpdate

  The clientrect is cached. Check if cache is valid.
-------------------------------------------------------------------------------}
function TWinControl.ClientRectNeedsInterfaceUpdate: boolean;
var
  InterfaceWidth, InterfaceHeight: integer;
  IntfClientRect: TRect;
begin
  if (not HandleAllocated) or (csDestroyingHandle in ControlState)
  or (csDestroying in ComponentState)
  then
    exit(false);
  if wcfClientRectNeedsUpdate in FWinControlFlags then
    exit(true);
  // get the current interface bounds
  LCLIntf.GetWindowSize(Handle,InterfaceWidth,InterfaceHeight);
  LCLIntf.GetClientRect(Handle,IntfClientRect);
  // The LCL is not always in sync with the interface.
  // Add the difference between LCL size and interface size to the
  // interface clientrect
  inc(IntfClientRect.Right,Width-InterfaceWidth);
  inc(IntfClientRect.Bottom,Height-InterfaceHeight);
  Result:=(FClientWidth<>IntfClientRect.Right)
       or (FClientHeight<>IntfClientRect.Bottom);
  {$IFDEF VerboseClientRectBugFix}
  if (Name=CheckClientRectName) and Result then
    DebugLn(['TWinControl.ClientRectNeedsInterfaceUpdate ',DbgSName(Self),' ',dbgs(IntfClientRect)]);
  {$ENDIF}
end;

{-------------------------------------------------------------------------------
  TWinControl DoSetBounds
  Params: ALeft, ATop, AWidth, AHeight : integer

  Anticipate the new clientwidth/height and call inherited

  Normally the clientwidth/clientheight is adjusted automatically by the
  interface. But it is up to interface when this will be done. The gtk for
  example just puts resize requests into a queue. The LCL would resize the
  children just after this procedure due to the clientrect. On complex forms with
  lots of nested controls, this would result in thousands of resizes.
  Changing the clientrect in the LCL to the most probable size reduces
  unneccessary resizes.
-------------------------------------------------------------------------------}
procedure TWinControl.DoSetBounds(ALeft, ATop, AWidth, AHeight : integer);
var
  OldWidth: LongInt;
  OldHeight: LongInt;
begin
  //DbgOut('[TWinControl.DoSetBounds] ',Name,':',ClassName,' OldHeight=',DbgS(FHeight),' NewHeight=',DbgS(AHeight));
  OldWidth:=Width;
  OldHeight:=Height;
  inherited DoSetBounds(ALeft, ATop, AWidth, AHeight);
  // adapt Clientrect
  inc(FClientWidth,Width-OldWidth);
  if FClientWidth<0 then FClientWidth:=0;
  inc(FClientHeight,Height-OldHeight);
  if FClientHeight<0 then FClientHeight:=0;
  Exclude(FWinControlFlags,wcfAdjustedLogicalClientRectValid);
end;

{------------------------------------------------------------------------------
  TWinControl EnableAlign
------------------------------------------------------------------------------}
procedure TWinControl.EnableAlign;
begin
  EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TWinControl.DisableAlign'){$ENDIF};
end;

procedure TWinControl.WriteLayoutDebugReport(const Prefix: string);
var
  i: Integer;
begin
  inherited WriteLayoutDebugReport(Prefix);
  for i:=0 to ControlCount-1 do
    Controls[i].WriteLayoutDebugReport(Prefix+'  ');
end;

procedure TWinControl.AutoAdjustLayout(AMode: TLayoutAdjustmentPolicy;
  const AFromDPI, AToDPI, AOldFormWidth, ANewFormWidth: Integer);
var
  i: Integer;
begin
  inherited AutoAdjustLayout(AMode, AFromDPI, AToDPI, AOldFormWidth, ANewFormWidth);

  for i:=0 to ControlCount-1 do
    Controls[i].AutoAdjustLayout(AMode, AFromDPI, AToDPI, AOldFormWidth, ANewFormWidth);
end;

{------------------------------------------------------------------------------
  TWinControl.CanTab
------------------------------------------------------------------------------}
function TWinControl.CanTab: Boolean;
begin
  Result := CanFocus and TWSWinControlClass(WidgetSetClass).CanFocus(Self);
end;

function TWinControl.DoDragMsg(ADragMessage: TDragMessage; APosition: TPoint; ADragObject: TDragObject; ATarget: TControl; ADocking: Boolean):LRESULT;
var
  TargetControl: TControl;
begin
  case ADragMessage of
    dmFindTarget:
      begin
        {$IFDEF VerboseDrag}
        DebugLn('TWinControl.DoDragMsg dmFindTarget ',Name,':',ClassName,' Start DragMsg.DragRec^.Pos=',IntToStr(APosition.X),',',IntToStr(APosition.Y));
        {$ENDIF}
        TargetControl := ControlAtPos(ScreentoClient(APosition),
                                      [capfAllowWinControls,capfRecursive]);
        if TargetControl = nil then TargetControl := Self;
        {$IFDEF VerboseDrag}
        DebugLn('TWinControl.DoDragMsg dmFindTarget ',Name,':',ClassName,' end Result=',TargetControl.Name,':',TargetControl.ClassName);
        {$ENDIF}
        Result := LRESULT(TargetControl);
      end;
    else
      Result := inherited;
  end;
end;

{------------------------------------------------------------------------------
  TWinControl GetChildren
------------------------------------------------------------------------------}
procedure TWinControl.GetChildren(Proc: TGetChildProc; Root: TComponent);
var
  I : Integer;
  Control : TControl;
begin
  for I := 0 to ControlCount-1 do
  begin
    Control := Controls[i];
    if Control.Owner = Root then Proc(Control);
  end;
end;

{-------------------------------------------------------------------------------
  function TWinControl.ChildClassAllowed(ChildClass: TClass): boolean;

  Allow TControl as child.
-------------------------------------------------------------------------------}
function TWinControl.ChildClassAllowed(ChildClass: TClass): boolean;
begin
  Result:=(ChildClass<>nil) and ChildClass.InheritsFrom(TControl);
end;

{-------------------------------------------------------------------------------
  TWinControl GetClientOrigin
  Result:  TPoint

  returns the screen coordinate of the topleft coordinate 0,0 of the client area
  Note that this value is the position as stored in the interface and is not
  always in sync with the LCL. When a control is moved, the LCL sets the bounds
  to the wanted position and sends a move message to the interface. It is up to
  the interface to handle moves instantly or queued.
-------------------------------------------------------------------------------}
function TWinControl.GetClientOrigin: TPoint;
var
  AControl: TWinControl;
begin
  Result.X := 0;
  Result.Y := 0;
  if HandleAllocated then
  begin
    // get the interface idea where the client area is on the screen
    LCLIntf.ClientToScreen(Handle, Result);
    // adjust the result by all bounds, that are not yet sent to the interface
    AControl := Self;
    repeat
      inc(Result.X, AControl.Left - AControl.FBoundsRealized.Left);
      inc(Result.Y, AControl.Top - AControl.FBoundsRealized.Top);
      AControl := AControl.Parent;
    until AControl = nil;
  end else
  if Parent <> nil then
    Result := inherited GetClientOrigin;
end;

{-------------------------------------------------------------------------------
  TWinControl GetClientRect
  Result:  TRect

  returns the client area. Starting at 0,0.
-------------------------------------------------------------------------------}
function TWinControl.GetClientRect: TRect;

  procedure StoreClientRect(NewClientRect: TRect);
  var
    ClientSizeChanged: boolean;
  begin
    if wcfClientRectNeedsUpdate in FWinControlFlags then begin
      ClientSizeChanged:=(FClientWidth<>NewClientRect.Right)
                      or (FClientHeight<>NewClientRect.Bottom);
      if ClientSizeChanged then begin
        FClientWidth:=NewClientRect.Right;
        FClientHeight:=NewClientRect.Bottom;
        {$IF defined(VerboseNewAutoSize) or defined(CHECK_POSITION)}
        {$IFDEF CHECK_POSITION}
        if CheckPosition(Self) then
        {$ENDIF}
          DebugLn(['StoreClientRect ',Name,':',ClassName,' ',FClientWidth,',',FClientHeight,' HandleAllocated=',HandleAllocated]);
        {$ENDIF}
        {$IFDEF VerboseClientRectBugFix}
        DebugLn(['StoreClientRect ',DbgSName(Self),' ',FClientWidth,',',FClientHeight,' HandleAllocated=',HandleAllocated,' wcfBoundsRealized=',wcfBoundsRealized in FWinControlFlags]);
        {$ENDIF}
        Exclude(FWinControlFlags,wcfAdjustedLogicalClientRectValid);
      end;
      Exclude(FWinControlFlags,wcfClientRectNeedsUpdate);
    end;
  end;

  procedure GetDefaults(var r: TRect);
  begin
    r:=inherited GetClientRect;
    if csLoading in ComponentState then begin
      if cfClientWidthLoaded in FControlFlags then
        r.Right:=FLoadedClientSize.cx;
      if cfClientHeightLoaded in FControlFlags then
        r.Bottom:=FLoadedClientSize.cy;
    end;
  end;

var
  InterfaceWidth, InterfaceHeight: integer;
begin
  if wcfClientRectNeedsUpdate in FWinControlFlags then begin
    //DebugLn(['TWinControl.GetClientRect ',DbgSName(Self),' ',HandleAllocated,' ',wcfBoundsRealized in FWinControlFlags]);
    if TWSWinControlClass(WidgetSetClass).GetDefaultClientRect(Self,
      Left, Top, Width, Height, Result)
    then begin
      // the LCL interface provided a ClientRect
    end
    else if HandleAllocated then
    begin
      // update clientrect from interface
      LCLIntf.GetClientRect(Handle, Result);
      // the LCL is not always in sync with the interface
      // -> adjust client rect based on LCL bounds
      // for example: if the Width in LCL differ from the Width of the Interface
      // object, then adjust the clientwidth accordingly
      // this often anticipates later LM_SIZE messages from the interface
      // and reduces resizes
      LCLIntf.GetWindowSize(Handle, InterfaceWidth, InterfaceHeight);
      {$IF defined(VerboseNewAutoSize) or defined(CHECK_POSITION)}
      {$IFDEF CHECK_POSITION}
      if CheckPosition(Self) then
      {$ENDIF}
        debugln('TWinControl.GetClientRect ',DbgSName(Self),' Interface=',dbgs(InterfaceWidth),',',dbgs(InterfaceHeight),' Result=',dbgs(Result),' Bounds=',dbgs(BoundsRect));
      {$ENDIF}
      {$IFDEF VerboseClientRectBugFix}
      //if Name=CheckClientRectName then
        debugln('TWinControl.GetClientRect ',DbgSName(Self),' InterfaceSize=',dbgs(InterfaceWidth),',',dbgs(InterfaceHeight),' Result=',dbgs(Result),' Bounds=',dbgs(BoundsRect));
      {$ENDIF}
      if (Width<>InterfaceWidth)
      or (Height<>InterfaceHeight) then
      begin
        // the LCL is not in sync with the interface
        if wcfBoundsRealized in FWinControlFlags then
        begin
          // no bounds were sent yet to the interface and it didn't initialize
          // them on its own
          // => the client bounds from the interface are not yet ready
          // they will probably change
          // to avoid resizes it is better use the defaults
          GetDefaults(Result);
        end else begin
          // -> adjust client rect based on LCL bounds
          // for example: if the Width in LCL differ from the Width of the Interface
          // object, then adjust the clientwidth accordingly
          // this often anticipates later LM_SIZE messages from the interface
          // and reduces resizes
          inc(Result.Right,Width-InterfaceWidth);
          inc(Result.Bottom,Height-InterfaceHeight);
        end;
      end;
    end else begin
      // no handle and no interface help => use defaults
      GetDefaults(Result);
    end;
    Result.Right:=Max(Result.Left,Result.Right);
    Result.Bottom:=Max(Result.Top,Result.Bottom);
    StoreClientRect(Result);

    {r:=inherited GetClientRect;
    if (r.Left<>Result.Left)
    or (r.Top<>Result.Top)
    or (r.Right<>Result.Right)
    or (r.Bottom<>Result.Bottom) then begin
      //DebugLn(' TWinControl.GetClientRect ',Name,':',ClassName,
      //  ' Old=',r.Left,',',r.Top,',',r.Right,',',r.Bottom,
      //  ' New=',Result.Left,',',Result.Top,',',Result.Right,',',Result.Bottom
      //  );
    end;}

  end else begin
    Result:=Rect(0,0,FClientWidth,FClientHeight);
  end;
end;

{-------------------------------------------------------------------------------
  TWinControl GetControlOrigin
  Result:  TPoint

  Returns the screen coordinate of the topleft coordinate 0,0 of the control
  area. (The topleft pixel of the control on the screen)
  Note that this value is the position as stored in the interface and is not
  always in sync with the LCL. When a control is moved, the LCL sets the bounds
  to the wanted position and sends a move message to the interface. It is up to
  the interface to handle moves instantly or queued.
-------------------------------------------------------------------------------}
function TWinControl.GetControlOrigin: TPoint;
var
  AControl: TWinControl;
  IntfBounds: TRect;
begin
  if HandleAllocated then
  begin
    // get the interface idea where the client area is on the screen
    LCLIntf.GetWindowRect(Handle,IntfBounds);
    Result.X := IntfBounds.Left;
    Result.Y := IntfBounds.Top;
    // adjust the result by all bounds, that are not yet sent to the interface
    AControl := Self;
    repeat
      inc(Result.X, AControl.Left - AControl.FBoundsRealized.Left);
      inc(Result.Y, AControl.Top - AControl.FBoundsRealized.Top);
      AControl := AControl.Parent;
    until AControl = nil;
  end else
    Result:=inherited GetControlOrigin;
end;

{------------------------------------------------------------------------------
  function TWinControl.GetChildrenRect(Scrolled: boolean): TRect;

  Returns the Client rectangle relative to the controls left, top.
  If Scrolled is true, the rectangle is moved by the current scrolling values
  (for an example see TScrollingWincontrol).
------------------------------------------------------------------------------}
function TWinControl.GetChildrenRect(Scrolled: boolean): TRect;
var
  ScrolledOffset: TPoint;
begin
  if HandleAllocated then begin
    LCLIntf.GetClientBounds(Handle,Result);
    if Scrolled then begin
      ScrolledOffset:=GetClientScrollOffset;
      inc(Result.Left,ScrolledOffset.X);
      inc(Result.Top,ScrolledOffset.Y);
      inc(Result.Right,ScrolledOffset.X);
      inc(Result.Bottom,ScrolledOffset.Y);
    end;
  end else
    Result:=inherited GetChildrenRect(Scrolled);
end;

{------------------------------------------------------------------------------
  TWinControl SetBorderStyle
------------------------------------------------------------------------------}
procedure TWinControl.SetBorderStyle(NewStyle: TBorderStyle);
begin
  FBorderStyle := NewStyle;
  if HandleAllocated then
    TWSWinControlClass(WidgetSetClass).SetBorderStyle(Self, NewStyle);
end;

{------------------------------------------------------------------------------
  TWinControl SetBorderWidth
------------------------------------------------------------------------------}
procedure TWinControl.SetBorderWidth(Value: TBorderWidth);
begin
  if FBorderWidth = Value then exit;
  FBorderWidth := Value;
  Perform(CM_BORDERCHANGED, 0, 0);
end;

procedure TWinControl.SetParentWindow(const AValue: HWND);
begin
  if (ParentWindow = AValue) or Assigned(Parent) then Exit;
  FParentWindow := AValue;
  if HandleAllocated then
    if (AValue <> 0) then
      LCLIntf.SetParent(Handle, AValue)
    else
      DestroyHandle;
  UpdateControlState;
end;

{------------------------------------------------------------------------------
  TWinControl.SetChildZPosition

  Set the position of the child control in the TWinControl(s)
------------------------------------------------------------------------------}
procedure TWinControl.SetChildZPosition(const AChild: TControl;
  const APosition: Integer);
var
  OldPos, NewPos: Integer;
  IsWinControl: boolean;
  i: Integer;
  WinControls: TFPList;
begin
  if AChild = nil
  then begin
    DebugLn('WARNING: TWinControl.SetChildZPosition: Child = nil');
    Exit;
  end;

  IsWinControl :=  AChild is TWincontrol;
  
  if FControls = nil then
  begin
    DebugLn('WARNING: TWinControl.SetChildZPosition: Unknown child');
    Exit;
  end;
  OldPos := FControls.IndexOf(AChild);
  if OldPos<0 then begin
    DebugLn('WARNING: TWinControl.SetChildZPosition: Not a child');
    Exit;
  end;

  NewPos := APosition;
  
  if NewPos < 0 then
    NewPos := 0;
  if NewPos >= FControls.Count then
    NewPos := FControls.Count - 1;
  
  if NewPos = OldPos then Exit;
  
  FControls.Move(OldPos, NewPos);
  
  if IsWinControl then
  begin
    if HandleAllocated and TWinControl(AChild).HandleAllocated then
    begin
      // ignore children without handle
      WinControls:=TFPList.Create;
      try
        for i:=FControls.Count-1 downto 0 do
        begin
          if (TObject(FControls[i]) is TWinControl) then
          begin
            WinControls.Add(FControls[i]);
          end else begin
            if i<OldPos then dec(OldPos);
            if i<NewPos then dec(NewPos);
          end;
        end;
        TWSWinControlClass(WidgetSetClass).SetChildZPosition(Self,
                              TWinControl(AChild), OldPos, NewPos, WinControls);
      finally
        WinControls.Free;
      end;
    end;
  end
  else begin
    AChild.InvalidateControl(AChild.IsVisible, True, True);
  end;
end;

{------------------------------------------------------------------------------
       TWinControl.SetTabOrder
------------------------------------------------------------------------------}
procedure TWinControl.SetTabOrder(NewTabOrder: TTabOrder);
begin
  if csLoading in ComponentState then
    FTabOrder := NewTabOrder
  else
    UpdateTabOrder(NewTabOrder);
end;

procedure TWinControl.SetTabStop(NewTabStop: Boolean);
begin
  if FTabStop = NewTabStop then
    Exit;
  FTabStop := NewTabStop;
  UpdateTabOrder(FTabOrder);
  Perform(CM_TABSTOPCHANGED, 0, 0);
end;

{------------------------------------------------------------------------------
  TControl UpdateTabOrder
------------------------------------------------------------------------------}
procedure TWinControl.UpdateTabOrder(NewTabOrder: TTabOrder);
var
  Count: Integer;
begin
  if FParent <> nil then
  begin
    FTabOrder := GetTabOrder;
    Count := ListCount(FParent.FTabList);
    if NewTabOrder < 0 then
      NewTabOrder := Count;
    if FTabOrder = -1 then
      Inc(Count);
    if NewTabOrder > Count then
      NewTabOrder := Count;
    if NewTabOrder <> FTabOrder then
    begin
      if FTabOrder <> - 1 then
        ListDelete(FParent.FTabList,FTabOrder);
      if NewTabOrder <> -1 then
      begin
        if NewTabOrder = Count then
          ListAdd(FParent.FTabList,Self)
        else
          ListInsert(FParent.FTabList,NewTabOrder,Self);
        FTabOrder := NewTabOrder;
      end;
    end;
  end;
end;

{-------------------------------------------------------------------------------
  procedure TWinControl.SendMoveSizeMessages(SizeChanged, PosChanged: boolean);
  
  Send Move and Size messages through the LCL message paths. This simulates the
  VCL behaviour and has no real effect.
-------------------------------------------------------------------------------}
procedure TWinControl.SendMoveSizeMessages(SizeChanged, PosChanged: boolean);
var
  SizeMsg : TLMSize;
  MoveMsg : TLMMove;
  //Flags: UINT;
begin
  if (not HandleAllocated)
  or ((not SizeChanged) and (not PosChanged)) then exit;

  Perform(LM_WindowposChanged, 0, 0);

  if SizeChanged then
  begin
    with SizeMsg do
    begin
      Msg := LM_SIZE;
      SizeType := 6; // force realign
      Width := FWidth;
      Height := FHeight;
      {$IFDEF CHECK_POSITION}
      if CheckPosition(Self) then
      DebugLn(' [TControl.SendMoveSizeMessages] ',Name,':',ClassName,' SizeMsg Width=',DbgS(Width),' Height=',DbgS(Height));
      {$ENDIF}
    end;
    WindowProc(TLMessage(SizeMsg));
  end;
  
  if PosChanged then
  begin
    with MoveMsg do
    begin
      Msg:= LM_MOVE;
      MoveType:= 1;
      XPos:= FLeft;
      YPos:= FTop;
      {$IFDEF CHECK_POSITION}
      if CheckPosition(Self) then
      DebugLn(' [TControl.SendMoveSizeMessages] ',Name,':',ClassName,' MoveMsg XPos=',Dbgs(XPos),' YPos=',Dbgs(YPos));
      {$ENDIF}
    end;
    WindowProc(TLMessage(MoveMsg));
  end;
end;

{------------------------------------------------------------------------------
  TWinControl UpdateShowing

  Check control's handle visibility.
  If handle should become visible the handle and child handles are created.
  The
------------------------------------------------------------------------------}
procedure TWinControl.UpdateShowing;

  procedure ChangeShowing(bShow: Boolean);
  begin
    if FShowing = bShow then Exit;
    FShowing := bShow;
    try
      {$IFDEF VerboseShowing}
      DebugLn(['ChangeShowing ',DbgSName(Self),' new FShowing=',FShowing]);
      {$ENDIF}
      Perform(CM_SHOWINGCHANGED, 0, 0); // see TWinControl.CMShowingChanged
    finally
      if FShowing<>(wcfHandleVisible in FWinControlFlags) then
      begin
        FShowing := wcfHandleVisible in FWinControlFlags;
        DebugLn(['TWinControl.UpdateShowing.ChangeShowing failed for ',DbgSName(Self),', Showing reset to ',FShowing]);
      end;
    end;
  end;

var
  bShow: Boolean;
  n: Integer;
begin
  bShow := HandleObjectShouldBeVisible;

  if bShow then
  begin
    if not HandleAllocated then CreateHandle;
    if Assigned(FControls) then
    begin
      for n := 0 to FControls.Count - 1 do
        if TObject(FControls[n]) is TWinControl then
          TWinControl(FControls[n]).UpdateShowing;
    end;
  end;
  if not HandleAllocated then
  begin
    {$IFDEF VerboseShowing}
    if bShow then
      DebugLn(['TWinControl.UpdateShowing ',DbgSName(Self),' handle not allocated']);
    {$ENDIF}
    Exit;
  end;

  if FShowing = bShow then Exit;
  //DebugLn(['TWinControl.UpdateShowing ',dbgsName(Self),' FShowing=',dbgs(FShowing),' bShow=',dbgs(bShow), ' IsWindowVisible=', IsWindowVisible(FHandle)]);
  if bShow then
  begin
    // the Handle should become visible
    // delay this until all other autosizing has been processed
    if AutoSizeDelayed or (not (caspShowing in AutoSizePhases)) then
    begin
      {$IFDEF VerboseShowing}
      if AutoSizeDelayed then DebugLn(['TWinControl.UpdateShowing ',DbgSName(Self),' SKIPPING because AutoSizeDelayed: ',AutoSizeDelayedReport]);
      if (not (caspShowing in AutoSizePhases)) then DebugLn(['TWinControl.UpdateShowing ',DbgSName(Self),' SKIPPING because wrong phase']);
      {$ENDIF}
      exit;
    end;
  end;
  ChangeShowing(bShow);
end;

procedure TWinControl.Update;
begin
  if HandleAllocated then UpdateWindow(Handle);
end;

{------------------------------------------------------------------------------
  TWinControl Focused
------------------------------------------------------------------------------}
function TWinControl.Focused: Boolean;
begin
  Result := CanTab and (HandleAllocated and (FindOwnerControl(GetFocus)=Self));
end;

function TWinControl.PerformTab(ForwardTab: boolean): boolean;
var
  NewFocus: TWinControl;
  ParentForm: TCustomForm;
begin
  Result := True;
  ParentForm := GetParentForm(Self);
  if ParentForm = nil then
    Exit;
  NewFocus := ParentForm.FindNextControl(Self, ForwardTab, True, False);
  if NewFocus = nil then
    Exit;

  NewFocus.SetFocus;
  Result := NewFocus.Focused;
end;

{------------------------------------------------------------------------------
  TWinControl SelectNext
  
  Find next control (Tab control or Child control).
  Like VCL the CurControl parameter is ignored.
------------------------------------------------------------------------------}
procedure TWinControl.SelectNext(CurControl: TWinControl; GoForward,
  CheckTabStop: Boolean);
begin
  CurControl := FindNextControl(CurControl, GoForward,
                                CheckTabStop, not CheckTabStop);
  if CurControl <> nil then CurControl.SetFocus;
end;

procedure TWinControl.SetTempCursor(Value: TCursor);
begin
  if not HandleAllocated then exit;
  TWSWinControlClass(WidgetSetClass).SetCursor(Self, Screen.Cursors[Value]);
end;

{------------------------------------------------------------------------------
  TWinControl FindChildControl
------------------------------------------------------------------------------}
function TWinControl.FindChildControl(const ControlName: String): TControl;
var
  I: Integer;
begin
  if FControls <> nil then
    for I := 0 to FControls.Count - 1 do begin
      Result:=TControl(FControls[I]);
      if CompareText(Result.Name, ControlName) = 0 then
        exit;
    end;
  Result := nil;
end;

procedure TWinControl.FlipChildren(AllLevels: Boolean);
var
  i: Integer;
  FlipControls: TFPList;
  CurControl: TControl;
begin
  if ControlCount = 0 then exit;
  FlipControls := TFPList.Create;
  
  DisableAlign;
  try
    // Collect all controls with Align Right and Left
    for i := 0 to ControlCount - 1 do begin
      CurControl:=Controls[i];
      if CurControl.Align in [alLeft,alRight] then
        FlipControls.Add(CurControl);
    end;
    // flip the rest
    DoFlipChildren;
    // reverse Right and Left alignments
    while FlipControls.Count > 0 do begin
      CurControl:=TControl(FlipControls[FlipControls.Count-1]);
      if CurControl.Align=alLeft then
        CurControl.Align:=alRight
      else if CurControl.Align=alRight then
        CurControl.Align:=alLeft;
      FlipControls.Delete(FlipControls.Count - 1);
    end;
  finally
    FlipControls.Free;
    EnableAlign;
  end;
  
  // flip recursively
  if AllLevels then begin
    for i := 0 to ControlCount - 1 do begin
      CurControl:=Controls[i];
      if CurControl is TWinControl then
        TWinControl(CurControl).FlipChildren(true);
    end;
  end;
end;

procedure TWinControl.ScaleBy(Multiplier, Divider: Integer);
begin
  ChangeScale(Multiplier, Divider);
end;

{------------------------------------------------------------------------------}
{  TWinControl FindNextControl                                                 }
{------------------------------------------------------------------------------}
function TWinControl.FindNextControl(CurrentControl: TWinControl; GoForward,
  CheckTabStop, CheckParent: Boolean): TWinControl;
var
  List: TFPList;
  Next: TWinControl;
  I, J: Longint;
begin
  try
    Result := nil;
    List := TFPList.Create;
    GetTabOrderList(List);
    //for i:=0 to List.Count-1 do
    //  debugln(['TWinControl.FindNextControl TabOrderList ',dbgs(i),' ',DbgSName(TObject(List[i]))]);
    if List.Count > 0 then
    begin
      J := List.IndexOf(CurrentControl);
      if J < 0 then
      begin
        if GoForward then
          J := List.Count - 1
        else
          J := 0;
      end;
      //DebugLn(['TWinControl.FindNextControl A ',DbgSName(CurrentControl),' ',dbgs(J),
      //  ' GoForward='+dbgs(GoForward)+' CheckTabStop='+dbgs(CheckTabStop)+' CheckParent='+dbgs(CheckParent)]);
      I := J;
      repeat
        if GoForward then
        begin
          Inc(I);
          if I >= List.Count then
            I := 0;
        end else
        begin
          Dec(I);
          if I < 0 then
            I := List.Count - 1;
        end;

        Next := TWinControl(List[I]);
{        DebugLn(['TWinControl.FindNextControl B ',Next.Name,' ',dbgs(I),
          ' ChckTabStop='+dbgs(CheckTabStop)+' TabStop='+dbgs(Next.TabStop)
          +' ChckParent='+dbgs(CheckParent)+' Parent=Self='+dbgs(Next.Parent = Self)
          +' Enabled='+dbgs(Next.Enabled)
          +' TestTab='+dbgs(((Not CheckTabStop) or Next.TabStop))
          +' TestPar='+dbgs(((not CheckParent) or (Next.Parent = Self)))
          +' TestEnVi='+dbgs(Next.Enabled and Next.IsVisible)]);}
        if (((not CheckTabStop) or Next.TabStop)
        and ((not CheckParent) or (Next.Parent = Self)))
        and (Next.Enabled and Next.IsVisible) then
          Result := Next;

        // if we reached the start then exit because we traversed the loop and
        // did not find any control
        if I = J then
          break;
      until (Result <> nil);
      //DebugLn(['TWinControl.FindNextControl END ',DbgSName(Result),' I=',dbgs(I)]);
    end;
  finally
    List.Free;
  end;
end;

procedure TWinControl.SelectFirst;
var
  Form : TCustomForm;
  Control : TWinControl;
begin
  Form := GetParentForm(Self);
  if Form <> nil then begin
    Control := FindNextControl(nil, true, true, false);
    if Control = nil then
      Control := FindNextControl(nil, true, false, false);
    if Control <> nil then
      Form.ActiveControl := Control;
  end;
end;

procedure TWinControl.FixupTabList;
var
  I, J: Integer;
  Control: TWinControl;
  List: TFPList;
  WinControls: TFPList;
begin
  if FControls <> nil then
  begin
    List := TFPList.Create;
    WinControls:=TFPList.Create;
    try
      for i:=0 to FControls.Count-1 do
        if TObject(FControls[i]) is TWinControl then
          WinControls.Add(FControls[i]);
      List.Count := WinControls.Count;
      for I := 0 to WinControls.Count - 1 do
      begin
        Control := TWinControl(WinControls[I]);
        J := Control.FTabOrder;
        if (J >= 0) and (J < WinControls.Count) then
          List[J] := Control;
      end;
      for I := 0 to List.Count - 1 do
      begin
        Control := TWinControl(List[I]);
        if Control <> nil then
          Control.UpdateTabOrder(TTabOrder(I));
      end;
    finally
      List.Free;
      WinControls.Free;
    end;
  end;
end;

{------------------------------------------------------------------------------
  TWinControl GetTabOrderList
------------------------------------------------------------------------------}
procedure TWinControl.GetTabOrderList(List: TFPList);
var
  I: Integer;
  lWinControl: TWinControl;
begin
  if FTabList <> nil then
    for I := 0 to FTabList.Count - 1 do
    begin
      lWinControl := TWinControl(FTabList[I]);
      // The tab order list should exclude injected LCL-CustomDrawn controls
      if lWinControl.CanFocus and (not LCLIntf.IsCDIntfControl(lWinControl)) then
        List.Add(lWinControl);
      lWinControl.GetTabOrderList(List);
    end;
end;

{------------------------------------------------------------------------------
  TWinControl IsControlMouseMsg
------------------------------------------------------------------------------}
function TWinControl.IsControlMouseMsg(var TheMessage): Boolean;
var
  MouseMessage: TLMMouse absolute TheMessage;
  MouseEventMessage: TLMMouseEvent;
  Control: TControl;
  ScrolledOffset, P: TPoint;
  ClientBounds: TRect;
begin
  { CaptureControl = nil means that widgetset has captured input, but it does
    not know anything about TControl controls }
  if (FindOwnerControl(GetCapture) = Self) and (CaptureControl <> nil) then
  begin
    Control := nil;
    //DebugLn(['TWinControl.IsControlMouseMsg A ', DbgSName(CaptureControl), ', ',DbgSName(CaptureControl.Parent),', Self: ', DbgSName(Self)]);
    if (CaptureControl.Parent = Self) then
      Control := CaptureControl;
  end
  else
  begin
    // do query wincontrol children, in case they overlap
    Control := ControlAtPos(SmallPointToPoint(MouseMessage.Pos),
                            [capfAllowWinControls]);
    if Control is TWinControl then
    begin
      // there is a TWinControl child at this position
      // TWinControl children get their own messages
      // => ignore here
      Control := nil;
    end;
  end;

  //DebugLn(['TWinControl.IsControlMouseMsg B ',DbgSName(Self),' Control=',DbgSName(Control),' Msg=',TheMessage.Msg]);
  Result := False;
  if Control <> nil then
  begin
    // map mouse coordinates to control
    ScrolledOffset := GetClientScrollOffset;

    P.X := MouseMessage.XPos - Control.Left + ScrolledOffset.X;
    P.Y := MouseMessage.YPos - Control.Top + ScrolledOffset.Y;
    if (Control is TWinControl) and TWinControl(Control).HandleAllocated then
    begin
      // map coordinates to client area of control
      LCLIntf.GetClientBounds(TWinControl(Control).Handle, ClientBounds);
      dec(P.X, ClientBounds.Left);
      dec(P.Y, ClientBounds.Top);
      {$IFDEF VerboseMouseBugfix}
      DebugLn(['TWinControl.IsControlMouseMsg ',Name,' -> ',Control.Name,
      ' MsgPos=',MouseMessage.Pos.X,',',MouseMessage.Pos.Y,
      ' Control=',Control.Left,',',Control.Top,
      ' ClientBounds=',ClientBounds.Left,',',ClientBounds.Top,
      ' Scrolled=',GetClientScrollOffset.X,',',GetClientScrollOffset.Y,
      ' P=',P.X,',',P.Y]
      );
      {$ENDIF}
    end;
    if MouseMessage.Msg = LM_MOUSEWHEEL then
    begin
      MouseEventMessage := TLMMouseEvent(TheMessage);
      MouseEventMessage.X := P.X;
      MouseEventMessage.Y := P.Y;
      Control.Dispatch(MouseEventMessage);
      MouseMessage.Result := MouseEventMessage.Result;
      Result := (MouseMessage.Result <> 0);
    end
    else
    begin
      MouseMessage.Result := Control.Perform(MouseMessage.Msg, WParam(MouseMessage.Keys),
                             LParam(Integer(PointToSmallPoint(P))));
      Result := True;
    end;
  end;
end;

procedure TWinControl.FontChanged(Sender: TObject);
begin
  if HandleAllocated and ([csLoading, csDestroying] * ComponentState = []) then
  begin
    TWSWinControlClass(WidgetSetClass).SetFont(Self, Font);
    Exclude(FWinControlFlags, wcfFontChanged);
  end
  else
    Include(FWinControlFlags, wcfFontChanged);
  inherited FontChanged(Sender);
  NotifyControls(CM_PARENTFONTCHANGED);
end;

procedure TWinControl.SetColor(Value: TColor);
begin
  if Value = Color then Exit;
  inherited SetColor(Value);
  if BrushCreated then
    if Color = clDefault then
      FBrush.Color := GetDefaultColor(dctBrush)
    else
      FBrush.Color := Color;
  if HandleAllocated and ([csLoading,csDestroying]*ComponentState=[]) then
  begin
    TWSWinControlClass(WidgetSetClass).SetColor(Self);
    Exclude(FWinControlFlags, wcfColorChanged);
  end
  else
    Include(FWinControlFlags, wcfColorChanged);
  NotifyControls(CM_PARENTCOLORCHANGED);
end;

procedure TWinControl.PaintHandler(var TheMessage: TLMPaint);

  function ControlMustBeClipped(AControl: TControl): boolean;
  begin
    Result := (csOpaque in AControl.ControlStyle) and AControl.IsVisible;
  end;

var
  I, Clip, SaveIndex: Integer;
  DC: HDC;
  PS: TPaintStruct; //defined in LCLIntf.pp
  ControlsNeedsClipping: boolean;
  CurControl: TControl;
begin
  //DebugLn('[TWinControl.PaintHandler] ',Name,':',ClassName,'  DC=',DbgS(TheMessage.DC,8));
  if (csDestroying in ComponentState) or (not HandleAllocated) then exit;

  {$IFDEF VerboseResizeFlicker}
  DebugLn('TWinControl.PaintHandler A ',Name,':',ClassName);
  {$ENDIF}
  {$IFDEF VerboseDsgnPaintMsg}
  if csDesigning in ComponentState then
    DebugLn('TWinControl.PaintHandler A ',Name,':',ClassName);
  {$ENDIF}

  //DebugLn(Format('Trace:> [TWinControl.PaintHandler] %s --> Msg.DC: 0x%x', [ClassName, TheMessage.DC]));
  DC := TheMessage.DC;
  if DC = 0 then
    DC := BeginPaint(Handle, PS);

  try
    // check if child controls need clipping
    //if Name='GroupBox1' then
      //DebugLn('[TWinControl.PaintHandler] ',DbgSName(Self),' B');
    ControlsNeedsClipping:=false;
    if FControls<>nil then
      for I := 0 to FControls.Count - 1 do
        if ControlMustBeClipped(TControl(FControls[I])) then begin
          ControlsNeedsClipping:=true;
          break;
        end;
    // exclude child controls and send new paint message
    //if Name='GroupBox1' then
      //debugln(['TWinControl.PaintHandler ControlsNeedsClipping=',ControlsNeedsClipping,' ControlCount=',ControlCount]);
    if not ControlsNeedsClipping then begin
      //DebugLn('[TWinControl.PaintHandler] ',DbgSName(Self),' no clipping ...');
      PaintWindow(DC)
    end else
    begin
      SaveIndex := SaveDC(DC);
      Clip := SimpleRegion;
      for I := 0 to FControls.Count - 1 do begin
        CurControl:=TControl(FControls[I]);
        if ControlMustBeClipped(CurControl) then
          with CurControl do begin
            //DebugLn('TWinControl.PaintHandler Exclude Child ',DbgSName(Self),' Control=',DbgSName(CurControl),'(',dbgs(CurControl.BoundsRect),')');
            Clip := ExcludeClipRect(DC, Left, Top, Left + Width, Top + Height);
            if Clip = NullRegion then Break;
          end;
      end;
      //DebugLn('[TWinControl.PaintHandler] ',DbgSName(Self),' with clipping ...');
      if Clip <> NullRegion then
        PaintWindow(DC);
      RestoreDC(DC, SaveIndex);
    end;
    // paint controls
    //DebugLn('[TWinControl.PaintHandler] ',DbgSName(Self),' PaintControls ...');
    if FDockSite and FUseDockManager and Assigned(DockManager) then
      DockManager.PaintSite(DC);
    PaintControls(DC, nil);
  finally
    if TheMessage.DC = 0 then
      EndPaint(Handle, PS);
  end;
  //DebugLn(Format('Trace:< [TWinControl.PaintHandler] %s', [ClassName]));
//DebugLn('[TWinControl.PaintHandler] END  ',Name,':',ClassName,'  DC=',DbgS(Message.DC,8));
end;

procedure TWinControl.PaintControls(DC: HDC; First: TControl);
var
  I, Count, SaveIndex: Integer;
//  FrameBrush: HBRUSH;
  TempControl : TControl;
  {off $Define VerboseControlDCOrigin}
  {$IFDEF VerboseControlDCOrigin}
  P: TPoint;
  {$ENDIF}
begin
  {$ifdef DEBUG_WINDOW_ORG}
  DebugLn(':> [TWinControl.PaintControls] A');
  {$endif}

  //DebugLn('[TWinControl.PaintControls] ',Name,':',ClassName,'  DC=',DbgS(DC,8));
  if (csDestroying in ComponentState)
  or ((DC=0) and (not HandleAllocated)) then
    exit;

  {$IFDEF VerboseDsgnPaintMsg}
  if csDesigning in ComponentState then
    DebugLn('TWinControl.PaintControls A ',Name,':',ClassName);
  {$ENDIF}

  // Controls that are not TWinControl, have no handle of their own, and so
  // they are repainted as part of the parent:
  if FControls <> nil then
  begin
    {$ifdef DEBUG_WINDOW_ORG}
    DebugLn(':> [TWinControl.PaintControls] B');
    {$endif}
    I := 0;
    if First <> nil then
    begin
      I := FControls.IndexOf(First);
      if I < 0 then I := 0;
    end;
    //debugln(['TWinControl.PaintControls ',DbgSName(Self),' ClientRect=',dbgs(ClientRect)]);
    Count := FControls.Count;
    while I < Count do
    begin
      TempControl := TControl(FControls.Items[I]);
      {$ifdef DEBUG_WINDOW_ORG}
      if Name='GroupBox1' then
        DebugLn(
        Format(':> [TWinControl.PaintControls] C  DC=%d TempControl=%s Left=%d Top=%d Width=%d Height=%d IsVisible=%s RectVisible=%s',
        [DC, DbgSName(TempControl),
          TempControl.Left, TempControl.Top, TempControl.Width, TempControl.Height,
          dbgs(IsVisible),
          dbgs(RectVisible(DC, TempControl.BoundsRect))
          ]));
      {$endif}
      if not (TempControl is TWinControl) then begin
        //DebugLn('TWinControl.PaintControls B Self=',Self.Name,':',Self.ClassName,' Control=',TempControl.Name,':',TempControl.ClassName,' ',TempControl.Left,',',TempControl.Top,',',TempControl.Width,',',TempControl.Height);
        with TempControl do
          if IsVisible
          and RectVisible(DC, TempControl.BoundsRect) then
          begin
            if csPaintCopy in Self.ControlState then
              Include(FControlState, csPaintCopy);
            SaveIndex := SaveDC(DC);

            {$ifdef DEBUG_WINDOW_ORG}
            DebugLn(
              Format(':> [TWinControl.PaintControls] Control=%s Left=%d Top=%d Width=%d Height=%d',
              [Self.Name, Left, Top, Width, Height]));
            {$endif}

            MoveWindowOrg(DC, Left, Top);
            {$IFDEF VerboseControlDCOrigin}
            DebugLn('TWinControl.PaintControls B Self=',DbgSName(Self),' Control=',DbgSName(TempControl),' ',dbgs(Left),',',dbgs(Top),',',dbgs(Width),',',dbgs(Height));
            {$ENDIF}
            IntersectClipRect(DC, 0, 0, Width, Height);
            {$IFDEF VerboseControlDCOrigin}
            DebugLn('TWinControl.PaintControls C');
            P:=Point(-1,-1);
            GetWindowOrgEx(DC,@P);
            debugln('  DCOrigin=',dbgs(P));
            {$ENDIF}
            Perform(LM_PAINT, WParam(DC), 0);
            {$IFDEF VerboseControlDCOrigin}
            DebugLn('TWinControl.PaintControls D TempControl=',DbgSName(TempControl));
            {$ENDIF}
            RestoreDC(DC, SaveIndex);
            Exclude(FControlState, csPaintCopy);
          end;
      end;
      Inc(I);
    end;
  end;
  //DebugLn('[TWinControl.PaintControls] END ',Name,':',ClassName,'  DC=',DbgS(DC,8));
end;

procedure TWinControl.PaintWindow(DC: HDC);
var
  Message: TLMessage;
begin
  //DebugLn('[TWinControl.PaintWindow] ',Name,':',Classname,'  DC=',DbgS(DC));
  if (csDestroying in ComponentState)
  or ((DC=0) and (not HandleAllocated)) then
    exit;

  {$IFDEF VerboseDsgnPaintMsg}
  if csDesigning in ComponentState then
    DebugLn('TWinControl.PaintWindow A ',Name,':',ClassName);
  {$ENDIF}

  Message.Msg := LM_PAINT;
  Message.WParam := WParam(DC);
  Message.LParam := 0;
  Message.Result := 0;
  DefaultHandler(Message);
end;

procedure TWinControl.CreateBrush;
begin
  if BrushCreated then exit;
  FBrush := TBrush.Create;
  if Color = clDefault then
    FBrush.Color := GetDefaultColor(dctBrush)
  else
    FBrush.Color := Color;
end;

procedure TWinControl.ScaleControls(Multiplier, Divider: Integer);
var
  i: Integer;
begin
  for i := 0 to ControlCount - 1 do
    Controls[i].ChangeScale(Multiplier, Divider);
end;

procedure TWinControl.ChangeScale(Multiplier, Divider: Integer);
var
  i: Integer;
begin
  if Multiplier <> Divider then
  begin
    DisableAlign;
    try
      ScaleControls(Multiplier, Divider);
      inherited;
      for i := 0 to ControlCount - 1 do
        Controls[i].UpdateAnchorRules;
    finally
      EnableAlign;
    end;
  end;
end;

{------------------------------------------------------------------------------
  procedure TWinControl.EraseBackground;
------------------------------------------------------------------------------}
procedure TWinControl.EraseBackground(DC: HDC);
var
  ARect: TRect;
begin
  if DC = 0 then Exit;
  ARect := Rect(0, 0, Width, Height);
  FillRect(DC, ARect, HBRUSH(Brush.Reference.Handle));
end;

{------------------------------------------------------------------------------
  function TWinControl.IntfUTF8KeyPress(var UTF8Key: TUTF8Char;
    RepeatCount: integer; SystemKey: boolean): boolean;

  Called by the interface after the navigation and specials keys are handled
  (e.g. after KeyDown but before KeyPress).
------------------------------------------------------------------------------}
function TWinControl.IntfUTF8KeyPress(var UTF8Key: TUTF8Char;
  RepeatCount: integer; SystemKey: boolean): boolean;
begin
  IncLCLRefCount;
  try
    Result := (RepeatCount > 0) and not SystemKey and DoUTF8KeyPress(UTF8Key);
  finally
    DecLCLRefCount;
  end;
end;

function TWinControl.IntfGetDropFilesTarget: TWinControl;
begin
  Result:=Self;
  repeat
    Result:=GetFirstParentForm(Result);
    if Result=nil then exit;
    if TCustomForm(Result).AllowDropFiles then exit;
    Result:=Result.Parent;
  until Result=nil;
end;

procedure TWinControl.PaintTo(DC: HDC; X, Y: Integer);
begin
  if HandleAllocated then
    TWSWinControlClass(WidgetSetClass).PaintTo(Self, DC, X, Y);
end;

procedure TWinControl.PaintTo(ACanvas: TCanvas; X, Y: Integer);
begin
  PaintTo(ACanvas.Handle, X, Y);
end;

procedure TWinControl.SetShape(AShape: TBitmap);
begin
  if not HandleAllocated then
    Exit;

  if (AShape <> nil) and (AShape.Width = Width) and (AShape.Height = Height) then
    TWSWinControlClass(WidgetSetClass).SetShape(Self, AShape.Handle)
  else
  if AShape = nil then
    TWSWinControlClass(WidgetSetClass).SetShape(Self, 0)
end;

procedure TWinControl.SetShape(AShape: TRegion);
begin
  LCLIntf.SetWindowRgn(Handle, AShape.Reference.Handle, True);
end;

{------------------------------------------------------------------------------
  TWinControl ControlAtPos
  Params: const Pos : TPoint
          AllowDisabled: Boolean
  Results: TControl

  Searches a child (not grand child) control, which client area contains Pos.
  Pos is relative to the ClientOrigin.
------------------------------------------------------------------------------}
function TWinControl.ControlAtPos(const Pos: TPoint; AllowDisabled: Boolean): TControl;
begin
  Result := ControlAtPos(Pos, AllowDisabled, False);
end;

{------------------------------------------------------------------------------
  TWinControl ControlAtPos
  Params: const Pos : TPoint
          AllowDisabled, AllowWinControls: Boolean
  Results: TControl

  Searches a child (not grand child) control, which client area contains Pos.
  Pos is relative to the ClientOrigin.
------------------------------------------------------------------------------}
function TWinControl.ControlAtPos(const Pos: TPoint;
  AllowDisabled, AllowWinControls: Boolean): TControl;
var
  Flags: TControlAtPosFlags;
begin
  Flags := [capfOnlyClientAreas];
  if AllowDisabled then Include(Flags, capfAllowDisabled);
  if AllowWinControls then Include(Flags, capfAllowWinControls);
  Result := ControlAtPos(Pos, Flags);
end;

{------------------------------------------------------------------------------
  TWinControl ControlAtPos
  Params: const Pos : TPoint
          Flags: TControlAtPosFlags
  Results: TControl

  Searches a child (not grand child) control, which contains Pos.
  Pos is relative to the ClientOrigin.
------------------------------------------------------------------------------}
function TWinControl.ControlAtPos(const Pos: TPoint;
  Flags: TControlAtPosFlags): TControl;
var
  I: Integer;
  P: TPoint;
  LControl: TControl;
  ClientBounds: TRect;

  function GetControlAtPos(AControl: TControl): Boolean;
  var
    ControlPos: TPoint;
    ControlClientBounds: TRect;
  begin
    with AControl do
    begin
      // MG: Delphi checks for PtInRect(ClientRect,P). But the client area is
      //     not always at 0,0, so I guess this is a bug in the VCL.

      ControlPos := Point(P.X - Left, P.Y - Top);
      Result := (ControlPos.X >= 0) and (ControlPos.Y >= 0) and
                (ControlPos.X < Width) and (ControlPos.Y < Height);
      if Result and (capfOnlyClientAreas in Flags) then
      begin
        ControlClientBounds := GetChildrenRect(false);
        Result:=PtInRect(ControlClientBounds, ControlPos);
      end;

      Result := Result
        and (
             (
              (csDesigning in ComponentState)
               and not (csNoDesignVisible in ControlStyle)
               // Here was a VCL bug: VCL checks if control is Visible,
               // which should be ignored at designtime
             )
             or
             (
              (not (csDesigning in ComponentState))
              and
              (Visible)
              and
              (Enabled or (capfAllowDisabled in Flags))
              and
              (Perform(CM_HITTEST, 0,
                       LParam(Integer(PointToSmallPoint(ControlPos)))) <> 0)
             )
            );
      {$IFDEF VerboseMouseBugfix}
      //if Result then
      DebugLn(['GetControlAtPos ',Name,':',ClassName,
      ' Pos=',Pos.X,',',Pos.Y,
      ' P=',P.X,',',P.Y,
      ' ControlPos=',dbgs(ControlPos),
      ' ClientBounds=',ClientBounds.Left,',',ClientBounds.Top,',',ClientBounds.Right,',',ClientBounds.Bottom,
      // ' OnlyCl=',OnlyClientAreas,
      ' Result=',Result]);
      {$ENDIF}
      if Result then
        LControl := AControl;
    end;
  end;

var
  ScrolledOffset: TPoint;
  OldClientOrigin: TPoint;
  NewClientOrigin: TPoint;
  NewPos: TPoint;
begin
  //debugln(['TWinControl.ControlAtPos START ',DbgSName(Self),' P=',dbgs(Pos)]);

  // check if Pos in visible client area
  ClientBounds := GetClientRect;
  ScrolledOffset := GetClientScrollOffset;
  if capfHasScrollOffset in Flags then
  begin
    { ClientBounds do not include scrolling offset }
    inc(ClientBounds.Left, ScrolledOffset.x);
    inc(ClientBounds.Right, ScrolledOffset.x);
    inc(ClientBounds.Top, ScrolledOffset.y);
    inc(ClientBounds.Bottom, ScrolledOffset.y);
  end;

  if not PtInRect(ClientBounds, Pos) then
  begin
    //debugln(['TWinControl.ControlAtPos OUT OF CLIENTBOUNDS ',DbgSName(Self),' P=',dbgs(Pos),' ClientBounds=',dbgs(ClientBounds)]);
    Result := nil;
    exit;
  end;

  // map Pos to logical client area
  P := Pos;
  if not (capfHasScrollOffset in Flags) then
  begin
    inc(P.X, ScrolledOffset.X);
    inc(P.Y, ScrolledOffset.Y);
  end;

  LControl := nil;
  if FControls<>nil then
  begin
    // check wincontrols
    if (capfAllowWinControls in Flags) then
      for I := FControls.Count - 1 downto 0 do
        if (TObject(FControls[i]) is TWinControl)
        and GetControlAtPos(TControl(FControls[I])) then
          Break;
    // check controls
    if (LControl = nil) then
      for I := FControls.Count - 1 downto 0 do
        if (not (TObject(FControls[i]) is TWinControl))
        and GetControlAtPos(TControl(FControls[I])) then
          Break;
  end;
  Result := LControl;
  
  // check recursive sub children
  if (capfRecursive in Flags) and (Result is TWinControl) and
     (TWinControl(Result).ControlCount > 0) then
  begin
    // in LCL ClientOrigin contains the scroll offset. At least this is so
    // for win32 and gtk2
    OldClientOrigin := ClientOrigin;
    NewClientOrigin := TWinControl(Result).ClientOrigin;
    NewPos := Pos;
    NewPos.X := NewPos.X - NewClientOrigin.X + OldClientOrigin.X;
    NewPos.Y := NewPos.Y - NewClientOrigin.Y + OldClientOrigin.Y;
    LControl := TWinControl(Result).ControlAtPos(NewPos, Flags + [capfHasScrollOffset]);
    //debugln(['TWinControl.RECURSED ControlAtPos Result=',DbgSName(Result),' LControl=',DbgSName(LControl),' ',dbgs(NewPos),' AllowDisabled=',AllowDisabled,' OnlyClientAreas=',OnlyClientAreas]);
    if LControl <> nil then
      Result := LControl;
  end;
  //debugln(['TWinControl.ControlAtPos END ',DbgSName(Self),' P=',dbgs(Pos),' Result=',DbgSName(Result)]);
end;

{-------------------------------------------------------------------------------
  function TWinControl.GetControlIndex(AControl: TControl): integer;


-------------------------------------------------------------------------------}
function TWinControl.GetControlIndex(AControl: TControl): integer;
begin
  if FControls <> nil then
    Result := FControls.IndexOf(AControl)
  else
    Result := -1;
end;

{-------------------------------------------------------------------------------
  function TWinControl.GetControlIndex(AControl: TControl): integer;


-------------------------------------------------------------------------------}
procedure TWinControl.SetControlIndex(AControl: TControl; NewIndex: integer);
begin
  SetChildZPosition(AControl, NewIndex);
end;

{------------------------------------------------------------------------------
  TWinControl DestroyHandle
------------------------------------------------------------------------------}
procedure TWinControl.DestroyHandle;
var
  i: integer;
  AWinControl: TWinControl;
begin
  //DebugLn(['TWinControl.DestroyHandle START ',DbgSName(Self)]);
  if not HandleAllocated then begin
    DebugLn('Warning: TWinControl.DestroyHandle ',Name,':',ClassName,' Handle not Allocated');
    //RaiseGDBException('');
  end;

  // First destroy all children handles
  //DebugLn(['TWinControl.DestroyHandle DESTROY CHILDS ',DbgSName(Self)]);
  Include(FControlState, csDestroyingHandle);
  try
    if FControls <> nil then begin
      for i:= 0 to FControls.Count - 1 do begin
        //DebugLn(['  ',i,' ',DbgSName(TObject(FWinControls[i]))]);
        AWinControl:=TWinControl(FControls[i]);
        if (AWinControl is TWinControl) and AWinControl.HandleAllocated then
          AWinControl.DestroyHandle;
      end;
    end;
    //DebugLn(['TWinControl.DestroyHandle DESTROY SELF ',DbgSName(Self)]);
    DestroyWnd;
  finally
    Exclude(FControlState, csDestroyingHandle);
  end;
  //DebugLn(['TWinControl.DestroyHandle END ',DbgSName(Self)]);
end;

{------------------------------------------------------------------------------
  TWinControl WndPRoc
------------------------------------------------------------------------------}
procedure TWinControl.WndProc(var Message: TLMessage);
var
  Form: TCustomForm;
begin
  //debugln(['TWinControl.WndProc ',DbgSName(Self),' ',Message.Msg]);
  //DebugLn(Format('Trace:[TWinControl.WndPRoc] %s(%s) --> Message = %d', [ClassName, Name, Message.Msg]));
  case Message.Msg of
    LM_SETFOCUS:
      begin
        //DebugLn(Format('Trace:[TWinControl.WndPRoc] %s --> LM_SETFOCUS', [ClassName]));
        {$IFDEF VerboseFocus}
        DebugLn('TWinControl.WndProc LM_SetFocus ',DbgSName(Self));
        {$ENDIF}
        Form := GetParentForm(Self);
        if Assigned(Form) and not (csDestroyingHandle in ControlState) and not (csDestroying in ComponentState) then
        begin
          if not Form.SetFocusedControl(Self) then
          begin
            {$IFDEF VerboseFocus}
            DebugLn('TWinControl.WndProc LM_SetFocus ',DbgSName(Self),' form=',DbgSName(Form),' Form.SetFocusedControl FAILED');
            {$ENDIF}
            Exit;
          end;
          Message.Result := 0;
        end;
        {$IFDEF VerboseFocus}
        DebugLn('TWinControl.WndProc AFTER form LM_SetFocus ',DbgSName(Self));
        {$ENDIF}
      end;

    LM_KILLFOCUS:
      begin
        //DebugLn(Format('Trace:[TWinControl.WndPRoc] %s --> _KILLFOCUS', [ClassName]));
        if csFocusing in ControlState then
        begin
          {$IFDEF VerboseFocus}
          DebugLn('TWinControl.WndProc LM_KillFocus during focusing ',Name,':',ClassName);
          {$ENDIF}
          Exit;
        end;
        Message.Result:=0;
      end;

    LM_NCHITTEST:
      begin
        inherited WndPRoc(Message);
        if (Message.Result = HTTRANSPARENT) and
           Assigned(ControlAtPos(ScreenToClient(SmallPointToPoint(TLMNCHitTest(Message).Pos)), False)) then
          Message.Result := HTCLIENT;
        Exit;
      end;

    // exclude only LM_MOUSEENTER, LM_MOUSELEAVE
    LM_MOUSEFIRST..LM_MOUSELAST,
    LM_MOUSEFIRST2..LM_RBUTTONQUADCLK,
    LM_XBUTTONTRIPLECLK..LM_MOUSELAST2:
       begin
         {$IFDEF VerboseMouseBugfix}
         DebugLn('TWinControl.WndPRoc A ',Name,':',ClassName);
         {$ENDIF}
         //if Message.Msg=LM_RBUTTONUP then begin DebugLn(['TWinControl.WndProc ',DbgSName(Self)]); DumpStack end;
         DoBeforeMouseMessage;
         if IsControlMouseMSG(Message) then
           Exit
         else
         begin
           if FDockSite and FUseDockManager and Assigned(DockManager) then
             DockManager.MessageHandler(Self, Message);
         end;
         {$IFDEF VerboseMouseBugfix}
         DebugLn('TWinControl.WndPRoc B ',Name,':',ClassName);
         {$ENDIF}
       end;

    LM_KEYFIRST..LM_KEYLAST:
      if Dragging then Exit;

    LM_CANCELMODE:
      if (FindOwnerControl(GetCapture) = Self)
      and (CaptureControl <> nil)
      and (CaptureControl.Parent = Self)
      then CaptureControl.Perform(LM_CANCELMODE,0,0);
    CM_MOUSEENTER,
    CM_MOUSELEAVE:
      begin
        if FDockSite and FUseDockManager and Assigned(DockManager) then
          DockManager.MessageHandler(Self, Message);
      end;
    CM_TEXTCHANGED, CM_VISIBLECHANGED, LM_SIZE, LM_MOVE:
      begin
        // forward message to the dock manager is we are docked
        if (HostDockSite <> nil) and (HostDockSite.UseDockManager) and
           Assigned(HostDockSite.DockManager) then
          HostDockSite.DockManager.MessageHandler(Self, Message);
      end;
  end;

  inherited WndProc(Message);
end;

procedure TWinControl.WSSetText(const AText: String);
begin
  TWSWinControlClass(WidgetSetClass).SetText(Self, AText);
end;

{------------------------------------------------------------------------------
  procedure TWinControl.DoAddDockClient(Client: TControl; const ARect: TRect);
  
  Default method for adding a dock client. Become the new parent and break
  old anchored controls.
 ------------------------------------------------------------------------------}
procedure TWinControl.DoAddDockClient(Client: TControl; const ARect: TRect);
begin
  //DebugLn(['TWinControl.DoAddDockClient ',DbgSName(Self),' Client=',DbgSName(Client),' OldParent=',DbgSName(Client.Parent),' Client.AnchoredControlCount=',Client.AnchoredControlCount]);
  Client.Parent := Self;
end;

{------------------------------------------------------------------------------
  procedure TWinControl.DockOver(Source: TDragDockObject; X, Y: Integer;
    State: TDragState; var Accept: Boolean);
    
  Called to check whether this control allows docking and where.
 ------------------------------------------------------------------------------}
procedure TWinControl.DockOver(Source: TDragDockObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
begin
  if State = dsDragMove then
    PositionDockRect(Source);
  DoDockOver(Source, X, Y, State, Accept);
end;

{------------------------------------------------------------------------------
  procedure TWinControl.DoDockOver(Source: TDragDockObject; X, Y: Integer;
    State: TDragState; var Accept: Boolean);
 ------------------------------------------------------------------------------}
procedure TWinControl.DoDockOver(Source: TDragDockObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
begin
  if Assigned(FOnDockOver) then
    FOnDockOver(Self, Source, X, Y, State, Accept);
end;

{------------------------------------------------------------------------------
  procedure TWinControl.DoRemoveDockClient(Client: TControl);

  Called to remove client from dock list.
  This method exists for descendent overrides.
 ------------------------------------------------------------------------------}
procedure TWinControl.DoRemoveDockClient(Client: TControl);
begin
  // empty (this method exists for descendent overrides)
  {$IFDEF VerboseDocking}
  DebugLn(['TWinControl.DoRemoveDockClient ',DbgSName(Self),' ',DbgSName(Client)]);
  {$ENDIF}
end;

{------------------------------------------------------------------------------
  function TWinControl.DoUnDock(NewTarget: TWinControl; Client: TControl
    ): Boolean;
 ------------------------------------------------------------------------------}
function TWinControl.DoUnDock(NewTarget: TWinControl; Client: TControl;
  KeepDockSiteSize: Boolean): Boolean;
var
  NewBounds: TRect;
begin
  {$IFDEF VerboseDocking}
  DebugLn('TWinControl.DoUnDock ',Name,' NewTarget=',DbgSName(NewTarget),' Client=',DbgSName(Client));
  {$ENDIF}
  Result := True;
  if Assigned(FOnUnDock) then
  begin
    FOnUnDock(Self, Client, NewTarget, Result);
    if not Result then
      Exit;
  end;

  if not KeepDockSiteSize then
  begin
    NewBounds := BoundsRect;
    case Client.Align of
      alLeft:
        inc(NewBounds.Left, Client.Width);
      alTop:
        inc(NewBounds.Top, Client.Height);
      alRight:
        dec(NewBounds.Right, Client.Width);
      alBottom:
        dec(NewBounds.Bottom, Client.Height);
    end;
    SetBoundsKeepBase(NewBounds.Left, NewBounds.Top,
                      NewBounds.Right - NewBounds.Left,
                      NewBounds.Bottom - NewBounds.Top);
  end;

  Result := Result and DoUndockClientMsg(NewTarget, Client);
end;

{------------------------------------------------------------------------------
  procedure TWinControl.GetSiteInfo(Client: TControl; var InfluenceRect: TRect;
    MousePos: TPoint; var CanDock: Boolean);
 ------------------------------------------------------------------------------}
procedure TWinControl.GetSiteInfo(Client: TControl; var InfluenceRect: TRect;
  MousePos: TPoint; var CanDock: Boolean);
const
  ADockMargin = 10;
begin
  GetWindowRect(Handle, InfluenceRect);
  //Margins to test docking (enlarged surface for test)
  InfluenceRect.Left := InfluenceRect.Left-ADockMargin;
  InfluenceRect.Top := InfluenceRect.Top-ADockMargin;
  InfluenceRect.Right := InfluenceRect.Right+ADockMargin;
  InfluenceRect.Bottom := InfluenceRect.Bottom+ADockMargin;

  if Assigned(FOnGetSiteInfo) then
    FOnGetSiteInfo(Self, Client, InfluenceRect, MousePos, CanDock);
end;

{------------------------------------------------------------------------------
  function TWinControl.GetParentHandle: HWND;
 ------------------------------------------------------------------------------}
function TWinControl.GetParentHandle: HWND;
begin
  if Parent <> nil then
    Result := Parent.Handle
  else
    Result := ParentWindow;
end;

{------------------------------------------------------------------------------
  function TWinControl.GetTopParentHandle: HWND;
 ------------------------------------------------------------------------------}
function TWinControl.GetTopParentHandle: HWND;
var
  AWinControl: TWinControl;
begin
  AWinControl := Self;
  while AWinControl.Parent <> nil do
    AWinControl := AWinControl.Parent;
  if AWinControl.ParentWindow = 0 then
    Result := AWinControl.Handle
  else
    Result := AWinControl.ParentWindow;
end;

{------------------------------------------------------------------------------
  procedure TWinControl.ReloadDockedControl(const AControlName: string;
    var AControl: TControl);
 ------------------------------------------------------------------------------}
procedure TWinControl.ReloadDockedControl(const AControlName: string;
  var AControl: TControl);
begin
  AControl := Owner.FindComponent(AControlName) as TControl;
end;

{------------------------------------------------------------------------------
  function TWinControl.CreateDockManager: TDockManager;
 ------------------------------------------------------------------------------}
function TWinControl.CreateDockManager: TDockManager;
begin
  if (DockManager = nil) and DockSite and UseDockManager then
    // this control can dock other controls, so it needs a TDockManager
    Result := DefaultDockManagerClass.Create(Self)
  else
    Result := DockManager;
end;

procedure TWinControl.SetDockManager(AMgr: TDockManager);
begin
  //use FDockManager only here!
  if Assigned(DockManager) and (DockManager <> AMgr) then
    if FDockManager.AutoFreeByControl then
      FDockManager.Free;
  FDockManager := AMgr; //can be nil
end;

{------------------------------------------------------------------------------
  procedure TWinControl.SetUseDockManager(const AValue: Boolean);
 ------------------------------------------------------------------------------}
procedure TWinControl.SetUseDockManager(const AValue: Boolean);
begin
  if FUseDockManager=AValue then exit;
  FUseDockManager:=AValue;
  if FUseDockManager and ([csDesigning,csDestroying]*ComponentState=[])
  and (DockManager=nil) then
    DockManager := CreateDockManager;
end;

procedure TWinControl.DoFloatMsg(ADockSource: TDragDockObject);
var
  WasVisible: Boolean;
begin
  if FloatingDockSiteClass = ClassType then
  begin
    WasVisible := Visible;
    try
      Dock(nil, ADockSource.DockRect);
    finally
      if WasVisible then BringToFront;
    end;
  end
  else
    inherited DoFloatMsg(ADockSource);
end;

function TWinControl.GetDockCaption(AControl: TControl): String;
begin
  Result := AControl.GetDefaultDockCaption;
  DoGetDockCaption(AControl, Result);
end;

procedure TWinControl.UpdateDockCaption(Exclude: TControl);
begin
 { Called when this is a hostdocksite and either the list of docked clients have
   changed or one of their captions.
   Exclude an currently undocking control. }
end;

procedure TWinControl.DoGetDockCaption(AControl: TControl; var ACaption: String);
begin
  if Assigned(FOnGetDockCaption) then
    OnGetDockCaption(Self, AControl, ACaption);
end;

{------------------------------------------------------------------------------
  procedure TWinControl.MainWndProc(var Message : TLMessage);

  The message handler of this wincontrol.
  Only needed by controls, which needs features not yet supported by the LCL.
 ------------------------------------------------------------------------------}
procedure TWinControl.MainWndProc(var Msg: TLMessage);
begin
  //DebugLn(Format('Trace:[TWinControl.MainWndPRoc] %s(%s) --> Message = %d', [ClassName, Name, Msg.Msg]));
end;

{------------------------------------------------------------------------------
  TWinControl SetFocus
------------------------------------------------------------------------------}
procedure TWinControl.SetFocus;
var
  Form: TCustomForm;
begin
  {$IFDEF VerboseFocus}
  DebugLn('[TWinControl.SetFocus] ',Name,':',ClassName,' Visible=',dbgs(Visible),' HandleAllocated=',dbgs(HandleAllocated));
  {$ENDIF}
  Form := GetParentForm(Self);
  if Form <> nil then
    Form.FocusControl(Self)
  else
  if IsVisible and HandleAllocated then
    LCLIntf.SetFocus(Handle);
end;

{------------------------------------------------------------------------------
  TWinControl KeyDown
------------------------------------------------------------------------------}
procedure TWinControl.KeyDown(var Key: Word; Shift: TShiftState);
begin
  if Assigned(FOnKeyDown) then FOnKeyDown(Self, Key, Shift);
  if Key <> 0 then
    DoCallKeyEventHandler(chtOnKeyDown, Key, Shift);
end;

{------------------------------------------------------------------------------
  TWinControl KeyDownBeforeInterface
------------------------------------------------------------------------------}
procedure TWinControl.KeyDownBeforeInterface(var Key: Word; Shift: TShiftState);
begin
  KeyDown(Key, Shift);
end;

{------------------------------------------------------------------------------
  TWinControl KeyDownAfterInterface
------------------------------------------------------------------------------}
procedure TWinControl.KeyDownAfterInterface(var Key: Word; Shift: TShiftState);
begin

end;

{------------------------------------------------------------------------------
  TWinControl KeyPress
------------------------------------------------------------------------------}
procedure TWinControl.KeyPress(var Key: char);
begin
  if Assigned(FOnKeyPress) then FOnKeyPress(Self, Key);
end;

{------------------------------------------------------------------------------
  TWinControl UTF8KeyPress
  
  Called before KeyPress.
------------------------------------------------------------------------------}
procedure TWinControl.UTF8KeyPress(var UTF8Key: TUTF8Char);
begin
  if Assigned(FOnUTF8KeyPress) then FOnUTF8KeyPress(Self, UTF8Key);
end;

{------------------------------------------------------------------------------
  TWinControl KeyUp
------------------------------------------------------------------------------}
procedure TWinControl.KeyUp(var Key: Word; Shift : TShiftState);
begin
  if Assigned(FOnKeyUp) then FOnKeyUp(Self, Key, Shift);
end;

procedure TWinControl.KeyUpBeforeInterface(var Key: Word; Shift: TShiftState);
begin
  //debugln('TWinControl.KeyUpBeforeInterface ',DbgSName(Self));
  KeyUp(Key,Shift);
end;

procedure TWinControl.KeyUpAfterInterface(var Key: Word; Shift: TShiftState);
begin
  //debugln('TWinControl.KeyUpAfterInterface ',DbgSName(Self));
end;

{------------------------------------------------------------------------------
  TWinControl DoKeyDownBeforeInterface
  
  returns true if handled
------------------------------------------------------------------------------}
function TWinControl.DoKeyDownBeforeInterface(var Message: TLMKey; IsRecurseCall: Boolean): Boolean;

  function IsShortCut: Boolean;
  var
    AParent: TWinControl;
    APopupMenu: TPopupMenu;
  begin
    Result := False;
    // check popup menu
    APopupMenu := PopupMenu;
    if Assigned(APopupMenu) and APopupMenu.IsShortCut(Message) then
      Exit(True);

    if IsRecurseCall then
      Exit;

    // let each parent form handle shortcuts
    AParent := Parent;
    while Assigned(AParent) do
    begin
      if (AParent is TCustomForm) and TCustomForm(AParent).IsShortcut(Message) then
        Exit(True);
      AParent := AParent.Parent;
    end;

    // let application handle shortcut
    if Assigned(Application) and Application.IsShortcut(Message) then
      Exit(True);
  end;

var
  F: TCustomForm;
  ShiftState: TShiftState;
  AParent: TWinControl;
begin
  //debugln('TWinControl.DoKeyDown ',DbgSName(Self),' ShiftState=',dbgs(KeyDataToShiftState(Message.KeyData)),' CharCode=',dbgs(Message.CharCode));
  Result := True;

  with Message do
  begin
    if CharCode = VK_UNKNOWN then Exit;
    ShiftState := KeyDataToShiftState(KeyData);

    if not IsRecurseCall then
    begin
      // let application handle the key
      if Assigned(Application) then
      begin
        Application.NotifyKeyDownBeforeHandler(Self, CharCode, ShiftState);
        if CharCode = VK_UNKNOWN then Exit;
      end;

      // let each parent form with keypreview handle the key
      AParent := Parent;
      while Assigned(AParent) do
      begin
        if (AParent is TCustomForm) then
        begin
          F := TCustomForm(AParent);
          if (F.KeyPreview) and (F.DoKeyDownBeforeInterface(Message, True)) then Exit;
        end;
        AParent := AParent.Parent;
      end;

      if CharCode = VK_UNKNOWN then Exit;
      ShiftState := KeyDataToShiftState(KeyData);

      // let drag object handle the key
      if DragManager.IsDragging then
      begin
        DragManager.KeyDown(CharCode, ShiftState);
        if CharCode = VK_UNKNOWN then Exit;
      end;
    end;

    // let user handle the key
    if not (csNoStdEvents in ControlStyle) then 
    begin
      KeyDownBeforeInterface(CharCode, ShiftState);
      if CharCode = VK_UNKNOWN then Exit;
    end;

    // check the shortcuts
    if IsShortCut then Exit;
  end;

  Result := False;
end;

function TWinControl.ChildKey(var Message: TLMKey): boolean;
begin
  if Assigned(Parent) then
    Result := Parent.ChildKey(Message)
  else
    Result := false;
end;

function TWinControl.DialogChar(var Message: TLMKey): boolean;
var
  I: integer;
begin
  // broadcast to children
  Result := False;
  for I := 0 to ControlCount - 1 do
  begin
    // for Delphi compatibility send it to all controls,
    // even those that can not focus or are disabled
    Result := Controls[I].DialogChar(Message);
    if Result then Exit;
  end;
end;

{------------------------------------------------------------------------------
  TWinControl DoRemainingKeyDown

  Returns True if key handled
------------------------------------------------------------------------------}
function TWinControl.DoRemainingKeyDown(var Message: TLMKeyDown): Boolean;
var
  ShiftState: TShiftState;
begin
  Result := True;
  
  ShiftState := KeyDataToShiftState(Message.KeyData);

  // let parent(s) handle key from child key
  if Assigned(Parent) and Parent.ChildKey(Message) then
    Exit;

  // handle LCL special keys
  ControlKeyDown(Message.CharCode, ShiftState);
  if Message.CharCode = VK_UNKNOWN then Exit;

  //DebugLn('TWinControl.WMKeyDown ',Name,':',ClassName);
  if not (csNoStdEvents in ControlStyle) then
  begin
    KeyDownAfterInterface(Message.CharCode, ShiftState);
    if Message.CharCode = VK_UNKNOWN then Exit;
    // Note: Message.CharCode can now be different or even 0
  end;

  // let application handle the remaining key
  if Assigned(Application) then
    Application.NotifyKeyDownHandler(Self, Message.CharCode, ShiftState);
  if Message.CharCode = VK_UNKNOWN then Exit;

  Result := False;
end;

{------------------------------------------------------------------------------
  TWinControl DoKeyPress
  
  Returns True if key handled
------------------------------------------------------------------------------}
function TWinControl.DoKeyPress(var Message : TLMKey): Boolean;
var
  F: TCustomForm;
  C: char;
  AParent: TWinControl;
begin
  Result := True;

  // let each parent form with keypreview handle the key
  AParent := Parent;
  while (AParent <> nil) do
  begin
    if (AParent is TCustomForm) then
    begin
      F := TCustomForm(AParent);
      if F.KeyPreview and F.DoKeyPress(Message) then Exit;
    end;
    AParent := AParent.Parent;
  end;

  if not (csNoStdEvents in ControlStyle) then
    with Message do
    begin
      C := Char(CharCode);
      KeyPress(C);
      CharCode := Ord(C);
      if Char(CharCode) = #0 then Exit;
    end;

  Result := False;
end;

{------------------------------------------------------------------------------
  TWinControl DoRemainingKeyPress
  
  Returns True if key handled
------------------------------------------------------------------------------}
function TWinControl.SendDialogChar(var Message : TLMKey): Boolean;
var
  ParentForm: TCustomForm;
begin
  Result := False;
  ParentForm := GetParentForm(Self);
  if ParentForm <> nil then
  begin
    Result := ParentForm.DialogChar(Message);
    if Result then 
      Message.CharCode := VK_UNKNOWN;
  end;
end;

function TWinControl.DoRemainingKeyUp(var Message: TLMKeyDown): Boolean;
var
  ShiftState: TShiftState;
begin
  //debugln('TWinControl.DoRemainingKeyUp ',DbgSName(Self));
  Result := True;

  ShiftState := KeyDataToShiftState(Message.KeyData);

  // handle LCL special keys
  ControlKeyUp(Message.CharCode,ShiftState);
  if Message.CharCode=VK_UNKNOWN then exit;

  if not (csNoStdEvents in ControlStyle) then
  begin
    KeyUpAfterInterface(Message.CharCode, ShiftState);
    if Message.CharCode=VK_UNKNOWN then exit;
    // Note: Message.CharCode can now be different or even 0
  end;
  Result := False;
end;

{------------------------------------------------------------------------------
  TWinControl DoUTF8KeyPress

  Returns True if key handled
------------------------------------------------------------------------------}
function TWinControl.DoUTF8KeyPress(var UTF8Key: TUTF8Char): boolean;
var
  AParent: TWinControl;
  F: TCustomForm;
begin
  Result := True;
  
  // let each parent form with keypreview handle the key
  AParent := Parent;
  while Assigned(AParent) do
  begin
    if (AParent is TCustomForm) then
    begin
      F := TCustomForm(AParent);
      if (F.KeyPreview) and F.DoUTF8KeyPress(UTF8Key) then Exit;
    end;
    AParent := AParent.Parent;
  end;
  
  if not (csNoStdEvents in ControlStyle) then
  begin
    UTF8KeyPress(UTF8Key);
    if UTF8Key = '' then Exit;
  end;
  
  Result := False;
end;

{------------------------------------------------------------------------------
  TWinControl DoKeyUpBeforeInterface

  Returns True if key handled
------------------------------------------------------------------------------}
function TWinControl.DoKeyUpBeforeInterface(var Message : TLMKey): Boolean;
var
  F: TCustomForm;
  ShiftState: TShiftState;
  AParent: TWinControl;
begin
  Result := True;

  // let each parent form with keypreview handle the key
  AParent:=Parent;
  while (AParent<>nil) do begin
    if (AParent is TCustomForm) then begin
      F := TCustomForm(AParent);
      if  (F.KeyPreview)
      and (F.DoKeyUpBeforeInterface(Message)) then Exit;
    end;
    AParent:=AParent.Parent;
  end;

  with Message do
  begin
    ShiftState := KeyDataToShiftState(KeyData);

    if DragManager.IsDragging then
    begin
      DragManager.KeyUp(CharCode, ShiftState);
      if CharCode = VK_UNKNOWN then Exit;
    end;
    
    if not (csNoStdEvents in ControlStyle)
    then begin
      KeyUpBeforeInterface(CharCode, ShiftState);
      if CharCode = VK_UNKNOWN then Exit;
    end;

    // TODO
    //if (CharCode = VK_APPS) and not (ssAlt in ShiftState) then
    //  CheckMenuPopup(SmallPoint(0, 0));
  end;
  Result := False;
end;

{------------------------------------------------------------------------------
  TWinControl ControlKeyDown
------------------------------------------------------------------------------}
procedure TWinControl.ControlKeyDown(var Key: Word; Shift: TShiftState);
begin
  Application.ControlKeyDown(Self,Key,Shift);
end;

procedure TWinControl.ControlKeyUp(var Key: Word; Shift: TShiftState);
begin
  //debugln('TWinControl.ControlKeyUp ',DbgSName(Self));
  Application.ControlKeyUp(Self,Key,Shift);
end;

{------------------------------------------------------------------------------
  TWinControl CreateParams
------------------------------------------------------------------------------}
procedure TWinControl.CreateParams(var Params : TCreateParams);
begin
  FillChar(Params, SizeOf(Params),0);
  Params.Caption := PChar(FCaption);
  Params.Style := WS_CHILD or WS_CLIPSIBLINGS or WS_CLIPCHILDREN;
  Params.ExStyle := 0;
  if csAcceptsControls in ControlStyle then
    Params.ExStyle := Params.ExStyle or WS_EX_CONTROLPARENT;
  if BorderStyle = bsSingle then
    Params.ExStyle := Params.ExStyle or WS_EX_CLIENTEDGE;
  if TabStop then
    Params.Style := Params.Style or WS_TABSTOP;

  if (Parent <> nil) then
    Params.WndParent := Parent.Handle
  else
    Params.WndParent := ParentWindow;

  Params.X := Left;
  Params.Y := Top;
  Params.Width := Width;
  Params.Height := Height;
end;

{------------------------------------------------------------------------------
  TWinControl Invalidate
------------------------------------------------------------------------------}
procedure TWinControl.Invalidate;
begin
  //DebugLn(['TWinControl.Invalidate ',DbgSName(Self),' HandleAllocated=',HandleAllocated]);
  if HandleAllocated and ([csDestroying,csLoading]*ComponentState=[]) then
    TWSWinControlClass(WidgetSetClass).Invalidate(Self);
end;

{------------------------------------------------------------------------------
  TWinControl AddControl

  Add Handle object to parents Handle object.
------------------------------------------------------------------------------}
procedure TWinControl.AddControl;
begin
  TWSControlClass(WidgetSetClass).AddControl(Self);
end;

{------------------------------------------------------------------------------
  TWinControl Repaint
------------------------------------------------------------------------------}
procedure TWinControl.Repaint;
begin
  if (not HandleAllocated) or (csDestroying in ComponentState) then exit;
  {$IFDEF VerboseDsgnPaintMsg}
  if csDesigning in ComponentState then
    DebugLn('TWinControl.Repaint A ',Name,':',ClassName);
  {$ENDIF}
  Invalidate;
  Update;
end;

{------------------------------------------------------------------------------
  TWinControl Insert
------------------------------------------------------------------------------}
procedure TWinControl.Insert(AControl : TControl);
begin
  Insert(AControl,ControlCount);
end;

{------------------------------------------------------------------------------
  procedure TWinControl.Insert(AControl: TControl; Index: integer);
------------------------------------------------------------------------------}
procedure TWinControl.Insert(AControl: TControl; Index: integer);
begin
  if AControl = nil then exit;
  if AControl.FParent<>nil then
    raise EInvalidOperation.Create('control has already a parent');
  
  if AControl = Self then
    raise EInvalidOperation.Create(rsAControlCanNotHaveItselfAsParent);

  ListInsert(FControls, Index, AControl);
  if AControl is TWinControl then
  begin
    ListAdd(FTabList, AControl);

    if (csDesigning in ComponentState) and (not (csLoading in ComponentState))
    and AControl.CanTab then
      TWinControl(AControl).TabStop := true;
  end;

  AControl.FParent := Self;
  if AControl.FAutoSizingLockCount>0 then
  begin
    // the AControl has disabled autosizing => disable it for the parent=self too
    //DebugLn([Space(FAutoSizingLockCount*2+2),'TWinControl.Insert ',DbgSName(Self),' Control=',DbgSName(AControl),' disable Parent']);
    DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.DisableAutoSizing'){$ENDIF};
  end;
end;

{------------------------------------------------------------------------------
  TWinControl ReAlign

  Realign all children
------------------------------------------------------------------------------}
procedure TWinControl.ReAlign;
begin
  AdjustSize;
end;

procedure TWinControl.ScrollBy_WS(DeltaX, DeltaY: Integer);
begin
  if HandleAllocated then
    TWSWinControlClass(WidgetSetClass).ScrollBy(Self, DeltaX, DeltaY)
  else
    raise Exception.Create('TWinControl.ScrollBy_WS: Handle not allocated');
end;

procedure TWinControl.ScrollBy(DeltaX, DeltaY: Integer);
var
  i: Integer;
begin
  // scroll inner controls
  DisableAutoSizing;
  try
    for i := 0 to ControlCount - 1 do
      with Controls[i] do
        SetBounds(Left + DeltaX, Top + DeltaY, Width, Height);
  finally
    EnableAutoSizing;
  end;
end;

{------------------------------------------------------------------------------
  TWinControl Remove
------------------------------------------------------------------------------}
procedure TWinControl.Remove(AControl : TControl);
begin
  if AControl <> nil then
  begin
    //DebugLn(Format('trace:[TWinControl.Remove] %s(%S) --> Remove: %s(%s)', [ClassName, Name, AControl.ClassName, AControl.Name]));
    if AControl is TWinControl then
      ListRemove(FTabList, AControl);
    ListRemove(FControls, AControl);
    ListRemove(FAlignOrder, AControl);
    AControl.FParent := nil;
    if AControl.FAutoSizingLockCount>0 then
    begin
      // AControl has disabled autosizing and thus for its parent=Self too
      // end disable autosize for parent=self
      //DebugLn([Space(FAutoSizingLockCount*2),'TWinControl.Remove ',DbgSName(Self),' Control=',DbgSName(AControl),' enable Parent']);
      EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.DisableAutoSizing'){$ENDIF};
    end;
  end;
end;

procedure TWinControl.AlignNonAlignedControls(ListOfControls: TFPList;
  var BoundsModified: Boolean);
{ All controls, not aligned/anchored by their own properties, can be auto aligned.

  Example:
    cclLeftToRightThenTopToBottom

  +-----------------------------------+
  |+---------------------------------+|
  || Control1 | Control2 | Control 3 ||
  |+---------------------------------+|
  |+---------------------------------+|
  || Control4 | Control5 | Control 6 ||
  |+---------------------------------+|
  |+---------------------+            |
  || Control7 | Control8 |            |
  |+---------------------+            |
  +-----------------------------------+
}
var
  Box: TAutoSizeBox;
  r: TRect;
begin
  // check if ChildSizing aligning is enabled
  if (ChildSizing.Layout=cclNone) or (ListOfControls.Count=0) then
    exit;

  //debugln('TWinControl.AlignNonAlignedControls ',DbgSName(Self),' ListOfControls.Count=',dbgs(ListOfControls.Count),' ',dbgs(ord(ChildSizing.EnlargeHorizontal)));
  
  Box:=TAutoSizeBox.Create;
  try
    r:=GetLogicalClientRect;
    BoundsModified:=Box.AlignControlsInTable(ListOfControls,ChildSizing,BiDiMode,
                                             r.Right,r.Bottom,true);
  finally
    Box.Free;
  end;
end;

class procedure TWinControl.WSRegisterClass;
begin
  inherited WSRegisterClass;
  RegisterWinControl;
  RegisterPropertyToSkip(TWinControl, 'ParentDoubleBuffered', 'VCL compatibility property', '');
  RegisterPropertyToSkip(TWinControl, 'ImeMode', 'VCL compatibility property', '');
  RegisterPropertyToSkip(TWinControl, 'ImeName', 'VCL compatibility property', '');
end;

function TWinControl.IsClientHeightStored: boolean;
begin
  // The ClientHeight is needed to restore children anchored akBottom
  Result:=ControlCount>0;
end;

function TWinControl.IsClientWidthStored: boolean;
begin
  // The ClientWidth is needed to restore children anchored akRight
  Result:=ControlCount>0;
end;

{------------------------------------------------------------------------------
  TWinControl RemoveFocus
------------------------------------------------------------------------------}
procedure TWinControl.RemoveFocus(Removing : Boolean);
var
  Form: TCustomForm;
begin
  Form := GetParentForm(Self);
  if Form <> nil then Form.DefocusControl(Self, Removing);
end;

{------------------------------------------------------------------------------
  TWinControl UpdateControlState

  Called by: RecreateWnd, TCustomTabControl.ShowCurrentPage,
    TWinControl.SetParentWindow, TWinControl.InsertControl,
    TWinControl.CMVisibleChanged
------------------------------------------------------------------------------}
procedure TWinControl.UpdateControlState;
begin
  if HandleObjectShouldBeVisible then
    AdjustSize // this will trigger DoAllAutoSize, which calls UpdateShowing
  else
    UpdateShowing; // hide immediately
end;

{------------------------------------------------------------------------------
  TWinControl InsertControl
------------------------------------------------------------------------------}
procedure TWinControl.InsertControl(AControl: TControl);
begin
  InsertControl(AControl, ControlCount);
end;

procedure TWinControl.InsertControl(AControl: TControl; Index: integer);
begin
  DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TWinControl.InsertControl'){$ENDIF};
  try
    AControl.ValidateContainer(Self);
    Perform(CM_CONTROLLISTCHANGE, WParam(AControl), LParam(True));
    Insert(AControl,Index);
    AControl.UpdateAlignIndex;
    if not (csReading in AControl.ComponentState) then
    begin
      AControl.Perform(CM_PARENTCOLORCHANGED, 0, 0);
      AControl.Perform(CM_PARENTSHOWHINTCHANGED, 0, 0);
      AControl.Perform(CM_PARENTBIDIMODECHANGED, 0, 0);
      AControl.Perform(CM_PARENTFONTCHANGED, 0, 0);
      AControl.UpdateBaseBounds(false,true,false);
      if AControl is TWinControl then
        TWinControl(AControl).UpdateControlState
      else
      if HandleAllocated then
        AControl.Invalidate;
      //DebugLn('TWinControl.InsertControl ',Name,':',ClassName);
    end;
    AdjustSize;
    Perform(CM_CONTROLCHANGE, WParam(AControl), LParam(True));
  finally
    EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TWinControl.InsertControl'){$ENDIF};
  end;
  //debugln(['TWinControl.InsertControl ',DbgSName(Self),' ',csDesigning in ComponentState,' ',DbgSName(AControl),' ',csDesigning in AControl.ComponentState]);
end;

{------------------------------------------------------------------------------
  TWinControl removeControl
------------------------------------------------------------------------------}
procedure TWinControl.RemoveControl(AControl: TControl);
var
  AWinControl: TWinControl;
begin
  DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TWinControl.RemoveControl'){$ENDIF};
  try
    Perform(CM_CONTROLCHANGE, WParam(AControl), LParam(False));
    if AControl is TWinControl then
    begin
      AWinControl := TWinControl(AControl);
      AWinControl.RemoveFocus(True);
      if AWinControl.HandleAllocated then
        AWinControl.DestroyHandle;
    end
    else
      if HandleAllocated then
        AControl.InvalidateControl(AControl.IsVisible, False, True);
    Remove(AControl);
    Perform(CM_CONTROLLISTCHANGE, WParam(AControl), LParam(False));
    if not (csDestroying in ComponentState) then
    begin
      InvalidatePreferredSize;
      AdjustSize;
    end;
  finally
    EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TWinControl.RemoveControl'){$ENDIF};
  end;
end;

function TWinControl.GetEnumeratorControls: TWinControlEnumerator;
begin
  Result:=TWinControlEnumerator.Create(Self,true);
end;

function TWinControl.GetEnumeratorControlsReverse: TWinControlEnumerator;
begin
  Result:=TWinControlEnumerator.Create(Self,false);
end;

{------------------------------------------------------------------------------
  TWinControl AlignControl
------------------------------------------------------------------------------}
procedure TWinControl.AlignControl(AControl: TControl);
var
  ARect: TRect;
  NewRect: TRect;
begin
  //if csDesigning in ComponentState then begin
  //  DbgOut('TWinControl.AlignControl ',Name,':',ClassName);
  //  if AControl<>nil then DebugLn(' AControl=',AControl.Name,':',AControl.ClassName) else DebugLn(' AControl=nil');;
  //end;
  if csDestroying in ComponentState then exit;

  // only called by DoAllAutoSize, so no check needed

  DisableAlign;
  try
    // store
    ARect := GetClientRect;
    AdjustClientRect(ARect);
    FAdjustClientRectRealized:=ARect;

    ARect:=GetLogicalClientRect;
    AlignControls(AControl, ARect);
    // some widgetsets updates their clientrect when the first child was moved
    // do a second pass if ClientRect changed
    NewRect:=GetLogicalClientRect;
    if not CompareRect(@ARect,@NewRect) then
      AlignControls(AControl, NewRect);
  finally
    EnableAlign;
  end;
end;

{------------------------------------------------------------------------------
  Method: TWinControl.ContainsControl
  Params:  Control: the control to be checked
  Returns: Self is a (super)parent of Control

  Checks if Control is a child of Self
 ------------------------------------------------------------------------------}
function TWinControl.ContainsControl(Control: TControl): Boolean;
begin
  while (Control <> nil) and (Control <> Self) do Control := Control.Parent;
  Result := Control = Self;
end;

{------------------------------------------------------------------------------
  TWinControl GetBorderStyle
------------------------------------------------------------------------------}
function TWinControl.GetBorderStyle: TBorderStyle;
begin
  Result := TBorderStyle(FBorderStyle);
end;

{------------------------------------------------------------------------------
  TWinControl GetBrush
------------------------------------------------------------------------------}
function TWinControl.GetBrush: TBrush;
begin
  if not BrushCreated then
    CreateBrush;
  Result := FBrush;
end;

{------------------------------------------------------------------------------
  TWinControl GetControl
------------------------------------------------------------------------------}
function TWinControl.GetControl(const Index: Integer): TControl;
begin
  Result := TControl(FControls[Index]);
end;

{------------------------------------------------------------------------------
  TWinControl GetControlCount
------------------------------------------------------------------------------}
function TWinControl.GetControlCount: Integer;
begin
  if FControls <> nil then
    Result := FControls.Count
  else
    Result := 0;
end;

function TWinControl.GetDockClientCount: Integer;
begin
  if FDockClients <> nil then
    Result := FDockClients.Count
  else
    Result := 0;
end;

function TWinControl.GetDockClients(Index: Integer): TControl;
begin
  if FDockClients <> nil then
    Result := TControl(FDockClients[Index])
  else
    Result := nil;
end;

{------------------------------------------------------------------------------
  TWinControl GetHandle
------------------------------------------------------------------------------}
function TWinControl.GetHandle: HWND;
begin
  //if not HandleAllocated then DebugLn('TWinControl.GetHandle Creating handle on the fly: ',DbgSName(Self));
  HandleNeeded;
  Result := FHandle;
end;

{------------------------------------------------------------------------------
  TWinControl SetHandle
  Params:  NewHandle
  Returns: Nothing
-------------------------------------------------------------------------------}
procedure TWinControl.SetHandle(NewHandle: HWND);
begin
  //if (NewHandle=0) and (AnsiCompareText(ClassName,'TPAGE')=0) then
  //  RaiseGDBException('TWincontrol.SetHandle');
  FHandle:=NewHandle;
  InvalidatePreferredSize;
end;

{------------------------------------------------------------------------------
  Method: TWinControl.Create
  Params:  None
  Returns: Nothing

  Constructor for the class.
 ------------------------------------------------------------------------------}
constructor TWinControl.Create(TheOwner : TComponent);
begin
  // do not set borderstyle, because TCustomForm needs to set it before calling
  // inherited, to have it set before handle is created via streaming
  // use property that bsNone is zero
  //FBorderStyle := bsNone;
  inherited Create(TheOwner);
  FCompStyle := csWinControl;
  FChildSizing:=TControlChildSizing.Create(Self);
  FChildSizing.OnChange:=@DoChildSizingChange;
  FBrush := nil; // Brush will be created on demand. Only few controls need it.
  FTabOrder := -1;
  FTabStop := False;
  InvalidateClientRectCache(false);
end;

{------------------------------------------------------------------------------
  TWinControl CreateParented
------------------------------------------------------------------------------}
constructor TWinControl.CreateParented(AParentWindow: HWND);
begin
  FParentWindow := AParentWindow;
  Create(nil);
end;

{------------------------------------------------------------------------------
  TWinControl CreateParentedControl
------------------------------------------------------------------------------}
class function TWinControl.CreateParentedControl(AParentWindow: HWND
  ): TWinControl;
begin
  Result := CreateParented(AParentWindow);
end;

{------------------------------------------------------------------------------
  Method: TWinControl.Destroy
  Params:  None
  Returns: Nothing

  Destructor for the class.
 ------------------------------------------------------------------------------}
destructor TWinControl.Destroy;
var
  n: Integer;
  Control: TControl;
begin
  //DebugLn('[TWinControl.Destroy] A  ',Name,':',ClassName);
  // prevent parent to try to focus a to be destroyed control
  if Parent <> nil then
    RemoveFocus(true);
  if HandleAllocated then
    DestroyHandle;
  //DebugLn('[TWinControl.Destroy] B  ',Name,':',ClassName);

  //for n:=0 to ComponentCount-1 do
  //  DebugLn('  n=',n,' ',Components[n].ClassName);

  n := ControlCount;

  while n > 0 do
  begin
    Control := Controls[n - 1];
    //DebugLn('[TWinControl.Destroy] C  ',Name,':',ClassName,' ',Control.Name,':',Control.ClassName);
    Remove(Control); // this sets Control.Parent to nil
    //DebugLn(['TWinControl.Destroy ',DbgSName(Control.HostDockSite)]);
    if Control.HostDockSite = Self then
      Control.HostDockSite := nil;
    // don't free the control, controls are freed by the owner
    n := ControlCount;
  end;

  // undock controls that use this as HostDockSite
  while DockClientCount>0 do begin
    Control:=DockClients[DockClientCount-1];
    //DebugLn(['TWinControl.Destroy ',DbgSName(Self),' undocking ',DbgSName(Control)]);
    Control.HostDockSite:=nil;
  end;

  FreeAndNil(FAlignOrder);
  FreeThenNil(FBrush);
  FreeThenNil(FChildSizing);
  if (FDockManager<>nil) then
    if FDockManager.AutoFreeByControl then
      FreeThenNil(FDockManager)
    else
      FDockManager:=nil;
  FreeThenNil(FDockClients);
  FreeThenNil(FTabList);
  //DebugLn('[TWinControl.Destroy] D  ',Name,':',ClassName);
  inherited Destroy;
  //DebugLn('[TWinControl.Destroy] END  ',Name,':',ClassName);
end;

{------------------------------------------------------------------------------
  Method: TWinControl.DoEnter
  Params: none
  Returns: Nothing

  Call user's callback for OnEnter.
 ------------------------------------------------------------------------------}
procedure TWinControl.DoEnter;
begin
  if Assigned(FOnEnter) then FOnEnter(Self);
end;

{------------------------------------------------------------------------------
  Method: TWinControl.DoExit
  Params: none
  Returns: Nothing

  Call user's callback for OnExit.
 ------------------------------------------------------------------------------}
procedure TWinControl.DoExit;
begin
  if Assigned(FOnExit) then FOnExit(Self);
end;

{------------------------------------------------------------------------------
  procedure TWinControl.DoFlipChildren;
  
  Flip children horizontally. That means mirroring the left position.
 ------------------------------------------------------------------------------}
procedure TWinControl.DoFlipChildren;
var
  i: Integer;
  CurControl: TControl;
  AWidth: Integer;
begin
  AWidth:=GetLogicalClientRect.Right;
  DisableAlign;
  for i:=0 to ControlCount-1 do begin
    CurControl:=Controls[i];
    CurControl.Left:=AWidth-CurControl.Left-CurControl.Width;
    CurControl.Anchors := BidiFlipAnchors(CurControl, True);
  end;
  EnableAlign;
end;

{------------------------------------------------------------------------------
  Method: TWinControl.CMEnabledChanged
  Params: Message
  Returns: Nothing

  Called when enabled is changed. Takes action to enable control
 ------------------------------------------------------------------------------}
procedure TWinControl.CMEnabledChanged(var Message: TLMessage);
begin
  if not Enabled and (Parent <> nil)
  then RemoveFocus(False);

  if HandleAllocated and not (csDesigning in ComponentState) then begin
    //if (not Enabled) then debugln('TWinControl.CMEnabledChanged disable ',Name,':',CLassName);
    EnableWindow(Handle, Enabled);
  end;
  inherited;
end;

{------------------------------------------------------------------------------
  Method: TWinControl.CMShowHintChanged
  Params: Message
  Returns: Nothing

  Called when showhint is changed. Notifies children
 ------------------------------------------------------------------------------}
procedure TWinControl.CMShowHintChanged(var Message: TLMessage);
begin
  NotifyControls(CM_PARENTSHOWHINTCHANGED);
end;

procedure TWinControl.CMBiDiModeChanged(var Message: TLMessage);
begin
  inherited CMBiDiModeChanged(Message);
  NotifyControls(CM_PARENTBIDIMODECHANGED);
  if HandleAllocated and (Message.wParam = 0) then
    TWSWinControlClass(WidgetSetClass).SetBiDiMode(Self,
       UseRightToLeftAlignment, UseRightToLeftReading, UseRightToLeftScrollBar);
  AdjustSize;
end;

procedure TWinControl.CMBorderChanged(var Message: TLMessage);
begin
  DoAdjustClientRectChange;
  AdjustSize;
  Invalidate;
end;

{------------------------------------------------------------------------------
  Method: TWinControl.WMSetFocus
  Params: Message
  Returns: Nothing

  SetFocus event handler
 ------------------------------------------------------------------------------}
procedure TWinControl.WMSetFocus(var Message: TLMSetFocus);
begin
  {$IFDEF VerboseFocus}
  DebugLn('TWinControl.WMSetFocus A ',Name,':',ClassName);
  {$ENDIF}
end;

{------------------------------------------------------------------------------
  Method: TWinControl.LMKillFocus
  Params:   Msg: The message
  Returns:  nothing

  event handler.
 ------------------------------------------------------------------------------}
procedure TWinControl.WMKillFocus(var Message: TLMKillFocus);
var
  ParentForm: TCustomForm;
begin
  //DebugLn('TWinControl.WMKillFocus A ',Name,':',ClassName);
  //DebugLn(Format('Trace: %s', [ClassName]));
  if [csLoading,csDestroying,csDesigning]*ComponentState=[] then
  begin
    ParentForm := GetParentForm(Self);
    if Assigned(ParentForm) and ParentForm.Active then
      EditingDone;
  end;
end;

{------------------------------------------------------------------------------
  Method: TWinControl.WMPaint
  Params:   Msg: The paint message
  Returns:  nothing

  Paint event handler.
 ------------------------------------------------------------------------------}
procedure TWinControl.WMPaint(var Msg: TLMPaint);
var
  DC,MemDC: HDC;
{$ifdef BUFFERED_WMPAINT}
  MemBitmap, OldBitmap : HBITMAP;
  MemWidth: Integer;
  MemHeight: Integer;
{$ENDIF}
  PS : TPaintStruct;
  ClientBoundRect: TRect;
begin
  //DebugLn('[TWinControl.WMPaint] ',DbgSName(Self),'  ',DbgS(Msg.DC));
  {$IFDEF VerboseResizeFlicker}
  DebugLn('[TWinControl.WMPaint] ',DbgSName(Self),'  Bounds=',dbgs(BoundsRect),' ClientRect=',dbgs(ClientRect));
  {$ENDIF}
  if ([csDestroying,csLoading]*ComponentState<>[]) or (not HandleAllocated) then
    exit;

  {$IFDEF VerboseDsgnPaintMsg}
  if csDesigning in ComponentState then
    DebugLn('TWinControl.WMPaint A ',Name,':',ClassName);
  {$ENDIF}

  //if Name='GroupBox1' then
  //  debugln(['TWinControl.WMPaint ',DbgSName(Self),' DoubleBuffered=',DoubleBuffered,' Msg.DC=',dbgs(Msg.DC),' csCustomPaint=',csCustomPaint in ControlState,' ControlCount=',ControlCount,' ClientRect=',dbgs(ClientRect)]);
  if (Msg.DC <> 0) or not DoubleBuffered then
  begin
    if not (csCustomPaint in ControlState) and (ControlCount = 0) then
      begin
        DefaultHandler(Msg);
      end
    else
      PaintHandler(Msg);
  end
  else begin
    // NOTE: not every interface uses this part
    //DebugLn('TWinControl.WMPaint Painting doublebuffered ',Name,':',classname);
{$ifdef BUFFERED_WMPAINT}
    DC := GetDC(0);
    MemWidth:=Width;
    MemHeight:=Height;
    MemBitmap := CreateCompatibleBitmap(DC, MemWidth, MemHeight);
    ReleaseDC(0, DC);
    MemDC := CreateCompatibleDC(0);
    OldBitmap := SelectObject(MemDC, MemBitmap);
{$ENDIF}
    try
      // Fetch a DC of the whole Handle (including client area)
      DC := BeginPaint(Handle, PS);
      if DC=0 then exit;
{$ifNdef BUFFERED_WMPAINT}
      MemDC := DC;
{$ENDIF}
      // erase background
      Include(FWinControlFlags,wcfEraseBackground);
      Perform(LM_ERASEBKGND, WParam(MemDC), 0);
      Exclude(FWinControlFlags,wcfEraseBackground);
      // create a paint message to paint the child controls.
      // WMPaint expects the DC origin to be equal to the client origin of its
      // parent
      // -> Move the DC Origin to the client origin
      if not GetClientBounds(Handle,ClientBoundRect) then exit;
      MoveWindowOrgEx(MemDC,ClientBoundRect.Left,ClientBoundRect.Top);
      // handle the paint message
      Msg.DC := MemDC;
      Perform(LM_PAINT, WParam(MemDC), 0);
      Msg.DC := 0;
      // restore the DC origin
      MoveWindowOrgEx(MemDC,-ClientBoundRect.Left,-ClientBoundRect.Top);
{$ifdef BUFFERED_WMPAINT}
      BitBlt(DC, 0,0, MemWidth, MemHeight, MemDC, 0, 0, SRCCOPY);
{$ENDIF}
      EndPaint(Handle, PS);
    finally
      Exclude(FWinControlFlags,wcfEraseBackground);
{$ifdef BUFFERED_WMPAINT}
      SelectObject(MemDC, OldBitmap);
      DeleteDC(MemDC);
      DeleteObject(MemBitmap);
{$ENDIF}
    end;
  end;
  //DebugLn(Format('Trace:< [TWinControl.WMPaint] %s', [ClassName]));
//DebugLn('[TWinControl.WMPaint] END ',Name,':',ClassName);
end;

{------------------------------------------------------------------------------
  Method: TWinControl.WMDestroy
  Params:   Msg: The destroy message
  Returns:  nothing

  event handler.
 ------------------------------------------------------------------------------}
procedure TWinControl.WMDestroy(var Message: TLMDestroy);
begin
  //DebugLn(Format('Trace: [TWinControl.LMDestroy] %s', [ClassName]));
  //DebugLn('TWinControl.WMDestroy ',Name,':',ClassName);
  // Our widget/window doesn't exist anymore
  Handle := 0;
end;

{------------------------------------------------------------------------------
  Method: TWinControl.WMMove
  Params:   Msg: The message
  Returns:  nothing

  event handler.
 ------------------------------------------------------------------------------}
procedure TWinControl.WMMove(var Message: TLMMove);
var
  NewWidth, NewHeight: Integer;
  NewBoundsRealized: TRect;
  TopParent: TControl;

  procedure RaiseLoop;
  begin
    raise Exception.Create('TWinControl.WMMove loop detected: '+DbgSName(Self)+' BoundsRealized='+dbgs(FBoundsRealized)+' NewBoundsRealized='+dbgs(NewBoundsRealized));
  end;

begin
  {$IF defined (VerboseSizeMsg) or defined(VerboseIntfSizing)}
  if (Message.MoveType and Move_SourceIsInterface)>0 then
   DebugLn(['TWinControl.WMMove A ',DbgSName(Self),' Message=',Message.XPos,',',Message.YPos,
    ' BoundsRealized=',FBoundsRealized.Left,',',FBoundsRealized.Top,
    ' FromIntf=',Message.MoveType=Move_SourceIsInterface,
    ',',FBoundsRealized.Right-FBoundsRealized.Left,
    'x',FBoundsRealized.Bottom-FBoundsRealized.Top]);
  {$ENDIF}
  NewWidth := Width;
  NewHeight := Height;
  if (Message.MoveType and Move_SourceIsInterface)>0 then
  begin
    if not (wcfBoundsRealized in FWinControlFlags) then exit;
    // interface widget has moved
    // -> update size and realized bounds
    NewWidth := FBoundsRealized.Right - FBoundsRealized.Left;
    NewHeight := FBoundsRealized.Bottom - FBoundsRealized.Top;
    // skip size update when window is minimized
    if HandleAllocated and (not IsIconic(Handle)) then
      GetWindowSize(Handle, NewWidth, NewHeight);

    NewBoundsRealized:=Bounds(Message.XPos, Message.YPos, NewWidth, NewHeight);
    if CompareRect(@NewBoundsRealized,@FBoundsRealized) then exit;

    TopParent:=GetTopParent;
    if (TopParent is TWinControl) and (wcfKillIntfSetBounds in TWinControl(TopParent).FWinControlFlags) then
      RaiseLoop;

    FBoundsRealized := NewBoundsRealized;
    if ([caspCreatingHandles,caspComputingBounds]*AutoSizePhases<>[]) then
    begin
      // while the LCL is creating handles the widgetset may send default bounds
      // we have not yet told the widgetset the final bounds
      // => the InvalidatePreferredSize and the InvalidateClientRectCache
      //    (invoked by the widgetset) may trigger a further loop in the auto
      //    size algorithm to take care of the new bounds
      // => do not call SetBounds, as this will set the Bounds to the widgetset
      //    default values.
      //DebugLn(['TWinControl.WMMove from intf ignored, because phases=',dbgs(AutoSizePhases),' boundsrealized=',wcfBoundsRealized in FWinControlFlags]);
      exit;
    end;
  end;
  if Assigned(Parent) then
    SetBoundsKeepBase(Message.XPos, Message.YPos, NewWidth, NewHeight)
  else
    SetBounds(Message.XPos, Message.YPos, NewWidth, NewHeight);
end;

{------------------------------------------------------------------------------
  Method: TWinControl.WMSize
  Params:   Message: TLMSize
  Returns:  nothing

  Event handler for size messages. This is called, whenever width, height,
  clientwidth or clientheight have changed.
  If the source of the message is the interface, the new size is stored
  in FBoundsRealized to avoid sending a size message back to the interface.
 ------------------------------------------------------------------------------}
procedure TWinControl.WMSize(var Message: TLMSize);
var
  NewLeft, NewTop: integer;
  NewBoundsRealized: TRect;
  TopParent: TControl;
  OldClientSize: TSize;
  NewClientSize: TSize;

  procedure RaiseLoop;
  var
    s: String;
  begin
    s:='TWinControl.WMSize loop detected, the widgetset does not like the LCL bounds or sends unneeded wmsize messages: '+DbgSName(Self)+' BoundsRealized='+dbgs(FBoundsRealized)+' NewBoundsRealized='+dbgs(NewBoundsRealized);
    if (OldClientSize.cx<>NewClientSize.cx)
      or (OldClientSize.cy<>NewClientSize.cy)
    then
      s:=s+' OldClientSize='+dbgs(OldClientSize)+' NewClientSize='+dbgs(NewClientSize);
    raise Exception.Create(s);
  end;

begin
  {$IF defined(VerboseSizeMsg) or defined(CHECK_POSITION) or defined(VerboseIntfSizing)}
  {$IFDEF CHECK_POSITION}
  if CheckPosition(Self) then
  {$ENDIF}
   if (Message.SizeType and Size_SourceIsInterface) > 0 then
    DebugLn(['TWinControl.WMSize A ',Name,':',ClassName,' Message=',Message.Width,',',Message.Height,
      ' BoundsRealized=',dbgs(FBoundsRealized),
      ' WChg=',FBoundsRealized.Right-FBoundsRealized.Left<>Message.Width,
      ' HChg=',FBoundsRealized.Bottom-FBoundsRealized.Top<>Message.Height,
      ' FromIntf=',(Message.SizeType and Size_SourceIsInterface)>0,' ClientRectInvalid=',ClientRectNeedsInterfaceUpdate]);
  {$ENDIF}

  NewLeft := Left;
  NewTop := Top;
  if ((Message.SizeType and Size_SourceIsInterface) > 0) then
  begin
    // interface widget has resized
    // -> update realized position and realized bounds
    {$IFDEF VerboseAllAutoSize}
    DebugLn(['TWinControl.WMSize FromIntf ',dbgsname(Self),' Message=',Message.Width,',',Message.Height,
      ' BoundsRealized=',dbgs(FBoundsRealized),
      ' wcfClientRectNeedsUpdate=',wcfClientRectNeedsUpdate in FWinControlFlags]);
    {$ENDIF}
    if not (wcfBoundsRealized in FWinControlFlags) then exit;
    {$IFDEF VerboseClientRectBugFix}
    //if Name=CheckClientRectName then
    DebugLn(['TWinControl.WMSize FromIntf ',dbgsname(Self),' Message=',Message.Width,',',Message.Height,
      ' BoundsRealized=',dbgs(FBoundsRealized),
      ' wcfClientRectNeedsUpdate=',wcfClientRectNeedsUpdate in FWinControlFlags]);
    {$ENDIF}

    NewLeft := FBoundsRealized.Left;
    NewTop := FBoundsRealized.Top;
    if HandleAllocated then
      GetWindowRelativePosition(Handle, NewLeft, NewTop);
    //if CheckPosition(Self) then
      //DebugLn(['TWinControl.WMSize GetWindowRelativePosition: ',DbgSName(Self),' ',NewLeft,',',NewTop,' ClientRectNeedsInterfaceUpdate=',ClientRectNeedsInterfaceUpdate]);
    NewBoundsRealized := Bounds(NewLeft, NewTop, Message.Width, Message.Height);
    OldClientSize := Size(0, 0);
    NewClientSize := Size(0, 0);
    if CompareRect(@NewBoundsRealized, @FBoundsRealized) then
    begin
      if not (wcfClientRectNeedsUpdate in FWinControlFlags) then
      begin
        OldClientSize := Size(FClientWidth, FClientHeight);
        NewClientSize := Size(ClientWidth, ClientHeight);
        if (OldClientSize.cx = NewClientSize.cx) and
           (OldClientSize.cy = NewClientSize.cy) then
           Exit;
      end;
    end;
    {$IFDEF VerboseAllAutoSize}
    {$IFDEF CHECK_POSITION}
    if CheckPosition(Self) then
    {$ENDIF}
    DebugLn(['TWinControl.WMSize Changed From Intf ',dbgsname(Self),' Message=',Message.Width,',',Message.Height,
      ' BoundsRealized=',dbgs(FBoundsRealized),
      ' wcfClientRectNeedsUpdate=',wcfClientRectNeedsUpdate in FWinControlFlags,
      ' ClientRectNeedsInterfaceUpdate=',ClientRectNeedsInterfaceUpdate]);
    {$ENDIF}

    TopParent := GetTopParent;
    if (TopParent is TWinControl) and (wcfKillIntfSetBounds in TWinControl(TopParent).FWinControlFlags) then
      RaiseLoop;

    FBoundsRealized := NewBoundsRealized;
    //DebugLn(['TWinControl.WMSize ',DbgSName(Self),' phases=',dbgs(AutoSizePhases)]);
    if ([caspCreatingHandles, caspComputingBounds] * AutoSizePhases <> []) then
    begin
      // while the LCL is creating handles the widgetset may send default bounds
      // we have not yet told the widgetset the final bounds
      // => the InvalidatePreferredSize and the InvalidateClientRectCache
      //    (invoked by the widgetset) may trigger a further loop in the auto
      //    size algorithm to take care of the new bounds
      // => do not call SetBounds, as this will set the Bounds to the widgetset
      //    default values.
      {$IFDEF CHECK_POSITION}
      if CheckPosition(Self) then
      {$ENDIF}
      // DebugLn(['TWinControl.WMSize from intf ignored, because phases=',dbgs(AutoSizePhases),' boundsrealized=',wcfBoundsRealized in FWinControlFlags]);
      Exit;
    end;

    if Assigned(Parent) then
      InvalidatePreferredSize;
  end;

  if Assigned(Parent) then
    SetBoundsKeepBase(NewLeft, NewTop, Message.Width, Message.Height)
  else
    SetBounds(NewLeft, NewTop, Message.Width, Message.Height);
  //if CheckPosition(Self) then
    //debugln(['TWinControl.WMSize ',DbgSName(Self),' ClientRectNeedsInterfaceUpdate=',ClientRectNeedsInterfaceUpdate]);
  if ((Message.SizeType and Size_SourceIsInterface) > 0) and ClientRectNeedsInterfaceUpdate then
    DoAdjustClientRectChange;
  {$IFDEF VerboseClientRectBugFix}
  {$IFDEF CHECK_POSITION}
  if CheckPosition(Self) then
  {$ENDIF}
  if ((Message.SizeType and Size_SourceIsInterface) > 0) then
  DebugLn(['TWinControl.WMSize END ',dbgsname(Self),' Message=',Message.Width,',',Message.Height,
    ' BoundsRealized=',dbgs(FBoundsRealized),' ClientRect=',dbgs(ClientRect),
    ' ']);
  {$ENDIF}
end;

{------------------------------------------------------------------------------
  Method: TWinControl.WMWindowPosChanged
  Params:   Message: TLMWindowPosChanged
  Returns:  nothing

  Event handler for size/move messages. This is called, whenever left, top,
  width, height, clientwidth or clientheight have changed.
  If the source of the message is the interface, the new size is stored
  in FBoundsRealized to avoid sending a SetBounds back to the interface.
 ------------------------------------------------------------------------------}
procedure TWinControl.WMWindowPosChanged(var Message: TLMWindowPosChanged);
var
  NewLeft, NewTop, NewWidth, NewHeight: integer;
  NewBoundsRealized: TRect;
  TopParent: TControl;

  procedure RaiseLoop;
  begin
    raise Exception.Create('TWinControl.WMWindowPosChanged loop detected: '+DbgSName(Self)+' BoundsRealized='+dbgs(FBoundsRealized)+' NewBoundsRealized='+dbgs(NewBoundsRealized));
  end;

begin
  if not Assigned(Message.WindowPos) or
    ((Message.WindowPos^.flags and SWP_SourceIsInterface) = 0) then
  begin
    inherited WMWindowPosChanged(Message);
    Exit;
  end;

  {$IFDEF VerboseAllAutoSize}
  DebugLn(DbgSName(Self) + ' : ' + DbgSWindowPosFlags(Message.WindowPos^.flags));
  {$ENDIF}

  NewLeft := Message.WindowPos^.x;
  NewTop := Message.WindowPos^.y;
  NewWidth := Message.WindowPos^.cx;
  NewHeight := Message.WindowPos^.cy;

  {$IF defined(VerboseSizeMsg) or defined(CHECK_POSITION) or defined(VerboseIntfSizing)}
  {$IFDEF CHECK_POSITION}
  if CheckPosition(Self) then
  {$ENDIF}
  DebugLn(['TWinControl.WMWindowPosChanged START ',DbgSName(Self),' Message=',NewLeft,',',NewTop,',',NewWidth,'x',NewHeight,
    ' BoundsRealized=',dbgs(FBoundsRealized),' FromIntf=',(Message.WindowPos^.flags and SWP_SourceIsInterface)>0,' ClientRectInvalid=',ClientRectNeedsInterfaceUpdate]);
  {$ENDIF}

  // interface widget has moved/resized
  // -> update realized bounds
  {$IFDEF VerboseAllAutoSize}
  DebugLn(['TWinControl.WMWindowPosChanged FROM INTF ',dbgsname(Self),' Message=',NewLeft,',',NewTop,',',NewWidth,'x',NewHeight,
    ' BoundsRealized=',dbgs(FBoundsRealized),
    ' wcfClientRectNeedsUpdate=',wcfClientRectNeedsUpdate in FWinControlFlags]);
  {$ENDIF}
  //DebugLn('TWinControl.WMSize B ',Name,':',ClassName,' ',NewLeft,',',NewTop);
  NewBoundsRealized := Bounds(NewLeft, NewTop, NewWidth, NewHeight);
  if CompareRect(@NewBoundsRealized,@FBoundsRealized)
  and (not (wcfClientRectNeedsUpdate in FWinControlFlags)) then
    exit;

  {$IFDEF VerboseAllAutoSize}
  DebugLn(['TWinControl.WMWindowPosChanged CHANGED BY INTF ',dbgsname(Self),' Message=',NewLeft,',',NewTop,',',NewWidth,'x',NewHeight,
    ' BoundsRealized=',dbgs(FBoundsRealized),
    ' wcfClientRectNeedsUpdate=',wcfClientRectNeedsUpdate in FWinControlFlags]);
  {$ENDIF}

  TopParent:=GetTopParent;
  if (TopParent is TWinControl)
    and (wcfKillIntfSetBounds in TWinControl(TopParent).FWinControlFlags)
  then
    RaiseLoop;

  FBoundsRealized := NewBoundsRealized;
  //DebugLn(['TWinControl.WMSize ',DbgSName(Self),' phases=',dbgs(AutoSizePhases)]);
  if ([caspCreatingHandles,caspComputingBounds]*AutoSizePhases<>[])
  or (not (wcfBoundsRealized in FWinControlFlags))
  then begin
    // while the LCL is creating handles the widgetset may send default bounds
    // we have not yet told the widgetset the final bounds
    // => the InvalidatePreferredSize and the InvalidateClientRectCache
    //    (invoked by the widgetset) may trigger a further loop in the auto
    //    size algorithm to take care of the new bounds
    // => do not call SetBounds, as this will set the Bounds to the widgetset
    //    default values.
    //DebugLn(['TWinControl.WMSize from intf ignored, because phases=',dbgs(AutoSizePhases),' boundsrealized=',wcfBoundsRealized in FWinControlFlags]);
    exit;
  end;

  if Parent<>nil then
    InvalidatePreferredSize;

  if Parent<>nil then
    SetBoundsKeepBase(NewLeft, NewTop, NewWidth, NewHeight)
  else
    SetBounds(NewLeft, NewTop, NewWidth, NewHeight);
  if ((Message.WindowPos^.flags and SWP_SourceIsInterface) > 0)
  and ClientRectNeedsInterfaceUpdate then
    DoAdjustClientRectChange;
end;

{------------------------------------------------------------------------------
  Method: TWinControl.CNKeyDown
  Params:   Msg: The message
  Returns:  nothing

  event handler.
 ------------------------------------------------------------------------------}
procedure TWinControl.CNKeyDown(var Message: TLMKeyDown);
begin
  //DebugLn('TWinControl.CNKeyDown ',Name,':',ClassName);
  if DoKeyDownBeforeInterface(Message, False) then
    Message.Result := 1
  else
    {inherited};  // there is nothing to inherit
end;

{------------------------------------------------------------------------------
  procedure TWinControl.CNSysKeyDown(var Message: TLMKeyDown);
 ------------------------------------------------------------------------------}
procedure TWinControl.CNSysKeyDown(var Message: TLMKeyDown);
begin
  if DoKeyDownBeforeInterface(Message, False) then
    Message.Result := 1
  else
    {inherited};  // there is nothing to inherit
end;

{------------------------------------------------------------------------------
  procedure TWinControl.CNSysKeyUp(var Message: TLMKeyUp);
 ------------------------------------------------------------------------------}
procedure TWinControl.CNSysKeyUp(var Message: TLMKeyUp);
begin
  if DoKeyUpBeforeInterface(Message) then 
    Message.Result := 1
  else
    {inherited}; // there is nothing to inherit
end;

{------------------------------------------------------------------------------
  Method: TWinControl.CNKeyUp
  Params:   Msg: The message
  Returns:  nothing

  event handler.
 ------------------------------------------------------------------------------}
procedure TWinControl.CNKeyUp(var Message: TLMKeyUp);
begin
  if DoKeyUpBeforeInterface(Message) then 
    Message.Result := 1
  else
    {inherited}; // there is nothing to inherit
end;

{------------------------------------------------------------------------------
  Method: TWinControl.CNChar
  Params:   Msg: The message
  Returns:  nothing

  event handler.
  CNChar is sent by the interface before it has handled the keypress itself.
 ------------------------------------------------------------------------------}
procedure TWinControl.CNChar(var Message: TLMKeyUp);
var
  c: TUTF8Char;
begin
  //debugln('TWinControl.CNChar B ',DbgSName(Self),' ',dbgs(Message.CharCode),' ',dbgs(IntfSendsUTF8KeyPress));
  if Widgetset.GetLCLCapability(lcSendsUTF8KeyPress) = LCL_CAPABILITY_NO then
  begin
    // current interface does not (yet) send UTF8 key press notifications
    // -> emulate
    if (Message.CharCode < %11000000) then
    begin
      c:=chr(Message.CharCode);
      IntfUTF8KeyPress(c,1,false);
      if (length(c)<>1) or (c[1]<>chr(Message.CharCode)) then
      begin
        // character changed
        if length(c)=1 then
          Message.CharCode:=ord(c[1])
        else
          Message.CharCode:=0;
      end;
    end;
    if Message.CharCode=0 then 
    begin
      Message.Result := 1;
      exit;
    end;
  end;

  {$ifdef VerboseKeyboard}
    debugln('TWinControl.CNChar A ',DbgSName(Self),' ',dbgs(Message.CharCode),' ',dbgs(IntfSendsUTF8KeyPress));
  {$endif}

  if DoKeyPress(Message) then
    Message.Result := 1
  else
    {inherited}; // there is nothing to inherit
end;

procedure TWinControl.WMSysChar(var Message: TLMKeyUp);
begin
  if SendDialogChar(Message) then 
    Message.Result := 1
  else
    {inherited}; // there is nothing to inherit
end;

{------------------------------------------------------------------------------
  Method: TWinControl.WMNofity
  Params:   Msg: The message
  Returns:  nothing

  event handler.
 ------------------------------------------------------------------------------}
procedure TWinControl.WMNotify(var Message: TLMNotify);
begin
  if not DoControlMsg(Message.NMHdr^.hwndfrom, Message) then
    inherited;
end;

{------------------------------------------------------------------------------
  Method: TWinControl.WMShowWindow
  Params:   Msg: The message
  Returns:  nothing

  event handler.
 ------------------------------------------------------------------------------}
procedure TWinControl.WMShowWindow(var Message: TLMShowWindow);
begin
  // DebugLn(['TWinControl.LMShowWindow ', dbgsName(self)]);
end;

{------------------------------------------------------------------------------
  Method: TWinControl.WMEnter
  Params:   Msg: The message
  Returns:  nothing

  event handler.
 ------------------------------------------------------------------------------}
procedure TWinControl.WMEnter(var Message: TLMEnter);
begin
  //DebugLn(Format('Trace: TODO: [TWinControl.LMEnter] %s', [ClassName]));
end;

{------------------------------------------------------------------------------
  Method: TWinControl.WMEraseBkgnd
  Params:   Msg: The message
  Returns:  nothing

  event handler.
 ------------------------------------------------------------------------------}
procedure TWinControl.WMEraseBkgnd(var Message: TLMEraseBkgnd);
begin
  if (Message.DC <> 0) and (wcfEraseBackground in FWinControlFlags) then
  begin
    EraseBackground(Message.DC);
    Message.Result := 1;
  end;
end;

{------------------------------------------------------------------------------
  Method: TWinControl.WMExit
  Params:   Msg: The message
  Returns:  nothing

  event handler.
 ------------------------------------------------------------------------------}
procedure TWinControl.WMExit(var Message: TLMExit);
begin
  //DebugLn(Format('Trace: TODO: [TWinControl.LMExit] %s', [ClassName]));
end;

{------------------------------------------------------------------------------
  Method: TWinControl.WMChar
  Params:   Msg: The message
  Returns:  nothing

  event handler.
  WMChar is sent by the interface after it has handled the keypress by itself.
 ------------------------------------------------------------------------------}
procedure TWinControl.WMChar(var Message: TLMChar);
begin
  //debugln('TWinControl.WMChar ',DbgSName(Self),' ',dbgs(Message.CharCode));
  if SendDialogChar(Message) then
    Message.Result := 1;
  //DebugLn(Format('Trace:[TWinControl.WMChar] %s', [ClassName]));
end;

{------------------------------------------------------------------------------
  Method: TWinControl.WMKeyDown
  Params:   Msg: The message
  Returns:  nothing

  Event handler for keys not handled by the interface
 ------------------------------------------------------------------------------}
procedure TWinControl.WMKeyDown(var Message: TLMKeyDown);
begin
  if DoRemainingKeyDown(Message) then
    Message.Result := 1;
end;

procedure TWinControl.WMSysKeyDown(var Message: TLMKeyDown);
begin
  if DoRemainingKeyDown(Message) then
    Message.Result := 1;
end;

{------------------------------------------------------------------------------
  procedure TWinControl.WMSysKeyUp(var Message: TLMKeyUp);
 ------------------------------------------------------------------------------}
procedure TWinControl.WMSysKeyUp(var Message: TLMKeyUp);
begin
  //debugln('TWinControl.WMSysKeyUp ',DbgSName(Self));
  if DoRemainingKeyUp(Message) then
    Message.Result := 1;
end;

{------------------------------------------------------------------------------
  Method: TWinControl.WMKeyUp
  Params:   Msg: The message
  Returns:  nothing

  event handler.
 ------------------------------------------------------------------------------}
procedure TWinControl.WMKeyUp(var Message: TLMKeyUp);
begin
  //debugln('TWinControl.WMKeyUp ',DbgSName(Self));
  if DoRemainingKeyUp(Message) then
    Message.Result := 1;
end;

{------------------------------------------------------------------------------
  function: TWinControl.HandleAllocated
  Params:   None
  Returns:  True is handle is allocated

  Checks if a handle is allocated. I.E. if the control is mapped
 ------------------------------------------------------------------------------}
function TWinControl.HandleAllocated : Boolean;
begin
  HandleAllocated := (FHandle <> 0);
end;

{------------------------------------------------------------------------------
  Method:  TWinControl.CreateHandle
  Params:  None
  Returns: Nothing

  Creates the handle ( = object) if not already done.
 ------------------------------------------------------------------------------}
procedure TWinControl.CreateHandle;
begin
  if (not HandleAllocated) then CreateWnd;
end;

{------------------------------------------------------------------------------
  Method:  TWinControl.CreateWnd
  Params:  None
  Returns: Nothing

  Creates the interface object and assigns the handle
 ------------------------------------------------------------------------------}
procedure TWinControl.CreateWnd;
var
  Params: TCreateParams;
  i: Integer;
  AWinControl: TWinControl;

{  procedure WriteClientRect(const Prefix: string);
  var r: TRect;
  begin
    LCLIntf.GetClientRect(Handle,r);
    if csDesigning in ComponentState then
      DebugLn('WriteClientRect ',Prefix,' ',Name,':',ClassName,' r=',r.Right,',',r.Bottom);
  end;}

begin
  //DebugLn('[TWinControl.CreateWnd] START ',DbgSName(Self));
  if (csDestroying in ComponentState) or Assigned(Parent) and (csDestroying in Parent.ComponentState) then
  begin
    DebugLn('[TWinControl.CreateWnd] NOTE: csDestroying ',DbgSName(Self));
    RaiseGDBException('');
    exit;
  end;

  if wcfInitializing in FWinControlFlags then
  begin
    DebugLn('[WARNING] Recursive call to CreateWnd for ',DbgSName(Self), ' while initializing');
    RaiseGDBException('');
    Exit;
  end;

  if wcfCreatingHandle in FWinControlFlags then
  begin
    DebugLn('[WARNING] Recursive call to CreateWnd for ',DbgSName(Self), ' while creating handle');
    RaiseGDBException('');
    Exit;
  end;

  if wcfCreatingChildHandles in FWinControlFlags then
  begin
    DebugLn('[WARNING] Recursive call to CreateWnd for ',DbgSName(Self), ' while creating children');
    RaiseGDBException('');
    Exit;
  end;
  
  if [csLoading,csDesigning]*ComponentState=[csLoading] then
  begin
    DebugLn('[HINT] TWinControl.CreateWnd creating Handle during loading ',DbgSName(Self),' csDesigning=',dbgs(csDesigning in ComponentState));
    //DumpStack;
    //RaiseGDBException('');
  end;

  FBoundsRealized := Rect(0,0,0,0);
  Exclude(FWinControlFlags, wcfBoundsRealized);

  DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TWinControl.CreateWnd'){$ENDIF};
  try
    if Assigned(Parent) and not Parent.HandleAllocated then
    begin
      // first create the parent handle
      Parent.HandleNeeded;
      if HandleAllocated then exit;
      DebugLn(['WARNING: TWinControl.CreateWnd: parent created handles, but not ours']);
    end;
    // Control is not visible at this moment. It will be shown in UpdateShowing
    FShowing := False;
    Exclude(FWinControlFlags, wcfHandleVisible);

    Include(FWinControlFlags, wcfCreatingHandle);
    try
      CreateParams(Params);
      with Params do
      begin
        if (WndParent = 0) and (Style and WS_CHILD <> 0) then
        begin
          DebugLn(['TWinControl.CreateWnd ',DbgSName(Self),' Parent=',DbgSName(Parent),' ERROR WndParent=0']);
          raise EInvalidOperation.CreateFmt(rsControlHasNoParentWindow, [Name]);
        end;
      end;

      //DebugLn(['TWinControl.CreateWnd Creating handle ... ',DbgSName(WidgetSetClass),' ',DbgSName(Self)]);
      FHandle := TWSWinControlClass(WidgetSetClass).CreateHandle(Self, Params);
      if not HandleAllocated then
      begin
        if WidgetSet.LCLPlatform=lpNoGUI then
          RaiseGDBException('TWinControl.CreateWnd: The nogui widgetset does not support visual controls.')
        else
          RaiseGDBException('TWinControl.CreateWnd: Handle creation failed creating '+DbgSName(Self));
      end;
      //debugln('TWinControl.CreateWnd update constraints ... ',DbgSName(Self));
      TWSWinControlClass(WidgetSetClass).SetBiDiMode(Self,
         UseRightToLeftAlignment, UseRightToLeftReading, UseRightToLeftScrollBar);

      Constraints.UpdateInterfaceConstraints;
      InvalidateClientRectCache(False);
      TWSWinControlClass(WidgetSetClass).ConstraintsChange(Self);

      //WriteClientRect('A');
      if Assigned(Parent) then
        AddControl
      else
      if ParentWindow <> 0 then
        LCLIntf.SetParent(FHandle, ParentWindow);
      //WriteClientRect('B');

      Include(FWinControlFlags, wcfInitializing);
      //DebugLn(['TWinControl.CreateWnd initializing window ...']);
      InitializeWnd;

    finally
      Exclude(FWinControlFlags, wcfInitializing);
      Exclude(FWinControlFlags, wcfCreatingHandle);
    end;

    Include(FWinControlFlags, wcfCreatingChildHandles);
    try
      //DebugLn('[TWinControl.CreateWnd] ',Name,':',ClassName,' ',Left,',',Top,',',Width,',',Height);
      //WriteClientRect('C');

      if FControls <> nil then
      begin
        for i := 0 to FControls.Count - 1 do
        begin
          AWinControl := TWinControl(FControls.Items[i]);
          //DebugLn(['TWinControl.CreateWnd create child handles self=',DbgSName(Self),' Child=',DbgSName(AWinControl)]);
          if (AWinControl is TWinControl) and AWinControl.IsControlVisible then
            AWinControl.HandleNeeded;
        end;
      end;

      ChildHandlesCreated;
    finally
      Exclude(FWinControlFlags, wcfCreatingChildHandles);
    end;

    InvalidatePreferredSize;
    if Assigned(FControls) then
      for i := 0 to FControls.Count - 1 do
        TControl(FControls[i]).InvalidatePreferredSize;
    // size this control
    AdjustSize;
  finally
    //DebugLn(['TWinControl.CreateWnd created ',DbgSName(Self),' enable autosizing ...']);
    (* If an error occured and FHandle was not created,
       then EnableAutoSizing must not be called.
       EnableAutoSizing will need the Handle, and trigger another attempt to create it.
       This leads to an endless loop/recursion.
       As a side effect the current control will be left in autosize-disabled *)
    if FHandle <> 0 then
      EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TWinControl.CreateWnd'){$ENDIF};
  end;

  //DebugLn('[TWinControl.CreateWnd] END ',Name,':',Classname);
  //WriteClientRect('D');
end;

{------------------------------------------------------------------------------
  Method:  TWinControl.InitializeWnd
  Params:  none
  Returns: Nothing

  Gets called after the window is created, but before the child controls are
  created. Place cached property code here.
 ------------------------------------------------------------------------------}
procedure TWinControl.InitializeWnd;
var
  CachedText: string;
begin
  //DebugLn(Format('Trace:[TWinControl.InitializeWnd]  %s', [ClassName]));
  // set all cached properties

  //DebugLn('[TWinControl.InitializeWnd] ',Name,':',ClassName,':', FCaption,' ',dbgs(Left),',',dbgs(Top),',',dbgs(Width),',',dbgs(Height));

  // First set the WinControl property some interfaces depends on it
  SetProp(Handle,'WinControl',TWinControl(Self));
  DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TWinControl.InitializeWnd'){$ENDIF};
  try
    {$IFDEF CHECK_POSITION}
    if CheckPosition(Self) then
      DebugLn('[TWinControl.InitializeWnd] A ',DbgSName(Self),
      ' OldRelBounds=',dbgs(FBoundsRealized),
      ' -> NewBounds=',dbgs(BoundsRect));
    {$ENDIF}

    if wcfColorChanged in FWinControlFlags then
    begin
      // replace by update style call
      TWSWinControlClass(WidgetSetClass).SetColor(Self);
      Exclude(FWinControlFlags, wcfColorChanged);
    end;
    if wcfFontChanged in FWinControlFlags then
    begin
      // replace by update style call
      TWSWinControlClass(WidgetSetClass).SetFont(Self,Font);
      Exclude(FWinControlFlags, wcfFontChanged);
    end;

    if not (csDesigning in ComponentState) then
      EnableWindow(Handle, Enabled);

    // Delay the setting of text until it is completely loaded
    if not (csLoading in ComponentState) then
    begin
      if GetCachedText(CachedText) then
        WSSetText(CachedText);
      InvalidatePreferredSize;
    end;

    if csDesigning in ComponentState then
      TWSWinControlClass(WidgetSetClass).SetCursor(Self, Screen.Cursors[crDefault])
    else
      TWSWinControlClass(WidgetSetClass).SetCursor(Self, Screen.Cursors[Cursor]);
  finally
    EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TWinControl.InitializeWnd'){$ENDIF};
  end;
  // send pending OnResize
  {$IFDEF VerboseOnResize}
  debugln(['TWinControl.InitializeWnd ',DbgSName(Self),' calling Resize ...']);
  {$ENDIF}
  Resize;
end;

procedure TWinControl.FinalizeWnd;
var
  S: string;
begin
  if not HandleAllocated then
    RaiseGDBException('TWinControl.FinalizeWnd Handle already destroyed');
  // make sure our text is saved
  if TWSWinControlClass(WidgetSetClass).GetText(Self, S) then
    FCaption := S;
  // if color has changed make sure it will be restored
  if FColor <> {$ifdef UseCLDefault}clDefault{$else}clWindow{$endif} then
    Include(FWinControlFlags,wcfColorChanged);
  RemoveProp(Handle,'WinControl');
  FAdjustClientRectRealized := Rect(0,0,0,0);
end;

{------------------------------------------------------------------------------
  procedure TWinControl.ParentFormHandleInitialized;

  Called after all children handles of the ParentForm are created.
 ------------------------------------------------------------------------------}
procedure TWinControl.ParentFormHandleInitialized;
var
  i: Integer;
begin
  inherited ParentFormHandleInitialized;
  // tell all controls about the final end of the handle creation phase
  if FControls<>nil then begin
    for i:=0 to FControls.Count-1 do
      TControl(FControls[i]).ParentFormHandleInitialized;
  end;
  //debugln('TWinControl.ParentFormHandleInitialized A ',DbgSName(Self));
end;

{------------------------------------------------------------------------------
  procedure TWinControl.ChildHandlesCreated;
  
  Called after all children handles are created.
 ------------------------------------------------------------------------------}
procedure TWinControl.ChildHandlesCreated;
begin
  Exclude(FWinControlFlags,wcfCreatingChildHandles);
end;

function TWinControl.GetMouseCapture: Boolean;
begin
  Result:=HandleAllocated and (GetCaptureControl=Self);
end;

{------------------------------------------------------------------------------
  function TWinControl.ParentHandlesAllocated: boolean;
  
  Checks if all Handles of all Parents are created.
 ------------------------------------------------------------------------------}
function TWinControl.ParentHandlesAllocated: boolean;
var
  CurControl: TWinControl;
begin
  Result:=false;
  CurControl:=Self;
  while CurControl<>nil do begin
    if (not CurControl.HandleAllocated)
    or (csDestroying in CurControl.ComponentState)
    or (csDestroyingHandle in CurControl.ControlState) then
      exit;
    CurControl:=CurControl.Parent;
  end;
  Result:=true;
end;

{------------------------------------------------------------------------------
  procedure TWinControl.Loaded;
 ------------------------------------------------------------------------------}
procedure TWinControl.Loaded;
var
  CachedText: string;
  i: Integer;
  AChild: TControl;
  LoadedClientSize: TSize;
  CurControl: TWinControl;
begin
  //DebugLn(['TWinControl.Loaded START ',DbgSName(Self),' cfWidthLoaded=',cfWidthLoaded in FControlFlags,' cfHeightLoaded=',cfHeightLoaded in FControlFlags,' ']);
  DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TWinControl.Loaded'){$ENDIF};
  try
    //DebugLn(['TWinControl.Loaded ',DbgSName(Self),' cfWidthLoaded=',cfWidthLoaded in FControlFlags,' cfHeightLoaded=',cfHeightLoaded in FControlFlags,' ']);
    if cfClientWidthLoaded in FControlFlags then
      LoadedClientSize.cx:=FLoadedClientSize.cx
    else begin
      CurControl:=Self;
      while CurControl<>nil do begin
        LoadedClientSize.cx:=CurControl.ClientWidth;
        if LoadedClientSize.cx>0 then break;
        LoadedClientSize.cx:=CurControl.Width;
        if LoadedClientSize.cx>0 then break;
        CurControl:=CurControl.Parent;
      end;
    end;
    if cfClientHeightLoaded in FControlFlags then
      LoadedClientSize.cy:=FLoadedClientSize.cy
    else begin
      CurControl:=Self;
      while CurControl<>nil do begin
        LoadedClientSize.cy:=CurControl.ClientHeight;
        if LoadedClientSize.cy>0 then break;
        LoadedClientSize.cy:=CurControl.Height;
        if LoadedClientSize.cy>0 then break;
        CurControl:=CurControl.Parent;
      end;
    end;
    for i:=0 to ControlCount-1 do begin
      AChild:=Controls[i];
      if AChild=nil then ;
      AChild.FBaseParentClientSize:=LoadedClientSize;
      //DebugLn(['TWinControl.Loaded Self=',DbgSName(Self),' AChild=',AChild,' AChild.FBaseParentClientSize=',dbgs(AChild.FBaseParentClientSize)]);
    end;
    if HandleAllocated then
    begin
      // Set cached caption
      if GetCachedText(CachedText) then
        WSSetText(CachedText);
      InvalidatePreferredSize;

      if wcfColorChanged in FWinControlFlags then
      begin
        TWSWinControlClass(WidgetSetClass).SetColor(Self);
        NotifyControls(CM_PARENTCOLORCHANGED);
        Exclude(FWinControlFlags, wcfColorChanged);
      end;
      if wcfFontChanged in FWinControlFlags then
      begin
        TWSWinControlClass(WidgetSetClass).SetFont(Self,Font);
        NotifyControls(CM_PARENTFONTCHANGED);
        FWinControlFlags:=FWinControlFlags-[wcfFontChanged];
      end;
    end;

    inherited Loaded;

    FixupTabList;

  finally
    //DebugLn(['TWinControl.Loaded enableautosizing ',DbgSName(Self),' ',dbgs(BoundsRect)]);
    EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TWinControl.Loaded'){$ENDIF};
    //DebugLn(['TWinControl.Loaded END ',DbgSName(Self),' ',dbgs(BoundsRect)]);
  end;
end;

procedure TWinControl.FormEndUpdated;
var
  i: Integer;
begin
  inherited FormEndUpdated;
  for i:=0 to ControlCount-1 do
    Controls[i].FormEndUpdated;
end;

{------------------------------------------------------------------------------
  Method:  TWinControl.DestroyWnd
  Params:  None
  Returns: Nothing

  Destroys the interface object.
 ------------------------------------------------------------------------------}
procedure TWinControl.DestroyWnd;
var
  i: integer;
begin
  if HandleAllocated then
  begin
    //DebugLn(['TWinControl.DestroyWnd ',DbgSName(Self)]);
    DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TWinControl.DestroyWnd'){$ENDIF};
    try
      FinalizeWnd;

      if FControls <> nil then
        for i := 0 to FControls.Count - 1 do
          TControl(FControls[i]).DoOnParentHandleDestruction;

      TWSWinControlClass(WidgetSetClass).DestroyHandle(Self);
      Handle := 0;
      Exclude(FWinControlFlags,wcfBoundsRealized);
      // Maybe handle is not needed at moment but later it will be created once
      // again. To propely initialize control after we need to restore color
      // and font. Request update.
      FWinControlFlags := FWinControlFlags + [wcfColorChanged, wcfFontChanged];
    finally
      EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TWinControl.DestroyWnd'){$ENDIF};
    end;
  end;
end;

{------------------------------------------------------------------------------
  Method:  TWinControl.HandleNeeded
  Params:  None
  Returns: Nothing

  Description of the procedure for the class.
 ------------------------------------------------------------------------------}
procedure TWinControl.HandleNeeded;
begin
  if (not HandleAllocated) and (not (csDestroying in ComponentState)) then
  begin
    if Parent = Self
    then begin
      //DebugLn(Format('Trace:[TWinControl.HandleNeeded] Somebody set Parent := Self in %s. DONT DO THAT !!', [Classname]));
    end
    else begin
      if (Parent <> nil) then
      begin
        Parent.HandleNeeded;
        // has parent triggered us to create our handle ?
        if HandleAllocated then
          exit;
      end;
    end;
    CreateHandle;
  end;
end;

function TWinControl.BrushCreated: Boolean;
begin
  Result := Assigned(FBrush);
end;

{------------------------------------------------------------------------------
  Method: TWinControl.BeginUpdateBounds
  Params:  None
  Returns: Nothing

  increases the BoundsLockCount
 ------------------------------------------------------------------------------}
procedure TWinControl.BeginUpdateBounds;
begin
  inc(FBoundsLockCount);
end;

{------------------------------------------------------------------------------
  Method: TControl.EndUpdateBounds
  Params:  None
  Returns: Nothing

  decreases the BoundsLockCount
 ------------------------------------------------------------------------------}
procedure TWinControl.EndUpdateBounds;
begin
  if FBoundsLockCount <= 0 then
    raise EInvalidOperation.CreateFmt('TWinControl.EndUpdateBounds %s too many calls.', [DbgSName(Self)]);
  dec(FBoundsLockCount);
  if FBoundsLockCount = 0 then
    SetBounds(Left, Top, Width, Height);
end;

procedure TWinControl.LockRealizeBounds;
begin
  inc(FRealizeBoundsLockCount);
end;

procedure TWinControl.UnlockRealizeBounds;
begin
  if FRealizeBoundsLockCount<=0 then
    RaiseGDBException('UnlockRealizeBounds');
  dec(FRealizeBoundsLockCount);
  if (FRealizeBoundsLockCount=0)
  and (not AutoSizeDelayed) and (caspRealizingBounds in AutoSizePhases)
  then
    RealizeBounds;
end;

{------------------------------------------------------------------------------
  procedure TWinControl.DockDrop(DockObject: TDragDockObject; X, Y: Integer);
  
  Docks the DockObject.Control onto this control.
  X, Y are only default values. More important is the DockObject.DropAlign
  property, which defines how to align DockObject.Control.
 ------------------------------------------------------------------------------}
procedure TWinControl.DockDrop(DragDockObject: TDragDockObject; X, Y: Integer);
begin
  if DoDockClientMsg(DragDockObject, Point(X, Y)) and Assigned(FOnDockDrop) then
    FOnDockDrop(Self, DragDockObject, X, Y);
end;

{------------------------------------------------------------------------------
  Method: TControl.GetIsResizing
  Params:  None
  Returns: Nothing

  decreases the BoundsLockCount
 ------------------------------------------------------------------------------}
function TWinControl.GetIsResizing: boolean;
begin
  Result:=BoundsLockCount>0;
end;

{------------------------------------------------------------------------------
  function TWinControl.GetTabOrder: TTabOrder;
 ------------------------------------------------------------------------------}
function TWinControl.GetTabOrder: TTabOrder;
begin
  if FParent <> nil then
    Result := ListIndexOf(FParent.FTabList,Self)
  else
    Result := FTabOrder;
end;

{------------------------------------------------------------------------------
  function TWinControl.GetVisibleDockClientCount: Integer;
 ------------------------------------------------------------------------------}
function TWinControl.GetVisibleDockClientCount: Integer;
var
  i: integer;
begin
  Result := 0;
  if FDockClients=nil then exit;
  for i:=FDockClients.Count-1 downto 0 do
    if TControl(FDockClients[I]).Visible then inc(Result);
end;

{------------------------------------------------------------------------------
  procedure TWinControl.SetChildSizing(const AValue: TControlChildSizing);
 ------------------------------------------------------------------------------}
procedure TWinControl.SetChildSizing(const AValue: TControlChildSizing);
begin
  if (FChildSizing=AValue) then exit;
  FChildSizing.Assign(AValue);
end;

{------------------------------------------------------------------------------
  procedure TWinControl.SetDockSite(const NewDockSite: Boolean);
  
  If NewDockSite=true it means, this control can dock other controls.
 ------------------------------------------------------------------------------}
procedure TWinControl.SetDockSite(const NewDockSite: Boolean);
begin
  if FDockSite=NewDockSite then exit;
  FDockSite := NewDockSite;
  if not (csDesigning in ComponentState) then begin
    DragManager.RegisterDockSite(Self,NewDockSite);
    if not NewDockSite then begin
      FreeAndNil(FDockClients);
      FDockClients := nil;
      DockManager := nil;
    end
    else begin
      if FDockClients = nil then FDockClients := TFPList.Create;
      DockManager := CreateDockManager;
    end;
  end;
end;

function TWinControl.DoDockClientMsg(DragDockObject: TDragDockObject;
  aPosition: TPoint): boolean;
var
  DestRect: TRect;
  Form: TCustomForm;
begin
  with DragDockObject do begin
    DestRect := DockRect;
    DisableAlign;
    try
      {$IFDEF VerboseDocking}
      Debugln(['TWinControl.DoDockClientMsg ',DbgSName(Self),' Control=',DbgSName(DragDockObject.Control),' DestRect=',dbgs(DestRect)]);
      {$ENDIF}
      DragDockObject.Control.Dock(Self, DestRect);
      if FUseDockManager and (DockManager <> nil) then
        DockManager.InsertControl(DragDockObject);
    finally
      EnableAlign;
    end;
    Form := GetParentForm(Self);
    if Form <> nil then Form.BringToFront;
    Result := true;
  end;
end;

function TWinControl.DoUndockClientMsg(NewTarget, Client: TControl): boolean;
begin
  Result := True;
  {$IFDEF VerboseDocking}
  DebugLn(['TWinControl.DoUnDockClientMsg ',DbgSName(Self),' Client=',DbgSName(Client),' Client.Parent=',DbgSName(Client.Parent)]);
  {$ENDIF}
  if FUseDockManager and (DockManager <> nil) then
    DockManager.RemoveControl(Client);
end;

{------------------------------------------------------------------------------
  Method:  TWinControl.SetBounds
  Params:  aLeft, aTop, aWidth, aHeight
  Returns: Nothing

  Sets the bounds of the control.
 ------------------------------------------------------------------------------}
procedure TWinControl.SetBounds(ALeft, ATop, AWidth, AHeight: integer);

  procedure CheckDesignBounds;
  begin
    if FRealizeBoundsLockCount > 0 then Exit;
    // the user changed the bounds
    if AWidth < 0 then
      raise EInvalidOperation.CreateFmt('TWinControl.SetBounds (%s): Negative width %d not allowed.', [DbgSName(Self), AWidth]);
    if AHeight < 0 then
      raise EInvalidOperation.CreateFmt('TWinControl.SetBounds (%s): Negative height %d not allowed.', [DbgSName(Self), AHeight]);
  end;

var
  NewBounds, OldBounds: TRect;
begin
  {$IFDEF CHECK_POSITION}
  //if csDesigning in ComponentState then
  if CheckPosition(Self) then
  DebugLn(['[TWinControl.SetBounds] START ',DbgSName(Self),
  ' Old=',dbgs(Bounds(Left,Top,Width,Height)),
  ' -> New=',dbgs(Bounds(ALeft,ATop,AWidth,AHeight)),
  ' Lock=',BoundsLockCount,
  ' Realized=',dbgs(FBoundsRealized)
  ]);
  {$ENDIF}
  if BoundsLockCount <> 0 then
    Exit;
  OldBounds := BoundsRect;
  NewBounds := Bounds(ALeft, ATop, AWidth, AHeight);

  if not CompareRect(@NewBounds, @OldBounds) then
  begin
    if [csDesigning,csDestroying,csLoading]*ComponentState=[csDesigning] then
      CheckDesignBounds;
    // LCL bounds are not up2date -> process new bounds
    DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TWinControl.SetBounds'){$ENDIF};
    try
      {$IFDEF CHECK_POSITION}
      //if csDesigning in ComponentState then
      if CheckPosition(Self) then
      DebugLn(['[TWinControl.SetBounds] Set LCL Bounds ',DbgSName(Self),
      ' OldBounds=',Dbgs(Bounds(Left,Top,Width,Height)),
      ' -> New=',Dbgs(Bounds(ALeft,ATop,AWidth,AHeight))]);
      {$ENDIF}
      inherited SetBounds(ALeft, ATop, AWidth, AHeight);
      //DebugLn(['TWinControl.SetBounds ',DbgSName(Self),' FUseDockManager=',FUseDockManager,' ',DbgSName(DockManager)]);
    finally
      EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TWinControl.SetBounds'){$ENDIF};
    end;
  end;
end;

{------------------------------------------------------------------------------
  procedure TWinControl.CalculatePreferredSize(var PreferredWidth,
    PreferredHeight: integer; WithThemeSpace" Boolean);
    
  Calculates the default/preferred width and height for a TWinControl, which is
  used by the LCL autosizing algorithms as default size. Only positive values
  are valid. Negative or 0 are treated as undefined and the LCL uses other sizes
  instead (exception: csAutoSize0x0).
  TWinControl overrides this:
  If there are children, their total preferred size is calculated.
  If this value can not be computed (e.g. the children depend too much on their
  parent clientrect), then the interface is asked for the preferred size.
  For example the preferred size of a TButton is the size, where the label fits
  exactly. This depends heavily on the current theme and widgetset.
  
  This value is independent of constraints and siblings, only the inner parts
  are relevant.

  WithThemeSpace: If true, adds space for stacking. For example: TRadioButton
  has a minimum size. But for stacking multiple TRadioButtons there should be
  some space around. This space is theme dependent, so it passed parameter to
  the widgetset.
 ------------------------------------------------------------------------------}
procedure TWinControl.CalculatePreferredSize(var PreferredWidth,
  PreferredHeight: integer; WithThemeSpace: Boolean);

  {$IFDEF VerboseCalculatePreferredSize}
  procedure trav(aControl: TControl; Prefix: string);
  var
    w: integer;
    h: integer;
    i: Integer;
  begin
    if not aControl.IsVisible then exit;
    if aControl<>Self then begin
      aControl.GetPreferredSize(w,h,true,true);
      debugln([Prefix,'Child ',DbgSName(aControl),' ',dbgs(aControl.BoundsRect),' Pref=',w,'x',h]);
    end;
    if aControl is TWinControl then
      for i:=0 to TWinControl(aControl).ControlCount-1 do
        trav(TWinControl(aControl).Controls[i],Prefix+'  ');
  end;

  function IsVerbose: boolean;
  begin
    Result:=(Name='MainScrollBox');
  end;
  {$ENDIF}

var
  Layout: TAutoSizeCtrlData;
  NewClientWidth: Integer;
  NewClientHeight: Integer;
  CurClientRect: TRect;
  NewMoveLeft, NewMoveRight: integer;
  FrameWidth: integer;
  FrameHeight: integer;
begin
  inherited CalculatePreferredSize(PreferredWidth,PreferredHeight,WithThemeSpace);
  
  if HandleAllocated then begin
    TWSWinControlClass(WidgetSetClass).GetPreferredSize(Self,
                               PreferredWidth, PreferredHeight, WithThemeSpace);
    {$IFDEF VerboseCalculatePreferredSize}
    if IsVerbose then debugln(['TWinControl.CalculatePreferredSize Widget ',DbgSName(Self),' ',DbgSName(WidgetSetClass),' Pref=',PreferredWidth,'x',PreferredHeight]);
    {$ENDIF}
  end;

  if ControlCount>0 then begin
    // Beware: ControlCount>0 does not mean that there are visible children

    // get the size requirements for the child controls
    Layout:=nil;
    try
      Layout:=TAutoSizeCtrlData.Create(Self);
      Layout.ComputePreferredClientArea(false,false,NewMoveLeft,NewMoveRight,
                                        NewClientWidth,NewClientHeight);
      //if (Name='OtherInfoGroupBox') or (Name='ProjectVersionInfoOptionsFrame') then
      //  debugln(['TWinControl.CalculatePreferredSize NewClientWidth=',NewClientWidth,' NewClientHeight=',NewClientHeight]);
      if (NewMoveLeft<>0) or (NewMoveRight<>0) then ;
    finally
      Layout.Free;
    end;

    // add clientarea frame
    GetPreferredSizeClientFrame(FrameWidth,FrameHeight);

    {$IF defined(VerboseAutoSize) or defined(VerboseAllAutoSize) or defined(VerboseCalculatePreferredSize)}
    {$IFDEF VerboseCalculatePreferredSize}
    if IsVerbose then
      trav(Self,'  ');
    if IsVerbose then
    {$ENDIF}
    //if (Name='OtherInfoGroupBox') or (Name='ProjectVersionInfoOptionsFrame') then
    debugln(['TWinControl.CalculatePreferredSize ',DbgSName(Self),
      ' HandleAllocated=',HandleAllocated,
      ' Cur=',Width,'x',Height,
      ' Client=',ClientWidth,'x',ClientHeight,
      ' PrefClient=',NewClientWidth,'x',NewClientHeight]);
    {$ENDIF}
    if NewClientWidth>0 then
      PreferredWidth:=Max(PreferredWidth,NewClientWidth+FrameWidth);
    if NewClientHeight>0 then
      PreferredHeight:=Max(PreferredHeight,NewClientHeight+FrameHeight);
  end;

  // add borderspacing
  if (PreferredWidth>0)
  or ((PreferredWidth=0) and (csAutoSize0x0 in ControlStyle)) then
    inc(PreferredWidth,BorderSpacing.InnerBorder*2);
  if (PreferredHeight>0)
  or ((PreferredHeight=0) and (csAutoSize0x0 in ControlStyle)) then
    inc(PreferredHeight,BorderSpacing.InnerBorder*2);
  {$IF defined(VerboseAutoSize) or defined(VerboseCalculatePreferredSize)}
  {$IFDEF VerboseCalculatePreferredSize}
  if IsVerbose then
  {$ENDIF}
  debugln(['TWinControl.CalculatePreferredSize ',DbgSName(Self),
    ' HandleAllocated=',dbgs(HandleAllocated),
    ' ClientFrame=',FrameWidth,'x',FrameHeight,
    ' Preferred=',dbgs(PreferredWidth),'x',dbgs(PreferredHeight)]);
  {$ENDIF}
end;

procedure TWinControl.GetPreferredSizeClientFrame(out aWidth, aHeight: integer);
begin
  aWidth:=Width-ClientWidth;
  aHeight:=Height-ClientHeight;
end;

{------------------------------------------------------------------------------
  Method:  TWinControl.RealGetText
  Params:  None
  Returns: The text

  Gets the text/caption of a control
 ------------------------------------------------------------------------------}
function TWinControl.RealGetText: TCaption;
begin
  Result := '';
  {$IFDEF VerboseTWinControlRealText}
  DebugLn(['TWinControl.RealGetText ',DbgSName(Self),' HandleAllocated=',HandleAllocated,' csLoading=',csLoading in ComponentState,' ']);
  if not HandleAllocated
  or (csLoading in ComponentState) then begin
    DebugLn(['TWinControl.RealGetText using inherited RealGetText']);
    Result := inherited RealGetText;
  end else begin
    DebugLn(['TWinControl.RealGetText using ',DbgSName(WidgetSetClass),' GetText']);
    if (not TWSWinControlClass(WidgetSetClass).GetText(Self, Result)) then begin
      DebugLn(['TWinControl.RealGetText FAILED, using RealGetText']);
      Result := inherited RealGetText;
    end;
  end;
  DebugLn(['TWinControl.RealGetText Result="',Result,'"']);
  {$ELSE}
  if not HandleAllocated 
  or (csLoading in ComponentState) 
  or (not TWSWinControlClass(WidgetSetClass).GetText(Self, Result))
  then Result := inherited RealGetText;
  {$ENDIF}
end;

{------------------------------------------------------------------------------
  Method:  TWinControl.GetTextLen
  Params:  None
  Returns: The length of the text

  Gets the length of the text/caption of a control
 ------------------------------------------------------------------------------}
function TWinControl.GetTextLen: Integer;
begin
  Result := 0;
  if not HandleAllocated 
  or (csLoading in ComponentState) 
  or not TWSWinControlClass(WidgetSetClass).GetTextLen(Self, Result)
  then Result := inherited GetTextLen;
end;

{------------------------------------------------------------------------------
  Method:  TWinControl.RealSetText
  Params:  Value: the text to be set
  Returns: Nothing

  Sets the text/caption of a control
 ------------------------------------------------------------------------------}
procedure TWinControl.RealSetText(const AValue: TCaption);
begin
  {$IFDEF VerboseTWinControlRealText}
  DebugLn(['TWinControl.RealSetText ',DbgSName(Self),' AValue="',AValue,'" HandleAllocated=',HandleAllocated,' csLoading=',csLoading in ComponentState]);
  {$ENDIF}
  if HandleAllocated and (not (csLoading in ComponentState)) then
  begin
    WSSetText(AValue);
    InvalidatePreferredSize;
    inherited RealSetText(AValue);
    AdjustSize;
  end
  else inherited RealSetText(AValue);
  {$IFDEF VerboseTWinControlRealText}
  DebugLn(['TWinControl.RealSetText ',DbgSName(Self),' END']);
  {$ENDIF}
end;

{------------------------------------------------------------------------------
  Method:  TWinControl.GetDeviceContext
  Params:  WindowHandle: the windowhandle of this control
  Returns: a Devicecontext

  Get the devicecontext for this WinControl.
 ------------------------------------------------------------------------------}
function TWinControl.GetDeviceContext(var WindowHandle: HWND): HDC;
begin
  Result := GetDC(Handle);
  //DebugLn('[TWinControl.GetDeviceContext] ',ClassName,' DC=',DbgS(Result,8),' Handle=',DbgS(FHandle));
  if Result = 0 then
     raise EOutOfResources.CreateFmt(rsErrorCreatingDeviceContext, [Name, ClassName]);

  WindowHandle := Handle;
end;

{------------------------------------------------------------------------------
  Method:  TWinControl.CMVisibleChanged
  Params:  Message : not used
  Returns: nothing

  Performs actions when visibility has changed
 ------------------------------------------------------------------------------}
procedure TWinControl.CMVisibleChanged(var Message : TLMessage);
begin
  if not FVisible and Assigned(Parent) then
    RemoveFocus(False);

  if not (csDesigning in ComponentState) or (csNoDesignVisible in ControlStyle) then
    UpdateControlState;
end;

procedure TWinControl.CMEnter(var Message: TLMessage);
begin
  DoEnter;
end;

procedure TWinControl.CMExit(var Message: TLMessage);
begin
  DoExit;
end;

procedure TWinControl.WMContextMenu(var Message: TLMContextMenu);
var
  Child: TControl;
begin
  // Check if at the click place we have a control and if so then pass the
  // message to it.
  // Don't check csDesigning here - let a child control check it.
  if (Message.Result <> 0) then
    Exit;

  if Message.XPos <> -1 then
  begin
    // don't allow disabled and don't search wincontrols - they receive their
    // message themself
    Child := ControlAtPos(ScreenToClient(SmallPointToPoint(Message.Pos)), [capfHasScrollOffset]);
    if Assigned(Child) then
      with Message do
      begin
        Result := Child.Perform(Msg, WParam(hWnd), LParam(Integer(Pos)));
        if (Result <> 0) then
          Exit;
      end;
  end;

  inherited;
end;

procedure TWinControl.DoSendShowHideToInterface;
var
  NewVisible: Boolean;
begin
  NewVisible := HandleObjectShouldBeVisible;
  if NewVisible <> (wcfHandleVisible in FWinControlFlags) then
  begin
    if NewVisible then
      Include(FWinControlFlags, wcfHandleVisible)
    else
      Exclude(FWinControlFlags, wcfHandleVisible);
    {$IF defined(VerboseNewAutoSize) or defined(VerboseIntfSizing) or defined(VerboseShowing)}
    DebugLn(['TWinControl.DoSendShowHideToInterface ',DbgSName(Self),' FBoundsRealized=',dbgs(FBoundsRealized),' New=',HandleObjectShouldBeVisible]);
    {$ENDIF}
    TWSWinControlClass(WidgetSetClass).ShowHide(Self);
  end;
end;

procedure TWinControl.ControlsAligned;
begin

end;

procedure TWinControl.DoSendBoundsToInterface;
var
  NewBounds: TRect;
  OldClientRect: TRect;
  NewClientRect: TRect;
  {$IF defined(VerboseResizeFlicker) or defined(VerboseSizeMsg)}
  OldBounds: TRect;
  {$ENDIF}
begin
  if (Parent = nil) and (not HandleObjectShouldBeVisible) then
  begin
    { do not move invisible forms
       Reason: It is common to do this:
            Form1:=TForm1.Create(nil);
            Form1.Top:=100;
            Form1.Left:=100;
            Form1.Show;
       This moves the form around and confuses some windowmanagers.
       Only send the last bounds. }
    Exit;
  end;
  NewBounds := Bounds(Left, Top, Width, Height);
  {$IF defined(VerboseResizeFlicker) or defined(VerboseSizeMsg)}
  if HandleAllocated then begin
    GetWindowRelativePosition(Handle,OldBounds.Left,OldBounds.Top);
    GetWindowSize(Handle,OldBounds.Right,OldBounds.Bottom);
    inc(OldBounds.Right,OldBounds.Left);
    inc(OldBounds.Bottom,OldBounds.Top);
  end else
    OldBounds:=NewBounds;
  DebugLn(['[TWinControl.DoSendBoundsToInterface] ',DbgSName(Self),
    ' Old=',dbgs(OldBounds),
    ' New=',dbgs(NewBounds),
    ' PosChanged=',(OldBounds.Left<>NewBounds.Left) or (OldBounds.Top<>NewBounds.Top),
    ' SizeChanged=w',(OldBounds.Right-OldBounds.Left<>NewBounds.Right-NewBounds.Left),
                 ',h', (OldBounds.Bottom-OldBounds.Top<>NewBounds.Bottom-NewBounds.Top),
    ' CurClient=',FClientWidth,'x',FClientHeight
    ]);
  {$ENDIF}
  {$IFDEF CHECK_POSITION}
  if CheckPosition(Self) then
    DebugLn('[TWinControl.DoSendBoundsToInterface] A ',DbgSName(Self),
    ' OldRelBounds=',dbgs(FBoundsRealized),
    ' -> NewBounds=',dbgs(NewBounds),
    ' ClientRect=',dbgs(ClientRect));
  {$ENDIF}

  {$IFDEF VerboseClientRectBugFix}
  //if Name=CheckClientRectName then
  DebugLn('[TWinControl.DoSendBoundsToInterface] A ',DbgSName(Self),
  ' OldRelBounds=',dbgs(FBoundsRealized),
  ' -> NewBounds=',dbgs(NewBounds)
  //,' Parent.Bounds=',dbgs(Parent.BoundsRect)
  //,' Parent.ClientRect=',dbgs(Parent.ClientRect)
  );
  {$ENDIF}

  {$IFDEF VerboseIntfSizing}
  if Visible then begin
    DebugLn('[TWinControl.DoSendBoundsToInterface] A ',DbgSName(Self),
    ' OldRelBounds=',dbgs(FBoundsRealized),
    ' -> NewBounds=',dbgs(NewBounds));
  end;
  {$ENDIF}
  FBoundsRealized := NewBounds;
  OldClientRect := ClientRect; // during a resize this is the anticipated new ClientRect
  Include(FWinControlFlags, wcfBoundsRealized); // Note: set before calling widgetset, because used in WMSize
  //if Parent=nil then DebugLn(['TWinControl.DoSendBoundsToInterface ',DbgSName(Self),' ',dbgs(BoundsRect)]);
  // this can trigger WMSize messages
  TWSWinControlClass(WidgetSetClass).SetBounds(Self, Left, Top, Width, Height);
  NewClientRect := ClientRect;
  if Visible and (not CompareRect(@OldClientRect,@NewClientRect)) then
  begin
    // the widgetset has changed the clientrect in an unexpected way
    {$IFDEF VerboseIntfSizing}
    debugln(['TWinControl.DoSendBoundsToInterface WS has changed ClientRect in an unexpected way: ',
      DbgSName(Self),' Bounds=',dbgs(BoundsRect),' ExpectedClientRect=',dbgs(OldClientRect),' New=',dbgs(NewClientRect)]);
    {$ENDIF}
    //DebugLn(['TWinControl.DoSendBoundsToInterface ',DbgSName(Self),' Bounds=',dbgs(BoundsRect),' OldClientRect=',dbgs(OldClientRect),' NewClientRect=',dbgs(NewClientRect)]);
    AdjustSize;
  end;
end;

procedure TWinControl.RealizeBounds;

  procedure Check;
  var
    c: TWinControl;
  begin
    c:=Self;
    while c<>nil do begin
      DebugLn(['Check ',DbgSName(c),' ',c.HandleAllocated,
        ' wcfCreatingHandle=',wcfCreatingHandle in FWinControlFlags,
        ' wcfInitializing=',wcfInitializing in FWinControlFlags,
        ' wcfCreatingChildHandles=',wcfCreatingChildHandles in FWinControlFlags,
        '']);
      c:=c.Parent;
    end;
    RaiseGDBException('');
  end;

var
  NewBounds: TRect;
begin
  NewBounds:=Bounds(Left, Top, Width, Height);
  if HandleAllocated
  and ([csLoading,csDestroying]*ComponentState=[])
  and (not (csDestroyingHandle in ControlState))
  and (not CompareRect(@NewBounds,@FBoundsRealized))
  then begin
    // the new bounds were not yet sent to the InterfaceObject -> send them
    {$IFDEF CHECK_POSITION}
    //if csDesigning in ComponentState then
    if CheckPosition(Self) then
    DebugLn('[TWinControl.RealizeBounds] A ',DbgSName(Self),
    ' OldRelBounds=',dbgs(FBoundsRealized),
    ' -> NewBounds=',dbgs(NewBounds));
    {$ENDIF}
    BeginUpdateBounds;
    try
      DoSendBoundsToInterface;
    finally
      EndUpdateBounds;
    end;
  end else begin
    {$IFDEF CHECK_POSITION}
    if CheckPosition(Self) then begin
      DbgOut('[TWinControl.RealizeBounds] NOT REALIZING ',DbgSName(Self),
      ' OldRelBounds=',dbgs(FBoundsRealized),
      ' -> NewBounds=',dbgs(NewBounds),
      ', because ');
      if not HandleAllocated then debugln('not HandleAllocated');
      if (csLoading in ComponentState) then debugln('csLoading');
      if (csDestroying in ComponentState) then debugln('csDestroying');
      if (CompareRect(@NewBounds,@FBoundsRealized)) then debugln('bounds not changed');
    end;
    {$ENDIF}
    if not HandleAllocated then Check;
  end;
end;

procedure TWinControl.RealizeBoundsRecursive;
var
  i: Integer;
  OldRealizing: boolean;
  AControl: TControl;
begin
  if not HandleAllocated then exit;
  OldRealizing:=wcfRealizingBounds in FWinControlFlags;
  Include(FWinControlFlags,wcfRealizingBounds);
  try
    if FControls<>nil then begin
      for i:=0 to FControls.Count-1 do begin
        AControl:=TControl(FControls[i]);
        if (AControl is TWinControl) then
          TWinControl(AControl).RealizeBoundsRecursive;
      end;
    end;
    RealizeBounds;
  finally
    if not OldRealizing then
      Exclude(FWinControlFlags,wcfRealizingBounds);
  end;
end;

{------------------------------------------------------------------------------
  Method:  TWinControl.CMShowingChanged
  Params:  Message : not used
  Returns: nothing

  Shows or hides a control
  Called by UpdateShowing
 ------------------------------------------------------------------------------}
procedure TWinControl.CMShowingChanged(var Message: TLMessage);
begin
  {$IFDEF VerboseShowing}
  DebugLn(['TWinControl.CMShowingChanged ',DbgSName(Self),' HandleAllocated=',HandleAllocated,' ',dbgs(ComponentState)]);
  {$ENDIF}
  if HandleAllocated and ([csDestroying,csLoading]*ComponentState=[]) then
    DoSendShowHideToInterface
  else
    Exclude(FWinControlFlags, wcfHandleVisible);
end;

{------------------------------------------------------------------------------
  Method:  TWinControl.ShowControl
  Params:  AControl: Control to show
  Returns: nothing

  Called by a child control (in TControl.Show), before setting Visible=true.
  Asks to show the child control and recursively shows the parents.
 ------------------------------------------------------------------------------}
procedure TWinControl.ShowControl(AControl: TControl);
begin
  if Parent <> nil then Parent.ShowControl(Self);
end;

{ TWinControlEnumerator }

function TWinControlEnumerator.GetCurrent: TControl;
begin
  if (FIndex>=0) and (FIndex<FParent.ControlCount) then
    Result:=FParent.Controls[FIndex]
  else
    Result:=nil;
end;

constructor TWinControlEnumerator.Create(Parent: TWinControl;
  aLowToHigh: boolean);
begin
  FParent:=Parent;
  FLowToHigh:=aLowToHigh;
  if FLowToHigh then
    FIndex:=-1
  else
    FIndex:=FParent.ControlCount;
end;

function TWinControlEnumerator.GetEnumerator: TWinControlEnumerator;
begin
  Result:=Self;
end;

function TWinControlEnumerator.MoveNext: Boolean;
begin
  if FLowToHigh then
  begin
    inc(FIndex);
    Result:=FIndex<FParent.ControlCount;
  end
  else begin
    dec(FIndex);
    Result:=FIndex>=0
  end;
end;

{ $UNDEF CHECK_POSITION}

{$IFDEF ASSERT_IS_ON}
  {$UNDEF ASSERT_IS_ON}
  {$C-}
{$ENDIF}
