{ Unit implementing anchor docking.

  Copyright (C) 2010 Mattias Gaertner mattias@freepascal.org

  This library is free software; you can redistribute it and/or modify it
  under the terms of the GNU Library General Public License as published by
  the Free Software Foundation; either version 2 of the License, or (at your
  option) any later version with the following modification:

  As a special exception, the copyright holders of this library give you
  permission to link this library with independent modules to produce an
  executable, regardless of the license terms of these independent modules,and
  to copy and distribute the resulting executable under terms of your choice,
  provided that you also meet, for each linked independent module, the terms
  and conditions of the license of that module. An independent module is a
  module which is not derived from or based on this library. If you modify
  this library, you may extend this exception to your version of the library,
  but you are not obligated to do so. If you do not wish to do so, delete this
  exception statement from your version.

  This program is distributed in the hope that it will be useful, but WITHOUT
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
  for more details.

  You should have received a copy of the GNU Library General Public License
  along with this library; if not, write to the Free Software Foundation,
  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

  Features:
    - dnd docking
    - preview rectangle while drag over
    - inside and outside docking
    - header with close button and hints
    - using stock item for close button glyph
    - auto header caption from content
    - hide header caption for floating form
    - auto site for headers to safe space (configurable)
    - bidimode for headers
    - page docking
    - pagecontrols uses TPageControl for native look&feel
    - page control is automatically removed if only one page left
    - scaling on resize (configurable)
    - auto insert splitters between controls (size configurable)
    - keep size when docking
    - header is automatically hidden when docked into page
    - save complete layout
    - restore layout:
       - close unneeded windows,
       - automatic clean up if windows are missing,
       - reusing existing docksites to minimize flickering
    - popup menu
       - close site
       - lock/unlock
       - header auto, left, top, right, bottom
       - undock (needed if no place to undock on screen)
       - merge (for example after moving a dock page into a layout)
       - enlarge side to left, top, right, bottom
       - move page left, right, leftmost, rightmost
       - close page
       - tab position (default, left, top, right, bottom)
       - options
    - dock site: MakeDockSite for forms, that should be able to dock other sites,
       but should not be docked themselves. Their Parent is always nil.
    - design time package for IDE
    - dnd move page index
    - dnd move page to another pagecontrol
    - on close button: save a restore layout
    - option to show/hide dock headers
    - option HeaderStyle to change appearance of grabbers

  ToDo:
    - option to save on IDE close (if MainForm is visible on active screen)
    - restore: put MainForm on active screen
    - restore custom dock site splitter without resizing content, only resize docked site
    - undock on hide
    - popup menu
       - shrink side left, top, right, bottom
    - implement a simple way to make forms dockable at designtime without any code
    - on show again (hide form, show form): restore layout
    - close button for pages
    - event for drawing grabbers+headers
    - save/restore other splitters

    Parent bug with links to all other:
    - http://bugs.freepascal.org/view.php?id=18298 default layout sometimes wrong main bar
    Other bugs:
    - http://bugs.freepascal.org/view.php?id=18553 docked form designer
    - http://bugs.freepascal.org/view.php?id=18538 second resize after restore
    - http://bugs.freepascal.org/view.php?id=19735 main bar on different screen size
    - http://bugs.freepascal.org/view.php?id=19810 multi monitor
    - http://bugs.freepascal.org/view.php?id=21076 views
}
unit AnchorDocking;

{$mode objfpc}{$H+}

{ $DEFINE VerboseAnchorDockRestore}
{ $DEFINE VerboseADCustomSite}
{ $DEFINE VerboseAnchorDockPages}

interface

uses
  Math, Classes, SysUtils, types,
  LCLType, LCLIntf, LCLProc,
  Controls, Forms, ExtCtrls, ComCtrls, Graphics, Themes, Menus, Buttons,
  LazConfigStorage, Laz2_XMLCfg, LazFileCache,
  AnchorDockStr, AnchorDockStorage;

{$IFDEF DebugDisableAutoSizing}
const ADAutoSizingReason = 'TAnchorDockMaster Delayed';
{$ENDIF}

type
  TAnchorDockHostSite = class;

  { TAnchorDockCloseButton
    Close button used in TAnchorDockHeader, uses the close button glyph of the
    theme shrinked to a small size. The glyph is shared by all close buttons. }

  TAnchorDockCloseButton = class(TCustomSpeedButton)
  protected
    function GetDrawDetails: TThemedElementDetails; override;
    procedure CalculatePreferredSize(var PreferredWidth,
           PreferredHeight: integer; {%H-}WithThemeSpace: Boolean); override;
  end;

  { TAnchorDockHeader
    The panel of a TAnchorDockHostSite containing the close button and the
    caption when the form is docked. The header can be shown at any of the four
    sides, shows a hint for long captions, starts dragging and shows the popup
    menu of the dockmaster.
    Hiding and aligning is done by its Parent, which is a TAnchorDockHostSite }

  TAnchorDockHeader = class(TCustomPanel)
  private
    FCloseButton: TCustomSpeedButton;
    FHeaderPosition: TADLHeaderPosition;
    procedure CloseButtonClick(Sender: TObject);
    procedure HeaderPositionItemClick(Sender: TObject);
    procedure UndockButtonClick(Sender: TObject);
    procedure MergeButtonClick(Sender: TObject);
    procedure EnlargeSideClick(Sender: TObject);
    procedure SetHeaderPosition(const AValue: TADLHeaderPosition);
  protected
    procedure Paint; override;
    procedure CalculatePreferredSize(var PreferredWidth,
          PreferredHeight: integer; WithThemeSpace: Boolean); override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X,
             Y: Integer); override;
    procedure UpdateHeaderControls;
    procedure SetAlign(Value: TAlign); override;
    procedure DoOnShowHint(HintInfo: PHintInfo); override;
    procedure PopupMenuPopup(Sender: TObject); virtual;
  public
    constructor Create(TheOwner: TComponent); override;
    property CloseButton: TCustomSpeedButton read FCloseButton;
    property HeaderPosition: TADLHeaderPosition read FHeaderPosition write SetHeaderPosition;
    property BevelOuter default bvNone;
  end;
  TAnchorDockHeaderClass = class of TAnchorDockHeader;

  { TAnchorDockSplitter
    A TSplitter used on a TAnchorDockHostSite with SiteType=adhstLayout.
    It can store DockBounds, used by its parent to scale. Scaling works by
    moving the splitters. All other controls are fully anchored to these
    splitters or their parent. }

  TAnchorDockSplitter = class(TCustomSplitter)
  private
    FCustomWidth: Boolean;
    FDockBounds: TRect;
    FDockParentClientSize: TSize;
    FDockRestoreBounds: TRect;
    FPercentPosition: Single;

    procedure UpdatePercentPosition;
  protected
    procedure SetResizeAnchor(const AValue: TAnchorKind); override;
    procedure PopupMenuPopup(Sender: TObject); virtual;
    procedure Paint; override;
  public
    procedure MoveSplitter(Offset: integer); override;
  public
    constructor Create(TheOwner: TComponent); override;
    property DockBounds: TRect read FDockBounds write FDockBounds;
    property DockParentClientSize: TSize read FDockParentClientSize write FDockParentClientSize;
    procedure UpdateDockBounds;
    procedure SetBounds(ALeft, ATop, AWidth, AHeight: integer); override; // any normal movement sets the DockBounds
    procedure SetBoundsPercentually;
    procedure SetBoundsKeepDockBounds(ALeft, ATop, AWidth, AHeight: integer); // movement for scaling keeps the DockBounds
    function SideAnchoredControlCount(Side: TAnchorKind): integer;
    function HasAnchoredControls: boolean;
    procedure SaveLayout(LayoutNode: TAnchorDockLayoutTreeNode);
    function HasOnlyOneSibling(Side: TAnchorKind; MinPos, MaxPos: integer): TControl;
    property DockRestoreBounds: TRect read FDockRestoreBounds write FDockRestoreBounds;
    property CustomWidth: Boolean read FCustomWidth write FCustomWidth;
  end;
  TAnchorDockSplitterClass = class of TAnchorDockSplitter;

  TAnchorDockPageControl = class;
  { TAnchorDockPage
    A page of a TAnchorDockPageControl. }

  TAnchorDockPage = class(TCustomPage)
  public
    procedure UpdateDockCaption(Exclude: TControl = nil); override;
    procedure InsertControl(AControl: TControl; Index: integer); override;
    procedure RemoveControl(AControl: TControl); override;
    function GetSite: TAnchorDockHostSite;
  end;
  TAnchorDockPageClass = class of TAnchorDockPage;

  { TAnchorDockPageControl
    Used for page docking.
    The parent is always a TAnchorDockHostSite with SiteType=adhstPages.
    Its children are all TAnchorDockPage.
    It shows the DockMaster popup menu and starts dragging. }

  TAnchorDockPageControl = class(TCustomTabControl)
  private
    function GetDockPages(Index: integer): TAnchorDockPage;
  protected
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X,Y: Integer); override;
    procedure PopupMenuPopup(Sender: TObject); virtual;
    procedure CloseButtonClick(Sender: TObject); virtual;
    procedure MoveLeftButtonClick(Sender: TObject); virtual;
    procedure MoveLeftMostButtonClick(Sender: TObject); virtual;
    procedure MoveRightButtonClick(Sender: TObject); virtual;
    procedure MoveRightMostButtonClick(Sender: TObject); virtual;
    procedure TabPositionClick(Sender: TObject); virtual;
  public
    constructor Create(TheOwner: TComponent); override;
    procedure UpdateDockCaption(Exclude: TControl = nil); override;
    property DockPages[Index: integer]: TAnchorDockPage read GetDockPages;
    procedure RemoveControl(AControl: TControl); override;
    function GetActiveSite: TAnchorDockHostSite;
  end;
  TAnchorDockPageControlClass = class of TAnchorDockPageControl;

  { TAnchorDockHostSite
    This form is the dockhostsite for all controls.
    When docked together they build a tree structure with the docked controls
    as leaf nodes.
    A TAnchorDockHostSite has four modes: TAnchorDockHostSiteType }

  TAnchorDockHostSiteType = (
    adhstNone,  // fresh created, no control docked
    adhstOneControl, // a control and the "Header" (TAnchorDockHeader)
    adhstLayout, // several controls/TAnchorDockHostSite separated by TAnchorDockSplitters
    adhstPages  // the "Pages" (TAnchorDockPageControl) with several pages
    );

  TAnchorDockHostSite = class(TCustomForm)
  private
    FDockRestoreBounds: TRect;
    FHeader: TAnchorDockHeader;
    FHeaderSide: TAnchorKind;
    FPages: TAnchorDockPageControl;
    FSiteType: TAnchorDockHostSiteType;
    FBoundSplitter: TAnchorDockSplitter;
    fUpdateLayout: integer;
    procedure SetHeaderSide(const AValue: TAnchorKind);
  protected
    procedure Notification(AComponent: TComponent; Operation: TOperation);
                           override;
    function DoDockClientMsg(DragDockObject: TDragDockObject;
                             aPosition: TPoint): boolean; override;
    function ExecuteDock(NewControl, DropOnControl: TControl; DockAlign: TAlign): boolean; virtual;
    function DockFirstControl(NewControl: TControl): boolean; virtual;
    function DockSecondControl(NewControl: TControl; DockAlign: TAlign;
                               Inside: boolean): boolean; virtual;
    function DockAnotherControl(Sibling, NewControl: TControl; DockAlign: TAlign;
                                Inside: boolean): boolean; virtual;
    procedure CreatePages; virtual;
    procedure FreePages; virtual;
    function DockSecondPage(NewControl: TControl): boolean; virtual;
    function DockAnotherPage(NewControl: TControl; InFrontOf: TControl): boolean; virtual;
    procedure AddCleanControl(AControl: TControl; TheAlign: TAlign = alNone);
    procedure RemoveControlFromLayout(AControl: TControl);
    procedure RemoveSpiralSplitter(AControl: TControl);
    procedure ClearChildControlAnchorSides(AControl: TControl);
    procedure Simplify;
    procedure SimplifyPages;
    procedure SimplifyOneControl;
    function GetOneControl: TControl;
    function GetSiteCount: integer;
    function IsOneSiteLayout(out Site: TAnchorDockHostSite): boolean;
    function IsTwoSiteLayout(out Site1, Site2: TAnchorDockHostSite): boolean;
    function GetUniqueSplitterName: string;
    function MakeSite(AControl: TControl): TAnchorDockHostSite;
    procedure MoveAllControls(dx, dy: integer);
    procedure AlignControls(AControl: TControl; var ARect: TRect); override;
    function CheckIfOneControlHidden: boolean;
    procedure DoDock(NewDockSite: TWinControl; var ARect: TRect); override;
    procedure SetParent(NewParent: TWinControl); override;
    function HeaderNeedsShowing: boolean;
    procedure DoClose(var CloseAction: TCloseAction); override;
    function CanUndock: boolean;
    procedure Undock;
    function CanMerge: boolean;
    procedure Merge;
    function EnlargeSide(Side: TAnchorKind;
                         OnlyCheckIfPossible: boolean): boolean;
    function EnlargeSideResizeTwoSplitters(ShrinkSplitterSide,
                         EnlargeSpitterSide: TAnchorKind;
                         OnlyCheckIfPossible: boolean): boolean;
    function EnlargeSideRotateSplitter(Side: TAnchorKind;
                         OnlyCheckIfPossible: boolean): boolean;
    procedure CreateBoundSplitter;
    procedure PositionBoundSplitter;
  public
    constructor CreateNew(AOwner: TComponent; Num: Integer = 0); override;
    destructor Destroy; override;
    function CloseQuery: boolean; override;
    function CloseSite: boolean; virtual;
    procedure RemoveControl(AControl: TControl); override;
    procedure InsertControl(AControl: TControl; Index: integer); override;
    procedure GetSiteInfo(Client: TControl; var InfluenceRect: TRect;
                          MousePos: TPoint; var CanDock: Boolean); override;
    function GetPageArea: TRect;
    procedure ChangeBounds(ALeft, ATop, AWidth, AHeight: integer;
                           KeepBase: boolean); override;
    procedure UpdateDockCaption(Exclude: TControl = nil); override;
    procedure UpdateHeaderAlign;
    procedure UpdateHeaderShowing;
    procedure BeginUpdateLayout;
    procedure EndUpdateLayout;
    function UpdatingLayout: boolean;

    // save/restore layout
    procedure SaveLayout(LayoutTree: TAnchorDockLayoutTree;
                         LayoutNode: TAnchorDockLayoutTreeNode);
    property DockRestoreBounds: TRect read FDockRestoreBounds write FDockRestoreBounds;

    property HeaderSide: TAnchorKind read FHeaderSide write SetHeaderSide;
    property Header: TAnchorDockHeader read FHeader;
    property Pages: TAnchorDockPageControl read FPages;
    property SiteType: TAnchorDockHostSiteType read FSiteType;
    property BoundSplitter: TAnchorDockSplitter read FBoundSplitter;
  end;
  TAnchorDockHostSiteClass = class of TAnchorDockHostSite;

  TADMResizePolicy = (
    admrpNone,
    admrpChild  // resize child
    );

  { TAnchorDockManager
    A TDockManager is the LCL connector to catch various docking events for a
    TControl. Every TAnchorDockHostSite and every custom dock site gets one
    TAnchorDockManager. The LCL frees it automatically when the Site is freed. }

  TAnchorDockManager = class(TDockManager)
  private
    FDockableSites: TAnchors;
    FDockSite: TAnchorDockHostSite;
    FInsideDockingAllowed: boolean;
    FPreferredSiteSizeAsSiteMinimum: boolean;
    FResizePolicy: TADMResizePolicy;
    FStoredConstraints: TRect;
    FSite: TWinControl;
    FSiteClientRect: TRect;
    procedure SetPreferredSiteSizeAsSiteMinimum(const AValue: boolean);
  public
    constructor Create(ADockSite: TWinControl); override;
    procedure GetControlBounds(Control: TControl; out AControlBounds: TRect);
      override;
    procedure InsertControl(Control: TControl; InsertAt: TAlign;
      DropCtl: TControl); override; overload;
    procedure InsertControl(ADockObject: TDragDockObject); override; overload;
    procedure LoadFromStream(Stream: TStream); override;
    procedure PositionDockRect(Client, DropCtl: TControl; DropAlign: TAlign;
      var DockRect: TRect); override; overload;
    procedure RemoveControl(Control: TControl); override;
    procedure ResetBounds(Force: Boolean); override;
    procedure SaveToStream(Stream: TStream); override;
    function GetDockEdge(ADockObject: TDragDockObject): boolean; override;
    procedure RestoreSite(SplitterPos: integer);
    procedure StoreConstraints;
    function GetSitePreferredClientSize: TPoint;

    property Site: TWinControl read FSite; // the associated TControl (a TAnchorDockHostSite or a custom dock site)
    property DockSite: TAnchorDockHostSite read FDockSite; // if Site is a TAnchorDockHostSite, this is it
    property DockableSites: TAnchors read FDockableSites write FDockableSites; // at which sides can be docked
    property InsideDockingAllowed: boolean read FInsideDockingAllowed write FInsideDockingAllowed; // if true allow to put a site into the custom dock site
    function GetChildSite: TAnchorDockHostSite; // get first child TAnchorDockHostSite
    property ResizePolicy: TADMResizePolicy read FResizePolicy write FResizePolicy;
    property StoredConstraints: TRect read FStoredConstraints write FStoredConstraints;
    function StoredConstraintsValid: boolean;
    property PreferredSiteSizeAsSiteMinimum: boolean read FPreferredSiteSizeAsSiteMinimum write SetPreferredSiteSizeAsSiteMinimum;
  end;
  TAnchorDockManagerClass = class of TAnchorDockManager;

  { TAnchorDockSettings }

  TADHeaderStyle = (
    adhsFrame3D,
    adhsLine,
    adhsLines,
    adhsPoints
    );
const
  adhsDefault = adhsFrame3D;
type
  TAnchorDockSettings = class
  private
    FAllowDragging: boolean;
    FChangeStamp: integer;
    FDockOutsideMargin: integer;
    FDockParentMargin: integer;
    FDragTreshold: integer;
    FHeaderAlignLeft: integer;
    FHeaderAlignTop: integer;
    FHeaderHint: string;
    FHeaderStyle: TADHeaderStyle;
    FHeaderFlatten: boolean;
    FHeaderFilled: boolean;
    FHideHeaderCaptionFloatingControl: boolean;
    FPageAreaInPercent: integer;
    FScaleOnResize: boolean;
    FShowHeader: boolean;
    FShowHeaderCaption: boolean;
    FSplitterWidth: integer;
    procedure SetAllowDragging(AValue: boolean);
    procedure SetDockOutsideMargin(AValue: integer);
    procedure SetDockParentMargin(AValue: integer);
    procedure SetDragTreshold(AValue: integer);
    procedure SetHeaderAlignLeft(AValue: integer);
    procedure SetHeaderAlignTop(AValue: integer);
    procedure SetHeaderHint(AValue: string);
    procedure SetHeaderStyle(AValue: TADHeaderStyle);
    procedure SetHideHeaderCaptionFloatingControl(AValue: boolean);
    procedure SetPageAreaInPercent(AValue: integer);
    procedure SetScaleOnResize(AValue: boolean);
    procedure SetShowHeader(AValue: boolean);
    procedure SetShowHeaderCaption(AValue: boolean);
    procedure SetSplitterWidth(AValue: integer);
    procedure SetHeaderFlatten(AValue: boolean);
    procedure SetHeaderFilled(AValue: boolean);
  public
    property DragTreshold: integer read FDragTreshold write SetDragTreshold;
    property DockOutsideMargin: integer read FDockOutsideMargin write SetDockOutsideMargin;
    property DockParentMargin: integer read FDockParentMargin write SetDockParentMargin;
    property PageAreaInPercent: integer read FPageAreaInPercent write SetPageAreaInPercent;
    property HeaderAlignTop: integer read FHeaderAlignTop write SetHeaderAlignTop;
    property HeaderAlignLeft: integer read FHeaderAlignLeft write SetHeaderAlignLeft;
    property HeaderHint: string read FHeaderHint write SetHeaderHint;
    property SplitterWidth: integer read FSplitterWidth write SetSplitterWidth;
    property ScaleOnResize: boolean read FScaleOnResize write SetScaleOnResize;
    property ShowHeader: boolean read FShowHeader write SetShowHeader;
    property ShowHeaderCaption: boolean read FShowHeaderCaption write SetShowHeaderCaption;
    property HideHeaderCaptionFloatingControl: boolean read FHideHeaderCaptionFloatingControl write SetHideHeaderCaptionFloatingControl;
    property AllowDragging: boolean read FAllowDragging write SetAllowDragging;
    property HeaderStyle: TADHeaderStyle read FHeaderStyle write SetHeaderStyle;
    property HeaderFlatten: boolean read FHeaderFlatten write SetHeaderFlatten;
    property HeaderFilled: boolean read FHeaderFilled write SetHeaderFilled;
    procedure IncreaseChangeStamp; inline;
    property ChangeStamp: integer read FChangeStamp;
    procedure LoadFromConfig(Config: TConfigStorage); overload;
    procedure LoadFromConfig(Path: string; Config: TRttiXMLConfig); overload;
    procedure SaveToConfig(Config: TConfigStorage); overload;
    procedure SaveToConfig(Path: string; Config: TRttiXMLConfig); overload;
    function IsEqual(Settings: TAnchorDockSettings): boolean; reintroduce;
    procedure Assign(Source: TAnchorDockSettings);
  end;

  TAnchorDockMaster = class;

  { TAnchorDockMaster
    The central instance that connects all sites and manages all global
    settings. Its global variable is the DockMaster.
    Applications only need to talk to the DockMaster. }

  TADCreateControlEvent = procedure(Sender: TObject; aName: string;
                var AControl: TControl; DoDisableAutoSizing: boolean) of object;
  TADShowDockMasterOptionsEvent = function(aDockMaster: TAnchorDockMaster): TModalResult;

  TAnchorDockMaster = class(TComponent)
  private
    FAllowDragging: boolean;
    FControls: TFPList; // list of TControl, custom host sites and docked controls, not helper controls (e.g. TAnchorDock*)
    FDockOutsideMargin: integer;
    FDockParentMargin: integer;
    FDragTreshold: integer;
    FHeaderAlignLeft: integer;
    FHeaderAlignTop: integer;
    FHeaderClass: TAnchorDockHeaderClass;
    FHeaderHint: string;
    FHeaderStyle: TADHeaderStyle;
    FHeaderFlatten: boolean;
    FHeaderFilled: boolean;
    FIdleConnected: Boolean;
    FManagerClass: TAnchorDockManagerClass;
    FOnCreateControl: TADCreateControlEvent;
    FOnOptionsChanged: TNotifyEvent;
    FOnShowOptions: TADShowDockMasterOptionsEvent;
    FOptionsChangeStamp: int64;
    FPageAreaInPercent: integer;
    FPageClass: TAnchorDockPageClass;
    FPageControlClass: TAnchorDockPageControlClass;
    FQueueSimplify: Boolean;
    FRestoreLayouts: TAnchorDockRestoreLayouts;
    FRestoring: boolean;
    FScaleOnResize: boolean;
    FShowHeader: boolean;
    FShowHeaderCaption: boolean;
    FHideHeaderCaptionFloatingControl: boolean;
    FShowMenuItemShowHeader: boolean;
    FSiteClass: TAnchorDockHostSiteClass;
    FSplitterClass: TAnchorDockSplitterClass;
    FSplitterWidth: integer;
    fNeedSimplify: TFPList; // list of TControl
    fNeedFree: TFPList; // list of TControl
    fSimplifying: boolean;
    fUpdateCount: integer;
    fDisabledAutosizing: TFPList; // list of TControl
    fTreeNameToDocker: TADNameToControl; // TAnchorDockHostSite, TAnchorDockSplitter or custom docksite
    fPopupMenu: TPopupMenu;
    function GetControls(Index: integer): TControl;
    function GetLocalizedHeaderHint: string;
    function CloseUnneededControls(Tree: TAnchorDockLayoutTree): boolean;
    function CreateNeededControls(Tree: TAnchorDockLayoutTree;
                DisableAutoSizing: boolean; ControlNames: TStrings): boolean;
    procedure MapTreeToControls(Tree: TAnchorDockLayoutTree);
    function RestoreLayout(Tree: TAnchorDockLayoutTree; Scale: boolean): boolean;
    procedure EnableAllAutoSizing;
    procedure ClearLayoutProperties(AControl: TControl; NewAlign: TAlign = alClient);
    procedure PopupMenuPopup(Sender: TObject);
    procedure ChangeLockButtonClick(Sender: TObject);
    procedure SetAllowDragging(AValue: boolean);
    procedure SetDockOutsideMargin(AValue: integer);
    procedure SetDockParentMargin(AValue: integer);
    procedure SetDragTreshold(AValue: integer);
    procedure SetHeaderHint(AValue: string);
    procedure SetHeaderStyle(AValue: TADHeaderStyle);
    procedure SetPageAreaInPercent(AValue: integer);
    procedure SetScaleOnResize(AValue: boolean);

    procedure SetHeaderFlatten(AValue: boolean);
    procedure SetHeaderFilled(AValue: boolean);

    procedure SetShowMenuItemShowHeader(AValue: boolean);
    procedure ShowHeadersButtonClick(Sender: TObject);
    procedure OptionsClick(Sender: TObject);
    procedure SetIdleConnected(const AValue: Boolean);
    procedure SetQueueSimplify(const AValue: Boolean);
    procedure SetRestoring(const AValue: boolean);
    procedure OptionsChanged;
  protected
    function DoCreateControl(aName: string; DisableAutoSizing: boolean): TControl;
    procedure AutoSizeAllHeaders(EnableAutoSizing: boolean);
    procedure DisableControlAutoSizing(AControl: TControl);
    procedure InvalidateHeaders;
    procedure Notification(AComponent: TComponent; Operation: TOperation);
          override;
    procedure SetHeaderAlignLeft(const AValue: integer);
    procedure SetHeaderAlignTop(const AValue: integer);
    procedure SetShowHeader(AValue: boolean);
    procedure SetShowHeaderCaption(const AValue: boolean);
    procedure SetHideHeaderCaptionFloatingControl(const AValue: boolean);
    procedure SetSplitterWidth(const AValue: integer);
    procedure OnIdle(Sender: TObject; var Done: Boolean);
    procedure AsyncSimplify({%H-}Data: PtrInt);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function FullRestoreLayout(Tree: TAnchorDockLayoutTree; Scale: Boolean): Boolean;
    function ControlCount: integer;
    property Controls[Index: integer]: TControl read GetControls;
    function IndexOfControl(const aName: string): integer;
    function FindControl(const aName: string): TControl;
    function IsSite(AControl: TControl): boolean;
    function IsAnchorSite(AControl: TControl): boolean;
    function IsCustomSite(AControl: TControl): boolean;
    function GetSite(AControl: TControl): TCustomForm;
    function GetAnchorSite(AControl: TControl): TAnchorDockHostSite;
    function GetControl(Site: TControl): TControl;
    function IsFloating(AControl: TControl): Boolean;
    function GetPopupMenu: TPopupMenu;
    function AddPopupMenuItem(AName, ACaption: string;
                    const OnClickEvent: TNotifyEvent; AParent: TMenuItem = nil): TMenuItem; virtual;
    function AddRemovePopupMenuItem(Add: boolean; AName, ACaption: string;
                    const OnClickEvent: TNotifyEvent; AParent: TMenuItem = nil): TMenuItem; virtual;

    // show / make a control dockable
    procedure MakeDockable(AControl: TControl; Show: boolean = true;
                           BringToFront: boolean = false;
                           AddDockHeader: boolean = true);
    procedure MakeDockSite(AForm: TCustomForm; Sites: TAnchors;
                           ResizePolicy: TADMResizePolicy;
                           AllowInside: boolean = false);
    procedure MakeVisible(AControl: TControl; SwitchPages: boolean);
    function ShowControl(ControlName: string; BringToFront: boolean = false): TControl;
    procedure CloseAll;

    // save/restore layouts
    procedure SaveLayoutToConfig(Config: TConfigStorage);
    procedure SaveMainLayoutToTree(LayoutTree: TAnchorDockLayoutTree);
    procedure SaveSiteLayoutToTree(AForm: TCustomForm;
                                   LayoutTree: TAnchorDockLayoutTree);
    function CreateRestoreLayout(AControl: TControl): TAnchorDockRestoreLayout;
    function ConfigIsEmpty(Config: TConfigStorage): boolean;
    function LoadLayoutFromConfig(Config: TConfigStorage; Scale: Boolean): boolean;
    // layout information for restoring hidden forms
    property RestoreLayouts: TAnchorDockRestoreLayouts read FRestoreLayouts
                                                      write FRestoreLayouts;
    property Restoring: boolean read FRestoring write SetRestoring;
    property IdleConnected: Boolean read FIdleConnected write SetIdleConnected;
    procedure LoadSettingsFromConfig(Config: TConfigStorage);
    procedure SaveSettingsToConfig(Config: TConfigStorage);
    procedure LoadSettings(Settings: TAnchorDockSettings);
    procedure SaveSettings(Settings: TAnchorDockSettings);
    function SettingsAreEqual(Settings: TAnchorDockSettings): boolean;
    procedure ResetSplitters;

    // manual docking
    procedure ManualFloat(AControl: TControl);
    procedure ManualDock(SrcSite: TAnchorDockHostSite; TargetSite: TCustomForm;
                         Align: TAlign; TargetControl: TControl = nil);
    function ManualEnlarge(Site: TAnchorDockHostSite; Side: TAnchorKind;
                         OnlyCheckIfPossible: boolean): boolean;

    // simplification/garbage collection
    procedure BeginUpdate;
    procedure EndUpdate;
    procedure NeedSimplify(AControl: TControl);
    procedure NeedFree(AControl: TControl);
    procedure SimplifyPendingLayouts;
    function AutoFreedIfControlIsRemoved(AControl, RemovedControl: TControl): boolean;
    function CreateSite(NamePrefix: string = '';
                        DisableAutoSizing: boolean = true): TAnchorDockHostSite;
    function CreateSplitter(NamePrefix: string = ''): TAnchorDockSplitter;
    property QueueSimplify: Boolean read FQueueSimplify write SetQueueSimplify;

    property OnCreateControl: TADCreateControlEvent read FOnCreateControl write FOnCreateControl;

    // options
    property OnShowOptions: TADShowDockMasterOptionsEvent read FOnShowOptions write FOnShowOptions;
    property OnOptionsChanged: TNotifyEvent read FOnOptionsChanged write FOnOptionsChanged;
    property DragTreshold: integer read FDragTreshold write SetDragTreshold default 4;
    property DockOutsideMargin: integer read FDockOutsideMargin write SetDockOutsideMargin default 10; // max distance for outside mouse snapping
    property DockParentMargin: integer read FDockParentMargin write SetDockParentMargin default 10; // max distance for snap to parent
    property PageAreaInPercent: integer read FPageAreaInPercent write SetPageAreaInPercent default 40; // size of inner mouse snapping area for page docking
    property ShowHeader: boolean read FShowHeader write SetShowHeader default true; // set to false to hide all headers
    property ShowMenuItemShowHeader: boolean read FShowMenuItemShowHeader write SetShowMenuItemShowHeader default false;
    property ShowHeaderCaption: boolean read FShowHeaderCaption write SetShowHeaderCaption default true; // set to false to remove the text in the headers
    property HideHeaderCaptionFloatingControl: boolean read FHideHeaderCaptionFloatingControl
                          write SetHideHeaderCaptionFloatingControl default true; // disables ShowHeaderCaption for floating controls
    property HeaderAlignTop: integer read FHeaderAlignTop write SetHeaderAlignTop default 80; // move header to top, when (width/height)*100<=HeaderAlignTop
    property HeaderAlignLeft: integer read FHeaderAlignLeft write SetHeaderAlignLeft default 120; // move header to left, when (width/height)*100>=HeaderAlignLeft
    property HeaderHint: string read FHeaderHint write SetHeaderHint; // if empty it uses resourcestring adrsDragAndDockC
    property HeaderStyle: TADHeaderStyle read FHeaderStyle write SetHeaderStyle default adhsDefault;
    property HeaderFlatten: boolean read FHeaderFlatten write SetHeaderFlatten default true;
    property HeaderFilled: boolean read FHeaderFilled write SetHeaderFilled default true;

    property SplitterWidth: integer read FSplitterWidth write SetSplitterWidth default 4;
    property ScaleOnResize: boolean read FScaleOnResize write SetScaleOnResize default true; // scale children when resizing a site
    property AllowDragging: boolean read FAllowDragging write SetAllowDragging default true;
    property OptionsChangeStamp: int64 read FOptionsChangeStamp;
    procedure IncreaseOptionsChangeStamp; inline;

    // for descendants
    property SplitterClass: TAnchorDockSplitterClass read FSplitterClass write FSplitterClass;
    property SiteClass: TAnchorDockHostSiteClass read FSiteClass write FSiteClass;
    property ManagerClass: TAnchorDockManagerClass read FManagerClass write FManagerClass;
    property HeaderClass: TAnchorDockHeaderClass read FHeaderClass write FHeaderClass;
    property PageControlClass: TAnchorDockPageControlClass read FPageControlClass write FPageControlClass;
    property PageClass: TAnchorDockPageClass read FPageClass write FPageClass;
  end;

var
  DockMaster: TAnchorDockMaster = nil;

const
  ADHeaderStyleNames: array[TADHeaderStyle] of string = (
    'Frame3D',
    'Line',
    'Lines',
    'Points'
    );

function StrToADHeaderStyle(const s: string): TADHeaderStyle;
function dbgs(SiteType: TAnchorDockHostSiteType): string; overload;

procedure DrawADHeader(Canvas: TCanvas; Style: TADHeaderStyle; r: TRect; Horizontal: boolean);

procedure CopyAnchorBounds(Source, Target: TControl);
procedure AnchorAndChangeBounds(AControl: TControl; Side: TAnchorKind;
                                Target: TControl);
function ControlsLeftTopOnScreen(AControl: TControl): TPoint;

type
  TAnchorControlsRect = array[TAnchorKind] of TControl;

function DockedControlIsVisible(Control: TControl): boolean;
function GetDockSplitter(Control: TControl; Side: TAnchorKind;
                         out Splitter: TAnchorDockSplitter): boolean;
function GetDockSplitterOrParent(Control: TControl; Side: TAnchorKind;
                                 out AnchorControl: TControl): boolean;
function CountAnchoredControls(Control: TControl; Side: TAnchorKind): Integer;
function NeighbourCanBeShrinked(EnlargeControl, Neighbour: TControl;
                                Side: TAnchorKind): boolean;
function ControlIsAnchoredIndirectly(StartControl: TControl; Side: TAnchorKind;
                                     DestControl: TControl): boolean;
procedure GetAnchorControlsRect(Control: TControl; out ARect: TAnchorControlsRect);
function GetEnclosingControlRect(ControlList: TFPlist;
                                 out ARect: TAnchorControlsRect): boolean;
function GetEnclosedControls(const ARect: TAnchorControlsRect): TFPList;

implementation

function StrToADHeaderStyle(const s: string): TADHeaderStyle;
begin
  for Result:=Low(TADHeaderStyle) to High(TADHeaderStyle) do
    if CompareText(ADHeaderStyleNames[Result],s)=0 then exit;
  Result:=adhsDefault;
end;

procedure DrawADHeader(Canvas: TCanvas; Style: TADHeaderStyle; r: TRect;
  Horizontal: boolean);
var
  Center: Integer;
  lx, ly, d, lt, lb, lm: Integer;
begin
  case Style of
  adhsFrame3D:
    begin
      Canvas.Frame3d(r,2,bvLowered);
      Canvas.Frame3d(r,4,bvRaised);
    end;
  adhsLine:
    if Horizontal then
    begin
      Center:=r.Top+(r.Bottom-r.Top) div 2;
      Canvas.Pen.Color:=clltgray;
      Canvas.Line(r.Left+5,Center-1,r.Right-3,Center-1);
      Canvas.Pen.Color:=clgray;
      Canvas.Line(r.Left+5,Center,r.Right-3,Center);
    end else
    begin
      Center:=r.Right+(r.Left-r.Right) div 2;
      Canvas.Pen.Color:=clltgray;
      Canvas.Line(Center-1,r.Top+3,Center-1,r.Bottom-5);
      Canvas.Pen.Color:=clgray;
      Canvas.Line(Center,r.Top+3,Center,r.Bottom-5);
    end;
  adhsLines:
    begin
      InflateRect(r,-2,-2);
      if Horizontal then
      begin
        lx:=0;
        ly:=3;
        r.Bottom:=r.top+(r.bottom-r.Top) div 3;
        r.top:=r.bottom-ly;
      end else
      begin
        lx:=3;
        ly:=0;
        r.Right:=r.Left+(r.Right-r.Left) div 3 ;
        r.Left:=r.Right-lx;
      end;
      DrawEdge(Canvas.Handle,r, BDR_RAISEDINNER, BF_RECT );
      OffsetRect(r,lx,ly);
      DrawEdge(Canvas.Handle,r, BDR_RAISEDINNER, BF_RECT );
      OffsetRect(r,lx,ly);
      DrawEdge(Canvas.Handle,r, BDR_RAISEDINNER, BF_RECT );
    end;
  adhsPoints:
    if Horizontal then begin
      lx := r.left+2;
      d := (r.Bottom - r.Top - 5) div 2;
      lt := r.Top + d;
      lb := lt + 4;
      lm := lt + 2;
      while lx < r.Right do
      begin
        Canvas.Pixels[lx, lt] := clBtnShadow;
        Canvas.Pixels[lx, lb] := clBtnShadow;
        Canvas.Pixels[lx+2, lm] := clBtnShadow;
        lx := lx + 4;
      end;
    end else begin
      ly := r.Bottom - 2;
      d := (r.Right - r.Left - 5) div 2;
      lt := r.Left + d;
      lb := lt + 4;
      lm := lt + 2;
      while ly > r.Top do
      begin
        Canvas.Pixels[lt, ly] := clBtnShadow;
        Canvas.Pixels[lb, ly] := clBtnShadow;
        Canvas.Pixels[lm, ly-2] := clBtnShadow;
        ly := ly - 4;
      end;
    end;
  end;
end;

function dbgs(SiteType: TAnchorDockHostSiteType): string; overload;
begin
  case SiteType of
  adhstNone: Result:='None';
  adhstOneControl: Result:='OneControl';
  adhstLayout: Result:='Layout';
  adhstPages: Result:='Pages';
  else Result:='?';
  end;
end;

procedure CopyAnchorBounds(Source, Target: TControl);
var
  a: TAnchorKind;
begin
  Target.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('CopyAnchorBounds'){$ENDIF};
  try
    Target.BoundsRect:=Source.BoundsRect;
    Target.Anchors:=Source.Anchors;
    Target.Align:=Source.Align;
    for a:=low(TAnchorKind) to high(TAnchorKind) do
      Target.AnchorSide[a].Assign(Source.AnchorSide[a]);
  finally
    Target.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('CopyAnchorBounds'){$ENDIF};
  end;
end;

procedure AnchorAndChangeBounds(AControl: TControl; Side: TAnchorKind;
  Target: TControl);
begin
  if Target=AControl.Parent then begin
    AControl.AnchorParallel(Side,0,Target);
    case Side of
    akTop: AControl.Top:=0;
    akLeft: AControl.Left:=0;
    akRight: AControl.Width:=AControl.Parent.ClientWidth-AControl.Left;
    akBottom: AControl.Height:=AControl.Parent.ClientHeight-AControl.Top;
    end;
  end else begin
    AControl.AnchorToNeighbour(Side,0,Target);
    case Side of
    akTop: AControl.Top:=Target.Top+Target.Height;
    akLeft: AControl.Left:=Target.Left+Target.Width;
    akRight: AControl.Width:=Target.Left-AControl.Width;
    akBottom: AControl.Height:=Target.Top-AControl.Height;
    end;
  end;
end;

function ControlsLeftTopOnScreen(AControl: TControl): TPoint;
begin
  if AControl.Parent<>nil then begin
    Result:=AControl.Parent.ClientOrigin;
    inc(Result.X,AControl.Left);
    inc(Result.Y,AControl.Top);
  end else begin
    Result:=AControl.Parent.ClientOrigin;
  end;
end;

function DockedControlIsVisible(Control: TControl): boolean;
begin
  while Control<>nil do begin
    if (not Control.IsControlVisible)
    and (not (Control is TAnchorDockPage)) then
      exit(false);
    Control:=Control.Parent;
  end;
  Result:=true;
end;

function GetDockSplitter(Control: TControl; Side: TAnchorKind; out
  Splitter: TAnchorDockSplitter): boolean;
begin
  Result:=false;
  Splitter:=nil;
  if not (Side in Control.Anchors) then exit;
  Splitter:=TAnchorDockSplitter(Control.AnchorSide[Side].Control);
  if not (Splitter is TAnchorDockSplitter) then begin
    Splitter:=nil;
    exit;
  end;
  if Splitter.Parent<>Control.Parent then exit;
  Result:=true;
end;

function GetDockSplitterOrParent(Control: TControl; Side: TAnchorKind; out
  AnchorControl: TControl): boolean;
begin
  Result:=false;
  AnchorControl:=nil;
  if not (Side in Control.Anchors) then exit;
  AnchorControl:=Control.AnchorSide[Side].Control;
  if (AnchorControl is TAnchorDockSplitter)
  and (AnchorControl.Parent=Control.Parent)
  then
    Result:=true
  else if AnchorControl=Control.Parent then
    Result:=true;
end;

function CountAnchoredControls(Control: TControl; Side: TAnchorKind): Integer;
{ return the number of siblings, that are anchored on Side of Control
  For example: if Side=akLeft it will return the number of controls, which
  right side is anchored to the left of Control }
var
  i: Integer;
  Neighbour: TControl;
begin
  Result:=0;
  for i:=0 to Control.AnchoredControlCount-1 do begin
    Neighbour:=Control.AnchoredControls[i];
    if (OppositeAnchor[Side] in Neighbour.Anchors)
    and (Neighbour.AnchorSide[OppositeAnchor[Side]].Control=Control) then
      inc(Result);
  end;
end;

function NeighbourCanBeShrinked(EnlargeControl, Neighbour: TControl;
  Side: TAnchorKind): boolean;
{ returns true if Neighbour can be shrinked on the opposite side of Side
}
const
  MinControlSize = 20;
var
  Splitter: TAnchorDockSplitter;
begin
  Result:=false;
  if not GetDockSplitter(EnlargeControl,OppositeAnchor[Side],Splitter) then
    exit;
  case Side of
  akLeft: // check if left side of Neighbour can be moved
    Result:=Neighbour.Left+Neighbour.Width
        >EnlargeControl.Left+EnlargeControl.Width+Splitter.Width+MinControlSize;
  akRight: // check if right side of Neighbour can be moved
    Result:=Neighbour.Left+MinControlSize+Splitter.Width<EnlargeControl.Left;
  akTop: // check if top side of Neighbour can be moved
    Result:=Neighbour.Top+Neighbour.Height
       >EnlargeControl.Top+EnlargeControl.Height+Splitter.Height+MinControlSize;
  akBottom: // check if bottom side of Neighbour can be moved
    Result:=Neighbour.Top+MinControlSize+Splitter.Height<EnlargeControl.Top;
  end;
end;

function ControlIsAnchoredIndirectly(StartControl: TControl; Side: TAnchorKind;
  DestControl: TControl): boolean;
{ true if there is an Anchor way from StartControl to DestControl over Side.
  For example:

    +-+|+-+
    |A|||B|
    +-+|+-+

  A is akLeft to B.
  B is akRight to A.
  The splitter is akLeft to B.
  The splitter is akRight to A.
  All other are false.
}
var
  Checked: array of Boolean;
  Parent: TWinControl;

  function Check(ControlIndex: integer): boolean;
  var
    AControl: TControl;
    SideControl: TControl;
    i: Integer;
  begin
    if Checked[ControlIndex] then
      exit(false);
    Checked[ControlIndex]:=true;
    AControl:=Parent.Controls[ControlIndex];
    if AControl=DestControl then exit(true);

    if (Side in AControl.Anchors) then begin
      SideControl:=AControl.AnchorSide[Side].Control;
      if (SideControl<>nil) and Check(Parent.GetControlIndex(SideControl)) then
        exit(true);
    end;
    for i:=0 to AControl.AnchoredControlCount-1 do begin
      if Checked[i] then continue;
      SideControl:=AControl.AnchoredControls[i];
      if OppositeAnchor[Side] in SideControl.Anchors then begin
        if (SideControl.AnchorSide[OppositeAnchor[Side]].Control=AControl)
        and Check(i) then
          exit(true);
      end;
    end;
    Result:=false;
  end;

var
  i: Integer;
begin
  if (StartControl=nil) or (DestControl=nil)
  or (StartControl.Parent=nil)
  or (StartControl.Parent<>DestControl.Parent)
  or (StartControl=DestControl) then
    exit(false);
  Parent:=StartControl.Parent;
  SetLength(Checked,Parent.ControlCount);
  for i:=0 to length(Checked)-1 do Checked[i]:=false;
  Result:=Check(Parent.GetControlIndex(StartControl));
end;

procedure GetAnchorControlsRect(Control: TControl; out ARect: TAnchorControlsRect);
var
  a: TAnchorKind;
begin
  for a:=Low(TAnchorKind) to High(TAnchorKind) do
    ARect[a]:=Control.AnchorSide[a].Control;
end;

function GetEnclosingControlRect(ControlList: TFPlist; out
  ARect: TAnchorControlsRect): boolean;
{ ARect will be the minimum TAnchorControlsRect around the controls in the list
  returns true, if there is such a TAnchorControlsRect.

  The controls in ARect will either be the Parent or a TLazDockSplitter
}
var
  Parent: TWinControl;

  function ControlIsValidAnchor(Control: TControl; Side: TAnchorKind): boolean;
  var
    i: Integer;
  begin
    Result:=false;
    if (Control=ARect[Side]) then exit(true);// this allows Parent at the beginning

    if not (Control is TAnchorDockSplitter) then
      exit;// not a splitter
    if (TAnchorDockSplitter(Control).ResizeAnchor in [akLeft,akRight])
      <>(Side in [akLeft,akRight]) then
        exit;// wrong alignment
    if ControlList.IndexOf(Control)>=0 then
      exit;// is an inner control
    if ControlIsAnchoredIndirectly(Control,Side,ARect[Side]) then
      exit; // this anchor would be worse than the current maximum
    for i:=0 to ControlList.Count-1 do begin
      if not ControlIsAnchoredIndirectly(Control,Side,TControl(ControlList[i]))
      then begin
        // this anchor is not above (below, ...) the inner controls
        exit;
      end;
    end;
    Result:=true;
  end;

var
  TopIndex: Integer;
  TopControl: TControl;
  RightIndex: Integer;
  RightControl: TControl;
  BottomIndex: Integer;
  BottomControl: TControl;
  LeftIndex: Integer;
  LeftControl: TControl;
  Candidates: TFPList;
  i: Integer;
  a: TAnchorKind;
begin
  Result:=false;
  if (ControlList=nil) or (ControlList.Count=0) then exit;

  // get Parent
  Parent:=TControl(ControlList[0]).Parent;
  if Parent=nil then exit;
  for i:=0 to ControlList.Count-1 do
    if TControl(ControlList[i]).Parent<>Parent then exit;

  // set the default rect: the Parent
  Result:=true;
  for a:=Low(TAnchorKind) to High(TAnchorKind) do
    ARect[a]:=Parent;

  // find all possible Candidates
  Candidates:=TFPList.Create;
  try
    Candidates.Add(Parent);
    for i:=0 to Parent.ControlCount-1 do
      if Parent.Controls[i] is TAnchorDockSplitter then
        Candidates.Add(Parent.Controls[i]);

    // now check every possible rectangle
    // Note: four loops seems to be dog slow, but the checks
    //       avoid most possibilities early
    for TopIndex:=0 to Candidates.Count-1 do begin
      TopControl:=TControl(Candidates[TopIndex]);
      if not ControlIsValidAnchor(TopControl,akTop) then continue;

      for RightIndex:=0 to Candidates.Count-1 do begin
        RightControl:=TControl(Candidates[RightIndex]);
        if (TopControl.AnchorSide[akRight].Control<>RightControl)
        and (RightControl.AnchorSide[akTop].Control<>TopControl) then
          continue; // not touching / not a corner
        if not ControlIsValidAnchor(RightControl,akRight) then continue;

        for BottomIndex:=0 to Candidates.Count-1 do begin
          BottomControl:=TControl(Candidates[BottomIndex]);
          if (RightControl.AnchorSide[akBottom].Control<>BottomControl)
          and (BottomControl.AnchorSide[akRight].Control<>RightControl) then
            continue; // not touching / not a corner
          if not ControlIsValidAnchor(BottomControl,akBottom) then continue;

          for LeftIndex:=0 to Candidates.Count-1 do begin
            LeftControl:=TControl(Candidates[LeftIndex]);
            if (BottomControl.AnchorSide[akLeft].Control<>LeftControl)
            and (LeftControl.AnchorSide[akBottom].Control<>BottomControl) then
              continue; // not touching / not a corner
            if (TopControl.AnchorSide[akLeft].Control<>LeftControl)
            and (LeftControl.AnchorSide[akTop].Control<>LeftControl) then
              continue; // not touching / not a corner
            if not ControlIsValidAnchor(LeftControl,akLeft) then continue;

            // found a better rectangle
            ARect[akLeft]  :=LeftControl;
            ARect[akRight] :=RightControl;
            ARect[akTop]   :=TopControl;
            ARect[akBottom]:=BottomControl;
          end;
        end;
      end;
    end;
  finally
    Candidates.Free;
  end;
end;

function GetEnclosedControls(const ARect: TAnchorControlsRect): TFPList;
{ return a list of all controls bounded by the anchors in ARect }
var
  Parent: TWinControl;

  procedure Fill(AControl: TControl);
  var
    a: TAnchorKind;
    SideControl: TControl;
    i: Integer;
  begin
    if AControl=nil then exit;
    if AControl=Parent then exit;// do not add Parent
    for a:=Low(TAnchorKind) to High(TAnchorKind) do
      if ARect[a]=AControl then exit;// do not add boundary

    if Result.IndexOf(AControl)>=0 then exit;// already added
    Result.Add(AControl);

    for a:=Low(TAnchorKind) to High(TAnchorKind) do
      Fill(AControl.AnchorSide[a].Control);
    for i:=0 to Parent.ControlCount-1 do begin
      SideControl:=Parent.Controls[i];
      for a:=Low(TAnchorKind) to High(TAnchorKind) do
        if SideControl.AnchorSide[a].Control=AControl then
          Fill(SideControl);
    end;
  end;

var
  i: Integer;
  AControl: TControl;
  LeftTopControl: TControl;
begin
  Result:=TFPList.Create;

  // find the Parent
  if (ARect[akLeft]=ARect[akRight]) and (ARect[akLeft] is TWinControl) then
    Parent:=TWinControl(ARect[akLeft])
  else
    Parent:=ARect[akLeft].Parent;

  // find the left, top most control
  for i:=0 to Parent.ControlCount-1 do begin
    AControl:=Parent.Controls[i];
    if (AControl.AnchorSide[akLeft].Control=ARect[akLeft])
    and (AControl.AnchorSide[akTop].Control=ARect[akTop]) then begin
      LeftTopControl:=AControl;
      break;
    end;
  end;
  if Result.Count=0 then exit;

  // use flood fill to find the rest
  Fill(LeftTopControl);
end;

{ TAnchorDockSettings }

procedure TAnchorDockSettings.SetAllowDragging(AValue: boolean);
begin
  if FAllowDragging=AValue then Exit;
  FAllowDragging:=AValue;
  IncreaseChangeStamp;
end;

procedure TAnchorDockSettings.SetDockOutsideMargin(AValue: integer);
begin
  if FDockOutsideMargin=AValue then Exit;
  FDockOutsideMargin:=AValue;
  IncreaseChangeStamp;
end;

procedure TAnchorDockSettings.SetDockParentMargin(AValue: integer);
begin
  if FDockParentMargin=AValue then Exit;
  FDockParentMargin:=AValue;
  IncreaseChangeStamp;
end;

procedure TAnchorDockSettings.SetDragTreshold(AValue: integer);
begin
  if FDragTreshold=AValue then Exit;
  FDragTreshold:=AValue;
  IncreaseChangeStamp;
end;

procedure TAnchorDockSettings.SetHeaderAlignLeft(AValue: integer);
begin
  if FHeaderAlignLeft=AValue then Exit;
  FHeaderAlignLeft:=AValue;
  IncreaseChangeStamp;
end;

procedure TAnchorDockSettings.SetHeaderAlignTop(AValue: integer);
begin
  if FHeaderAlignTop=AValue then Exit;
  FHeaderAlignTop:=AValue;
  IncreaseChangeStamp;
end;

procedure TAnchorDockSettings.SetHeaderHint(AValue: string);
begin
  if FHeaderHint=AValue then Exit;
  FHeaderHint:=AValue;
  IncreaseChangeStamp;
end;

procedure TAnchorDockSettings.SetHeaderStyle(AValue: TADHeaderStyle);
begin
  if FHeaderStyle=AValue then Exit;
  FHeaderStyle:=AValue;
  IncreaseChangeStamp;
end;

procedure TAnchorDockSettings.SetHideHeaderCaptionFloatingControl(
  AValue: boolean);
begin
  if FHideHeaderCaptionFloatingControl=AValue then Exit;
  FHideHeaderCaptionFloatingControl:=AValue;
  IncreaseChangeStamp;
end;

procedure TAnchorDockSettings.SetPageAreaInPercent(AValue: integer);
begin
  if FPageAreaInPercent=AValue then Exit;
  FPageAreaInPercent:=AValue;
  IncreaseChangeStamp;
end;

procedure TAnchorDockSettings.SetScaleOnResize(AValue: boolean);
begin
  if FScaleOnResize=AValue then Exit;
  FScaleOnResize:=AValue;
  IncreaseChangeStamp;
end;

procedure TAnchorDockSettings.SetHeaderFlatten(AValue: boolean);
begin
  if FHeaderFlatten=AValue then Exit;
  FHeaderFlatten:=AValue;
  IncreaseChangeStamp;
end;

procedure TAnchorDockSettings.SetHeaderFilled(AValue: boolean);
begin
  if FHeaderFilled=AValue then Exit;
  FHeaderFilled:=AValue;
  IncreaseChangeStamp;
end;

procedure TAnchorDockSettings.SetShowHeader(AValue: boolean);
begin
  if FShowHeader=AValue then Exit;
  FShowHeader:=AValue;
  IncreaseChangeStamp;
end;

procedure TAnchorDockSettings.SetShowHeaderCaption(AValue: boolean);
begin
  if FShowHeaderCaption=AValue then Exit;
  FShowHeaderCaption:=AValue;
  IncreaseChangeStamp;
end;

procedure TAnchorDockSettings.SetSplitterWidth(AValue: integer);
begin
  if FSplitterWidth=AValue then Exit;
  FSplitterWidth:=AValue;
  IncreaseChangeStamp;
end;

procedure TAnchorDockSettings.Assign(Source: TAnchorDockSettings);
begin
  FAllowDragging := Source.FAllowDragging;
  FChangeStamp := Source.FChangeStamp;
  FDockOutsideMargin := Source.FDockOutsideMargin;
  FDockParentMargin := Source.FDockParentMargin;
  FDragTreshold := Source.FDragTreshold;
  FHeaderAlignLeft := Source.FHeaderAlignLeft;
  FHeaderAlignTop := Source.FHeaderAlignTop;
  FHeaderHint := Source.FHeaderHint;
  FHeaderStyle := Source.FHeaderStyle;
  FHeaderFlatten := Source.FHeaderFlatten;
  FHeaderFilled := Source.FHeaderFilled;
  FHideHeaderCaptionFloatingControl := Source.FHideHeaderCaptionFloatingControl;
  FPageAreaInPercent := Source.FPageAreaInPercent;
  FScaleOnResize := Source.FScaleOnResize;
  FShowHeader := Source.FShowHeader;
  FShowHeaderCaption := Source.FShowHeaderCaption;
  FSplitterWidth := Source.FSplitterWidth;
end;

procedure TAnchorDockSettings.IncreaseChangeStamp;
begin
  LUIncreaseChangeStamp(fChangeStamp);
end;

procedure TAnchorDockSettings.LoadFromConfig(Config: TConfigStorage);
begin
  Config.AppendBasePath('Settings/');
  DragTreshold:=Config.GetValue('DragThreshold',4);
  DockOutsideMargin:=Config.GetValue('DockOutsideMargin',10);
  DockParentMargin:=Config.GetValue('DockParentMargin',10);
  PageAreaInPercent:=Config.GetValue('PageAreaInPercent',40);
  HeaderAlignTop:=Config.GetValue('HeaderAlignTop',80);
  HeaderAlignLeft:=Config.GetValue('HeaderAlignLeft',120);
  SplitterWidth:=Config.GetValue('SplitterWidth',4);
  ScaleOnResize:=Config.GetValue('ScaleOnResize',true);
  ShowHeader:=Config.GetValue('ShowHeader',true);
  ShowHeaderCaption:=Config.GetValue('ShowHeaderCaption',true);
  HideHeaderCaptionFloatingControl:=Config.GetValue('HideHeaderCaptionFloatingControl',true);
  AllowDragging:=Config.GetValue('AllowDragging',true);
  HeaderStyle:=StrToADHeaderStyle(Config.GetValue('HeaderStyle',ADHeaderStyleNames[adhsDefault]));
  HeaderFlatten:=Config.GetValue('HeaderFlatten',true);
  HeaderFilled:=Config.GetValue('HeaderFilled',true);
  Config.UndoAppendBasePath;
end;

procedure TAnchorDockSettings.SaveToConfig(Path: string; Config: TRttiXMLConfig
  );
begin
  Config.SetDeleteValue(Path+'DragThreshold',DragTreshold,4);
  Config.SetDeleteValue(Path+'DockOutsideMargin',DockOutsideMargin,10);
  Config.SetDeleteValue(Path+'DockParentMargin',DockParentMargin,10);
  Config.SetDeleteValue(Path+'PageAreaInPercent',PageAreaInPercent,40);
  Config.SetDeleteValue(Path+'HeaderAlignTop',HeaderAlignTop,80);
  Config.SetDeleteValue(Path+'HeaderAlignLeft',HeaderAlignLeft,120);
  Config.SetDeleteValue(Path+'SplitterWidth',SplitterWidth,4);
  Config.SetDeleteValue(Path+'ScaleOnResize',ScaleOnResize,true);
  Config.SetDeleteValue(Path+'ShowHeader',ShowHeader,true);
  Config.SetDeleteValue(Path+'ShowHeaderCaption',ShowHeaderCaption,true);
  Config.SetDeleteValue(Path+'HideHeaderCaptionFloatingControl',HideHeaderCaptionFloatingControl,true);
  Config.SetDeleteValue(Path+'AllowDragging',AllowDragging,true);
  Config.SetDeleteValue(Path+'HeaderStyle',ADHeaderStyleNames[HeaderStyle],ADHeaderStyleNames[adhsDefault]);
  Config.SetDeleteValue(Path+'HeaderFlatten',HeaderFlatten,true);
  Config.SetDeleteValue(Path+'HeaderFilled',HeaderFilled,true);
end;

procedure TAnchorDockSettings.SaveToConfig(Config: TConfigStorage);
begin
  Config.AppendBasePath('Settings/');
  Config.SetDeleteValue('DragThreshold',DragTreshold,4);
  Config.SetDeleteValue('DockOutsideMargin',DockOutsideMargin,10);
  Config.SetDeleteValue('DockParentMargin',DockParentMargin,10);
  Config.SetDeleteValue('PageAreaInPercent',PageAreaInPercent,40);
  Config.SetDeleteValue('HeaderAlignTop',HeaderAlignTop,80);
  Config.SetDeleteValue('HeaderAlignLeft',HeaderAlignLeft,120);
  Config.SetDeleteValue('SplitterWidth',SplitterWidth,4);
  Config.SetDeleteValue('ScaleOnResize',ScaleOnResize,true);
  Config.SetDeleteValue('ShowHeader',ShowHeader,true);
  Config.SetDeleteValue('ShowHeaderCaption',ShowHeaderCaption,true);
  Config.SetDeleteValue('HideHeaderCaptionFloatingControl',HideHeaderCaptionFloatingControl,true);
  Config.SetDeleteValue('AllowDragging',AllowDragging,true);
  Config.SetDeleteValue('HeaderStyle',ADHeaderStyleNames[HeaderStyle],ADHeaderStyleNames[adhsDefault]);
  Config.SetDeleteValue('HeaderFlatten',HeaderFlatten,true);
  Config.SetDeleteValue('HeaderFilled',HeaderFilled,true);
  Config.UndoAppendBasePath;
end;

function TAnchorDockSettings.IsEqual(Settings: TAnchorDockSettings): boolean;
begin
  Result:=(DragTreshold=Settings.DragTreshold)
      and (DockOutsideMargin=Settings.DockOutsideMargin)
      and (DockParentMargin=Settings.DockParentMargin)
      and (PageAreaInPercent=Settings.PageAreaInPercent)
      and (HeaderAlignTop=Settings.HeaderAlignTop)
      and (HeaderAlignLeft=Settings.HeaderAlignLeft)
      and (HeaderHint=Settings.HeaderHint)
      and (SplitterWidth=Settings.SplitterWidth)
      and (ScaleOnResize=Settings.ScaleOnResize)
      and (ShowHeader=Settings.ShowHeader)
      and (ShowHeaderCaption=Settings.ShowHeaderCaption)
      and (HideHeaderCaptionFloatingControl=Settings.HideHeaderCaptionFloatingControl)
      and (AllowDragging=Settings.AllowDragging)
      and (HeaderStyle=Settings.HeaderStyle)
      and (HeaderFlatten=Settings.HeaderFlatten)
      and (HeaderFilled=Settings.HeaderFilled)
      ;
end;

procedure TAnchorDockSettings.LoadFromConfig(Path: string;
  Config: TRttiXMLConfig);
begin
  DragTreshold:=Config.GetValue(Path+'DragThreshold',4);
  DockOutsideMargin:=Config.GetValue(Path+'DockOutsideMargin',10);
  DockParentMargin:=Config.GetValue(Path+'DockParentMargin',10);
  PageAreaInPercent:=Config.GetValue(Path+'PageAreaInPercent',40);
  HeaderAlignTop:=Config.GetValue(Path+'HeaderAlignTop',80);
  HeaderAlignLeft:=Config.GetValue(Path+'HeaderAlignLeft',120);
  SplitterWidth:=Config.GetValue(Path+'SplitterWidth',4);
  ScaleOnResize:=Config.GetValue(Path+'ScaleOnResize',true);
  ShowHeader:=Config.GetValue(Path+'ShowHeader',true);
  ShowHeaderCaption:=Config.GetValue(Path+'ShowHeaderCaption',true);
  HideHeaderCaptionFloatingControl:=Config.GetValue(Path+'HideHeaderCaptionFloatingControl',true);
  AllowDragging:=Config.GetValue(Path+'AllowDragging',true);
  HeaderStyle:=StrToADHeaderStyle(Config.GetValue(Path+'HeaderStyle',ADHeaderStyleNames[adhsDefault]));
  HeaderFlatten:=Config.GetValue(Path+'HeaderFlatten',true);
  HeaderFilled:=Config.GetValue(Path+'HeaderFilled',true);
end;

{ TAnchorDockMaster }

function TAnchorDockMaster.GetControls(Index: integer): TControl;
begin
  Result:=TControl(FControls[Index]);
end;

function TAnchorDockMaster.GetLocalizedHeaderHint: string;
begin
  if HeaderHint<>'' then
    Result:=HeaderHint
  else
    Result:=adrsDragAndDockC;
end;

procedure TAnchorDockMaster.SetHeaderAlignLeft(const AValue: integer);
begin
  if FHeaderAlignLeft=AValue then exit;
  FHeaderAlignLeft:=AValue;
  FHeaderAlignTop:=Min(FHeaderAlignLeft-1,FHeaderAlignTop);
  OptionsChanged;
end;

procedure TAnchorDockMaster.SetHeaderAlignTop(const AValue: integer);
begin
  if FHeaderAlignTop=AValue then exit;
  FHeaderAlignTop:=AValue;
  FHeaderAlignLeft:=Max(FHeaderAlignTop+1,FHeaderAlignLeft);
  OptionsChanged;
end;

function TAnchorDockMaster.CloseUnneededControls(Tree: TAnchorDockLayoutTree
  ): boolean;
var
  i: Integer;
  AControl: TControl;
begin
  i:=ControlCount-1;
  while i>=0 do begin
    AControl:=Controls[i];
    if DockedControlIsVisible(AControl)
    and (Tree.Root.FindChildNode(AControl.Name,true)=nil)
    and (Application.MainForm<>AControl) then begin
      DisableControlAutoSizing(AControl);
      // AControl is currently on a visible site, but not in the Tree
      // => close site
      if AControl.HostDockSite <> nil then
      begin
        debugln(['TAnchorDockMaster.CloseUnneededControls Control=',DbgSName(AControl),' Site=',AControl.HostDockSite.Name]);
        if AControl.HostDockSite is TAnchorDockHostSite then begin
          if not TAnchorDockHostSite(AControl.HostDockSite).CloseSite then begin
            if FControls.IndexOf(AControl)<0 then
              AControl:=nil;
            debugln(['TAnchorDockMaster.CloseUnneededControls CloseSite failed Control=',DbgSName(AControl)]);
            exit(false);
          end;
        end;
      end;
      if FControls.IndexOf(AControl)>=0 then begin
        // the control is still there
        if AControl.HostDockSite<>nil then begin
          AControl.HostDockSite.Visible:=false;
          AControl.HostDockSite.Parent:=nil;
        end else begin
          AControl.Visible:=False;
          AControl.Parent:=nil;
        end;
      end;
    end;
    i:=Min(i,ControlCount)-1;
  end;
  Result:=true;
end;

function TAnchorDockMaster.CreateNeededControls(Tree: TAnchorDockLayoutTree;
  DisableAutoSizing: boolean; ControlNames: TStrings): boolean;

  procedure CreateControlsForNode(Node: TAnchorDockLayoutTreeNode);
  var
    i: Integer;
    AControl: TControl;
  begin
    if (Node.NodeType in [adltnControl,adltnCustomSite])
    and (Node.Name<>'') then begin
      AControl:=FindControl(Node.Name);
      if AControl<>nil then begin
        //debugln(['CreateControlsForNode ',Node.Name,' already exists']);
        if DisableAutoSizing then
          DisableControlAutoSizing(AControl);
      end else begin
        //debugln(['CreateControlsForNode ',Node.Name,' needs creation']);
        AControl:=DoCreateControl(Node.Name,true);
        if AControl<>nil then begin
          try
            if DisableAutoSizing and (fDisabledAutosizing.IndexOf(AControl)<0)
            then begin
              fDisabledAutosizing.Add(AControl);
              AControl.FreeNotification(Self);
            end;
            if Node.NodeType=adltnControl then
              MakeDockable(AControl,false)
            else if not IsCustomSite(AControl) then
              raise EAnchorDockLayoutError.Create('not a docksite: '+DbgSName(AControl));
          finally
            if not DisableAutoSizing then
              AControl.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}(ADAutoSizingReason){$ENDIF};
          end;
        end else begin
          debugln(['CreateControlsForNode ',Node.Name,' failed to create']);
        end;
      end;
      if AControl<>nil then
        ControlNames.Add(AControl.Name);
    end;
    for i:=0 to Node.Count-1 do
      CreateControlsForNode(Node[i]);
  end;

begin
  Result:=false;
  CreateControlsForNode(Tree.Root);
  Result:=true;
end;

procedure TAnchorDockMaster.MapTreeToControls(Tree: TAnchorDockLayoutTree);

  procedure MapHostDockSites(Node: TAnchorDockLayoutTreeNode);
  // map in TreeNameToDocker each control name to its HostDockSite or custom dock site
  var
    i: Integer;
    AControl: TControl;
  begin
    if Node.IsSplitter then exit;
    if (Node.NodeType=adltnControl) then begin
      AControl:=FindControl(Node.Name);
      if (AControl<>nil) and (AControl.HostDockSite is TAnchorDockHostSite) then
        fTreeNameToDocker[Node.Name]:=AControl.HostDockSite;
      // ignore kids
      exit;
    end;
    if (Node.NodeType=adltnCustomSite) then begin
      AControl:=FindControl(Node.Name);
      if IsCustomSite(AControl) then
        fTreeNameToDocker[Node.Name]:=AControl;
    end;
    for i:=0 to Node.Count-1 do
      MapHostDockSites(Node[i]); // recursive
  end;

  procedure MapTopLevelSites(Node: TAnchorDockLayoutTreeNode);
  // map in TreeNameToDocker each RootWindow node name to a site with a
  // corresponding control
  // For example: if there is control on a complex site (SiteA), and the control
  //    has a node in the Tree, then the root node of the tree node is mapped to
  //    the SiteA. This way the corresponding root forms are kept which reduces
  //    flickering.

    function FindMappedControl(ChildNode: TAnchorDockLayoutTreeNode
      ): TCustomForm;
    var
      i: Integer;
    begin
      if ChildNode.NodeType in [adltnControl,adltnCustomSite] then
        Result:=TCustomForm(fTreeNameToDocker[ChildNode.Name])
      else
        for i:=0 to ChildNode.Count-1 do begin
          Result:=FindMappedControl(ChildNode[i]); // search recursive
          if Result<>nil then exit;
        end;
    end;

  var
    i: Integer;
    RootSite: TCustomForm;
    Site: TCustomForm;
  begin
    if Node.IsSplitter then exit;
    if Node.IsRootWindow then begin
      if Node.Name='' then exit;
      if Node.NodeType=adltnControl then exit;
      // Node is a complex site
      if fTreeNameToDocker[Node.Name]<>nil then exit;
      // and not yet mapped to a site
      Site:=FindMappedControl(Node);
      if Site=nil then exit;
      // and there is sub node mapped to a site (anchor or custom)
      RootSite:=GetParentForm(Site);
      if not (RootSite is TAnchorDockHostSite) then exit;
      // and the mapped site has a root site
      if fTreeNameToDocker.ControlToName(RootSite)<>'' then exit;
      // and the root site is not yet mapped
      // => map the root node to the root site
      fTreeNameToDocker[Node.Name]:=RootSite;
    end else
      for i:=0 to Node.Count-1 do
        MapTopLevelSites(Node[i]); // recursive
  end;

  procedure MapBottomUp(Node: TAnchorDockLayoutTreeNode);
  { map the other nodes to existing sites
    The heuristic works like this:
      if a child node was mapped to a site and the site has a parent site then
      map this node to this parent site.
  }
  var
    i: Integer;
    BestSite: TControl;
  begin
    if Node.IsSplitter then exit;
    BestSite:=fTreeNameToDocker[Node.Name];
    for i:=0 to Node.Count-1 do begin
      MapBottomUp(Node[i]); // recursive
      if BestSite=nil then
        BestSite:=fTreeNameToDocker[Node[i].Name];
    end;
    if (fTreeNameToDocker[Node.Name]=nil) and (BestSite<>nil) then begin
      // search the parent site of a child site
      repeat
        BestSite:=BestSite.Parent;
        if BestSite is TAnchorDockHostSite then begin
          if fTreeNameToDocker.ControlToName(BestSite)='' then
            fTreeNameToDocker[Node.Name]:=BestSite;
          break;
        end;
      until (BestSite=nil);
    end;
  end;

  procedure MapSplitters(Node: TAnchorDockLayoutTreeNode);
  { map the splitter nodes to existing splitters
    The heuristic works like this:
      If a node is mapped to a site and the node is at Side anchored to a
        splitter node and the site is anchored at Side to a splitter
      then map the splitter node to the splitter.
  }
  var
    i: Integer;
    Side: TAnchorKind;
    Site: TControl;
    SplitterNode: TAnchorDockLayoutTreeNode;
    Splitter: TControl;
  begin
    if Node.IsSplitter then exit;
    for i:=0 to Node.Count-1 do
      MapSplitters(Node[i]); // recursive

    if Node.Parent=nil then exit;
    // node is a child node
    Site:=fTreeNameToDocker[Node.Name];
    if Site=nil then exit;
    // node is mapped to a site
    // check each side
    for Side:=Low(TAnchorKind) to high(TAnchorKind) do begin
      if Node.Anchors[Side]='' then continue;
      Splitter:=Site.AnchorSide[Side].Control;
      if (not (Splitter is TAnchorDockSplitter))
      or (Splitter.Parent<>Site.Parent) then continue;
      SplitterNode:=Node.Parent.FindChildNode(Node.Anchors[Side],false);
      if (SplitterNode=nil) then continue;
      // this Side of node is anchored to a splitter node
      if fTreeNameToDocker[SplitterNode.Name]<>nil then continue;
      // the SplitterNode is not yet mapped
      if fTreeNameToDocker.ControlToName(Splitter)<>'' then continue;
      // there is an unmapped splitter anchored to the Site
      // => map the splitter to the splitter node
      // Note: Splitter.Name can be different from SplitterNode.Name !
      fTreeNameToDocker[SplitterNode.Name]:=Splitter;
    end;
  end;

begin
  MapHostDockSites(Tree.Root);
  MapTopLevelSites(Tree.Root);
  MapBottomUp(Tree.Root);
  MapSplitters(Tree.Root);
end;

function TAnchorDockMaster.RestoreLayout(Tree: TAnchorDockLayoutTree;
  Scale: boolean): boolean;
var
  WorkArea, SrcWorkArea: TRect;

  function SrcRectValid(const r: TRect): boolean;
  begin
    Result:=(r.Left<r.Right) and (r.Top<r.Bottom);
  end;

  function ScaleTopLvlX(p: integer): integer;
  begin
    Result:=p;
    if SrcRectValid(SrcWorkArea) and SrcRectValid(WorkArea) then
      Result:=((p-SrcWorkArea.Left)*(WorkArea.Right-WorkArea.Left))
                div (SrcWorkArea.Right-SrcWorkArea.Left)
              +WorkArea.Left;
  end;

  function ScaleTopLvlY(p: integer): integer;
  begin
    Result:=p;
    if SrcRectValid(SrcWorkArea) and SrcRectValid(WorkArea) then
      Result:=((p-SrcWorkArea.Top)*(WorkArea.Bottom-WorkArea.Top))
                   div (SrcWorkArea.Bottom-SrcWorkArea.Top)
              +WorkArea.Top;
  end;

  function ScaleChildX(p: integer): integer;
  begin
    Result:=p;
    if SrcRectValid(SrcWorkArea) and SrcRectValid(WorkArea) then
      Result:=p*(WorkArea.Right-WorkArea.Left)
                div (SrcWorkArea.Right-SrcWorkArea.Left);
  end;

  function ScaleChildY(p: integer): integer;
  begin
    Result:=p;
    if SrcRectValid(SrcWorkArea) and SrcRectValid(WorkArea) then
      Result:=p*(WorkArea.Bottom-WorkArea.Top)
                div (SrcWorkArea.Bottom-SrcWorkArea.Top);
  end;

  procedure SetupSite(Site: TCustomForm;
    Node: TAnchorDockLayoutTreeNode; Parent: TWinControl);
  var
    aManager: TAnchorDockManager;
    NewBounds: TRect;
    aMonitor: TMonitor;
    aHostSite: TAnchorDockHostSite;
  begin
    if Parent=nil then begin
      if (Node.Monitor>=0) and (Node.Monitor<Screen.MonitorCount) then
        aMonitor:=Screen.Monitors[Node.Monitor]
      else
        aMonitor:=Site.Monitor;
      WorkArea:=aMonitor.WorkareaRect;
      {$IFDEF VerboseAnchorDockRestore}
      debugln(['TAnchorDockMaster.RestoreLayout.SetupSite WorkArea=',dbgs(WorkArea)]);
      {$ENDIF}
    end;
    if IsCustomSite(Site) then begin
      aManager:=TAnchorDockManager(Site.DockManager);
      if Node.Count>0 then begin
        // this custom dock site gets a child => store and clear constraints
        aManager.StoreConstraints;
      end;
    end;
    Site.Constraints.MaxWidth:=0;
    Site.Constraints.MaxHeight:=0;
    NewBounds:=Node.BoundsRect;
    if Parent=nil then begin
      NewBounds:=Rect(ScaleTopLvlX(NewBounds.Left),ScaleTopLvlY(NewBounds.Top),
                      ScaleTopLvlX(NewBounds.Right),ScaleTopLvlY(NewBounds.Bottom));
    end else begin
      NewBounds:=Rect(ScaleChildX(NewBounds.Left),ScaleChildY(NewBounds.Top),
                      ScaleChildX(NewBounds.Right),ScaleChildY(NewBounds.Bottom));
    end;
    {$IFDEF VerboseAnchorDockRestore}
    if Scale then
      debugln(['TAnchorDockMaster.RestoreLayout.SetupSite scale Site=',DbgSName(Site),' Caption="',Site.Caption,'" OldWorkArea=',dbgs(SrcWorkArea),' CurWorkArea=',dbgs(WorkArea),' OldBounds=',dbgs(Node.BoundsRect),' NewBounds=',dbgs(NewBounds)]);
    {$ENDIF}
    Site.BoundsRect:=NewBounds;
    Site.Visible:=true;
    Site.Parent:=Parent;
    if IsCustomSite(Parent) then begin
      aManager:=TAnchorDockManager(Parent.DockManager);
      Site.Align:=Node.Align;
      {$IFDEF VerboseAnchorDockRestore}
      debugln(['TAnchorDockMaster.RestoreLayout.SetupSite custom Site=',DbgSName(Site),' Site.Bounds=',dbgs(Site.BoundsRect),' BoundSplitterPos=',Node.BoundSplitterPos]);
      {$ENDIF}
      aManager.RestoreSite(Node.BoundSplitterPos);
      Site.HostDockSite:=Parent;
    end;
    if Site is TAnchorDockHostSite then begin
      aHostSite:=TAnchorDockHostSite(Site);
      aHostSite.Header.HeaderPosition:=Node.HeaderPosition;
      aHostSite.DockRestoreBounds:=NewBounds;
      if (Node.NodeType<>adltnPages) and (aHostSite.Pages<>nil) then
        aHostSite.FreePages;
    end;
    if Parent=nil then begin
      Site.WindowState:=Node.WindowState;
    end else begin
      Site.WindowState:=wsNormal;
    end;
  end;

  function GetNodeSite(Node: TAnchorDockLayoutTreeNode): TAnchorDockHostSite;
  begin
    Result:=TAnchorDockHostSite(fTreeNameToDocker[Node.Name]);
    if Result is TAnchorDockHostSite then exit;
    if Result<>nil then
      exit(nil);
    Result:=CreateSite;
    fDisabledAutosizing.Add(Result);
    fTreeNameToDocker[Node.Name]:=Result;
  end;

  function Restore(Node: TAnchorDockLayoutTreeNode; Parent: TWinControl): TControl;
  var
    AControl: TControl;
    Site: TAnchorDockHostSite;
    Splitter: TAnchorDockSplitter;
    i, j: Integer;
    Side: TAnchorKind;
    AnchorControl: TControl;
    ChildNode: TAnchorDockLayoutTreeNode;
    NewBounds: TRect;
    aPageName: String;
    aPage: TCustomPage;
  begin
    Result:=nil;
    if Scale and SrcRectValid(Node.WorkAreaRect) then
      SrcWorkArea:=Node.WorkAreaRect;
    {$IFDEF VerboseAnchorDockRestore}
    debugln(['TAnchorDockMaster.RestoreLayout.Restore Node="',Node.Name,'" ',dbgs(Node.NodeType),' Bounds=',dbgs(Node.BoundsRect),' Parent=',DbgSName(Parent),' ']);
    {$ENDIF}
    if Node.NodeType=adltnControl then begin
      // restore control
      // the control was already created
      // => dock it
      AControl:=FindControl(Node.Name);
      if AControl=nil then begin
        debugln(['TAnchorDockMaster.RestoreLayout.Restore can not find control ',Node.Name]);
        exit;
      end;
      DisableControlAutoSizing(AControl);
      if AControl.HostDockSite=nil then
        MakeDockable(AControl,false)
      else
        ClearLayoutProperties(AControl);
      Site:=AControl.HostDockSite as TAnchorDockHostSite;
      {$IFDEF VerboseAnchorDockRestore}
      debugln(['TAnchorDockMaster.RestoreLayout.Restore Control Node.Name=',Node.Name,' Control=',DbgSName(AControl),' Site=',DbgSName(Site)]);
      {$ENDIF}
      AControl.Visible:=true;
      SetupSite(Site,Node,Parent);
      Result:=Site;
    end else if Node.NodeType=adltnCustomSite then begin
      // restore custom dock site
      // the control was already created
      // => position it
      AControl:=FindControl(Node.Name);
      if AControl=nil then begin
        debugln(['TAnchorDockMaster.RestoreLayout.Restore WARNING: can not find control ',Node.Name]);
        exit;
      end;
      if not IsCustomSite(AControl) then begin
        debugln(['TAnchorDockMaster.RestoreLayout.Restore WARNING: ',Node.Name,' is not a custom dock site ',DbgSName(AControl)]);
        exit;
      end;
      DisableControlAutoSizing(AControl);
      SetupSite(TCustomForm(AControl),Node,nil);
      Result:=AControl;
      // restore docked site
      if Node.Count>0 then begin
        Restore(Node[0],TCustomForm(AControl));
      end;
    end else if Node.IsSplitter then begin
      // restore splitter
      Splitter:=TAnchorDockSplitter(fTreeNameToDocker[Node.Name]);
      if Splitter=nil then begin
        Splitter:=CreateSplitter;
        fTreeNameToDocker[Node.Name]:=Splitter;
      end;
      {$IFDEF VerboseAnchorDockRestore}
      debugln(['TAnchorDockMaster.RestoreLayout.Restore Splitter Node.Name=',Node.Name,' ',dbgs(Node.NodeType),' Splitter=',DbgSName(Splitter)]);
      {$ENDIF}
      Splitter.Parent:=Parent;
      NewBounds:=Node.BoundsRect;
      if SrcRectValid(SrcWorkArea) then
        NewBounds:=Rect(ScaleChildX(NewBounds.Left),ScaleChildY(NewBounds.Top),
          ScaleChildX(NewBounds.Right),ScaleChildY(NewBounds.Bottom));
      Splitter.DockRestoreBounds:=NewBounds;
      Splitter.BoundsRect:=NewBounds;
      if Node.NodeType=adltnSplitterVertical then begin
        Splitter.ResizeAnchor:=akLeft;
        Splitter.AnchorSide[akLeft].Control:=nil;
        Splitter.AnchorSide[akRight].Control:=nil;
      end else begin
        Splitter.ResizeAnchor:=akTop;
        Splitter.AnchorSide[akTop].Control:=nil;
        Splitter.AnchorSide[akBottom].Control:=nil;
      end;
      Result:=Splitter;
    end else if Node.NodeType=adltnLayout then begin
      // restore layout
      Site:=GetNodeSite(Node);
      {$IFDEF VerboseAnchorDockRestore}
      debugln(['TAnchorDockMaster.RestoreLayout.Restore Layout Node.Name=',Node.Name,' ChildCount=',Node.Count]);
      {$ENDIF}
      Site.BeginUpdateLayout;
      try
        SetupSite(Site,Node,Parent);
        Site.FSiteType:=adhstLayout;
        Site.Header.Parent:=nil;
        // create children
        for i:=0 to Node.Count-1 do
          Restore(Node[i],Site);
        // anchor children
        for i:=0 to Node.Count-1 do begin
          ChildNode:=Node[i];
          AControl:=fTreeNameToDocker[ChildNode.Name];
          {$IFDEF VerboseAnchorDockRestore}
          debugln(['  Restore layout child anchors Site=',DbgSName(Site),' ChildNode.Name=',ChildNode.Name,' Control=',DbgSName(AControl)]);
          {$ENDIF}
          if AControl=nil then continue;
          for Side:=Low(TAnchorKind) to high(TAnchorKind) do begin
            if ((ChildNode.NodeType=adltnSplitterHorizontal)
                and (Side in [akTop,akBottom]))
            or ((ChildNode.NodeType=adltnSplitterVertical)
                and (Side in [akLeft,akRight]))
            then continue;
            AnchorControl:=nil;
            if ChildNode.Anchors[Side]<>'' then begin
              AnchorControl:=fTreeNameToDocker[ChildNode.Anchors[Side]];
              if AnchorControl=nil then
                debugln(['WARNING: TAnchorDockMaster.RestoreLayout.Restore: Node=',ChildNode.Name,' Anchor[',dbgs(Side),']=',ChildNode.Anchors[Side],' not found']);
            end;
            if AnchorControl<>nil then
              AControl.AnchorToNeighbour(Side,0,AnchorControl)
            else
              AControl.AnchorParallel(Side,0,Site);
          end;
        end;
        // free unneeded helper controls (e.g. splitters)
        for i:=Site.ControlCount-1 downto 0 do begin
          AControl:=Site.Controls[i];
          if fTreeNameToDocker.ControlToName(AControl)<>'' then continue;
          if AControl is TAnchorDockSplitter then begin
            AControl.Free;
          end;
        end;
      finally
        Site.EndUpdateLayout;
      end;
      Result:=Site;
    end else if Node.NodeType=adltnPages then begin
      // restore pages
      Site:=GetNodeSite(Node);
      {$IFDEF VerboseAnchorDockRestore}
      debugln(['TAnchorDockMaster.RestoreLayout.Restore Pages Node.Name=',Node.Name,' ChildCount=',Node.Count]);
      {$ENDIF}
      Site.BeginUpdateLayout;
      j:=0;
      try
        SetupSite(Site,Node,Parent);
        Site.FSiteType:=adhstPages;
        Site.Header.Parent:=nil;
        if Site.Pages=nil then
          Site.CreatePages;
        for i:=0 to Node.Count-1 do begin
          aPageName:=Node[i].Name;
          if j>=Site.Pages.PageCount then
            Site.Pages.Pages.Add(aPageName);
          aPage:=Site.Pages.Page[j];
          inc(j);
          AControl:=Restore(Node[i],aPage);
          if AControl=nil then continue;
          AControl.Align:=alClient;
          for Side:=Low(TAnchorKind) to high(TAnchorKind) do
            AControl.AnchorSide[Side].Control:=nil;
        end;
      finally
        while Site.Pages.PageCount>j do
          Site.Pages.Page[Site.Pages.PageCount-1].Free;
        Site.SimplifyPages;
        Site.EndUpdateLayout;
      end;
      Result:=Site;
    end else begin
      // create children
      for i:=0 to Node.Count-1 do
        Restore(Node[i],Parent);
    end;
  end;

begin
  Result:=true;
  WorkArea:=Rect(0,0,0,0);
  SrcWorkArea:=WorkArea;
  Restore(Tree.Root,nil);
  Restoring:=true;
end;

function TAnchorDockMaster.DoCreateControl(aName: string;
  DisableAutoSizing: boolean): TControl;
begin
  Result:=nil;
  OnCreateControl(Self,aName,Result,DisableAutoSizing);
  if Result=nil then
    debugln(['TAnchorDockMaster.DoCreateControl WARNING: control not found: "',aName,'"']);
  if (Result<>nil) and (Result.Name<>aName) then
    raise Exception.Create('TAnchorDockMaster.DoCreateControl'+Format(
      adrsRequestedButCreated, [aName, Result.Name]));
end;

procedure TAnchorDockMaster.DisableControlAutoSizing(AControl: TControl);
begin
  if fDisabledAutosizing.IndexOf(AControl)>=0 then exit;
  //debugln(['TAnchorDockMaster.DisableControlAutoSizing ',DbgSName(AControl)]);
  fDisabledAutosizing.Add(AControl);
  AControl.FreeNotification(Self);
  AControl.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}(ADAutoSizingReason){$ENDIF};
end;

procedure TAnchorDockMaster.EnableAllAutoSizing;
var
  i: Integer;
  AControl: TControl;
begin
  i:=fDisabledAutosizing.Count-1;
  while (i>=0) do begin
    AControl:=TControl(fDisabledAutosizing[i]);
    //debugln(['TAnchorDockMaster.EnableAllAutoSizing ',DbgSName(AControl)]);
    fDisabledAutosizing.Delete(i);
    AControl.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}(ADAutoSizingReason){$ENDIF};
    i:=Min(i,fDisabledAutosizing.Count)-1;
  end;
end;

procedure TAnchorDockMaster.ClearLayoutProperties(AControl: TControl;
  NewAlign: TAlign);
var
  a: TAnchorKind;
begin
  AControl.AutoSize:=false;
  AControl.Align:=NewAlign;
  AControl.BorderSpacing.Around:=0;
  AControl.BorderSpacing.Left:=0;
  AControl.BorderSpacing.Top:=0;
  AControl.BorderSpacing.Right:=0;
  AControl.BorderSpacing.Bottom:=0;
  AControl.BorderSpacing.InnerBorder:=0;
  for a:=Low(TAnchorKind) to High(TAnchorKind) do
    AControl.AnchorSide[a].Control:=nil;
end;

procedure TAnchorDockMaster.PopupMenuPopup(Sender: TObject);
var
  Popup: TPopupMenu;
  ChangeLockItem: TMenuItem;
  ShowHeadersItem: TMenuItem;
begin
  if not (Sender is TPopupMenu) then exit;
  Popup:=TPopupMenu(Sender);
  Popup.Items.Clear;

  // top popup menu item can be clicked by accident, so use something simple:
  // lock/unlock
  ChangeLockItem:=AddPopupMenuItem('AnchorDockMasterChangeLockMenuItem',
                                   adrsLocked,@ChangeLockButtonClick);
  ChangeLockItem.Checked:=not AllowDragging;
  ChangeLockItem.ShowAlwaysCheckable:=true;

  if Popup.PopupComponent is TAnchorDockHeader then
    TAnchorDockHeader(Popup.PopupComponent).PopupMenuPopup(Sender)
  else if Popup.PopupComponent is TAnchorDockPageControl then
    TAnchorDockPageControl(Popup.PopupComponent).PopupMenuPopup(Sender)
  else if Popup.PopupComponent is TAnchorDockSplitter then
    TAnchorDockSplitter(Popup.PopupComponent).PopupMenuPopup(Sender);

  if ShowMenuItemShowHeader or (not ShowHeader) then begin
    ShowHeadersItem:=AddPopupMenuItem('AnchorDockMasterShowHeaderMenuItem',
                                      adrsShowHeaders, @ShowHeadersButtonClick);
    ShowHeadersItem.Checked:=ShowHeader;
    ShowHeadersItem.ShowAlwaysCheckable:=true;
  end;

  if Assigned(OnShowOptions) then
    AddPopupMenuItem('OptionsMenuItem', adrsDockingOptions, @OptionsClick);
end;

procedure TAnchorDockMaster.ResetSplitters;
var
  I: Integer;
  S: TAnchorDockSplitter;
begin
  for I := 0 to ComponentCount-1 do
    if Components[I] is TAnchorDockSplitter then
    begin
      S := TAnchorDockSplitter(Components[I]);
      S.UpdateDockBounds;
      S.UpdatePercentPosition;
    end;
end;

function TAnchorDockMaster.FullRestoreLayout(Tree: TAnchorDockLayoutTree;
  Scale: Boolean): Boolean;
var
  ControlNames: TStringList;
begin
  Result:=false;
  ControlNames:=TStringList.Create;
  fTreeNameToDocker:=TADNameToControl.Create;
  try
    // close all unneeded forms/controls (not helper controls like splitters)
    if not CloseUnneededControls(Tree) then exit;

    BeginUpdate;
    try
      // create all needed forms/controls (not helper controls like splitters)
      if not CreateNeededControls(Tree,true,ControlNames) then exit;

      // simplify layouts
      ControlNames.Sort;
      {$IFDEF VerboseAnchorDockRestore}
      debugln(['TAnchorDockMaster.LoadLayoutFromConfig controls: ']);
      debugln(ControlNames.Text);
      {$ENDIF}
      // if some forms/controls could not be created the layout needs to be adapted
      Tree.Root.Simplify(ControlNames);

      // reuse existing sites to reduce flickering
      MapTreeToControls(Tree);
      {$IFDEF VerboseAnchorDockRestore}
      fTreeNameToDocker.WriteDebugReport('TAnchorDockMaster.LoadLayoutFromConfig Map');
      {$ENDIF}

      // create sites, move controls
      RestoreLayout(Tree,Scale);
    finally
      EndUpdate;
    end;
  finally
    // clean up
    FreeAndNil(fTreeNameToDocker);
    ControlNames.Free;
    // commit (this can raise an exception, when it triggers events)
    EnableAllAutoSizing;
  end;
  ResetSplitters; // reset splitters' DockBounds after EnableAllAutoSizing. fixes issue #18538
  {$IFDEF VerboseAnchorDockRestore}
  DebugWriteChildAnchors(Application.MainForm,true,false);
  {$ENDIF}
  Result:=true;
end;

procedure TAnchorDockMaster.SetHideHeaderCaptionFloatingControl(
  const AValue: boolean);
var
  Site: TAnchorDockHostSite;
  i: Integer;
begin
  if AValue=HideHeaderCaptionFloatingControl then exit;
  fHideHeaderCaptionFloatingControl:=AValue;
  for i:=0 to ComponentCount-1 do begin
    Site:=TAnchorDockHostSite(Components[i]);
    if not (Site is TAnchorDockHostSite) then continue;
    Site.UpdateDockCaption;
  end;
  OptionsChanged;
end;

procedure TAnchorDockMaster.SetSplitterWidth(const AValue: integer);
var
  i: Integer;
  Splitter: TAnchorDockSplitter;
begin
  if (AValue<1) or (AValue=SplitterWidth) then exit;
  FSplitterWidth:=AValue;
  for i:=0 to ComponentCount-1 do begin
    Splitter:=TAnchorDockSplitter(Components[i]);
    if not (Splitter is TAnchorDockSplitter) then continue;
    if not Splitter.CustomWidth then
    begin
      if Splitter.ResizeAnchor in [akLeft,akRight] then
        Splitter.Width:=SplitterWidth
      else
        Splitter.Height:=SplitterWidth;
    end;
  end;
  OptionsChanged;
end;

procedure TAnchorDockMaster.OnIdle(Sender: TObject; var Done: Boolean);
begin
  if Done then ;
  IdleConnected:=false;
  Restoring:=false;
end;

procedure TAnchorDockMaster.AsyncSimplify(Data: PtrInt);
begin
  FQueueSimplify:=false;
  SimplifyPendingLayouts;
end;

procedure TAnchorDockMaster.ChangeLockButtonClick(Sender: TObject);
begin
  AllowDragging:=not AllowDragging;
end;

procedure TAnchorDockMaster.SetAllowDragging(AValue: boolean);
begin
  if FAllowDragging=AValue then Exit;
  FAllowDragging:=AValue;
  OptionsChanged;
end;

procedure TAnchorDockMaster.SetDockOutsideMargin(AValue: integer);
begin
  if FDockOutsideMargin=AValue then Exit;
  FDockOutsideMargin:=AValue;
  OptionsChanged;
end;

procedure TAnchorDockMaster.SetDockParentMargin(AValue: integer);
begin
  if FDockParentMargin=AValue then Exit;
  FDockParentMargin:=AValue;
  OptionsChanged;
end;

procedure TAnchorDockMaster.SetDragTreshold(AValue: integer);
begin
  if FDragTreshold=AValue then Exit;
  FDragTreshold:=AValue;
  OptionsChanged;
end;

procedure TAnchorDockMaster.SetHeaderHint(AValue: string);
begin
  if FHeaderHint=AValue then Exit;
  FHeaderHint:=AValue;
  OptionsChanged;
end;

procedure TAnchorDockMaster.SetHeaderStyle(AValue: TADHeaderStyle);
begin
  if FHeaderStyle=AValue then Exit;
  FHeaderStyle:=AValue;
  OptionsChanged;
  InvalidateHeaders;
end;

procedure TAnchorDockMaster.SetPageAreaInPercent(AValue: integer);
begin
  if FPageAreaInPercent=AValue then Exit;
  FPageAreaInPercent:=AValue;
  OptionsChanged;
end;

procedure TAnchorDockMaster.SetHeaderFlatten(AValue: boolean);
begin
  if FHeaderFlatten=AValue then Exit;
  FHeaderFlatten:=AValue;
  OptionsChanged;
  InvalidateHeaders;
end;

procedure TAnchorDockMaster.SetHeaderFilled(AValue: boolean);
begin
  if FHeaderFilled=AValue then Exit;
  FHeaderFilled:=AValue;
  OptionsChanged;
  InvalidateHeaders;
end;

procedure TAnchorDockMaster.SetScaleOnResize(AValue: boolean);
begin
  if FScaleOnResize=AValue then Exit;
  FScaleOnResize:=AValue;
  OptionsChanged;
end;

procedure TAnchorDockMaster.SetShowMenuItemShowHeader(AValue: boolean);
begin
  if FShowMenuItemShowHeader=AValue then Exit;
  FShowMenuItemShowHeader:=AValue;
  OptionsChanged;
end;

procedure TAnchorDockMaster.ShowHeadersButtonClick(Sender: TObject);
begin
  ShowHeader:=not ShowHeader;
end;

procedure TAnchorDockMaster.OptionsClick(Sender: TObject);
begin
  if Assigned(OnShowOptions) then OnShowOptions(Self);
end;

procedure TAnchorDockMaster.SetIdleConnected(const AValue: Boolean);
begin
  if FIdleConnected=AValue then exit;
  FIdleConnected:=AValue;
  if IdleConnected then
    Application.AddOnIdleHandler(@OnIdle,true)
  else
    Application.RemoveOnIdleHandler(@OnIdle);
end;

procedure TAnchorDockMaster.SetQueueSimplify(const AValue: Boolean);
begin
  if FQueueSimplify=AValue then exit;
  FQueueSimplify:=AValue;
  if FQueueSimplify then
    Application.QueueAsyncCall(@AsyncSimplify,0)
  else
    Application.RemoveAsyncCalls(Self);
end;

procedure TAnchorDockMaster.SetRestoring(const AValue: boolean);
var
  AComponent: TComponent;
  i: Integer;
begin
  if FRestoring=AValue then exit;
  FRestoring:=AValue;
  if FRestoring then begin
    IdleConnected:=true;
  end else begin
    for i:=0 to ComponentCount-1 do begin
      AComponent:=Components[i];
      if AComponent is TAnchorDockHostSite then
        TAnchorDockHostSite(AComponent).DockRestoreBounds:=Rect(0,0,0,0)
      else if AComponent is TAnchorDockSplitter then
        TAnchorDockSplitter(AComponent).DockRestoreBounds:=Rect(0,0,0,0)
    end;
  end;
end;

procedure TAnchorDockMaster.OptionsChanged;
begin
  IncreaseOptionsChangeStamp;
  if Assigned(OnOptionsChanged) then
    OnOptionsChanged(Self);
end;

procedure TAnchorDockMaster.SetShowHeader(AValue: boolean);
var
  i: Integer;
  Site: TAnchorDockHostSite;
begin
  if FShowHeader=AValue then exit;
  FShowHeader:=AValue;
  for i:=0 to ComponentCount-1 do begin
    Site:=TAnchorDockHostSite(Components[i]);
    if not (Site is TAnchorDockHostSite) then continue;
    if (Site.Header<>nil) then begin
      DisableControlAutoSizing(Site);
      Site.UpdateHeaderShowing;
    end;
  end;
  EnableAllAutoSizing;
  OptionsChanged;
end;

procedure TAnchorDockMaster.SetShowHeaderCaption(const AValue: boolean);
var
  i: Integer;
  Site: TAnchorDockHostSite;
begin
  if FShowHeaderCaption=AValue then exit;
  FShowHeaderCaption:=AValue;
  for i:=0 to ComponentCount-1 do begin
    Site:=TAnchorDockHostSite(Components[i]);
    if not (Site is TAnchorDockHostSite) then continue;
    Site.UpdateDockCaption;
  end;
  OptionsChanged;
end;

procedure TAnchorDockMaster.Notification(AComponent: TComponent;
  Operation: TOperation);
var
  AControl: TControl;
begin
  inherited Notification(AComponent, Operation);
  if Operation=opRemove then begin
    if AComponent is TControl then begin
      AControl:=TControl(AComponent);
      FControls.Remove(AControl);
      fNeedSimplify.Remove(AControl);
      fNeedFree.Remove(AControl);
      fDisabledAutosizing.Remove(AControl);
      if fTreeNameToDocker<>nil then
        fTreeNameToDocker.RemoveControl(AControl);
    end;
  end;
end;

procedure TAnchorDockMaster.InvalidateHeaders;
var
  i: Integer;
  Site: TAnchorDockHostSite;
begin
  for i:=0 to ComponentCount-1 do begin
    Site:=TAnchorDockHostSite(Components[i]);
    if not (Site is TAnchorDockHostSite) then continue;
    if (Site.Header<>nil) and (Site.Header.Parent<>nil) then
      Site.Header.Invalidate;
  end;
end;

procedure TAnchorDockMaster.AutoSizeAllHeaders(EnableAutoSizing: boolean);
var
  i: Integer;
  Site: TAnchorDockHostSite;
begin
  for i:=0 to ComponentCount-1 do begin
    Site:=TAnchorDockHostSite(Components[i]);
    if not (Site is TAnchorDockHostSite) then continue;
    if (Site.Header<>nil) and (Site.Header.Parent<>nil) then begin
      Site.Header.InvalidatePreferredSize;
      DisableControlAutoSizing(Site);
    end;
  end;
  if EnableAutoSizing then
    EnableAllAutoSizing;
end;

constructor TAnchorDockMaster.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FControls:=TFPList.Create;
  FAllowDragging:=true;
  FDragTreshold:=4;
  FDockOutsideMargin:=10;
  FDockParentMargin:=10;
  FPageAreaInPercent:=40;
  FHeaderAlignTop:=80;
  HeaderAlignLeft:=120;
  FHeaderHint:='';
  FShowHeader:=true;
  FShowHeaderCaption:=true;
  FHideHeaderCaptionFloatingControl:=true;
  FSplitterWidth:=4;
  FScaleOnResize:=true;
  fNeedSimplify:=TFPList.Create;
  fNeedFree:=TFPList.Create;
  fDisabledAutosizing:=TFPList.Create;
  FSplitterClass:=TAnchorDockSplitter;
  FSiteClass:=TAnchorDockHostSite;
  FManagerClass:=TAnchorDockManager;
  FHeaderClass:=TAnchorDockHeader;
  FHeaderFlatten:=true;
  FHeaderFilled:=true;
  FPageControlClass:=TAnchorDockPageControl;
  FPageClass:=TAnchorDockPage;
  FRestoreLayouts:=TAnchorDockRestoreLayouts.Create;
end;

destructor TAnchorDockMaster.Destroy;
var
  AControl: TControl;
  {$IFDEF VerboseAnchorDocking}
  i: Integer;
  {$ENDIF}
begin
  QueueSimplify:=false;
  FreeAndNil(FRestoreLayouts);
  FreeAndNil(fPopupMenu);
  FreeAndNil(fTreeNameToDocker);
  if FControls.Count>0 then begin
    while ControlCount>0 do begin
      AControl:=Controls[ControlCount-1];
      debugln(['TAnchorDockMaster.Destroy: still in list: ',DbgSName(AControl),' Caption="',AControl.Caption,'"']);
      AControl.Free;
    end;
  end;
  FreeAndNil(fNeedSimplify);
  FreeAndNil(FControls);
  FreeAndNil(fNeedFree);
  FreeAndNil(fDisabledAutosizing);
  {$IFDEF VerboseAnchorDocking}
  for i:=0 to ComponentCount-1 do begin
    debugln(['TAnchorDockMaster.Destroy ',i,'/',ComponentCount,' ',DbgSName(Components[i])]);
  end;
  {$ENDIF}
  inherited Destroy;
end;

function TAnchorDockMaster.ControlCount: integer;
begin
  Result:=FControls.Count;
end;

function TAnchorDockMaster.IndexOfControl(const aName: string): integer;
begin
  Result:=ControlCount-1;
  while (Result>=0) and (Controls[Result].Name<>aName) do dec(Result);
end;

function TAnchorDockMaster.FindControl(const aName: string): TControl;
var
  i: LongInt;
begin
  i:=IndexOfControl(aName);
  if i>=0 then
    Result:=Controls[i]
  else
    Result:=nil;
end;

function TAnchorDockMaster.IsSite(AControl: TControl): boolean;
begin
  Result:=(AControl is TAnchorDockHostSite) or IsCustomSite(AControl);
end;

function TAnchorDockMaster.IsAnchorSite(AControl: TControl): boolean;
begin
  Result:=AControl is TAnchorDockHostSite;
end;

function TAnchorDockMaster.IsCustomSite(AControl: TControl): boolean;
begin
  Result:=(AControl is TCustomForm) // also checks for nil
      and (AControl.Parent=nil)
      and (TCustomForm(AControl).DockManager is TAnchorDockManager);
end;

function TAnchorDockMaster.GetSite(AControl: TControl): TCustomForm;
begin
  Result:=nil;
  if AControl=nil then
    exit
  else if IsCustomSite(AControl) then
    Result:=TCustomForm(AControl)
  else if AControl is TAnchorDockHostSite then
    Result:=TAnchorDockHostSite(AControl)
  else if (AControl.HostDockSite is TAnchorDockHostSite) then
    Result:=TAnchorDockHostSite(AControl.HostDockSite);
end;

function TAnchorDockMaster.GetAnchorSite(AControl: TControl): TAnchorDockHostSite;
begin
  Result:=nil;
  if AControl=nil then
    Result:=nil
  else if AControl is TAnchorDockHostSite then
    Result:=TAnchorDockHostSite(AControl)
  else if (AControl.HostDockSite is TAnchorDockHostSite) then
    Result:=TAnchorDockHostSite(AControl.HostDockSite);
end;

function TAnchorDockMaster.GetControl(Site: TControl): TControl;
var
  AnchorSite: TAnchorDockHostSite;
begin
  Result:=nil;
  if IsCustomSite(Site) then
    Result:=Site
  else if Site is TAnchorDockHostSite then begin
    AnchorSite:=TAnchorDockHostSite(Site);
    if AnchorSite.SiteType=adhstOneControl then
      Result:=AnchorSite.GetOneControl;
  end else if (Site<>nil) and (Site.HostDockSite is TAnchorDockHostSite)
  and (TAnchorDockHostSite(Site.HostDockSite).SiteType=adhstOneControl) then
    Result:=Site;
end;

function TAnchorDockMaster.IsFloating(AControl: TControl): Boolean;
begin
  if AControl is TAnchorDockHostSite then begin
    Result:=(TAnchorDockHostSite(AControl).SiteType=adhstOneControl)
            and (AControl.Parent=nil);
  end else if (AControl.HostDockSite is TAnchorDockHostSite) then begin
    Result:=(TAnchorDockHostSite(AControl.HostDockSite).SiteType=adhstOneControl)
        and (AControl.HostDockSite.Parent=nil);
  end else
    Result:=AControl.Parent=nil;
end;

function TAnchorDockMaster.GetPopupMenu: TPopupMenu;
begin
  if fPopupMenu=nil then begin
    fPopupMenu:=TPopupMenu.Create(Self);
    fPopupMenu.OnPopup:=@PopupMenuPopup;
  end;
  Result:=fPopupMenu;
end;

function TAnchorDockMaster.AddPopupMenuItem(AName, ACaption: string;
  const OnClickEvent: TNotifyEvent; AParent: TMenuItem): TMenuItem;
begin
  Result:=TMenuItem(fPopupMenu.FindComponent(AName));
  if Result=nil then begin
    Result:=TMenuItem.Create(fPopupMenu);
    Result.Name:=AName;
    if AParent=nil then
      fPopupMenu.Items.Add(Result)
    else
      AParent.Add(Result);
  end;
  Result.Caption:=ACaption;
  Result.OnClick:=OnClickEvent;
end;

function TAnchorDockMaster.AddRemovePopupMenuItem(Add: boolean; AName,
  ACaption: string; const OnClickEvent: TNotifyEvent; AParent: TMenuItem
  ): TMenuItem;
begin
  if Add then
    Result:=AddPopupMenuItem(AName,ACaption,OnClickEvent,AParent)
  else begin
    Result:=TMenuItem(fPopupMenu.FindComponent(AName));
    if Result<>nil then
      FreeAndNil(Result);
  end;
end;

procedure TAnchorDockMaster.MakeDockable(AControl: TControl; Show: boolean;
  BringToFront: boolean; AddDockHeader: boolean);
var
  Site: TAnchorDockHostSite;
begin
  if AControl.Name='' then
    raise Exception.Create('TAnchorDockMaster.MakeDockable '+
      adrsMissingControlName);
  if (AControl is TCustomForm) and (fsModal in TCustomForm(AControl).FormState)
  then
    raise Exception.Create('TAnchorDockMaster.MakeDockable '+
      adrsModalFormsCanNotBeMadeDockable);
  if IsCustomSite(AControl) then
    raise Exception.Create('TAnchorDockMaster.MakeDockable '+
      adrsControlIsAlreadyADocksite);
  Site:=nil;
  AControl.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockMaster.DisableControlAutoSizing'){$ENDIF};
  try
    if AControl is TAnchorDockHostSite then begin
      // already a site
      Site:=TAnchorDockHostSite(AControl);
    end else if AControl.Parent=nil then begin

      if FControls.IndexOf(AControl)<0 then begin
        FControls.Add(AControl);
        AControl.FreeNotification(Self);
      end;

      // create docksite
      Site:=CreateSite;
      try
        try
          Site.BoundsRect:=AControl.BoundsRect;
          ClearLayoutProperties(AControl);
          // dock
          AControl.ManualDock(Site);
          AControl.Visible:=true;
          if not AddDockHeader then
            Site.Header.Parent:=nil;
        except
          FreeAndNil(Site);
          raise;
        end;
      finally
        if Site<>nil then
          Site.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}(ADAutoSizingReason){$ENDIF};
      end;
    end else if AControl.Parent is TAnchorDockHostSite then begin
      // AControl is already docked => show site
      Site:=TAnchorDockHostSite(AControl.Parent);
      AControl.Visible:=true;
    end else begin
      raise Exception.Create('TAnchorDockMaster.MakeDockable '+Format(
        adrsNotSupportedHasParent, [DbgSName(AControl), DbgSName(AControl)]));
    end;
    if (Site<>nil) and Show then
      MakeVisible(Site,BringToFront);
  finally
    AControl.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockMaster.DisableControlAutoSizing'){$ENDIF};
  end;
  // BringToFront
  if Show and BringToFront and (Site<>nil) then begin
    GetParentForm(Site).BringToFront;
    Site.SetFocus;
  end;
end;

procedure TAnchorDockMaster.MakeDockSite(AForm: TCustomForm; Sites: TAnchors;
  ResizePolicy: TADMResizePolicy; AllowInside: boolean);
var
  AManager: TAnchorDockManager;
begin
  if AForm.Name='' then
    raise Exception.Create('TAnchorDockMaster.MakeDockable '+
      adrsMissingControlName);
  if AForm.DockManager<>nil then
    raise Exception.Create('TAnchorDockMaster.MakeDockSite DockManager<>nil');
  if AForm.Parent<>nil then
    raise Exception.Create('TAnchorDockMaster.MakeDockSite Parent='+DbgSName(AForm.Parent));
  if fsModal in AForm.FormState then
    raise Exception.Create('TAnchorDockMaster.MakeDockSite '+
      adrsModalFormsCanNotBeMadeDockable);
  if Sites=[] then
    raise Exception.Create('TAnchorDockMaster.MakeDockSite Sites=[]');
  AForm.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockMaster.MakeDockSite'){$ENDIF};
  try
    if FControls.IndexOf(AForm)<0 then begin
      FControls.Add(AForm);
      AForm.FreeNotification(Self);
    end;
    AManager:=ManagerClass.Create(AForm);
    AManager.DockableSites:=Sites;
    AManager.InsideDockingAllowed:=AllowInside;
    AManager.ResizePolicy:=ResizePolicy;
    AForm.DockManager:=AManager;
    AForm.UseDockManager:=true;
    AForm.DockSite:=true;
  finally
    AForm.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockMaster.MakeDockSite'){$ENDIF};
  end;
end;

procedure TAnchorDockMaster.MakeVisible(AControl: TControl; SwitchPages: boolean);
begin
  while AControl<>nil do begin
    AControl.Visible:=true;
    if SwitchPages and (AControl is TAnchorDockPage) then
      TAnchorDockPageControl(AControl.Parent).PageIndex:=
        TAnchorDockPage(AControl).PageIndex;
    AControl:=AControl.Parent;
  end;
end;

function TAnchorDockMaster.ShowControl(ControlName: string;
  BringToFront: boolean): TControl;
begin
  Result:=DoCreateControl(ControlName,false);
  if Result=nil then exit;
  MakeDockable(Result,true,BringToFront);
end;

procedure TAnchorDockMaster.CloseAll;
var
  i: Integer;
  AForm: TCustomForm;
  AControl: TWinControl;
begin
  // hide all forms
  i:=Screen.CustomFormCount-1;
  while i>=0 do begin
    AForm:=GetParentForm(Screen.CustomForms[i]);
    AForm.Hide;
    i:=Min(i,Screen.CustomFormCount)-1;
  end;

  // close all forms except the MainForm
  i:=Screen.CustomFormCount-1;
  while i>=0 do begin
    AForm:=Screen.CustomForms[i];
    if (AForm<>Application.MainForm) and not AForm.IsParentOf(Application.MainForm)
    then begin
      AControl:=AForm;
      while (AControl.Parent<>nil)
      and (AControl.Parent<>Application.MainForm) do begin
        AControl:=AControl.Parent;
        if AControl is TCustomForm then AForm:=TCustomForm(AControl);
      end;
      AForm.Close;
    end;
    i:=Min(i,Screen.CustomFormCount)-1;
  end;
end;

procedure TAnchorDockMaster.SaveLayoutToConfig(Config: TConfigStorage);
var
  Tree: TAnchorDockLayoutTree;
begin
  Tree:=TAnchorDockLayoutTree.Create;
  try
    Config.AppendBasePath('MainConfig/');
    SaveMainLayoutToTree(Tree);
    Tree.SaveToConfig(Config);
    Config.UndoAppendBasePath;
    Config.AppendBasePath('Restores/');
    RestoreLayouts.SaveToConfig(Config);
    Config.UndoAppendBasePath;
    {$IFDEF VerboseAnchorDocking}
    WriteDebugLayout('TAnchorDockMaster.SaveLayoutToConfig ',Tree.Root);
    {$ENDIF}
    //DebugWriteChildAnchors(Tree.Root);
  finally
    Tree.Free;
  end;
end;

procedure TAnchorDockMaster.SaveMainLayoutToTree(LayoutTree: TAnchorDockLayoutTree);
var
  i: Integer;
  AControl: TControl;
  Site: TAnchorDockHostSite;
  SavedSites: TFPList;
  LayoutNode: TAnchorDockLayoutTreeNode;
  AForm: TCustomForm;
  VisibleControls: TStringList;
begin
  SavedSites:=TFPList.Create;
  VisibleControls:=TStringList.Create;
  try
    for i:=0 to ControlCount-1 do begin
      AControl:=Controls[i];
      if not DockedControlIsVisible(AControl) then continue;
      VisibleControls.Add(AControl.Name);
      AForm:=GetParentForm(AControl);
      if AForm=nil then continue;
      if SavedSites.IndexOf(AForm)>=0 then continue;
      SavedSites.Add(AForm);
      debugln(['TAnchorDockMaster.SaveMainLayoutToTree AForm=',DbgSName(AForm)]);
      DebugWriteChildAnchors(AForm,true,true);
      if (AForm is TAnchorDockHostSite) then begin
        Site:=TAnchorDockHostSite(AForm);
        LayoutNode:=LayoutTree.NewNode(LayoutTree.Root);
        Site.SaveLayout(LayoutTree,LayoutNode);
      end else if IsCustomSite(AForm) then begin
        // custom dock site
        LayoutNode:=LayoutTree.NewNode(LayoutTree.Root);
        LayoutNode.NodeType:=adltnCustomSite;
        LayoutNode.Assign(AForm);
        // can have one normal dock site
        Site:=TAnchorDockManager(AForm.DockManager).GetChildSite;
        if Site<>nil then begin
          LayoutNode:=LayoutTree.NewNode(LayoutNode);
          Site.SaveLayout(LayoutTree,LayoutNode);
          {if Site.BoundSplitter<>nil then begin
            LayoutNode:=LayoutTree.NewNode(LayoutNode);
            Site.BoundSplitter.SaveLayout(LayoutNode);
          end;}
        end;
      end else
        raise EAnchorDockLayoutError.Create('invalid root control for save: '+DbgSName(AControl));
    end;
    // remove invisible controls
    LayoutTree.Root.Simplify(VisibleControls);
  finally
    VisibleControls.Free;
    SavedSites.Free;
  end;
end;

procedure TAnchorDockMaster.SaveSiteLayoutToTree(AForm: TCustomForm;
  LayoutTree: TAnchorDockLayoutTree);
var
  LayoutNode: TAnchorDockLayoutTreeNode;
  Site: TAnchorDockHostSite;
begin
  if (AForm is TAnchorDockHostSite) then begin
    Site:=TAnchorDockHostSite(AForm);
    Site.SaveLayout(LayoutTree,LayoutTree.Root);
  end else if IsCustomSite(AForm) then begin
    LayoutTree.Root.NodeType:=adltnCustomSite;
    LayoutTree.Root.Assign(AForm);
    // can have one normal dock site
    Site:=TAnchorDockManager(AForm.DockManager).GetChildSite;
    if Site<>nil then begin
      LayoutNode:=LayoutTree.NewNode(LayoutTree.Root);
      Site.SaveLayout(LayoutTree,LayoutNode);
    end;
  end else
    raise EAnchorDockLayoutError.Create('invalid root control for save: '+DbgSName(AForm));
end;

function TAnchorDockMaster.CreateRestoreLayout(AControl: TControl
  ): TAnchorDockRestoreLayout;
{ Create a restore layout for AControl and its child controls.
  It contains the whole parent structure so that the restore knows where to
  put AControl.
}

  procedure AddControlNames(SubControl: TControl;
    RestoreLayout: TAnchorDockRestoreLayout);
  var
    i: Integer;
  begin
    if (FControls.IndexOf(SubControl)>=0)
    and not RestoreLayout.HasControlName(SubControl.Name) then
      RestoreLayout.ControlNames.Add(SubControl.Name);
    if SubControl is TWinControl then
      for i:=0 to TWinControl(SubControl).ControlCount-1 do
        AddControlNames(TWinControl(SubControl).Controls[i],RestoreLayout);
  end;

var
  AForm: TCustomForm;
begin
  if not IsSite(AControl) then
    raise Exception.Create('TAnchorDockMaster.CreateRestoreLayout: not a site '+DbgSName(AControl));
  AForm:=GetParentForm(AControl);
  Result:=TAnchorDockRestoreLayout.Create(TAnchorDockLayoutTree.Create);
  if AForm=nil then exit;
  SaveSiteLayoutToTree(AForm,Result.Layout);
  AddControlNames(AControl,Result);
end;

function TAnchorDockMaster.ConfigIsEmpty(Config: TConfigStorage): boolean;
begin
  Result:=Config.GetValue('MainConfig/Nodes/ChildCount',0)=0;
end;

function TAnchorDockMaster.LoadLayoutFromConfig(Config: TConfigStorage;
  Scale: Boolean): boolean;
var
  Tree: TAnchorDockLayoutTree;
  ControlNames: TStringList;
begin
  Result:=false;
  ControlNames:=TStringList.Create;
  fTreeNameToDocker:=TADNameToControl.Create;
  Tree:=TAnchorDockLayoutTree.Create;
  try
    // load layout
    Config.AppendBasePath('MainConfig/');
    try
      Tree.LoadFromConfig(Config);
    finally
      Config.UndoAppendBasePath;
    end;
    // load restore layouts for hidden forms
    Config.AppendBasePath('Restores/');
    try
      RestoreLayouts.LoadFromConfig(Config);
    finally
      Config.UndoAppendBasePath;
    end;

    {$IFDEF VerboseAnchorDockRestore}
    WriteDebugLayout('TAnchorDockMaster.LoadLayoutFromConfig ',Tree.Root);
    DebugWriteChildAnchors(Tree.Root);
    {$ENDIF}

    // close all unneeded forms/controls (not helper controls like splitters)
    if not CloseUnneededControls(Tree) then exit;

    BeginUpdate;
    try
      // create all needed forms/controls (not helper controls like splitters)
      if not CreateNeededControls(Tree,true,ControlNames) then exit;

      // simplify layouts
      ControlNames.Sort;
      {$IFDEF VerboseAnchorDockRestore}
      debugln(['TAnchorDockMaster.LoadLayoutFromConfig controls: ']);
      debugln(ControlNames.Text);
      {$ENDIF}
      // if some forms/controls could not be created the layout needs to be adapted
      Tree.Root.Simplify(ControlNames);

      // reuse existing sites to reduce flickering
      MapTreeToControls(Tree);
      {$IFDEF VerboseAnchorDockRestore}
      fTreeNameToDocker.WriteDebugReport('TAnchorDockMaster.LoadLayoutFromConfig Map');
      {$ENDIF}

      // create sites, move controls
      RestoreLayout(Tree,Scale);
    finally
      EndUpdate;
    end;
  finally
    // clean up
    FreeAndNil(fTreeNameToDocker);
    ControlNames.Free;
    Tree.Free;
    // commit (this can raise an exception)
    EnableAllAutoSizing;
  end;
  {$IFDEF VerboseAnchorDockRestore}
  DebugWriteChildAnchors(Application.MainForm,true,false);
  {$ENDIF}
  Result:=true;
end;

procedure TAnchorDockMaster.LoadSettingsFromConfig(Config: TConfigStorage);
var
  Settings: TAnchorDockSettings;
begin
  Settings:=TAnchorDockSettings.Create;
  try
    Settings.LoadFromConfig(Config);
    LoadSettings(Settings);
  finally
    Settings.Free;
  end;
end;

procedure TAnchorDockMaster.SaveSettingsToConfig(Config: TConfigStorage);
var
  Settings: TAnchorDockSettings;
begin
  Settings:=TAnchorDockSettings.Create;
  try
    SaveSettings(Settings);
    Settings.SaveToConfig(Config);
  finally
    Settings.Free;
  end;
end;

procedure TAnchorDockMaster.LoadSettings(Settings: TAnchorDockSettings);
begin
  DragTreshold                     := Settings.DragTreshold;
  DockOutsideMargin                := Settings.DockOutsideMargin;
  DockParentMargin                 := Settings.DockParentMargin;
  PageAreaInPercent                := Settings.PageAreaInPercent;
  HeaderAlignTop                   := Settings.HeaderAlignTop;
  HeaderAlignLeft                  := Settings.HeaderAlignLeft;
  SplitterWidth                    := Settings.SplitterWidth;
  ScaleOnResize                    := Settings.ScaleOnResize;
  ShowHeader                       := Settings.ShowHeader;
  ShowHeaderCaption                := Settings.ShowHeaderCaption;
  HideHeaderCaptionFloatingControl := Settings.HideHeaderCaptionFloatingControl;
  AllowDragging                    := Settings.AllowDragging;
  HeaderStyle                      := Settings.HeaderStyle;
  HeaderFlatten                    := Settings.HeaderFlatten;
  HeaderFilled                     := Settings.HeaderFilled;
end;

procedure TAnchorDockMaster.SaveSettings(Settings: TAnchorDockSettings);
begin
  Settings.DragTreshold:=DragTreshold;
  Settings.DockOutsideMargin:=DockOutsideMargin;
  Settings.DockParentMargin:=DockParentMargin;
  Settings.PageAreaInPercent:=PageAreaInPercent;
  Settings.HeaderAlignTop:=HeaderAlignTop;
  Settings.HeaderAlignLeft:=HeaderAlignLeft;
  Settings.SplitterWidth:=SplitterWidth;
  Settings.ScaleOnResize:=ScaleOnResize;
  Settings.ShowHeader:=ShowHeader;
  Settings.ShowHeaderCaption:=ShowHeaderCaption;
  Settings.HideHeaderCaptionFloatingControl:=HideHeaderCaptionFloatingControl;
  Settings.AllowDragging:=AllowDragging;
  Settings.HeaderStyle:=HeaderStyle;
  Settings.HeaderFlatten:=HeaderFlatten;
  Settings.HeaderFilled:=HeaderFilled;
end;

function TAnchorDockMaster.SettingsAreEqual(Settings: TAnchorDockSettings
  ): boolean;
var
  Cur: TAnchorDockSettings;
begin
  Cur:=TAnchorDockSettings.Create;
  try
    SaveSettings(Cur);
    Result:=Cur.IsEqual(Settings);
  finally
    Cur.Free;
  end;
end;

procedure TAnchorDockMaster.ManualFloat(AControl: TControl);
var
  Site: TAnchorDockHostSite;
begin
  Site:=GetAnchorSite(AControl);
  if Site=nil then exit;
  Site.Undock;
end;

procedure TAnchorDockMaster.ManualDock(SrcSite: TAnchorDockHostSite;
  TargetSite: TCustomForm; Align: TAlign; TargetControl: TControl);
var
  Site: TAnchorDockHostSite;
  aManager: TAnchorDockManager;
  DockObject: TDragDockObject;
begin
  debugln(['TAnchorDockMaster.ManualDock SrcSite=',DbgSName(SrcSite),' TargetSite=',DbgSName(TargetSite),' Align=',dbgs(Align),' TargetControl=',DbgSName(TargetControl)]);
  if SrcSite=TargetSite then exit;
  if SrcSite.IsParentOf(TargetSite) then
    raise Exception.Create('TAnchorDockMaster.ManualDock SrcSite.IsParentOf(TargetSite)');
  if TargetSite.IsParentOf(SrcSite) then
    raise Exception.Create('TAnchorDockMaster.ManualDock TargetSite.IsParentOf(SrcSite)');

  if IsCustomSite(TargetSite) then begin
    aManager:=TAnchorDockManager(TargetSite.DockManager);
    Site:=aManager.GetChildSite;
    if Site=nil then begin
      // dock as first site into custom dock site
      debugln(['TAnchorDockMaster.ManualDock dock as first site into custom dock site: SrcSite=',DbgSName(SrcSite),' TargetSite=',DbgSName(TargetSite),' Align=',dbgs(Align)]);
      BeginUpdate;
      try
        DockObject := TDragDockObject.Create(SrcSite);
        try
          DockObject.DropAlign:=Align;
          DockObject.DockRect:=SrcSite.BoundsRect;
          aManager.InsertControl(DockObject);
        finally
          DockObject.Free;
        end;
      finally
        EndUpdate;
      end;
      exit;
    end;
    // else: dock into child site of custom dock site
  end else begin
    // dock to or into TargetSite
    if not (TargetSite is TAnchorDockHostSite) then
      raise Exception.Create('TAnchorDockMaster.ManualDock invalid TargetSite');
    Site:=TAnchorDockHostSite(TargetSite);
  end;
  if AutoFreedIfControlIsRemoved(Site,SrcSite) then
    raise Exception.Create('TAnchorDockMaster.ManualDock TargetSite depends on SrcSite');
  BeginUpdate;
  try
    Site.ExecuteDock(SrcSite,TargetControl,Align);
  finally
    EndUpdate;
  end;
end;

function TAnchorDockMaster.ManualEnlarge(Site: TAnchorDockHostSite;
  Side: TAnchorKind; OnlyCheckIfPossible: boolean): boolean;
begin
  Result:=(Site<>nil) and Site.EnlargeSide(Side,OnlyCheckIfPossible);
end;

procedure TAnchorDockMaster.BeginUpdate;
begin
  inc(fUpdateCount);
end;

procedure TAnchorDockMaster.EndUpdate;
begin
  if fUpdateCount<=0 then
    RaiseGDBException('');
  dec(fUpdateCount);
  if fUpdateCount=0 then
    SimplifyPendingLayouts;
end;

procedure TAnchorDockMaster.NeedSimplify(AControl: TControl);
begin
  if Self=nil then exit;
  if csDestroying in ComponentState then exit;
  if csDestroying in AControl.ComponentState then exit;
  if fNeedSimplify=nil then exit;
  if fNeedSimplify.IndexOf(AControl)>=0 then exit;
  if not ((AControl is TAnchorDockHostSite)
          or (AControl is TAnchorDockPage))
  then
    exit;
  if Application.Terminated then exit;
  //debugln(['TAnchorDockMaster.NeedSimplify ',DbgSName(AControl),' Caption="',AControl.Caption,'"']);
  fNeedSimplify.Add(AControl);
  AControl.FreeNotification(Self);
  QueueSimplify:=true;
end;

procedure TAnchorDockMaster.NeedFree(AControl: TControl);
begin
  //debugln(['TAnchorDockMaster.NeedFree ',DbgSName(AControl),' ',csDestroying in AControl.ComponentState]);
  if fNeedFree.IndexOf(AControl)>=0 then exit;
  if csDestroying in AControl.ComponentState then exit;
  fNeedFree.Add(AControl);
  AControl.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}(ADAutoSizingReason){$ENDIF};
  AControl.Parent:=nil;
  AControl.Visible:=false;
end;

procedure TAnchorDockMaster.SimplifyPendingLayouts;
var
  AControl: TControl;
  Changed: Boolean;
  i: Integer;
begin
  if fSimplifying or (fUpdateCount>0) then exit;
  fSimplifying:=true;
  try
    // simplify layout (do not free controls in this step, only mark them)
    repeat
      Changed:=false;
      i:=fNeedSimplify.Count-1;
      while i>=0 do begin
        AControl:=TControl(fNeedSimplify[i]);
        if (csDestroying in AControl.ComponentState)
        or (fNeedFree.IndexOf(AControl)>=0) then begin
          fNeedSimplify.Delete(i);
          Changed:=true;
        end else if (AControl is TAnchorDockHostSite) then begin
          //debugln(['TAnchorDockMaster.SimplifyPendingLayouts ',DbgSName(AControl),' ',dbgs(TAnchorDockHostSite(AControl).SiteType),' UpdatingLayout=',TAnchorDockHostSite(AControl).UpdatingLayout]);
          if not TAnchorDockHostSite(AControl).UpdatingLayout then begin
            fNeedSimplify.Delete(i);
            Changed:=true;
            if TAnchorDockHostSite(AControl).SiteType=adhstNone then
            begin
              //debugln(['TAnchorDockMaster.SimplifyPendingLayouts free empty site: ',dbgs(pointer(AControl)),' Caption="',AControl.Caption,'"']);
              NeedFree(AControl);
            end else begin
              TAnchorDockHostSite(AControl).Simplify;
            end;
          end;
        end else if AControl is TAnchorDockPage then begin
          fNeedSimplify.Delete(i);
          Changed:=true;
          NeedFree(AControl);
        end else
          RaiseGDBException('TAnchorDockMaster.SimplifyPendingLayouts inconsistency');
        i:=Min(fNeedSimplify.Count,i)-1;
      end;
    until not Changed;

    // free unneeded controls
    for i := fNeedFree.Count - 1 downto 0 do
      if not (csDestroying in TControl(fNeedFree[i]).ComponentState) then
        TControl(fNeedFree[i]).Free;
    fNeedFree.Clear;
  finally
    fSimplifying:=false;
  end;
end;

function TAnchorDockMaster.AutoFreedIfControlIsRemoved(AControl,
  RemovedControl: TControl): boolean;
{ returns true if the simplification algorithm will automatically free
     AControl when RemovedControl is removed
  Some sites are dummy sites that were autocreated. They will be auto freed
  if not needed anymore.
  1. A TAnchorDockPage has a TAnchorDockHostSite as child. If the child is freed
     the page will be freed.
  2. When a TAnchorDockPageControl has only one page left the content is moved
     up and the pagecontrol and page will be freed.
  3. When a adhstLayout site has only one child site left, the content is moved up
     and the child site will be freed.
  4. When the control of a adhstOneControl site is freed the site will be freed.
}
var
  ParentSite: TAnchorDockHostSite;
  Page: TAnchorDockPage;
  PageControl: TAnchorDockPageControl;
  OtherPage: TAnchorDockPage;
  Site, Site1, Site2: TAnchorDockHostSite;
begin
  Result:=false;
  if (RemovedControl=nil) or (AControl=nil) then exit;
  while RemovedControl<>nil do begin
    if RemovedControl=AControl then exit(true);
    if RemovedControl is TAnchorDockPage then begin
      // a page will be removed
      Page:=TAnchorDockPage(RemovedControl);
      if not (Page.Parent is TAnchorDockPageControl) then exit;
      PageControl:=TAnchorDockPageControl(Page.Parent);
      if PageControl.PageCount>2 then exit;
      if PageControl.PageCount=2 then begin
        // this pagecontrol will be replaced by the content of the other page
        if PageControl=AControl then exit(true);
        if PageControl.Page[0]=Page then
          OtherPage:=PageControl.DockPages[1]
        else
          OtherPage:=PageControl.DockPages[0];
        // the other page will be removed (its content will be moved up)
        if OtherPage=AControl then exit(true);
        if (OtherPage.ControlCount>0) then begin
          if (OtherPage.Controls[0] is TAnchorDockHostSite)
          and (OtherPage.Controls[0]=RemovedControl) then
            exit(true); // the site of the other page will be removed (its content moved up)
        end;
        exit;
      end;
      // the last page of the pagecontrol is freed => the pagecontrol will be removed too
    end else if RemovedControl is TAnchorDockPageControl then begin
      // the pagecontrol will be removed
      if not (RemovedControl.Parent is TAnchorDockHostSite) then exit;
      // a pagecontrol is always the only child of a site
      // => the site will be removed too
    end else if RemovedControl is TAnchorDockHostSite then begin
      // a site will be removed
      Site:=TAnchorDockHostSite(RemovedControl);
      if Site.Parent is TAnchorDockPage then begin
        // a page has only one site
        // => the page will be removed too
      end else if Site.Parent is TAnchorDockHostSite then begin
        ParentSite:=TAnchorDockHostSite(Site.Parent);
        if (ParentSite.SiteType=adhstOneControl)
        or ParentSite.IsOneSiteLayout(Site) then begin
          // the control of a OneControl site is removed => the ParentSite is freed too
        end else if ParentSite.SiteType=adhstLayout then begin
          if ParentSite.IsTwoSiteLayout(Site1,Site2) then begin
            // when there are two sites and one of them is removed
            // the content of the other will be moved up and then both sites are
            // removed
            if (Site1=AControl) or (Site2=AControl) then
              exit(true);
          end;
          exit; // removing only site will not free the layout
        end else begin
          raise Exception.Create('TAnchorDockMaster.AutoFreedIfControlIsRemoved ParentSiteType='+dbgs(ParentSite.SiteType)+' ChildSiteType='+dbgs(Site.SiteType));
        end;
      end else
        exit; // other classes will never be auto freed
    end else begin
      // control is not a site => check if control is in a OneControl site
      if not (RemovedControl.Parent is TAnchorDockHostSite) then exit;
      ParentSite:=TAnchorDockHostSite(RemovedControl.Parent);
      if (ParentSite.SiteType<>adhstOneControl) then exit;
      if ParentSite.GetOneControl<>RemovedControl then exit;
      // the control of a OneControl site is removed => the site is freed too
    end;
    RemovedControl:=RemovedControl.Parent;
  end;
end;

function TAnchorDockMaster.CreateSite(NamePrefix: string;
  DisableAutoSizing: boolean): TAnchorDockHostSite;
var
  i: Integer;
  NewName: String;
begin
  Result:=TAnchorDockHostSite(SiteClass.NewInstance);
  {$IFDEF DebugDisableAutoSizing}
  if DisableAutoSizing then
    Result.DisableAutoSizing(ADAutoSizingReason)
  else
    Result.DisableAutoSizing('TAnchorDockMaster.CreateSite');
  {$ELSE}
  Result.DisableAutoSizing;
  {$ENDIF};
  try
    Result.CreateNew(Self,1);
    i:=0;
    repeat
      inc(i);
      NewName:=NamePrefix+AnchorDockSiteName+IntToStr(i);
    until (Screen.FindForm(NewName)=nil) and (FindComponent(NewName)=nil);
    Result.Name:=NewName;
  finally
    if not DisableAutoSizing then
      Result.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockMaster.CreateSite'){$ENDIF};
  end;
end;

function TAnchorDockMaster.CreateSplitter(NamePrefix: string
  ): TAnchorDockSplitter;
var
  i: Integer;
  NewName: String;
begin
  Result:=SplitterClass.Create(Self);
  i:=0;
  repeat
    inc(i);
    NewName:=NamePrefix+AnchorDockSplitterName+IntToStr(i);
  until FindComponent(NewName)=nil;
  Result.Name:=NewName;
end;

procedure TAnchorDockMaster.IncreaseOptionsChangeStamp;
begin
  LUIncreaseChangeStamp64(FOptionsChangeStamp);
end;

{ TAnchorDockHostSite }

procedure TAnchorDockHostSite.SetHeaderSide(const AValue: TAnchorKind);
begin
  if FHeaderSide=AValue then exit;
  FHeaderSide:=AValue;
end;

procedure TAnchorDockHostSite.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if Operation=opRemove then begin
    if AComponent=Pages then FPages:=nil;
    if AComponent=Header then FHeader:=nil;
    if AComponent=BoundSplitter then FBoundSplitter:=nil;
  end;
end;

function TAnchorDockHostSite.DoDockClientMsg(DragDockObject: TDragDockObject;
  aPosition: TPoint): boolean;
begin
  if aPosition.X=0 then ;
  Result:=ExecuteDock(DragDockObject.Control,DragDockObject.DropOnControl,
                      DragDockObject.DropAlign);
end;

function TAnchorDockHostSite.ExecuteDock(NewControl, DropOnControl: TControl;
  DockAlign: TAlign): boolean;
begin
  if UpdatingLayout then exit;
  //debugln(['TAnchorDockHostSite.ExecuteDock Self="',Caption,'"  Control=',DbgSName(NewControl),' DropOnControl=',DbgSName(DropOnControl),' Align=',dbgs(DockAlign)]);

  DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.ExecuteDock HostSite'){$ENDIF};
  try
    BeginUpdateLayout;
    try
      DockMaster.SimplifyPendingLayouts;
      NewControl.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.ExecuteDock NewControl'){$ENDIF};

      if (NewControl.Parent=Self) and (SiteType=adhstLayout) then begin
        // change of layout, one child is docked to the outer side
        RemoveControlFromLayout(NewControl);
      end else if (NewControl.Parent=Parent) and (Parent is TAnchorDockHostSite)
      and (TAnchorDockHostSite(Parent).SiteType=adhstLayout) then begin
        // change of layout, one sibling is moved
        TAnchorDockHostSite(Parent).RemoveControlFromLayout(NewControl);
      end;

      if SiteType=adhstNone then begin
        // make a control dockable by docking it into a TAnchorDockHostSite;
        Result:=DockFirstControl(NewControl);
      end else if DockAlign=alClient then begin
        // page docking
        if SiteType=adhstOneControl then begin
          if Parent is TAnchorDockPage then begin
            // add as sibling page
            Result:=(Parent.Parent.Parent as TAnchorDockHostSite).DockAnotherPage(NewControl,nil);
          end else
            // create pages
            Result:=DockSecondPage(NewControl);
        end else if SiteType=adhstPages then
          // add as sibling page
          Result:=DockAnotherPage(NewControl,DropOnControl);
      end else if DockAlign in [alLeft,alTop,alRight,alBottom] then
      begin
        // anchor docking
        if SiteType=adhstOneControl then begin
          if Parent is TAnchorDockHostSite then begin
            // add site as sibling
            Result:=TAnchorDockHostSite(Parent).DockAnotherControl(Self,NewControl,
                      DockAlign,DropOnControl<>nil);
          end else
            // create layout
            Result:=DockSecondControl(NewControl,DockAlign,DropOnControl<>nil);
        end else if SiteType=adhstLayout then
          // add site as sibling
          Result:=DockAnotherControl(nil,NewControl,DockAlign,DropOnControl<>nil);
      end;

      NewControl.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.ExecuteDock NewControl'){$ENDIF};
    finally
      EndUpdateLayout;
    end;
  finally
    EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.ExecuteDock HostSite'){$ENDIF};
  end;
end;

function TAnchorDockHostSite.DockFirstControl(NewControl: TControl): boolean;
var
  DestRect: TRect;
begin
  if SiteType<>adhstNone then
    RaiseGDBException('TAnchorDockHostSite.DockFirstControl inconsistency');
  // create adhstOneControl
  DestRect := ClientRect;
  NewControl.Dock(Self, DestRect);
  FSiteType:=adhstOneControl;
  if NewControl is TCustomForm then begin
    Icon.Assign(TCustomForm(NewControl).Icon);
  end;
  Result:=true;
end;

function TAnchorDockHostSite.DockSecondControl(NewControl: TControl;
  DockAlign: TAlign; Inside: boolean): boolean;
{ Convert a adhstOneControl into a adhstLayout by docking NewControl
  at a side (DockAlign).
  If Inside=true this DockSite is not expanded and both controls share the old space.
  If Inside=false this DockSite is expanded.
}
var
  OldSite: TAnchorDockHostSite;
  OldControl: TControl;
begin
  Result:=true;
  debugln(['TAnchorDockHostSite.DockSecondControl Self="',Caption,'" AControl=',DbgSName(NewControl),' Align=',dbgs(DockAlign),' Inside=',Inside]);
  if SiteType<>adhstOneControl then
    RaiseGDBException('TAnchorDockHostSite.DockSecondControl inconsistency: not adhstOneControl');
  if not (DockAlign in [alLeft,alTop,alRight,alBottom]) then
    RaiseGDBException('TAnchorDockHostSite.DockSecondControl inconsistency: DockAlign='+dbgs(DockAlign));

  FSiteType:=adhstLayout;

  // remove header (keep it for later use)
  Header.Parent:=nil;

  // put the OldControl into a site of its own (OldSite) and dock OldSite
  OldControl:=GetOneControl;
  OldSite:=MakeSite(OldControl);
  AddCleanControl(OldSite);
  OldSite.AnchorClient(0);
  // the LCL will compute the bounds later after EnableAutoSizing
  // but the bounds are needed now => set them manually
  OldSite.BoundsRect:=Rect(0,0,ClientWidth,ClientHeight);

  Result:=DockAnotherControl(OldSite,NewControl,DockAlign,Inside);

  debugln(['TAnchorDockHostSite.DockSecondControl END Self="',Caption,'" AControl=',DbgSName(NewControl),' Align=',dbgs(DockAlign),' Inside=',Inside]);
end;

function TAnchorDockHostSite.DockAnotherControl(Sibling, NewControl: TControl;
  DockAlign: TAlign; Inside: boolean): boolean;
var
  Splitter: TAnchorDockSplitter;
  a: TAnchorKind;
  NewSite: TAnchorDockHostSite;
  NewBounds: TRect;
  MainAnchor: TAnchorKind;
  i: Integer;
  NewSiblingWidth: Integer;
  NewSiblingHeight: Integer;
  NewSize: LongInt;
  BoundsIncreased: Boolean;
  NewParentBounds: TRect;
begin
  Result:=false;
  if SiteType<>adhstLayout then
    RaiseGDBException('TAnchorDockHostSite.DockAnotherControl inconsistency');
  if not (DockAlign in [alLeft,alTop,alRight,alBottom]) then
    RaiseGDBException('TAnchorDockHostSite.DockAnotherControl inconsistency');

  // add a splitter
  Splitter:=DockMaster.CreateSplitter;
  if DockAlign in [alLeft,alRight] then begin
    Splitter.ResizeAnchor:=akLeft;
    Splitter.Width:=DockMaster.SplitterWidth;
  end else begin
    Splitter.ResizeAnchor:=akTop;
    Splitter.Height:=DockMaster.SplitterWidth;
  end;
  Splitter.Parent:=Self;

  // dock the NewControl
  NewSite:=MakeSite(NewControl);
  AddCleanControl(NewSite);

  BoundsIncreased:=false;
  if (not Inside) then begin
    if (Parent=nil) then begin
      // expand Self
      NewBounds:=BoundsRect;
      case DockAlign of
      alLeft:
        begin
          dec(NewBounds.Left,NewSite.Width+Splitter.Width);
          MoveAllControls(NewSite.Width+Splitter.Width,0);
        end;
      alRight:
        inc(NewBounds.Right,NewSite.Width+Splitter.Width);
      alTop:
        begin
          dec(NewBounds.Top,NewSite.Height+Splitter.Height);
          MoveAllControls(0,NewSite.Height+Splitter.Height);
        end;
      alBottom:
        inc(NewBounds.Bottom,NewSite.Height+Splitter.Height);
      end;
      BoundsRect:=NewBounds;
      BoundsIncreased:=true;
    end else if DockMaster.IsCustomSite(Parent) then begin
      // Parent is a custom docksite
      // => expand Self and Parent
      // expand Parent (the custom docksite)
      NewParentBounds:=Parent.BoundsRect;
      NewBounds:=BoundsRect;
      case DockAlign of
      alLeft:
        begin
          i:=NewSite.Width+Splitter.Width;
          dec(NewParentBounds.Left,i);
          dec(NewBounds.Left,i);
          MoveAllControls(i,0);
        end;
      alRight:
        begin
          i:=NewSite.Width+Splitter.Width;
          inc(NewBounds.Right,i);
          inc(NewParentBounds.Right,i);
        end;
      alTop:
        begin
          i:=NewSite.Height+Splitter.Height;
          dec(NewBounds.Top,i);
          dec(NewParentBounds.Top,i);
          MoveAllControls(0,i);
        end;
      alBottom:
        begin
          i:=NewSite.Height+Splitter.Height;
          inc(NewParentBounds.Bottom,i);
          inc(NewBounds.Bottom,i);
        end;
      end;
      Parent.BoundsRect:=NewParentBounds;
      BoundsRect:=NewBounds;
      BoundsIncreased:=true;
      TAnchorDockManager(Parent.DockManager).FSiteClientRect:=Parent.ClientRect;
    end;
    debugln(['TAnchorDockHostSite.DockAnotherControl AFTER ENLARGE ',Caption]);
    //DebugWriteChildAnchors(Self,true,true);
  end;

  // anchors
  MainAnchor:=MainAlignAnchor[DockAlign];
  if Inside and (Sibling<>nil) then begin
    { Example: insert right of Sibling
                    #                                  #
        ################          ########################
            -------+#                -------+#+-------+#
            Sibling|#     ----->     Sibling|#|NewSite|#
            -------+#                -------+#+-------+#
        ################          ########################
                    #                                  #
     }
    for a:=low(TAnchorKind) to high(TAnchorKind) do begin
      if a in AnchorAlign[DockAlign] then begin
        NewSite.AnchorSide[a].Assign(Sibling.AnchorSide[a]);
      end else begin
        NewSite.AnchorToNeighbour(a,0,Splitter);
      end;
    end;
    Sibling.AnchorToNeighbour(MainAnchor,0,Splitter);

    if DockAlign in [alLeft,alRight] then begin
      Splitter.AnchorSide[akTop].Assign(Sibling.AnchorSide[akTop]);
      Splitter.AnchorSide[akBottom].Assign(Sibling.AnchorSide[akBottom]);
      // resize and move
      // the NewSite gets at maximum half the space
      // Many bounds are later set by the LCL anchoring. When docking several
      // controls at once the bounds are needed earlier.
      NewSize:=Max(1,Min(NewSite.Width,Sibling.Width div 2));
      NewBounds:=Rect(0,0,NewSize,Sibling.Height);
      NewSiblingWidth:=Max(1,Sibling.Width-NewSize-Splitter.Width);
      if DockAlign=alLeft then begin
        // alLeft: NewControl, Splitter, Sibling
        Splitter.SetBounds(Sibling.Left+NewSize,Sibling.Top,
                           Splitter.Width,Sibling.Height);
        OffsetRect(NewBounds,Sibling.Left,Sibling.Top);
        Sibling.SetBounds(Splitter.Left+Splitter.Width,Sibling.Top,
                          NewSiblingWidth,Sibling.Height);
      end else begin
        // alRight: Sibling, Splitter, NewControl
        Sibling.Width:=NewSiblingWidth;
        Splitter.SetBounds(Sibling.Left+Sibling.Width,Sibling.Top,
                           Splitter.Width,Sibling.Height);
        OffsetRect(NewBounds,Splitter.Left+Splitter.Width,Sibling.Top);
      end;
      NewSite.BoundsRect:=NewBounds;
    end else begin
      Splitter.AnchorSide[akLeft].Assign(Sibling.AnchorSide[akLeft]);
      Splitter.AnchorSide[akRight].Assign(Sibling.AnchorSide[akRight]);
      // resize and move
      // the NewSite gets at maximum half the space
      // Many bounds are later set by the LCL anchoring. When docking several
      // controls at once the bounds are needed earlier.
      NewSize:=Max(1,Min(NewSite.Height,Sibling.Height div 2));
      NewSiblingHeight:=Max(1,Sibling.Height-NewSize-Splitter.Height);
      if DockAlign=alTop then begin
        // alTop: NewControl, Splitter, Sibling
        Splitter.SetBounds(Sibling.Left,Sibling.Top+NewSize,
                           Sibling.Width,Splitter.Height);
        NewSite.SetBounds(Sibling.Left,Sibling.Top,Sibling.Width,NewSize);
        Sibling.SetBounds(Sibling.Left,Splitter.Top+Splitter.Height,
                          Sibling.Width,NewSiblingHeight);
      end else begin
        // alBottom: Sibling, Splitter, NewControl
        Sibling.Height:=NewSiblingHeight;
        Splitter.SetBounds(Sibling.Left,Sibling.Top+Sibling.Height,
                           Sibling.Width,Splitter.Height);
        NewSite.SetBounds(Sibling.Left,Splitter.Top+Splitter.Height,
                          Sibling.Width,NewSize);
      end;
    end;
  end else begin
    { Example: insert right of all siblings
        ##########         #######################
        --------+#         --------+#+----------+#
        SiblingA|#         SiblingA|#|          |#
        --------+#         --------+#|          |#
        ##########  -----> ##########|NewControl|#
        --------+#         --------+#|          |#
        SiblingB|#         SiblingB|#|          |#
        --------+#         --------+#+----------+#
        ##########         #######################
    }
    if DockAlign in [alLeft,alRight] then
      NewSize:=NewSite.Width
    else
      NewSize:=NewSite.Height;
    for i:=0 to ControlCount-1 do begin
      Sibling:=Controls[i];
      if Sibling.AnchorSide[MainAnchor].Control=Self then begin
        // this Sibling is anchored to the docked site
        // anchor it to the splitter
        Sibling.AnchorToNeighbour(MainAnchor,0,Splitter);
        if not BoundsIncreased then begin
          // the NewSite gets at most half the space
          if DockAlign in [alLeft,alRight] then
            NewSize:=Min(NewSize,Sibling.Width div 2)
          else
            NewSize:=Min(NewSize,Sibling.Height div 2);
        end;
      end;
    end;
    NewSize:=Max(1,NewSize);

    // anchor Splitter and NewSite
    a:=ClockwiseAnchor[MainAnchor];
    Splitter.AnchorParallel(a,0,Self);
    Splitter.AnchorParallel(OppositeAnchor[a],0,Self);
    NewSite.AnchorParallel(a,0,Self);
    NewSite.AnchorParallel(OppositeAnchor[a],0,Self);
    NewSite.AnchorParallel(MainAnchor,0,Self);
    NewSite.AnchorToNeighbour(OppositeAnchor[MainAnchor],0,Splitter);

    // Many bounds are later set by the LCL anchoring. When docking several
    // controls at once the bounds are needed earlier.
    if DockAlign in [alLeft,alRight] then begin
      if DockAlign=alLeft then begin
        // alLeft: NewSite, Splitter, other siblings
        Splitter.SetBounds(NewSize,0,Splitter.Width,ClientHeight);
        NewSite.SetBounds(0,0,NewSize,ClientHeight);
      end else begin
        // alRight: other siblings, Splitter, NewSite
        NewSite.SetBounds(ClientWidth-NewSize,0,NewSize,ClientHeight);
        Splitter.SetBounds(NewSite.Left-Splitter.Width,0,Splitter.Width,ClientHeight);
      end;
    end else begin
      if DockAlign=alTop then begin
        // alTop: NewSite, Splitter, other siblings
        Splitter.SetBounds(0,NewSize,ClientWidth,Splitter.Height);
        NewSite.SetBounds(0,0,ClientWidth,NewSize);
      end else begin
        // alBottom: other siblings, Splitter, NewSite
        NewSite.SetBounds(0,ClientHeight-NewSize,ClientWidth,NewSize);
        Splitter.SetBounds(0,NewSite.Top-Splitter.Height,ClientWidth,Splitter.Height);
      end;
    end;
    // shrink siblings
    for i:=0 to ControlCount-1 do begin
      Sibling:=Controls[i];
      if Sibling.AnchorSide[MainAnchor].Control=Splitter then begin
        NewBounds:=Sibling.BoundsRect;
        case DockAlign of
        alLeft: NewBounds.Left:=Splitter.Left+Splitter.Width;
        alRight: NewBounds.Right:=Splitter.Left;
        alTop: NewBounds.Top:=Splitter.Top+Splitter.Height;
        alBottom: NewBounds.Bottom:=Splitter.Top;
        end;
        NewBounds.Right:=Max(NewBounds.Left+1,NewBounds.Right);
        NewBounds.Bottom:=Max(NewBounds.Top+1,NewBounds.Bottom);
        Sibling.BoundsRect:=NewBounds;
      end;
    end;
  end;

  //debugln(['TAnchorDockHostSite.DockAnotherControl ',DbgSName(Self)]);
  //DebugWriteChildAnchors(Self,true,true);
  Result:=true;
end;

procedure TAnchorDockHostSite.CreatePages;
begin
  if FPages<>nil then
    RaiseGDBException('');
  FPages:=DockMaster.PageControlClass.Create(nil); // do not own it, pages can be moved to another site
  FPages.FreeNotification(Self);
  FPages.Parent:=Self;
  FPages.Align:=alClient;
end;

procedure TAnchorDockHostSite.FreePages;
begin
  FreeAndNil(FPages);
end;

function TAnchorDockHostSite.DockSecondPage(NewControl: TControl): boolean;
var
  OldControl: TControl;
  OldSite: TAnchorDockHostSite;
begin
  {$IFDEF VerboseAnchorDockPages}
  debugln(['TAnchorDockHostSite.DockSecondPage Self="',Caption,'" AControl=',DbgSName(NewControl)]);
  {$ENDIF}
  if SiteType<>adhstOneControl then
    RaiseGDBException('TAnchorDockHostSite.DockSecondPage inconsistency');

  FSiteType:=adhstPages;
  CreatePages;

  // remove header (keep it for later use)
  {$IFDEF VerboseAnchorDockPages}
  debugln(['TAnchorDockHostSite.DockSecondPage Self="',Caption,'" removing header ...']);
  {$ENDIF}
  Header.Parent:=nil;

  // put the OldControl into a page of its own
  {$IFDEF VerboseAnchorDockPages}
  debugln(['TAnchorDockHostSite.DockSecondPage Self="',Caption,'" move oldcontrol to site of its own ...']);
  {$ENDIF}
  OldControl:=GetOneControl;
  OldSite:=MakeSite(OldControl);
  OldSite.HostDockSite:=nil;
  {$IFDEF VerboseAnchorDockPages}
  debugln(['TAnchorDockHostSite.DockSecondPage Self="',Caption,'" adding oldcontrol site ...']);
  {$ENDIF}
  FPages.Pages.Add(OldSite.Caption);
  OldSite.Parent:=FPages.Page[0];
  OldSite.Align:=alClient;
  OldSite.Visible:=true;

  Result:=DockAnotherPage(NewControl,nil);
end;

function TAnchorDockHostSite.DockAnotherPage(NewControl: TControl;
  InFrontOf: TControl): boolean;
var
  NewSite: TAnchorDockHostSite;
  NewIndex: LongInt;
begin
  {$IFDEF VerboseAnchorDockPages}
  debugln(['TAnchorDockHostSite.DockAnotherPage Self="',Caption,'" make new control (',DbgSName(NewControl),') dockable ...']);
  {$ENDIF}
  if SiteType<>adhstPages then
    RaiseGDBException('TAnchorDockHostSite.DockAnotherPage inconsistency');

  NewSite:=MakeSite(NewControl);
  //debugln(['TAnchorDockHostSite.DockAnotherPage Self="',Caption,'" adding newcontrol site ...']);
  NewIndex:=FPages.PageCount;
  if (InFrontOf is TAnchorDockPage)
  and (InFrontOf.Parent=Pages) then
    NewIndex:=TAnchorDockPage(InFrontOf).PageIndex;
  Pages.Pages.Insert(NewIndex,NewSite.Caption);
  //debugln(['TAnchorDockHostSite.DockAnotherPage ',DbgSName(FPages.Page[1])]);
  NewSite.Parent:=FPages.Page[NewIndex];
  NewSite.Align:=alClient;
  NewSite.Visible:=true;
  FPages.PageIndex:=NewIndex;

  Result:=true;
end;

procedure TAnchorDockHostSite.AddCleanControl(AControl: TControl;
  TheAlign: TAlign);
var
  a: TAnchorKind;
begin
  AControl.Parent:=Self;
  AControl.Align:=TheAlign;
  AControl.Anchors:=[akLeft,akTop,akRight,akBottom];
  for a:=Low(TAnchorKind) to high(TAnchorKind) do
    AControl.AnchorSide[a].Control:=nil;
  AControl.Visible:=true;
end;

procedure TAnchorDockHostSite.RemoveControlFromLayout(AControl: TControl);

  procedure RemoveControlBoundSplitter(Splitter: TAnchorDockSplitter;
    Side: TAnchorKind);
  var
    i: Integer;
    Sibling: TControl;
    NewBounds: TRect;
  begin
    //debugln(['RemoveControlBoundSplitter START ',DbgSName(Splitter)]);
    { Example: Side=akRight
                          #             #
        #####################     #########
           ---+S+--------+#         ---+#
           ---+S|AControl|#   --->  ---+#
           ---+S+--------+#         ---+#
        #####################     #########
    }
    for i:=Splitter.AnchoredControlCount-1 downto 0 do begin
      Sibling:=Splitter.AnchoredControls[i];
      if Sibling.AnchorSide[Side].Control=Splitter then begin
        // anchor Sibling to next
        Sibling.AnchorSide[Side].Assign(AControl.AnchorSide[Side]);
        // enlarge Sibling
        NewBounds:=Sibling.BoundsRect;
        case Side of
        akTop: NewBounds.Top:=AControl.Top;
        akLeft: NewBounds.Left:=AControl.Left;
        akRight: NewBounds.Right:=AControl.Left+AControl.Width;
        akBottom: NewBounds.Bottom:=AControl.Top+AControl.Height;
        end;
        Sibling.BoundsRect:=NewBounds;
      end;
    end;
    //debugln(['RemoveControlBoundSplitter ',DbgSName(Splitter)]);
    Splitter.Free;

    ClearChildControlAnchorSides(AControl);
    //DebugWriteChildAnchors(GetParentForm(Self),true,true);
  end;

  procedure ConvertToOneControlType(OnlySiteLeft: TAnchorDockHostSite);
  var
    a: TAnchorKind;
    NewBounds: TRect;
    p: TPoint;
    i: Integer;
    Sibling: TControl;
    NewParentBounds: TRect;
  begin
    BeginUpdateLayout;
    try
      // remove splitter
      for i:=ControlCount-1 downto 0 do begin
        Sibling:=Controls[i];
        if Sibling is TAnchorDockSplitter then
          Sibling.Free
        else if Sibling is TAnchorDockHostSite then
          for a:=low(TAnchorKind) to high(TAnchorKind) do
            Sibling.AnchorSide[a].Control:=nil;
      end;
      if (Parent=nil) then begin
        // shrink this site
        NewBounds:=OnlySiteLeft.BoundsRect;
        p:=ClientOrigin;
        OffsetRect(NewBounds,p.x,p.y);
        BoundsRect:=NewBounds;
      end else if DockMaster.IsCustomSite(Parent) then begin
        // parent is a custom dock site
        // shrink this site and the parent
        NewParentBounds:=Parent.BoundsRect;
        case Align of
        alTop:
          begin
            inc(NewParentBounds.Top,Height-OnlySiteLeft.Height);
            Width:=Parent.ClientWidth;
            Height:=OnlySiteLeft.Height;
          end;
        alBottom:
          begin
            dec(NewParentBounds.Bottom,Height-OnlySiteLeft.Height);
            Width:=Parent.ClientWidth;
            Height:=OnlySiteLeft.Height;
          end;
        alLeft:
          begin
            inc(NewParentBounds.Left,Width-OnlySiteLeft.Width);
            Width:=OnlySiteLeft.Width;
            Height:=Parent.ClientHeight;
          end;
        alRight:
          begin
            dec(NewParentBounds.Right,Width-OnlySiteLeft.Width);
            Width:=OnlySiteLeft.Width;
            Height:=Parent.ClientHeight;
          end;
        end;
        Parent.BoundsRect:=NewParentBounds;
      end;

      // change type
      FSiteType:=adhstOneControl;
      OnlySiteLeft.Align:=alClient;
      Header.Parent:=Self;
      UpdateHeaderAlign;

      //debugln(['TAnchorDockHostSite.RemoveControlFromLayout.ConvertToOneControlType AFTER CONVERT "',Caption,'" to onecontrol OnlySiteLeft="',OnlySiteLeft.Caption,'"']);
      //DebugWriteChildAnchors(GetParentForm(Self),true,true);

      DockMaster.NeedSimplify(Self);
    finally
      EndUpdateLayout;
    end;
  end;

var
  Side: TAnchorKind;
  Splitter: TAnchorDockSplitter;
  OnlySiteLeft: TAnchorDockHostSite;
  Sibling: TControl;
  SplitterCount: Integer;
begin
  debugln(['TAnchorDockHostSite.RemoveControlFromLayout Self="',Caption,'" AControl=',DbgSName(AControl),'="',AControl.Caption,'"']);
  if SiteType<>adhstLayout then
    RaiseGDBException('TAnchorDockHostSite.RemoveControlFromLayout inconsistency');

  if IsOneSiteLayout(OnlySiteLeft) then begin
    ClearChildControlAnchorSides(AControl);
    ConvertToOneControlType(OnlySiteLeft);
    exit;
  end;

  // remove a splitter and fill the gap
  SplitterCount:=0;
  for Side:=Low(TAnchorKind) to high(TAnchorKind) do begin
    Sibling:=AControl.AnchorSide[OppositeAnchor[Side]].Control;
    if Sibling is TAnchorDockSplitter then begin
      inc(SplitterCount);
      Splitter:=TAnchorDockSplitter(Sibling);
      if Splitter.SideAnchoredControlCount(Side)=1 then begin
        // Splitter is only used by AControl at Side
        RemoveControlBoundSplitter(Splitter,Side);
        exit;
      end;
    end;
  end;

  if SplitterCount=4 then begin
    RemoveSpiralSplitter(AControl);
    exit;
  end;

  ClearChildControlAnchorSides(AControl);
end;

procedure TAnchorDockHostSite.RemoveSpiralSplitter(AControl: TControl);
{ Merge two splitters and delete one of them.
  Prefer the pair with shortest distance between.

  For example:
                   3            3
     111111111111113            3
        2+--------+3            3
        2|AControl|3  --->  111111111
        2+--------+3            2
        24444444444444          2
        2                       2
   Everything anchored to 4 is now anchored to 1.
   And right side of 1 is now anchored to where the right side of 4 was anchored.
}
var
  Splitters: array[TAnchorKind] of TAnchorDockSplitter;
  Side: TAnchorKind;
  Keep: TAnchorKind;
  DeleteSplitter: TAnchorDockSplitter;
  i: Integer;
  Sibling: TControl;
  NextSide: TAnchorKind;
  NewBounds: TRect;
begin
  for Side:=low(TAnchorKind) to high(TAnchorKind) do
    Splitters[Side]:=AControl.AnchorSide[Side].Control as TAnchorDockSplitter;
  // Prefer the pair with shortest distance between
  if (Splitters[akRight].Left-Splitters[akLeft].Left)
    <(Splitters[akBottom].Top-Splitters[akTop].Top)
  then
    Keep:=akLeft
  else
    Keep:=akTop;
  DeleteSplitter:=Splitters[OppositeAnchor[Keep]];
  // transfer anchors from the deleting splitter to the kept splitter
  for i:=0 to ControlCount-1 do begin
    Sibling:=Controls[i];
    for Side:=low(TAnchorKind) to high(TAnchorKind) do begin
      if Sibling.AnchorSide[Side].Control=DeleteSplitter then
        Sibling.AnchorSide[Side].Control:=Splitters[Keep];
    end;
  end;
  // longen kept splitter
  NextSide:=ClockwiseAnchor[Keep];
  if Splitters[Keep].AnchorSide[NextSide].Control<>Splitters[NextSide] then
    NextSide:=OppositeAnchor[NextSide];
  Splitters[Keep].AnchorSide[NextSide].Control:=
                                    DeleteSplitter.AnchorSide[NextSide].Control;
  case NextSide of
  akTop: Splitters[Keep].Top:=DeleteSplitter.Top;
  akLeft: Splitters[Keep].Left:=DeleteSplitter.Left;
  akRight: Splitters[Keep].Width:=DeleteSplitter.Left+DeleteSplitter.Width-Splitters[Keep].Left;
  akBottom: Splitters[Keep].Height:=DeleteSplitter.Top+DeleteSplitter.Height-Splitters[Keep].Top;
  end;

  // move splitter to the middle
  if Keep=akLeft then
    Splitters[Keep].Left:=(Splitters[Keep].Left+DeleteSplitter.Left) div 2
  else
    Splitters[Keep].Top:=(Splitters[Keep].Top+DeleteSplitter.Top) div 2;
  // adjust all anchored controls
  for i:=0 to ControlCount-1 do begin
    Sibling:=Controls[i];
    for Side:=low(TAnchorKind) to high(TAnchorKind) do begin
      if Sibling.AnchorSide[Side].Control=Splitters[Keep] then begin
        NewBounds:=Sibling.BoundsRect;
        case Side of
        akTop: NewBounds.Top:=Splitters[Keep].Top+Splitters[Keep].Height;
        akLeft: NewBounds.Left:=Splitters[Keep].Left+Splitters[Keep].Width;
        akRight: NewBounds.Right:=Splitters[Keep].Left;
        akBottom: NewBounds.Bottom:=Splitters[Keep].Top;
        end;
        Sibling.BoundsRect:=NewBounds;
      end;
    end;
  end;

  // delete the splitter
  DeleteSplitter.Free;

  ClearChildControlAnchorSides(AControl);
end;

procedure TAnchorDockHostSite.ClearChildControlAnchorSides(AControl: TControl);
var
  Side: TAnchorKind;
  Sibling: TControl;
begin
  for Side:=Low(TAnchorKind) to high(TAnchorKind) do begin
    Sibling:=AControl.AnchorSide[Side].Control;
    if (Sibling=nil) then continue;
    if (Sibling.Parent=Self) then
      AControl.AnchorSide[Side].Control:=nil;
  end;
end;

procedure TAnchorDockHostSite.Simplify;
var
  AControl: TControl;
begin
  if (Pages<>nil) and (Pages.PageCount=1) then
    SimplifyPages
  else if (SiteType=adhstOneControl) then begin
    AControl:=GetOneControl;
    debugln(['TAnchorDockHostSite.Simplify ',DbgSName(Self),' ',DbgSName(AControl)]);
    if AControl is TAnchorDockHostSite then
      SimplifyOneControl
    else if (AControl=nil) or (csDestroying in AControl.ComponentState) then
      DockMaster.NeedFree(Self);
  end;
end;

procedure TAnchorDockHostSite.SimplifyPages;
var
  Page: TAnchorDockPage;
  Site: TAnchorDockHostSite;
begin
  if Pages=nil then exit;
  if Pages.PageCount=1 then begin
    {$IFDEF VerboseAnchorDockPages}
    debugln(['TAnchorDockHostSite.SimplifyPages "',Caption,'" PageCount=1']);
    {$ENDIF}
    DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.SimplifyPages'){$ENDIF};
    BeginUpdateLayout;
    try
      // move the content of the Page to the place where Pages is
      Page:=Pages.DockPages[0];
      Site:=Page.GetSite;
      Site.Parent:=Self;
      if Site<>nil then
        CopyAnchorBounds(Pages,Site);
      if SiteType=adhstPages then
        FSiteType:=adhstOneControl;
      // free Pages
      FreePages;
      if SiteType=adhstOneControl then
        SimplifyOneControl;
    finally
      EndUpdateLayout;
      EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.SimplifyPages'){$ENDIF};
    end;
    //debugln(['TAnchorDockHostSite.SimplifyPages END Self="',Caption,'"']);
    //DebugWriteChildAnchors(GetParentForm(Self),true,true);
  end else if Pages.PageCount=0 then begin
    //debugln(['TAnchorDockHostSite.SimplifyPages "',Caption,'" PageCount=0 Self=',dbgs(Pointer(Self))]);
    FSiteType:=adhstNone;
    FreePages;
    DockMaster.NeedSimplify(Self);
  end;
end;

procedure TAnchorDockHostSite.SimplifyOneControl;
var
  Site: TAnchorDockHostSite;
  i: Integer;
  Child: TControl;
  a: TAnchorKind;
begin
  if SiteType<>adhstOneControl then exit;
  if not IsOneSiteLayout(Site) then exit;
  debugln(['TAnchorDockHostSite.SimplifyOneControl Self="',Caption,'" Site="',Site.Caption,'"']);
  DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.SimplifyOneControl'){$ENDIF};
  BeginUpdateLayout;
  try
    // move the content of Site up and free Site
    // Note: it is not possible to do it the other way round, because moving a
    // form to screen changes the z order and focus
    FSiteType:=Site.SiteType;

    // header
    Header.Align:=Site.Header.Align;
    Header.Caption:=Site.Header.Caption;
    UpdateHeaderShowing;
    Caption:=Site.Caption;

    Site.BeginUpdateLayout;
    // move controls from Site to Self
    i:=Site.ControlCount-1;
    while i>=0 do begin
      Child:=Site.Controls[i];
      if Child.Owner<>Site then begin
        //debugln(['TAnchorDockHostSite.SimplifyOneControl Self="',Caption,'" Child=',DbgSName(Child),'="',Child.Caption,'"']);
        Child.Parent:=Self;
        if Child=Site.Pages then begin
          FPages:=Site.Pages;
          Site.FPages:=nil;
        end;
        if Child.HostDockSite=Site then
          Child.HostDockSite:=Self;
        for a:=low(TAnchorKind) to high(TAnchorKind) do begin
          if Child.AnchorSide[a].Control=Site then
            Child.AnchorSide[a].Control:=Self;
        end;
      end;
      i:=Min(i,Site.ControlCount)-1;
    end;
    Site.EndUpdateLayout;

    // delete Site
    Site.FSiteType:=adhstNone;
    DockMaster.NeedFree(Site);
  finally
    EndUpdateLayout;
    EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.SimplifyOneControl'){$ENDIF};
  end;

  //debugln(['TAnchorDockHostSite.SimplifyOneControl END Self="',Caption,'"']);
  //DebugWriteChildAnchors(GetParentForm(Self),true,true);
end;

function TAnchorDockHostSite.GetOneControl: TControl;
var
  i: Integer;
begin
  for i:=0 to ControlCount-1 do begin
    Result:=Controls[i];
    if Result.Owner<>Self then exit;
  end;
  Result:=nil;
end;

function TAnchorDockHostSite.GetSiteCount: integer;
var
  i: Integer;
  Child: TControl;
begin
  Result:=0;
  for i:=0 to ControlCount-1 do begin
    Child:=Controls[i];
    if not (Child is TAnchorDockHostSite) then continue;
    if not Child.IsControlVisible then continue;
    inc(Result);
  end;
end;

function TAnchorDockHostSite.IsOneSiteLayout(out Site: TAnchorDockHostSite
  ): boolean;
var
  i: Integer;
  Child: TControl;
begin
  Site:=nil;
  for i:=0 to ControlCount-1 do begin
    Child:=Controls[i];
    if not (Child is TAnchorDockHostSite) then continue;
    if not Child.IsControlVisible then continue;
    if Site<>nil then exit(false);
    Site:=TAnchorDockHostSite(Child);
  end;
  Result:=Site<>nil;
end;

function TAnchorDockHostSite.IsTwoSiteLayout(out Site1,
  Site2: TAnchorDockHostSite): boolean;
var
  i: Integer;
  Child: TControl;
begin
  Site1:=nil;
  Site2:=nil;
  for i:=0 to ControlCount-1 do begin
    Child:=Controls[i];
    if not (Child is TAnchorDockHostSite) then continue;
    if not Child.IsControlVisible then continue;
    if Site1=nil then
      Site1:=TAnchorDockHostSite(Child)
    else if Site2=nil then
      Site2:=TAnchorDockHostSite(Child)
    else
      exit(false);
  end;
  Result:=Site2<>nil;
end;

function TAnchorDockHostSite.GetUniqueSplitterName: string;
var
  i: Integer;
begin
  i:=0;
  repeat
    inc(i);
    Result:=AnchorDockSplitterName+IntToStr(i);
  until FindComponent(Result)=nil;
end;

function TAnchorDockHostSite.MakeSite(AControl: TControl): TAnchorDockHostSite;
begin
  if AControl is TAnchorDockHostSite then
    Result:=TAnchorDockHostSite(AControl)
  else begin
    Result:=DockMaster.CreateSite;
    try
      AControl.ManualDock(Result,nil,alClient);
    finally
      Result.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}(ADAutoSizingReason){$ENDIF};
    end;
  end;
end;

procedure TAnchorDockHostSite.MoveAllControls(dx, dy: integer);
// move all children, except the sides that are anchored to parent left,top
var
  i: Integer;
  Child: TControl;
  NewBounds: TRect;
begin
  for i:=0 to ControlCount-1 do begin
    Child:=Controls[i];
    NewBounds:=Child.BoundsRect;
    OffsetRect(NewBounds,dx,dy);
    if Child.AnchorSideLeft.Control=Self then
      NewBounds.Left:=0;
    if Child.AnchorSideTop.Control=Self then
      NewBounds.Top:=0;
    Child.BoundsRect:=NewBounds;
  end;
end;

procedure TAnchorDockHostSite.AlignControls(AControl: TControl; var ARect: TRect);
var
  i: Integer;
  Child: TControl;
  Splitter: TAnchorDockSplitter;
begin
  inherited AlignControls(AControl, ARect);
  if csDestroying in ComponentState then exit;

  if DockMaster.ScaleOnResize and (not UpdatingLayout)
  and (not DockMaster.Restoring) then begin
    // scale splitters
    for i:=0 to ControlCount-1 do begin
      Child:=Controls[i];
      if not Child.IsControlVisible then continue;
      if Child is TAnchorDockSplitter then begin
        Splitter:=TAnchorDockSplitter(Child);
        //debugln(['TAnchorDockHostSite.AlignControls ',Caption,' ',DbgSName(Splitter),' OldBounds=',dbgs(Splitter.BoundsRect),' BaseBounds=',dbgs(Splitter.DockBounds),' BaseParentSize=',dbgs(Splitter.DockParentClientSize),' ParentSize=',ClientWidth,'x',ClientHeight]);
        Splitter.SetBoundsPercentually;
        //debugln(['TAnchorDockHostSite.AlignControls ',Caption,' ',DbgSName(Child),' NewBounds=',dbgs(Child.BoundsRect)]);
      end;
    end;
  end;
end;

function TAnchorDockHostSite.CheckIfOneControlHidden: boolean;
var
  Child: TControl;
begin
  Result:=false;
  //debugln(['TAnchorDockHostSite.CheckIfOneControlHidden ',DbgSName(Self),' UpdatingLayout=',UpdatingLayout,' Visible=',Visible,' Parent=',DbgSName(Parent),' csDestroying=',csDestroying in ComponentState,' SiteType=',dbgs(SiteType)]);
  if UpdatingLayout or (not IsControlVisible)
  or (csDestroying in ComponentState)
  or (SiteType<>adhstOneControl)
  then
    exit;
  Child:=GetOneControl;
  if (Child=nil) then exit;
  if Child.IsControlVisible then exit;

  // docked child was hidden/closed
  Result:=true;
  // => undock
  BeginUpdateLayout;
  DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.CheckIfOneControlHidden'){$ENDIF};
  try
    debugln(['TAnchorDockHostSite.CheckIfOneControlHidden ',DbgSName(Self),' UpdatingLayout=',UpdatingLayout,' Visible=',Visible,' Parent=',DbgSName(Parent),' csDestroying=',csDestroying in ComponentState,' SiteType=',dbgs(SiteType),' Child=',DbgSName(Child),' Child.csDestroying=',csDestroying in Child.ComponentState]);
    Visible:=false;
    Parent:=nil;
  finally
    EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.CheckIfOneControlHidden'){$ENDIF};
  end;
  EndUpdateLayout;
  if (not (Child is TCustomForm)) or (csDestroying in Child.ComponentState) then
    Release;
end;

procedure TAnchorDockHostSite.DoDock(NewDockSite: TWinControl; var ARect: TRect);
begin
  inherited DoDock(NewDockSite, ARect);
  if DockMaster <> nil then
    DockMaster.SimplifyPendingLayouts;
end;

procedure TAnchorDockHostSite.SetParent(NewParent: TWinControl);
var
  OldCaption: string;
  OldParent: TWinControl;
begin
  OldParent:=Parent;
  if NewParent=OldParent then exit;
  inherited SetParent(NewParent);
  OldCaption:=Caption;
  UpdateDockCaption;
  if OldCaption<>Caption then begin
    // UpdateDockCaption has not updated parents => do it now
    if Parent is TAnchorDockHostSite then
      TAnchorDockHostSite(Parent).UpdateDockCaption;
    if Parent is TAnchorDockPage then
      TAnchorDockPage(Parent).UpdateDockCaption;
  end;
  UpdateHeaderShowing;

  if (BoundSplitter<>nil) and (BoundSplitter.Parent<>Parent) then begin
    //debugln(['TAnchorDockHostSite.SetParent freeing splitter: ',DbgSName(BoundSplitter)]);
    FreeAndNil(FBoundSplitter);
  end;
  if Parent=nil then
    BorderStyle:=bsSizeable
  else
    BorderStyle:=bsNone;
end;

function TAnchorDockHostSite.HeaderNeedsShowing: boolean;
begin
  Result:=(SiteType<>adhstLayout)
      and (not (Parent is TAnchorDockPage))
      and Assigned(DockMaster) and DockMaster.ShowHeader;
end;

procedure TAnchorDockHostSite.DoClose(var CloseAction: TCloseAction);
begin
  inherited DoClose(CloseAction);
end;

function TAnchorDockHostSite.CanUndock: boolean;
begin
  Result:=Parent<>nil;
end;

procedure TAnchorDockHostSite.Undock;
var
  p: TPoint;
begin
  if Parent=nil then exit;
  DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.Undock'){$ENDIF};
  try
    p := Point(0,0);
    p := ClientToScreen(p);
    Parent:=nil;
    SetBounds(p.x,p.y,Width,Height);
  finally
    EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.Undock'){$ENDIF};
  end;
end;

function TAnchorDockHostSite.CanMerge: boolean;
begin
  Result:=(SiteType=adhstLayout)
      and (Parent is TAnchorDockHostSite)
      and (TAnchorDockHostSite(Parent).SiteType=adhstLayout);
end;

procedure TAnchorDockHostSite.Merge;
{ Move all child controls to parent and delete this site
}
var
  ParentSite: TAnchorDockHostSite;
  i: Integer;
  Child: TControl;
  Side: TAnchorKind;
begin
  ParentSite:=Parent as TAnchorDockHostSite;
  if (SiteType<>adhstLayout) or (ParentSite.SiteType<>adhstLayout) then
    RaiseGDBException('');
  ParentSite.BeginUpdateLayout;
  DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.Merge'){$ENDIF};
  try
    for i := ControlCount - 1 downto 0 do begin
      Child := Controls[i];
      if Child.Owner <> Self then
      begin
        Child.Parent := ParentSite;
        Child.SetBounds(Child.Left + Left, Child.Top + Top, Child.Width, Child.Height);
        for Side := Low(TAnchorKind) to High(TAnchorKind) do
          if Child.AnchorSide[Side].Control = Self then
            Child.AnchorSide[Side].Assign(AnchorSide[Side]);
      end;
    end;
    Parent:=nil;
    DockMaster.NeedFree(Self);
  finally
    ParentSite.EndUpdateLayout;
    // not needed, because this site is freed: EnableAutoSizing;
  end;
end;

function TAnchorDockHostSite.EnlargeSide(Side: TAnchorKind;
  OnlyCheckIfPossible: boolean): boolean;
{
 Shrink one splitter, enlarge the other splitter.

     |#|         |#         |#|         |#
     |#| Control |#         |#|         |#
   --+#+---------+#   --> --+#| Control |#
   ===============#       ===#|         |#
   --------------+#       --+#|         |#
       A         |#        A|#|         |#
   --------------+#       --+#+---------+#
   ==================     ===================

 Move one neighbor splitter, enlarge Control, resize one splitter, rotate the other splitter.

     |#|         |#|          |#|         |#|
     |#| Control |#|          |#|         |#|
   --+#+---------+#+--  --> --+#| Control |#+--
   ===================      ===#|         |#===
   --------+#+--------      --+#|         |#+--
           |#|   B            |#|         |#|B
           |#+--------        |#|         |#+--
       A   |#=========       A|#|         |#===
           |#+--------        |#|         |#+--
           |#|   C            |#|         |#|C
   --------+#+--------      --+#+---------+#+--
   ===================      ===================
}
begin
  Result:=true;
  if EnlargeSideResizeTwoSplitters(Side,ClockwiseAnchor[Side],
                                   OnlyCheckIfPossible) then exit;
  if EnlargeSideResizeTwoSplitters(Side,OppositeAnchor[ClockwiseAnchor[Side]],
                                   OnlyCheckIfPossible) then exit;
  if EnlargeSideRotateSplitter(Side,OnlyCheckIfPossible) then exit;
  Result:=false;
end;

function TAnchorDockHostSite.EnlargeSideResizeTwoSplitters(ShrinkSplitterSide,
  EnlargeSpitterSide: TAnchorKind; OnlyCheckIfPossible: boolean): boolean;
{ Shrink one neighbor control, enlarge Self. Two splitters are resized.

  For example: ShrinkSplitterSide=akBottom, EnlargeSpitterSide=akLeft

    |#|        |#         |#|        |#
    |#|  Self  |#         |#|        |#
  --+#+--------+#   --> --+#|  Self  |#
  ==============#       ===#|        |#
  -------------+#       --+#|        |#
      A        |#        A|#|        |#
  -------------+#       --+#+--------+#
  =================     ==================



}
var
  ParentSite: TAnchorDockHostSite;
  ShrinkSplitter: TAnchorDockSplitter;
  EnlargeSplitter: TAnchorDockSplitter;
  KeptSide: TAnchorKind;
  KeptAnchorControl: TControl;
  Sibling: TControl;
  ShrinkControl: TControl;
  i: Integer;
begin
  Result:=false;
  if not (Parent is TAnchorDockHostSite) then exit;
  ParentSite:=TAnchorDockHostSite(Parent);
  if not OnlyCheckIfPossible then begin
    ParentSite.BeginUpdateLayout;
    ParentSite.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.EnlargeSideResizeTwoSplitters'){$ENDIF};
  end;
  try
    // check ShrinkSplitter
    ShrinkSplitter:=TAnchorDockSplitter(AnchorSide[ShrinkSplitterSide].Control);
    if not (ShrinkSplitter is TAnchorDockSplitter) then exit;
    // check if EnlargeSpitterSide is a neighbor ShrinkSplitterSide
    if (EnlargeSpitterSide<>ClockwiseAnchor[ShrinkSplitterSide])
    and (EnlargeSpitterSide<>OppositeAnchor[ClockwiseAnchor[ShrinkSplitterSide]]) then
      exit;
    // check EnlargeSpitter
    EnlargeSplitter:=TAnchorDockSplitter(AnchorSide[EnlargeSpitterSide].Control);
    if not (EnlargeSplitter is TAnchorDockSplitter) then exit;
    // check if KeptSide is anchored to a splitter or parent
    KeptSide:=OppositeAnchor[EnlargeSpitterSide];
    KeptAnchorControl:=AnchorSide[KeptSide].Control;
    if not ((KeptAnchorControl=ParentSite)
            or (KeptAnchorControl is TAnchorDockSplitter)) then exit;
    // check if ShrinkSplitter is anchored/stops at KeptAnchorControl
    if ShrinkSplitter.AnchorSide[KeptSide].Control<>KeptAnchorControl then exit;

    // check if there is a control to shrink
    ShrinkControl:=nil;
    for i:=0 to ShrinkSplitter.AnchoredControlCount-1 do begin
      Sibling:=ShrinkSplitter.AnchoredControls[i];
      if (Sibling.AnchorSide[OppositeAnchor[ShrinkSplitterSide]].Control=ShrinkSplitter)
      and (Sibling.AnchorSide[KeptSide].Control=KeptAnchorControl) then begin
        ShrinkControl:=Sibling;
        break;
      end;
    end;
    if ShrinkControl=nil then exit;

    if OnlyCheckIfPossible then begin
      // check if ShrinkControl is large enough for shrinking
      case EnlargeSpitterSide of
      akTop: if ShrinkControl.Top>=EnlargeSplitter.Top then exit;
      akLeft: if ShrinkControl.Left>=EnlargeSplitter.Left then exit;
      akRight: if ShrinkControl.Left+ShrinkControl.Width
                      <=EnlargeSplitter.Left+EnlargeSplitter.Width then exit;
      akBottom: if ShrinkControl.Top+ShrinkControl.Height
                      <=EnlargeSplitter.Top+EnlargeSplitter.Height then exit;
      end;
    end else begin
      // do it
      // enlarge the EnlargeSplitter and Self
      AnchorAndChangeBounds(EnlargeSplitter,ShrinkSplitterSide,
                          ShrinkControl.AnchorSide[ShrinkSplitterSide].Control);
      AnchorAndChangeBounds(Self,ShrinkSplitterSide,
                          ShrinkControl.AnchorSide[ShrinkSplitterSide].Control);
      // shrink the ShrinkSplitter and ShrinkControl
      AnchorAndChangeBounds(ShrinkSplitter,KeptSide,EnlargeSplitter);
      AnchorAndChangeBounds(ShrinkControl,KeptSide,EnlargeSplitter);
    end;

  finally
    if not OnlyCheckIfPossible then begin
      ParentSite.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.EnlargeSideResizeTwoSplitters'){$ENDIF};
      ParentSite.EndUpdateLayout;
    end;
  end;
  Result:=true;
end;

function TAnchorDockHostSite.EnlargeSideRotateSplitter(Side: TAnchorKind;
  OnlyCheckIfPossible: boolean): boolean;
{ Shrink splitter at Side, enlarge both neighbor splitters,
  rotate the splitter behind, enlarge Control,
  shrink controls at rotate splitter

     |#|         |#|          |#|         |#|
     |#| Control |#|          |#|         |#|
   --+#+---------+#+--  --> --+#| Control |#+--
   ===================      ===#|         |#===
   --------+#+--------      --+#|         |#+--
           |#|   B            |#|         |#|B
           |#+--------        |#|         |#+--
       A   |#=========       A|#|         |#===
           |#+--------        |#|         |#+--
           |#|   C            |#|         |#|C
   --------+#+--------      --+#+---------+#+--
   ===================      ===================
}
var
  Splitter: TAnchorDockSplitter;
  CWSide: TAnchorKind;
  CWSplitter: TAnchorDockSplitter;
  CCWSide: TAnchorKind;
  i: Integer;
  Sibling: TControl;
  BehindSide: TAnchorKind;
  RotateSplitter: TAnchorDockSplitter;
  CCWSplitter: TAnchorDockSplitter;
begin
  Result:=false;
  // check if there is a splitter at Side
  Splitter:=TAnchorDockSplitter(AnchorSide[Side].Control);
  if not (Splitter is TAnchorDockSplitter) then exit;
  // check if there is a splitter at clockwise Side
  CWSide:=ClockwiseAnchor[Side];
  CWSplitter:=TAnchorDockSplitter(AnchorSide[CWSide].Control);
  if not (CWSplitter is TAnchorDockSplitter) then exit;
  // check if there is a splitter at counter clockwise Side
  CCWSide:=OppositeAnchor[CWSide];
  CCWSplitter:=TAnchorDockSplitter(AnchorSide[CCWSide].Control);
  if not (CCWSplitter is TAnchorDockSplitter) then exit;
  // check if neighbor splitters end at Splitter
  if CWSplitter.AnchorSide[Side].Control<>Splitter then exit;
  if CCWSplitter.AnchorSide[Side].Control<>Splitter then exit;
  // find the rotate splitter behind Splitter
  BehindSide:=OppositeAnchor[Side];
  RotateSplitter:=nil;
  for i:=0 to Splitter.AnchoredControlCount-1 do begin
    Sibling:=Splitter.AnchoredControls[i];
    if Sibling.AnchorSide[BehindSide].Control<>Splitter then continue;
    if not (Sibling is TAnchorDockSplitter) then continue;
    if Side in [akLeft,akRight] then begin
      if Sibling.Top<Top-DockMaster.SplitterWidth then continue;
      if Sibling.Top>Top+Height then continue;
    end else begin
      if Sibling.Left<Left-DockMaster.SplitterWidth then continue;
      if Sibling.Left>Left+Width then continue;
    end;
    if RotateSplitter=nil then
      RotateSplitter:=TAnchorDockSplitter(Sibling)
    else
      // there are multiple splitters behind
      exit;
  end;
  if RotateSplitter=nil then exit;
  // check that all siblings at RotateSplitter are large enough to shrink
  for i:=0 to RotateSplitter.AnchoredControlCount-1 do begin
    Sibling:=RotateSplitter.AnchoredControls[i];
    if Side in [akLeft,akRight] then begin
      if (Sibling.Top>Top-DockMaster.SplitterWidth)
      and (Sibling.Top+Sibling.Height<Top+Height+DockMaster.SplitterWidth) then
        exit;
    end else begin
      if (Sibling.Left>Left-DockMaster.SplitterWidth)
      and (Sibling.Left+Sibling.Width<Left+Width+DockMaster.SplitterWidth) then
        exit;
    end;
  end;
  Result:=true;
  if OnlyCheckIfPossible then exit;

  //debugln(['TAnchorDockHostSite.EnlargeSideRotateSplitter BEFORE Self=',DbgSName(Self),'=',dbgs(BoundsRect),' Side=',dbgs(Side),' CWSide=',dbgs(CWSide),' CWSplitter=',CWSplitter.Name,'=',dbgs(CWSplitter.BoundsRect),' CCWSide=',dbgs(CCWSide),' CCWSplitter=',CCWSplitter.Name,'=',dbgs(CCWSplitter.BoundsRect),' Behind=',dbgs(BehindSide),'=',RotateSplitter.Name,'=',dbgs(RotateSplitter.BoundsRect)]);

  DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.EnlargeSideRotateSplitter'){$ENDIF};
  try
    // enlarge the two neighbor splitters
    AnchorAndChangeBounds(CWSplitter,Side,RotateSplitter.AnchorSide[Side].Control);
    AnchorAndChangeBounds(CCWSplitter,Side,RotateSplitter.AnchorSide[Side].Control);
    // enlarge control
    AnchorAndChangeBounds(Self,Side,RotateSplitter.AnchorSide[Side].Control);
    // shrink the neighbors and anchor them to the enlarge splitters
    for i:=0 to Parent.ControlCount-1 do begin
      Sibling:=Parent.Controls[i];
      if Sibling.AnchorSide[CWSide].Control=RotateSplitter then
        AnchorAndChangeBounds(Sibling,CWSide,CCWSplitter)
      else if Sibling.AnchorSide[CCWSide].Control=RotateSplitter then
        AnchorAndChangeBounds(Sibling,CCWSide,CWSplitter);
    end;
    // rotate the RotateSplitter
    RotateSplitter.AnchorSide[Side].Control:=nil;
    RotateSplitter.AnchorSide[BehindSide].Control:=nil;
    RotateSplitter.ResizeAnchor:=Side;
    AnchorAndChangeBounds(RotateSplitter,CCWSide,Splitter.AnchorSide[CCWSide].Control);
    AnchorAndChangeBounds(RotateSplitter,CWSide,CCWSplitter);
    if Side in [akLeft,akRight] then begin
      RotateSplitter.Left:=Splitter.Left;
      RotateSplitter.Width:=DockMaster.SplitterWidth;
    end else begin
      RotateSplitter.Top:=Splitter.Top;
      RotateSplitter.Height:=DockMaster.SplitterWidth;
    end;
    // shrink Splitter
    AnchorAndChangeBounds(Splitter,CCWSide,CWSplitter);
    // anchor some siblings of Splitter to RotateSplitter
    for i:=0 to Parent.ControlCount-1 do begin
      Sibling:=Parent.Controls[i];
      case Side of
      akLeft: if Sibling.Top<Top then continue;
      akRight: if Sibling.Top>Top then continue;
      akTop: if Sibling.Left>Left then continue;
      akBottom: if Sibling.Left<Left then continue;
      end;
      if Sibling.AnchorSide[BehindSide].Control=Splitter then
        Sibling.AnchorSide[BehindSide].Control:=RotateSplitter
      else if Sibling.AnchorSide[Side].Control=Splitter then
        Sibling.AnchorSide[Side].Control:=RotateSplitter;
    end;
    //debugln(['TAnchorDockHostSite.EnlargeSideRotateSplitter AFTER Self=',DbgSName(Self),'=',dbgs(BoundsRect),' Side=',dbgs(Side),' CWSide=',dbgs(CWSide),' CWSplitter=',CWSplitter.Name,'=',dbgs(CWSplitter.BoundsRect),' CCWSide=',dbgs(CCWSide),' CCWSplitter=',CCWSplitter.Name,'=',dbgs(CCWSplitter.BoundsRect),' Behind=',dbgs(BehindSide),'=',RotateSplitter.Name,'=',dbgs(RotateSplitter.BoundsRect)]);
  finally
    EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.EnlargeSideRotateSplitter'){$ENDIF};
  end;
end;

procedure TAnchorDockHostSite.CreateBoundSplitter;
begin
  if BoundSplitter<>nil then exit;
  FBoundSplitter:=DockMaster.CreateSplitter;
  BoundSplitter.FreeNotification(Self);
  BoundSplitter.Align:=Align;
  BoundSplitter.Parent:=Parent;
end;

procedure TAnchorDockHostSite.PositionBoundSplitter;
begin
  case Align of
  alTop: BoundSplitter.SetBounds(0,Height,Parent.ClientWidth,BoundSplitter.Height);
  alBottom: BoundSplitter.SetBounds(0,Parent.ClientHeight-Height-BoundSplitter.Height,
                                Parent.ClientWidth,BoundSplitter.Height);
  alLeft: BoundSplitter.SetBounds(Width,0,BoundSplitter.Width,Parent.ClientHeight);
  alRight: BoundSplitter.SetBounds(Parent.ClientWidth-Width-BoundSplitter.Width,0
                              ,BoundSplitter.Width,Parent.ClientHeight);
  end;
end;

function TAnchorDockHostSite.CloseQuery: boolean;

  function Check(AControl: TWinControl): boolean;
  var
    i: Integer;
    Child: TControl;
  begin
    for i:=0 to AControl.ControlCount-1 do begin
      Child:=AControl.Controls[i];
      if Child is TWinControl then begin
        if Child is TCustomForm then begin
          if not TCustomForm(Child).CloseQuery then exit(false);
        end else begin
          if not Check(TWinControl(Child)) then exit(false);
        end;
      end;
    end;
    Result:=true;
  end;

begin
  Result:=Check(Self);
end;

function TAnchorDockHostSite.CloseSite: boolean;
var
  AControl: TControl;
  AForm: TCustomForm;
  IsMainForm: Boolean;
  CloseAction: TCloseAction;
  NeedEnableAutoSizing: Boolean;
begin
  Result:=CloseQuery;
  if not Result then exit;

  debugln(['TAnchorDockHostSite.CloseSite ',DbgSName(Self),' SiteType=',dbgs(SiteType)]);
  case SiteType of
  adhstNone:
    begin
      Release;
      exit;
    end;
  adhstOneControl:
    begin
      DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.CloseSite'){$ENDIF};
      NeedEnableAutoSizing:=true;
      try
        AControl:=GetOneControl;
        if AControl is TCustomForm then begin
          AForm:=TCustomForm(AControl);
          IsMainForm := (Application.MainForm = AForm)
                        or (AForm.IsParentOf(Application.MainForm));
          if IsMainForm then
            CloseAction := caFree
          else
            CloseAction := caHide;
          // ToDo: TCustomForm(AControl).DoClose(CloseAction);
          case CloseAction of
          caHide: Hide;
          caMinimize: WindowState := wsMinimized;
          caFree:
            begin
              // if form is MainForm, then terminate the application
              // the owner of the MainForm is the application,
              // so the Application will take care of free-ing the form
              // and Release is not necessary
              if IsMainForm then
                Application.Terminate
              else begin
                NeedEnableAutoSizing:=false;
                Release;
                AForm.Release;
                exit;
              end;
            end;
          end;
        end else begin
          AControl.Visible:=false;
          NeedEnableAutoSizing:=false;
          Release;
          exit;
        end;
        Visible:=false;
        Parent:=nil;
      finally
        if NeedEnableAutoSizing then
          EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.CloseSite'){$ENDIF};
      end;
    end;
  end;
end;

procedure TAnchorDockHostSite.RemoveControl(AControl: TControl);
begin
  //debugln(['TAnchorDockHostSite.RemoveControl ',DbgSName(Self),'=',Caption,' ',DbgSName(AControl)]);
  DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.RemoveControl'){$ENDIF};
  try
    inherited RemoveControl(AControl);
    if not (csDestroying in ComponentState) then begin
      if (not ((AControl is TAnchorDockHeader)
               or (AControl is TAnchorDockSplitter)))
      then begin
        //debugln(['TAnchorDockHostSite.RemoveControl START ',Caption,' ',dbgs(SiteType),' ',DbgSName(AControl),' UpdatingLayout=',UpdatingLayout]);
        if (SiteType=adhstLayout) then
          RemoveControlFromLayout(AControl)
        else
          DockMaster.NeedSimplify(Self);
        UpdateDockCaption;
        //debugln(['TAnchorDockHostSite.RemoveControl END ',Caption,' ',dbgs(SiteType),' ',DbgSName(AControl)]);
      end;
    end;
  finally
    EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.RemoveControl'){$ENDIF};
  end;
end;

procedure TAnchorDockHostSite.InsertControl(AControl: TControl; Index: integer);
begin
  DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.InsertControl'){$ENDIF};
  try
    inherited InsertControl(AControl, Index);
    if not ((AControl is TAnchorDockSplitter)
            or (AControl is TAnchorDockHeader))
    then
      UpdateDockCaption;
  finally
    EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.InsertControl'){$ENDIF};
  end;
end;

procedure TAnchorDockHostSite.UpdateDockCaption(Exclude: TControl);
var
  i: Integer;
  Child: TControl;
  NewCaption, OldCaption: String;
begin
  if csDestroying in ComponentState then exit;
  NewCaption:='';
  for i:=0 to ControlCount-1 do begin
    Child:=Controls[i];
    if Child=Exclude then continue;
    if (Child.HostDockSite=Self) or (Child is TAnchorDockHostSite)
    or (Child is TAnchorDockPageControl) then begin
      if NewCaption<>'' then
        NewCaption:=NewCaption+',';
      NewCaption:=NewCaption+Child.Caption;
    end;
  end;
  OldCaption:=Caption;
  Caption:=NewCaption;
  //debugln(['TAnchorDockHostSite.UpdateDockCaption Caption="',Caption,'" NewCaption="',NewCaption,'" HasParent=',Parent<>nil,' ',DbgSName(Header)]);
  if ((Parent=nil) and DockMaster.HideHeaderCaptionFloatingControl)
  or (not DockMaster.ShowHeaderCaption) then
    Header.Caption:=''
  else
    Header.Caption:=Caption;
  if OldCaption<>Caption then begin
    //debugln(['TAnchorDockHostSite.UpdateDockCaption Caption="',Caption,'" NewCaption="',NewCaption,'" HasParent=',Parent<>nil]);
    if Parent is TAnchorDockHostSite then
      TAnchorDockHostSite(Parent).UpdateDockCaption;
    if Parent is TAnchorDockPage then
      TAnchorDockPage(Parent).UpdateDockCaption;
  end;

  // do not show close button for mainform
  Header.CloseButton.Visible:=not IsParentOf(Application.MainForm);
end;

procedure TAnchorDockHostSite.GetSiteInfo(Client: TControl;
  var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean);
var
  ADockMargin: LongInt;
begin
  GetWindowRect(Handle, InfluenceRect);

  if (Parent=nil) or DockMaster.IsCustomSite(Parent) then begin
    // allow docking outside => enlarge margins
    ADockMargin:=DockMaster.DockOutsideMargin;
    //debugln(['TAnchorDockHostSite.GetSiteInfo ',DbgSName(Self),' allow outside ADockMargin=',ADockMargin,' ',dbgs(InfluenceRect)]);
    InfluenceRect.Left := InfluenceRect.Left-ADockMargin;
    InfluenceRect.Top := InfluenceRect.Top-ADockMargin;
    InfluenceRect.Right := InfluenceRect.Right+ADockMargin;
    InfluenceRect.Bottom := InfluenceRect.Bottom+ADockMargin;
  end else if Parent is TAnchorDockHostSite then begin
    // do not cover parent site => shrink margins
    ADockMargin:=DockMaster.DockParentMargin;
    ADockMargin:=Min(ADockMargin,Min(ClientWidth,ClientHeight) div 10);
    ADockMargin:=Max(0,ADockMargin);
    //debugln(['TAnchorDockHostSite.GetSiteInfo ',DbgSName(Self),' do not cover parent ADockMargin=',ADockMargin,' ',dbgs(InfluenceRect)]);
    InfluenceRect.Left := InfluenceRect.Left+ADockMargin;
    InfluenceRect.Top := InfluenceRect.Top+ADockMargin;
    InfluenceRect.Right := InfluenceRect.Right-ADockMargin;
    InfluenceRect.Bottom := InfluenceRect.Bottom-ADockMargin;
  end;

  CanDock:=(Client is TAnchorDockHostSite)
           and not DockMaster.AutoFreedIfControlIsRemoved(Self,Client);
  //debugln(['TAnchorDockHostSite.GetSiteInfo ',DbgSName(Self),' ',dbgs(BoundsRect),' ',Caption,' CanDock=',CanDock,' PtIn=',PtInRect(InfluenceRect,MousePos)]);

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

function TAnchorDockHostSite.GetPageArea: TRect;
begin
  Result:=Rect(0,0,Width*DockMaster.PageAreaInPercent div 100,
               Height*DockMaster.PageAreaInPercent div 100);
  OffsetRect(Result,(Width*(100-DockMaster.PageAreaInPercent)) div 200,
                    (Height*(100-DockMaster.PageAreaInPercent)) div 200);
end;

procedure TAnchorDockHostSite.ChangeBounds(ALeft, ATop, AWidth,
  AHeight: integer; KeepBase: boolean);
begin
  inherited ChangeBounds(ALeft, ATop, AWidth, AHeight, KeepBase);
  if Header<>nil then UpdateHeaderAlign;
end;

procedure TAnchorDockHostSite.UpdateHeaderAlign;
begin
  if Header=nil then exit;
  case Header.HeaderPosition of
  adlhpAuto:
    if Header.Align in [alLeft,alRight] then begin
      if (ClientHeight>0)
      and ((ClientWidth*100 div ClientHeight)<=DockMaster.HeaderAlignTop) then
        Header.Align:=alTop;
    end else begin
      if (ClientHeight>0)
      and ((ClientWidth*100 div ClientHeight)>=DockMaster.HeaderAlignLeft) then
      begin
        if Application.BidiMode=bdRightToLeft then
          Header.Align:=alRight
        else
          Header.Align:=alLeft;
      end;
    end;
  adlhpLeft: Header.Align:=alLeft;
  adlhpTop: Header.Align:=alTop;
  adlhpRight: Header.Align:=alRight;
  adlhpBottom: Header.Align:=alBottom;
  end;
end;

procedure TAnchorDockHostSite.UpdateHeaderShowing;
begin
  if Header=nil then exit;
  if HeaderNeedsShowing then
    Header.Parent:=Self
  else
    Header.Parent:=nil;
end;

procedure TAnchorDockHostSite.BeginUpdateLayout;
begin
  inc(fUpdateLayout);
  if fUpdateLayout=1 then DockMaster.BeginUpdate;
end;

procedure TAnchorDockHostSite.EndUpdateLayout;
begin
  if fUpdateLayout=0 then RaiseGDBException('TAnchorDockHostSite.EndUpdateLayout');
  dec(fUpdateLayout);
  if fUpdateLayout=0 then
    DockMaster.EndUpdate;
end;

function TAnchorDockHostSite.UpdatingLayout: boolean;
begin
  Result:=(fUpdateLayout>0) or (csDestroying in ComponentState);
end;

procedure TAnchorDockHostSite.SaveLayout(
  LayoutTree: TAnchorDockLayoutTree; LayoutNode: TAnchorDockLayoutTreeNode);
var
  i: Integer;
  Site: TAnchorDockHostSite;
  ChildNode: TAnchorDockLayoutTreeNode;
  Child: TControl;
  Splitter: TAnchorDockSplitter;
  OneControl: TControl;
begin
  if SiteType=adhstOneControl then
    OneControl:=GetOneControl
  else
    OneControl:=nil;
  if (SiteType=adhstOneControl) and (OneControl<>nil)
  and (not (OneControl is TAnchorDockHostSite)) then begin
    LayoutNode.NodeType:=adltnControl;
    LayoutNode.Assign(Self);
    LayoutNode.Name:=OneControl.Name;
    LayoutNode.HeaderPosition:=Header.HeaderPosition;
  end else if (SiteType in [adhstLayout,adhstOneControl]) then begin
    LayoutNode.NodeType:=adltnLayout;
    for i:=0 to ControlCount-1 do begin
      Child:=Controls[i];
      if Child.Owner=Self then continue;
      if (Child is TAnchorDockHostSite) then begin
        Site:=TAnchorDockHostSite(Child);
        ChildNode:=LayoutTree.NewNode(LayoutNode);
        Site.SaveLayout(LayoutTree,ChildNode);
      end else if (Child is TAnchorDockSplitter) then begin
        Splitter:=TAnchorDockSplitter(Child);
        ChildNode:=LayoutTree.NewNode(LayoutNode);
        Splitter.SaveLayout(ChildNode);
      end;
    end;
    LayoutNode.Assign(Self);
    LayoutNode.HeaderPosition:=Header.HeaderPosition;
  end else if SiteType=adhstPages then begin
    LayoutNode.NodeType:=adltnPages;
    for i:=0 to Pages.PageCount-1 do begin
      Site:=Pages.DockPages[i].GetSite;
      if Site<>nil then begin
        ChildNode:=LayoutTree.NewNode(LayoutNode);
        Site.SaveLayout(LayoutTree,ChildNode);
      end;
    end;
    LayoutNode.Assign(Self);
    LayoutNode.HeaderPosition:=Header.HeaderPosition;
  end else
    LayoutNode.NodeType:=adltnNone;
  if BoundSplitter<>nil then begin
    if Align in [alLeft,alRight] then
      LayoutNode.BoundSplitterPos:=BoundSplitter.Left
    else
      LayoutNode.BoundSplitterPos:=BoundSplitter.Top;
  end;
end;

constructor TAnchorDockHostSite.CreateNew(AOwner: TComponent; Num: Integer);
begin
  inherited CreateNew(AOwner,Num);
  Visible:=false;
  FHeaderSide:=akTop;
  FHeader:=DockMaster.HeaderClass.Create(Self);
  FHeader.Align:=alTop;
  FHeader.Parent:=Self;
  FSiteType:=adhstNone;
  UpdateHeaderAlign;
  DragKind:=dkDock;
  DockManager:=DockMaster.ManagerClass.Create(Self);
  UseDockManager:=true;
  DragManager.RegisterDockSite(Self,true);
end;

destructor TAnchorDockHostSite.Destroy;
//var i: Integer;
begin
  //debugln(['TAnchorDockHostSite.Destroy ',DbgSName(Self),' Caption="',Caption,'" Self=',dbgs(Pointer(Self)),' ComponentCount=',ComponentCount,' ControlCount=',ControlCount]);
  {for i:=0 to ComponentCount-1 do
    debugln(['TAnchorDockHostSite.Destroy Component ',i,'/',ComponentCount,' ',DbgSName(Components[i])]);
  for i:=0 to ControlCount-1 do
    debugln(['TAnchorDockHostSite.Destroy Control ',i,'/',ControlCount,' ',DbgSName(Controls[i])]);}
  FreePages;
  inherited Destroy;
end;

{ TAnchorDockHeader }

procedure TAnchorDockHeader.PopupMenuPopup(Sender: TObject);
var
  HeaderPosItem: TMenuItem;
  ParentSite: TAnchorDockHostSite;
  Side: TAnchorKind;
  SideCaptions: array[TAnchorKind] of string;
  Item: TMenuItem;
  ContainsMainForm: boolean;
  s: String;
begin
  ParentSite:=TAnchorDockHostSite(Parent);
  SideCaptions[akLeft]:=adrsLeft;
  SideCaptions[akTop]:=adrsTop;
  SideCaptions[akRight]:=adrsRight;
  SideCaptions[akBottom]:=adrsBottom;

  // menu items: undock, merge
  DockMaster.AddRemovePopupMenuItem(ParentSite.CanUndock,'UndockMenuItem',
                                    adrsUndock,@UndockButtonClick);
  DockMaster.AddRemovePopupMenuItem(ParentSite.CanMerge,'MergeMenuItem',
                                    adrsMerge, @MergeButtonClick);

  // menu items: header position
  HeaderPosItem:=DockMaster.AddPopupMenuItem('HeaderPosMenuItem',
                                             adrsHeaderPosition, nil);
  Item:=DockMaster.AddPopupMenuItem('HeaderPosAutoMenuItem', adrsAutomatically,
                   @HeaderPositionItemClick, HeaderPosItem);
  if Item<>nil then begin
    Item.Tag:=ord(adlhpAuto);
    Item.Checked:=HeaderPosition=TADLHeaderPosition(Item.Tag);
  end;
  for Side:=Low(TAnchorKind) to High(TAnchorKind) do begin
    Item:=DockMaster.AddPopupMenuItem('HeaderPos'+DbgS(Side)+'MenuItem',
                     SideCaptions[Side], @HeaderPositionItemClick,
                     HeaderPosItem);
    if Item=nil then continue;
    Item.Tag:=ord(Side)+1;
    Item.Checked:=HeaderPosition=TADLHeaderPosition(Item.Tag);
  end;

  // menu items: enlarge
  for Side:=Low(TAnchorKind) to High(TAnchorKind) do begin
    Item:=DockMaster.AddRemovePopupMenuItem(ParentSite.EnlargeSide(Side,true),
      'Enlarge'+DbgS(Side)+'MenuItem', Format(adrsEnlargeSide, [
        SideCaptions[Side]]),@EnlargeSideClick);
    if Item<>nil then Item.Tag:=ord(Side);
  end;

  // menu item: close or quit
  ContainsMainForm:=ParentSite.IsParentOf(Application.MainForm);
  if ContainsMainForm then
    s:=Format(adrsQuit, [Application.Title])
  else
    s:=adrsClose;
  DockMaster.AddRemovePopupMenuItem(CloseButton.Visible,'CloseMenuItem',s,
                                    @CloseButtonClick);
end;

procedure TAnchorDockHeader.CloseButtonClick(Sender: TObject);
begin
  if Parent is TAnchorDockHostSite then begin
    DockMaster.RestoreLayouts.Add(DockMaster.CreateRestoreLayout(Parent),true);
    TAnchorDockHostSite(Parent).CloseSite;
  end;
end;

procedure TAnchorDockHeader.HeaderPositionItemClick(Sender: TObject);
var
  Item: TMenuItem;
begin
  if not (Sender is TMenuItem) then exit;
  Item:=TMenuItem(Sender);
  HeaderPosition:=TADLHeaderPosition(Item.Tag);
end;

procedure TAnchorDockHeader.UndockButtonClick(Sender: TObject);
begin
  TAnchorDockHostSite(Parent).Undock;
end;

procedure TAnchorDockHeader.MergeButtonClick(Sender: TObject);
begin
  TAnchorDockHostSite(Parent).Merge;
end;

procedure TAnchorDockHeader.EnlargeSideClick(Sender: TObject);
var
  Side: TAnchorKind;
begin
  if not (Sender is TMenuItem) then exit;
  Side:=TAnchorKind(TMenuItem(Sender).Tag);
  TAnchorDockHostSite(Parent).EnlargeSide(Side,false);
end;

procedure TAnchorDockHeader.SetHeaderPosition(const AValue: TADLHeaderPosition);
begin
  if FHeaderPosition=AValue then exit;
  FHeaderPosition:=AValue;
  if Parent is TAnchorDockHostSite then
    TAnchorDockHostSite(Parent).UpdateHeaderAlign;
end;

procedure TAnchorDockHeader.Paint;
var
  r: TRect;
  TxtH: longint;
  TxtW: longint;
  dx,dy: Integer;
begin
  r:=ClientRect;
  Canvas.Brush.Color := clForm;
  if DockMaster.HeaderFilled then
     Canvas.FillRect(r);
  if not DockMaster.HeaderFlatten then
     Canvas.Frame3d(r,1,bvRaised);
  {case DockMaster.HeaderStyle of
  adhsPoints: Canvas.Brush.Color := clForm;
  else Canvas.Frame3d(r,1,bvRaised);
  end;
  Canvas.FillRect(r);}

  if CloseButton.IsControlVisible and (CloseButton.Parent=Self) then begin
    if Align in [alLeft,alRight] then
      r.Top:=CloseButton.Top+CloseButton.Height+1
    else
      r.Right:=CloseButton.Left-1;
  end;

  // caption
  if Caption<>'' then begin
    Canvas.Brush.Color:=clNone;
    Canvas.Brush.Style:=bsClear;
    TxtH:=Canvas.TextHeight('ABCMgq');
    TxtW:=Canvas.TextWidth(Caption);
    if Align in [alLeft,alRight] then begin
      // vertical
      dx:=Max(0,(r.Right-r.Left-TxtH) div 2);
      {$IFDEF LCLWin32}
      dec(dx,2);
      {$ENDIF}
      dy:=Max(0,(r.Bottom-r.Top-TxtW) div 2);
      Canvas.Font.Orientation:=900;
      if TxtW<(r.Bottom-r.Top)then
      begin
        // text fits
        Canvas.TextOut(r.Left+dx-1,r.Bottom-dy,Caption);
        DrawADHeader(Canvas,DockMaster.HeaderStyle,Rect(r.Left,r.Top,r.Right,r.Bottom-dy-TxtW-1),false);
        DrawADHeader(Canvas,DockMaster.HeaderStyle,Rect(r.Left,r.Bottom-dy+1,r.Right,r.Bottom),false);
      end else begin
        // text does not fit
        DrawADHeader(Canvas,DockMaster.HeaderStyle,r,false);
      end;
    end else begin
      // horizontal
      dx:=Max(0,(r.Right-r.Left-TxtW) div 2);
      dy:=Max(0,(r.Bottom-r.Top-TxtH) div 2);
      Canvas.Font.Orientation:=0;
      if TxtW<(r.right-r.Left)then
      begin
        // text fits
        Canvas.TextRect(r,dx+2,dy,Caption);
        DrawADHeader(Canvas,DockMaster.HeaderStyle,Rect(r.Left,r.Top,r.Left+dx-1,r.Bottom),true);
        DrawADHeader(Canvas,DockMaster.HeaderStyle,Rect(r.Left+dx+TxtW+2,r.Top,r.Right,r.Bottom),true);
      end else begin
        // text does not fit
        DrawADHeader(Canvas,DockMaster.HeaderStyle,r,true);
      end;
    end;
  end else begin
    if Align in [alLeft,alRight] then
      DrawADHeader(Canvas,DockMaster.HeaderStyle,r,false)
    else
      DrawADHeader(Canvas,DockMaster.HeaderStyle,r,true);
  end;
end;

procedure TAnchorDockHeader.CalculatePreferredSize(var PreferredWidth,
  PreferredHeight: integer; WithThemeSpace: Boolean);
const
  TestTxt = 'ABCXYZ123gqj';
var
  DC: HDC;
  R: TRect;
  OldFont: HGDIOBJ;
  Flags: cardinal;
  NeededHeight: Integer;
begin
  inherited CalculatePreferredSize(PreferredWidth,PreferredHeight,WithThemeSpace);
  if Caption<>'' then begin
    DC := GetDC(Parent.Handle);
    try
      R := Rect(0, 0, 10000, 10000);
      OldFont := SelectObject(DC, HGDIOBJ(Font.Reference.Handle));
      Flags := DT_CALCRECT or DT_EXPANDTABS or DT_SINGLELINE or DT_NOPREFIX;

      DrawText(DC, PChar(TestTxt), Length(TestTxt), R, Flags);
      SelectObject(DC, OldFont);
      NeededHeight := R.Bottom - R.Top + BevelWidth*2;
    finally
      ReleaseDC(Parent.Handle, DC);
    end;
    if Align in [alLeft,alRight] then begin
      PreferredWidth:=Max(NeededHeight,PreferredWidth);
    end else begin
      PreferredHeight:=Max(NeededHeight,PreferredHeight);
    end;
  end;
end;

procedure TAnchorDockHeader.MouseDown(Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
begin
  inherited MouseDown(Button, Shift, X, Y);
  if (Button=mbLeft) and DockMaster.AllowDragging then
    DragManager.DragStart(Parent,false,DockMaster.DragTreshold);
end;

procedure TAnchorDockHeader.UpdateHeaderControls;
begin
  if Align in [alLeft,alRight] then begin
    if CloseButton<>nil then
      CloseButton.Align:=alTop;
  end else begin
    if CloseButton<>nil then
      CloseButton.Align:=alRight;
  end;
  //debugln(['TAnchorDockHeader.UpdateHeaderControls ',dbgs(Align),' ',dbgs(CloseButton.Align)]);
end;

procedure TAnchorDockHeader.SetAlign(Value: TAlign);
begin
  if Value=Align then exit;
  DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.SetAlign'){$ENDIF};
  try
    inherited SetAlign(Value);
    UpdateHeaderControls;
  finally
    EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockHostSite.SetAlign'){$ENDIF};
  end;
end;

procedure TAnchorDockHeader.DoOnShowHint(HintInfo: PHintInfo);
var
  s: String;
  p: LongInt;
  c: String;
begin
  s:=DockMaster.GetLocalizedHeaderHint;
  p:=Pos('%s',s);
  if p>0 then begin
    if Parent<>nil then
      c:=Parent.Caption
    else
      c:='';
    s:=Format(s,[c]);
  end;
  //debugln(['TAnchorDockHeader.DoOnShowHint "',s,'" "',DockMaster.HeaderHint,'"']);
  HintInfo^.HintStr:=s;
  inherited DoOnShowHint(HintInfo);
end;

constructor TAnchorDockHeader.Create(TheOwner: TComponent);
begin
  inherited Create(TheOwner);
  FHeaderPosition:=adlhpAuto;
  FCloseButton:=TAnchorDockCloseButton.Create(Self);
  BevelOuter:=bvNone;
  BorderWidth:=0;
  with FCloseButton do begin
    Name:='CloseButton';
    Parent:=Self;
    Flat:=true;
    ShowHint:=true;
    Hint:=adrsClose;
    OnClick:=@CloseButtonClick;
    AutoSize:=true;
  end;
  Align:=alTop;
  AutoSize:=true;
  ShowHint:=true;
  PopupMenu:=DockMaster.GetPopupMenu;
end;

{ TAnchorDockCloseButton }

function TAnchorDockCloseButton.GetDrawDetails: TThemedElementDetails;

function WindowPart: TThemedWindow;
  begin
    // no check states available
    Result := twSmallCloseButtonNormal;
    if not IsEnabled then
      Result := twSmallCloseButtonDisabled
    else
    if FState in [bsDown, bsExclusive] then
      Result := twSmallCloseButtonPushed
    else
    if FState = bsHot then
      Result := twSmallCloseButtonHot
    else
      Result := twSmallCloseButtonNormal;
  end;

begin
  Result := ThemeServices.GetElementDetails(WindowPart);
end;

procedure TAnchorDockCloseButton.CalculatePreferredSize(var PreferredWidth,
  PreferredHeight: integer; WithThemeSpace: Boolean);
begin
  with ThemeServices.GetDetailSize(ThemeServices.GetElementDetails(twSmallCloseButtonNormal)) do
  begin
    PreferredWidth:=cx;
    PreferredHeight:=cy;
    {$IF defined(LCLGtk2) or defined(Carbon)}
    inc(PreferredWidth,2);
    inc(PreferredHeight,2);
    {$ENDIF}
  end;
end;

{ TAnchorDockManager }

procedure TAnchorDockManager.SetPreferredSiteSizeAsSiteMinimum(
  const AValue: boolean);
begin
  if FPreferredSiteSizeAsSiteMinimum=AValue then exit;
  FPreferredSiteSizeAsSiteMinimum:=AValue;
  if DockSite=nil then
    Site.AdjustSize;
end;

constructor TAnchorDockManager.Create(ADockSite: TWinControl);
begin
  inherited Create(ADockSite);
  FSite:=ADockSite;
  FDockableSites:=[akLeft,akTop,akBottom,akRight];
  FInsideDockingAllowed:=true;
  FPreferredSiteSizeAsSiteMinimum:=true;
  if (ADockSite is TAnchorDockHostSite) then
    FDockSite:=TAnchorDockHostSite(ADockSite);
end;

procedure TAnchorDockManager.GetControlBounds(Control: TControl; out
  AControlBounds: TRect);
begin
  if Control=nil then ;
  AControlBounds:=Rect(0,0,0,0);
  //debugln(['TAnchorDockManager.GetControlBounds DockSite="',DockSite.Caption,'" Control=',DbgSName(Control)]);
end;

procedure TAnchorDockManager.InsertControl(Control: TControl; InsertAt: TAlign;
  DropCtl: TControl);
begin
  if Control=nil then;
  if InsertAt=alNone then ;
  if DropCtl=nil then ;
end;

procedure TAnchorDockManager.InsertControl(ADockObject: TDragDockObject);
var
  NewSiteBounds: TRect;
  NewChildBounds: TRect;
  Child: TControl;
  ChildSite: TAnchorDockHostSite;
  SplitterWidth: Integer;
begin
  if DockSite<>nil then begin
    // handled by TAnchorDockHostSite
    //debugln(['TAnchorDockManager.InsertControl DockSite="',DockSite.Caption,'" Control=',DbgSName(ADockObject.Control),' InsertAt=',dbgs(ADockObject.DropAlign)])
  end else begin
    debugln(['TAnchorDockManager.InsertControl DockSite=nil Site="',DbgSName(Site),'" Control=',DbgSName(ADockObject.Control),' InsertAt=',dbgs(ADockObject.DropAlign),' Site.Bounds=',dbgs(Site.BoundsRect),' Control.Client=',dbgs(ADockObject.Control.ClientRect),' Parent=',DbgSName(ADockObject.Control.Parent)]);
    Site.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockManager.InsertControl'){$ENDIF};
    try
      // align dragged Control
      Child:=ADockObject.Control;
      Child.Parent:=Site;
      Child.Align:=ADockObject.DropAlign;
      Child.Width:=ADockObject.DockRect.Right-ADockObject.DockRect.Left;
      Child.Height:=ADockObject.DockRect.Bottom-ADockObject.DockRect.Top;

      SplitterWidth:=0;
      ChildSite:=nil;
      if Child is TAnchorDockHostSite then begin
        ChildSite:=TAnchorDockHostSite(Child);
        ChildSite.CreateBoundSplitter;
        SplitterWidth:=DockMaster.SplitterWidth;
      end;

      // resize Site
      NewSiteBounds:=Site.BoundsRect;
      case ADockObject.DropAlign of
      alLeft: dec(NewSiteBounds.Left,Child.ClientWidth+SplitterWidth);
      alRight: dec(NewSiteBounds.Right,Child.ClientWidth+SplitterWidth);
      alTop: dec(NewSiteBounds.Top,Child.ClientHeight+SplitterWidth);
      alBottom: inc(NewSiteBounds.Bottom,Child.ClientHeight+SplitterWidth);
      end;
      if not StoredConstraintsValid then
        StoreConstraints;
      if ADockObject.DropAlign in [alLeft,alRight] then
        Site.Constraints.MaxWidth:=0
      else
        Site.Constraints.MaxHeight:=0;
      Site.BoundsRect:=NewSiteBounds;
      //debugln(['TAnchorDockManager.InsertControl Site.BoundsRect=',dbgs(Site.BoundsRect),' NewSiteBounds=',dbgs(NewSiteBounds),' Child.ClientRect=',dbgs(Child.ClientRect)]);
      FSiteClientRect:=Site.ClientRect;

      // resize child
      NewChildBounds:=Child.BoundsRect;
      case ADockObject.DropAlign of
      alTop: NewChildBounds:=Bounds(0,0,Site.ClientWidth,Child.ClientHeight);
      alBottom: NewChildBounds:=Bounds(0,Site.ClientHeight-Child.ClientHeight,
                                       Site.ClientWidth,Child.ClientHeight);
      alLeft: NewChildBounds:=Bounds(0,0,Child.ClientWidth,Site.ClientHeight);
      alRight: NewChildBounds:=Bounds(Site.ClientWidth-Child.ClientWidth,0,
                                      Child.ClientWidth,Site.ClientHeight);
      end;
      Child.BoundsRect:=NewChildBounds;

      if ChildSite<>nil then
        ChildSite.PositionBoundSplitter;

      // only allow to dock one control
      DragManager.RegisterDockSite(Site,false);
      debugln(['TAnchorDockManager.InsertControl AFTER Site="',DbgSName(Site),'" Control=',DbgSName(ADockObject.Control),' InsertAt=',dbgs(ADockObject.DropAlign),' Site.Bounds=',dbgs(Site.BoundsRect),' Control.ClientRect=',dbgs(ADockObject.Control.ClientRect)]);

    finally
      Site.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockManager.InsertControl'){$ENDIF};
    end;
  end;
end;

procedure TAnchorDockManager.LoadFromStream(Stream: TStream);
begin
  debugln(['TAnchorDockManager.LoadFromStream not implemented Site="',DbgSName(Site),'"']);
  if Stream=nil then ;
end;

procedure TAnchorDockManager.PositionDockRect(Client, DropCtl: TControl;
  DropAlign: TAlign; var DockRect: TRect);
{ Client = dragged source site (a TAnchorDockHostSite)
  DropCtl is target control (the DockSite, DockSite.Pages or one of the pages)
  DropAlign: where on Client DropCtl should be placed
  DockRect: the estimated new bounds of DropCtl
}
var
  Offset: TPoint;
  Inside: Boolean;
begin
  if (DropAlign=alClient) and (DockSite<>nil) and (DockSite.Pages<>nil) then begin
    // dock into pages
    if DropCtl=DockSite.Pages then begin
      // dock as last page
      DockRect:=DockSite.Pages.TabRect(DockSite.Pages.PageCount-1);
      case DockSite.Pages.TabPosition of
      tpTop,tpBottom: DockRect.Left:=(DockRect.Left+DockRect.Right) div 2;
      tpLeft,tpRight: DockRect.Top:=(DockRect.Top+DockRect.Bottom) div 2;
      end;
      Offset:=DockSite.Pages.ClientOrigin;
      OffsetRect(DockRect,Offset.X,Offset.Y);
      exit;
    end else if DropCtl is TAnchorDockPage then begin
      // dock in front of page
      DockRect:=DockSite.Pages.TabRect(TAnchorDockPage(DropCtl).PageIndex);
      case DockSite.Pages.TabPosition of
      tpTop,tpBottom: DockRect.Right:=(DockRect.Left+DockRect.Right) div 2;
      tpLeft,tpRight: DockRect.Bottom:=(DockRect.Top+DockRect.Bottom) div 2;
      end;
      Offset:=DockSite.Pages.ClientOrigin;
      OffsetRect(DockRect,Offset.X,Offset.Y);
      exit;
    end;
  end;

  Inside:=(DropCtl=Site);
  if (not Inside) and (Site.Parent<>nil) then begin
    if (Site.Parent is TAnchorDockHostSite)
    or (not (Site.Parent.DockManager is TAnchorDockManager))
    or (Site.Parent.Parent<>nil) then
      Inside:=true;
  end;
  case DropAlign of
  alLeft:
    if Inside then
      DockRect:=Rect(0,0,Min(Client.Width,Site.ClientWidth div 2),Site.ClientHeight)
    else
      DockRect:=Rect(-Client.Width,0,0,Site.ClientHeight);
  alRight:
    if Inside then begin
      DockRect:=Rect(0,0,Min(Client.Width,Site.Width div 2),Site.ClientHeight);
      OffsetRect(DockRect,Site.ClientWidth-DockRect.Right,0);
    end else
      DockRect:=Bounds(Site.ClientWidth,0,Client.Width,Site.ClientHeight);
  alTop:
    if Inside then
      DockRect:=Rect(0,0,Site.ClientWidth,Min(Client.Height,Site.ClientHeight div 2))
    else
      DockRect:=Rect(0,-Client.Height,Site.ClientWidth,0);
  alBottom:
    if Inside then begin
      DockRect:=Rect(0,0,Site.ClientWidth,Min(Client.Height,Site.ClientHeight div 2));
      OffsetRect(DockRect,0,Site.ClientHeight-DockRect.Bottom);
    end else
      DockRect:=Bounds(0,Site.ClientHeight,Site.ClientWidth,Client.Height);
  alClient:
    begin
      // paged docking => show center
      if DockSite<>nil then
        DockRect:=DockSite.GetPageArea;
    end;
  else
    exit; // use default
  end;
  Offset:=Site.ClientOrigin;
  OffsetRect(DockRect,Offset.X,Offset.Y);
end;

procedure TAnchorDockManager.RemoveControl(Control: TControl);
var
  NewBounds: TRect;
  ChildSite: TAnchorDockHostSite;
  SplitterWidth: Integer;
begin
  if DockSite<>nil then
    {$IFDEF VerboseAnchorDocking}
    debugln(['TAnchorDockManager.RemoveControl DockSite="',DockSite.Caption,'" Control=',DbgSName(Control)])
    {$ENDIF}
  else begin
    {$IFDEF VerboseAnchorDocking}
    debugln(['TAnchorDockManager.RemoveControl Site="',DbgSName(Site),'" Control=',DbgSName(Control)]);
    {$ENDIF}
    if Control is TAnchorDockHostSite then begin
      SplitterWidth:=0;
      if Control is TAnchorDockHostSite then begin
        ChildSite:=TAnchorDockHostSite(Control);
        if ChildSite.BoundSplitter<>nil then
          SplitterWidth:=DockMaster.SplitterWidth;
      end;

      // shrink Site
      NewBounds:=Site.BoundsRect;
      case Control.Align of
      alTop: inc(NewBounds.Top,Control.Height+SplitterWidth);
      alBottom: dec(NewBounds.Bottom,Control.Height+SplitterWidth);
      alLeft: inc(NewBounds.Left,Control.Width+SplitterWidth);
      alRight: dec(NewBounds.Right,Control.Width+SplitterWidth);
      end;
      if StoredConstraintsValid then begin
        // restore constraints
        with Site.Constraints do begin
          MinWidth:=FStoredConstraints.Left;
          MinHeight:=FStoredConstraints.Top;
          MaxWidth:=FStoredConstraints.Right;
          MaxHeight:=FStoredConstraints.Bottom;
        end;
        FStoredConstraints:=Rect(0,0,0,0);
      end;
      Site.BoundsRect:=NewBounds;
      {$IFDEF VerboseAnchorDocking}
      debugln(['TAnchorDockManager.RemoveControl Site=',DbgSName(Site),' ',dbgs(Site.BoundsRect)]);
      {$ENDIF}

      // Site can dock a control again
      DragManager.RegisterDockSite(Site,true);
    end;
  end;
end;

procedure TAnchorDockManager.ResetBounds(Force: Boolean);
var
  OldSiteClientRect: TRect;
  WidthDiff: Integer;
  HeightDiff: Integer;
  ClientRectChanged: Boolean;

  procedure AlignChilds;
  var
    i: Integer;
    b: TRect;
    AControl: TControl;
    ChildMaxSize: TPoint;
    SiteMinSize: TPoint;
    Child: TAnchorDockHostSite;
  begin
    if ClientRectChanged and DockMaster.Restoring then begin
      // ClientRect changed => restore bounds
      for i:=0 to Site.ControlCount-1 do begin
        AControl:=Site.Controls[i];
        b:=Rect(0,0,0,0);
        if AControl is TAnchorDockHostSite then
          b:=TAnchorDockHostSite(AControl).DockRestoreBounds
        else if AControl is TAnchorDockSplitter then
          b:=TAnchorDockSplitter(AControl).DockRestoreBounds;
        if (b.Right<=b.Left) or (b.Bottom<=b.Top) then
          b:=AControl.BoundsRect;
        {$IFDEF VerboseAnchorDockRestore}
        debugln(['TAnchorDockManager.ResetBounds RESTORE ',DbgSName(AControl),' Cur=',dbgs(AControl.BoundsRect),' Restore=',dbgs(b)]);
        {$ENDIF}
        if AControl is TAnchorDockSplitter then begin
          // fit splitter into clientarea
          if AControl.AnchorSide[akLeft].Control=nil then
            b.Left:=Max(0,Min(b.Left,Site.ClientWidth-10));
          if AControl.AnchorSide[akTop].Control=nil then
            b.Top:=Max(0,Min(b.Top,Site.ClientHeight-10));
          if TAnchorDockSplitter(AControl).ResizeAnchor in [akLeft,akRight] then
          begin
            b.Right:=b.Left+DockMaster.SplitterWidth;
            b.Bottom:=Max(1,Min(b.Bottom,Site.ClientHeight-b.Top));
          end
          else begin
            b.Right:=Max(1,Min(b.Right,Site.ClientWidth-b.Left));
            b.Bottom:=b.Top+DockMaster.SplitterWidth;
          end;
        end;

        AControl.BoundsRect:=b;
        if AControl is TAnchorDockSplitter then
          TAnchorDockSplitter(AControl).UpdateDockBounds;
      end;
      exit;
    end;

    if DockSite<>nil then exit;
    Child:=GetChildSite;
    if Child=nil then exit;

    {$IFDEF VerboseAnchorDockRestore}
    debugln(['TAnchorDockManager.ResetBounds ',DbgSName(Site),' ',dbgs(Child.BaseBounds),' ',WidthDiff,',',HeightDiff]);
    {$ENDIF}
    ChildMaxSize:=Point(Site.ClientWidth-DockMaster.SplitterWidth,
                        Site.ClientHeight-DockMaster.SplitterWidth);
    if PreferredSiteSizeAsSiteMinimum then begin
      SiteMinSize:=GetSitePreferredClientSize;
      if Child.Align in [alLeft,alRight] then begin
        ChildMaxSize.X:=Max(0,(ChildMaxSize.X-SiteMinSize.X));
      end else begin
        ChildMaxSize.Y:=Max(0,(ChildMaxSize.Y-SiteMinSize.Y));
      end;
      {$IF defined(VerboseAnchorDockRestore) or defined(VerboseADCustomSite)}
      debugln(['TAnchorDockManager.ResetBounds ChildMaxSize=',dbgs(ChildMaxSize),' SiteMinSize=',dbgs(SiteMinSize),' Site.Client=',dbgs(Site.ClientRect)]);
      {$ENDIF}
    end;

    case ResizePolicy of
    admrpChild:
      begin
        if Child.Align in [alLeft,alRight] then
          Child.Width:=Max(1,Min(ChildMaxSize.X,Child.Width+WidthDiff))
        else begin
          i:=Max(1,Min(ChildMaxSize.Y,Child.Height+HeightDiff));
          {$IFDEF VerboseAnchorDockRestore}
          debugln(['TAnchorDockManager.ResetBounds Child=',DbgSName(Child),' OldHeight=',Child.Height,' NewHeight=',i]);
          {$ENDIF}
          Child.Height:=i;
        end;
      end;
    end;
  end;

begin
  if Force then ;

  //debugln(['TAnchorDockManager.ResetBounds Site="',Site.Caption,'" Force=',Force,' ',dbgs(Site.ClientRect)]);
  OldSiteClientRect:=FSiteClientRect;
  FSiteClientRect:=Site.ClientRect;
  WidthDiff:=FSiteClientRect.Right-OldSiteClientRect.Right;
  HeightDiff:=FSiteClientRect.Bottom-OldSiteClientRect.Bottom;
  ClientRectChanged:=(WidthDiff<>0) or (HeightDiff<>0);
  if ClientRectChanged or PreferredSiteSizeAsSiteMinimum then
    AlignChilds;
end;

procedure TAnchorDockManager.SaveToStream(Stream: TStream);
begin
  if Stream=nil then ;
  debugln(['TAnchorDockManager.SaveToStream not implemented Site="',DbgSName(Site),'"']);
end;

function TAnchorDockManager.GetDockEdge(ADockObject: TDragDockObject): boolean;
var
  BestDistance: Integer;

  procedure FindMinDistance(CurAlign: TAlign; CurDistance: integer);
  begin
    if CurDistance<0 then
      CurDistance:=-CurDistance;
    if CurDistance>=BestDistance then exit;
    ADockObject.DropAlign:=CurAlign;
    BestDistance:=CurDistance;
  end;

var
  p: TPoint;
  LastTabRect: TRect;
  TabIndex: longint;
begin
  //debugln(['TAnchorDockManager.GetDockEdge ',DbgSName(Site),' ',DbgSName(DockSite),' DockableSites=',dbgs(DockableSites)]);
  if DockableSites=[] then begin
    ADockObject.DropAlign:=alNone;
    exit(false);
  end;

  p:=Site.ScreenToClient(ADockObject.DragPos);
  //debugln(['TAnchorDockManager.GetDockEdge ',dbgs(p),' ',dbgs(Site.BoundsRect),' ',DbgSName(Site)]);
  if (DockSite<>nil) and (DockSite.Pages<>nil) then begin
    // page docking
    ADockObject.DropAlign:=alClient;
    p:=DockSite.Pages.ScreenToClient(ADockObject.DragPos);
    LastTabRect:=DockSite.Pages.TabRect(DockSite.Pages.PageCount-1);
    if (p.Y>=LastTabRect.Top) and (p.y<LastTabRect.Bottom) then begin
      // specific tab
      if p.X>=LastTabRect.Right then begin
        // insert as last
        ADockObject.DropOnControl:=DockSite.Pages;
      end else begin
        TabIndex:=DockSite.Pages.TabIndexAtClientPos(p);
        if TabIndex>=0 then begin
          // insert in front of an existing
          ADockObject.DropOnControl:=DockSite.Pages.Page[TabIndex];
        end;
      end;
    end;
  end else if (DockSite<>nil) and PtInRect(DockSite.GetPageArea,p) then begin
    // page docking
    ADockObject.DropAlign:=alClient;
  end else begin

    // check side
    BestDistance:=High(Integer);
    if akLeft in DockableSites then FindMinDistance(alLeft,p.X);
    if akRight in DockableSites then FindMinDistance(alRight,Site.ClientWidth-p.X);
    if akTop in DockableSites then FindMinDistance(alTop,p.Y);
    if akBottom in DockableSites then FindMinDistance(alBottom,Site.ClientHeight-p.Y);

    // check inside
    if InsideDockingAllowed
    and ( ((ADockObject.DropAlign=alLeft) and (p.X>=0))
       or ((ADockObject.DropAlign=alTop) and (p.Y>=0))
       or ((ADockObject.DropAlign=alRight) and (p.X<Site.ClientWidth))
       or ((ADockObject.DropAlign=alBottom) and (p.Y<Site.ClientHeight)) )
    then
      ADockObject.DropOnControl:=Site
    else
      ADockObject.DropOnControl:=nil;
  end;
  //debugln(['TAnchorDockManager.GetDockEdge ADockObject.DropAlign=',dbgs(ADockObject.DropAlign),' DropOnControl=',DbgSName(ADockObject.DropOnControl)]);
  Result:=true;
end;

procedure TAnchorDockManager.RestoreSite(SplitterPos: integer);
var
  ChildSite: TAnchorDockHostSite;
begin
  FSiteClientRect:=Site.ClientRect;
  if DockSite<>nil then exit;
  ChildSite:=GetChildSite;
  {$IFDEF VerboseAnchorDockRestore}
  debugln(['TAnchorDockManager.RestoreSite START ',DbgSName(Site),' ChildSite=',DbgSName(ChildSite)]);
  {$ENDIF}
  if ChildSite<>nil then begin
    ChildSite.CreateBoundSplitter;
    ChildSite.PositionBoundSplitter;
    if ChildSite.Align in [alLeft,alRight] then
      ChildSite.BoundSplitter.Left:=SplitterPos
    else
      ChildSite.BoundSplitter.Top:=SplitterPos;
    case ChildSite.Align of
    alTop: ChildSite.Height:=ChildSite.BoundSplitter.Top;
    alBottom: ChildSite.Height:=Site.ClientHeight
                  -(ChildSite.BoundSplitter.Top+ChildSite.BoundSplitter.Height);
    alLeft: ChildSite.Width:=ChildSite.BoundSplitter.Left;
    alRight: ChildSite.Width:=Site.ClientWidth
                  -(ChildSite.BoundSplitter.Left+ChildSite.BoundSplitter.Width);
    end;
    // only allow to dock one control
    DragManager.RegisterDockSite(Site,false);
    {$IFDEF VerboseAnchorDockRestore}
    debugln(['TAnchorDockManager.RestoreSite ',DbgSName(Site),' ChildSite=',DbgSName(ChildSite),' Site.Bounds=',dbgs(Site.BoundsRect),' Site.Client=',dbgs(Site.ClientRect),' ChildSite.Bounds=',dbgs(ChildSite.BoundsRect),' Splitter.Bounds=',dbgs(ChildSite.BoundSplitter.BoundsRect)]);
    {$ENDIF}
  end;
end;

procedure TAnchorDockManager.StoreConstraints;
begin
  with Site.Constraints do
    FStoredConstraints:=Rect(MinWidth,MinHeight,MaxWidth,MaxHeight);
end;

function TAnchorDockManager.GetSitePreferredClientSize: TPoint;
{ Compute the preferred inner size of Site without the ChildSite and without
  the splitter
}
var
  ChildSite: TAnchorDockHostSite;
  Splitter: TAnchorDockSplitter;
  SplitterSize: TPoint;
  i: Integer;
  ChildControl: TControl;
  PrefWidth: Integer;
  PrefHeight: Integer;
  SplitterAnchor: TAnchorKind; // side where a child is anchored to the splitter
  ChildPrefWidth: integer;
  ChildPrefHeight: integer;
  ChildBottom: Integer;
  ChildRight: Integer;
begin
  Result:=Point(0,0);
  Site.GetPreferredSize(Result.X,Result.Y);
  // compute the bounds without the Splitter and ChildSite
  ChildSite:=GetChildSite;
  if ChildSite=nil then exit;
  Splitter:=ChildSite.BoundSplitter;
  if Splitter=nil then exit;
  SplitterSize:=Point(0,0);
  Splitter.GetPreferredSize(SplitterSize.X,SplitterSize.Y);
  PrefWidth:=0;
  PrefHeight:=0;
  if ChildSite.Align in [alLeft,alRight] then
    PrefHeight:=Result.Y
  else
    PrefWidth:=Result.X;
  SplitterAnchor:=MainAlignAnchor[ChildSite.Align];
  for i:=0 to Site.ControlCount-1 do begin
    ChildControl:=Site.Controls[i];
    if (ChildControl=Splitter) or (ChildControl=ChildSite) then continue;
    if (ChildControl.AnchorSide[SplitterAnchor].Control=Splitter)
    or ((ChildControl.Align in [alLeft,alTop,alRight,alBottom,alClient])
      and (SplitterAnchor in AnchorAlign[ChildControl.Align]))
    then begin
      // this control could be resized by the splitter
      // => use its position and preferred size for a preferred size of the ChildSite
      ChildPrefWidth:=0;
      ChildPrefHeight:=0;
      ChildControl.GetPreferredSize(ChildPrefWidth,ChildPrefHeight);
      //debugln(['  ChildControl=',DbgSName(ChildControl),' ',ChildPrefWidth,',',ChildPrefHeight]);
      case ChildSite.Align of
      alTop:
        begin
          ChildBottom:=ChildControl.Top+ChildControl.Height;
          PrefHeight:=Max(PrefHeight,Site.ClientHeight-ChildBottom-ChildPrefHeight);
        end;
      alBottom:
        PrefHeight:=Max(PrefHeight,ChildControl.Top+ChildPrefHeight);
      alLeft:
        begin
          ChildRight:=ChildControl.Left+ChildControl.Width;
          PrefWidth:=Max(PrefWidth,Site.ClientWidth-ChildRight-ChildPrefWidth);
        end;
      alRight:
        PrefWidth:=Max(PrefWidth,ChildControl.Left+ChildPrefWidth);
      end;
    end;
  end;
  {$IFDEF VerboseADCustomSite}
  debugln(['TAnchorDockManager.GetSitePreferredClientSize DefaultSitePref=',dbgs(Result),' Splitter.Align=',dbgs(Splitter.Align),' ChildSite.Align=',dbgs(ChildSite.Align),' NewPref=',PrefWidth,',',PrefHeight]);
  {$ENDIF}
  Result.X:=PrefWidth;
  Result.Y:=PrefHeight;
end;

function TAnchorDockManager.GetChildSite: TAnchorDockHostSite;
var
  i: Integer;
begin
  for i:=0 to Site.ControlCount-1 do
    if Site.Controls[i] is TAnchorDockHostSite then begin
      Result:=TAnchorDockHostSite(Site.Controls[i]);
      exit;
    end;
  Result:=nil;
end;

function TAnchorDockManager.StoredConstraintsValid: boolean;
begin
  with FStoredConstraints do
    Result:=(Left<>0) or (Top<>0) or (Right<>0) or (Bottom<>0);
end;

{ TAnchorDockSplitter }

procedure TAnchorDockSplitter.SetResizeAnchor(const AValue: TAnchorKind);
begin
  inherited SetResizeAnchor(AValue);

  case ResizeAnchor of
  akLeft: Anchors:=AnchorAlign[alLeft];
  akTop: Anchors:=AnchorAlign[alTop];
  akRight: Anchors:=AnchorAlign[alRight];
  akBottom: Anchors:=AnchorAlign[alBottom];
  end;

  UpdatePercentPosition;

  //debugln(['TAnchorDockSplitter.SetResizeAnchor ',DbgSName(Self),' ResizeAnchor=',dbgs(ResizeAnchor),' Align=',dbgs(Align),' Anchors=',dbgs(Anchors)]);
end;

procedure TAnchorDockSplitter.PopupMenuPopup(Sender: TObject);
begin

end;

procedure TAnchorDockSplitter.UpdateDockBounds;
begin
  FDockBounds:=BoundsRect;
  if Parent<>nil then begin
    FDockParentClientSize.cx:=Parent.ClientWidth;
    FDockParentClientSize.cy:=Parent.ClientHeight;
  end else begin
    FDockParentClientSize.cx:=0;
    FDockParentClientSize.cy:=0;
  end;
  if FPercentPosition < 0 then
    UpdatePercentPosition;
end;

procedure TAnchorDockSplitter.UpdatePercentPosition;
begin
  case ResizeAnchor of
    akTop, akBottom:
      if FDockParentClientSize.cy > 0 then
        FPercentPosition := Top / FDockParentClientSize.cy
      else
        FPercentPosition := -1;
  else
    if FDockParentClientSize.cx > 0 then
      FPercentPosition := Left / FDockParentClientSize.cx
    else
      FPercentPosition := -1;
  end;
end;

procedure TAnchorDockSplitter.SetBounds(ALeft, ATop, AWidth, AHeight: integer);
begin
  DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockSplitter.SetBounds'){$ENDIF};
  try
    inherited SetBounds(ALeft, ATop, AWidth, AHeight);
    UpdateDockBounds;
  finally
    EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockSplitter.SetBounds'){$ENDIF};
  end;
end;

procedure TAnchorDockSplitter.SetBoundsKeepDockBounds(ALeft, ATop, AWidth, AHeight: integer);
begin
  inherited SetBounds(ALeft,ATop,AWidth,AHeight);
end;

procedure TAnchorDockSplitter.SetBoundsPercentually;
var
  NewLeft, NewTop: Integer;
begin
  if ResizeAnchor in [akLeft,akRight] then
  begin
    if DockParentClientSize.cx> 0 then
    begin
      if (FPercentPosition > 0) or SameValue(FPercentPosition, 0) then
        NewLeft := Round(FPercentPosition*Parent.ClientWidth)
      else
        NewLeft := (DockBounds.Left*Parent.ClientWidth) div DockParentClientSize.cx;
      NewTop := Top;
      SetBoundsKeepDockBounds(NewLeft,NewTop,Width,Height);
    end;
  end else
  begin
    if DockParentClientSize.cy> 0 then
    begin
      NewLeft := Left;
      if (FPercentPosition > 0) or SameValue(FPercentPosition, 0) then
        NewTop := Round(FPercentPosition*Parent.ClientHeight)
      else
        NewTop := (DockBounds.Top*Parent.ClientHeight) div DockParentClientSize.cy;
      SetBoundsKeepDockBounds(NewLeft,NewTop,Width,Height);
    end;
  end;
  if FPercentPosition < 0 then
    UpdatePercentPosition;
end;

function TAnchorDockSplitter.SideAnchoredControlCount(Side: TAnchorKind): integer;
var
  Sibling: TControl;
  i: Integer;
begin
  Result:=0;
  for i:=0 to AnchoredControlCount-1 do begin
    Sibling:=AnchoredControls[i];
    if Sibling.AnchorSide[OppositeAnchor[Side]].Control=Self then
      inc(Result);
  end;
end;

function TAnchorDockSplitter.HasAnchoredControls: boolean;
// returns true if this splitter has at least one non splitter control anchored to it
var
  i: Integer;
  Sibling: TControl;
begin
  Result:=false;
  for i:=0 to AnchoredControlCount-1 do begin
    Sibling:=AnchoredControls[i];
    if Sibling is TAnchorDockSplitter then continue;
    exit(true);
  end;
end;

procedure TAnchorDockSplitter.SaveLayout(
  LayoutNode: TAnchorDockLayoutTreeNode);
begin
  if ResizeAnchor in [akLeft,akRight] then
    LayoutNode.NodeType:=adltnSplitterVertical
  else
    LayoutNode.NodeType:=adltnSplitterHorizontal;
  LayoutNode.Assign(Self);
end;

function TAnchorDockSplitter.HasOnlyOneSibling(Side: TAnchorKind; MinPos,
  MaxPos: integer): TControl;
var
  i: Integer;
  AControl: TControl;
begin
  Result:=nil;
  for i:=0 to AnchoredControlCount-1 do begin
    AControl:=AnchoredControls[i];
    if AControl.AnchorSide[OppositeAnchor[Side]].Control<>Self then continue;
    // AControl is anchored at Side to this splitter
    if (Side in [akLeft,akRight]) then begin
      if (AControl.Left>MaxPos) or (AControl.Left+AControl.Width<MinPos) then
        continue;
    end else begin
      if (AControl.Top>MaxPos) or (AControl.Top+AControl.Height<MinPos) then
        continue;
    end;
    // AControl is in range
    if Result=nil then
      Result:=AControl
    else begin
      // there is more than one control
      Result:=nil;
      exit;
    end;
  end;
end;

procedure TAnchorDockSplitter.MoveSplitter(Offset: integer);
begin
  FPercentPosition:=-1;
  inherited MoveSplitter(Offset);
  UpdatePercentPosition;
end;

procedure TAnchorDockSplitter.Paint;
begin
  if Enabled then
    inherited Paint
  else
  begin
    Canvas.Brush.Color := clDefault;
    Canvas.FillRect(ClientRect);
  end;
end;

constructor TAnchorDockSplitter.Create(TheOwner: TComponent);
begin
  inherited Create(TheOwner);
  Align:=alNone;
  ResizeAnchor:=akLeft;
  // make sure the splitter never vanish
  Constraints.MinWidth:=2;
  Constraints.MinHeight:=2;
  PopupMenu:=DockMaster.GetPopupMenu;
  FPercentPosition:=-1;
end;

{ TAnchorDockPageControl }

function TAnchorDockPageControl.GetDockPages(Index: integer): TAnchorDockPage;
begin
  Result:=TAnchorDockPage(Page[Index]);
end;

procedure TAnchorDockPageControl.MouseDown(Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  ATabIndex: LongInt;
  APage: TCustomPage;
  Site: TAnchorDockHostSite;
begin
  inherited MouseDown(Button, Shift, X, Y);
  ATabIndex := TabIndexAtClientPos(Point(X,Y));
  if (Button = mbLeft) and DockMaster.AllowDragging and (ATabIndex >= 0) then
  begin
    APage:=Page[ATabIndex];
    if (APage.ControlCount>0) and (APage.Controls[0] is TAnchorDockHostSite) then
    begin
      Site:=TAnchorDockHostSite(APage.Controls[0]);
      DragManager.DragStart(Site,false,DockMaster.DragTreshold);
    end;
  end;
end;

procedure TAnchorDockPageControl.PopupMenuPopup(Sender: TObject);
var
  ContainsMainForm: Boolean;
  s: String;
  TabPositionSection: TMenuItem;
  Item: TMenuItem;
  tp: TTabPosition;
begin
  // movement
  if PageIndex>0 then
    DockMaster.AddPopupMenuItem('MoveLeftMenuItem', adrsMovePageLeft,
                                              @MoveLeftButtonClick);
  if PageIndex>1 then
    DockMaster.AddPopupMenuItem('MoveLeftMostMenuItem', adrsMovePageLeftmost,
                                              @MoveLeftMostButtonClick);

  if PageIndex<PageCount-1 then
    DockMaster.AddPopupMenuItem('MoveRightMenuItem', adrsMovePageRight,
                                              @MoveRightButtonClick);
  if PageIndex<PageCount-2 then
    DockMaster.AddPopupMenuItem('MoveRightMostMenuItem', adrsMovePageRightmost,
                                              @MoveRightMostButtonClick);

  // tab position
  TabPositionSection:=DockMaster.AddPopupMenuItem('TabPositionMenuItem',
                                                  adrsTabPosition,nil);
  for tp:=Low(TTabPosition) to high(TTabPosition) do begin
    case tp of
    tpTop: s:=adrsTop;
    tpBottom: s:=adrsBottom;
    tpLeft: s:=adrsLeft;
    tpRight: s:=adrsRight;
    end;
    Item:=DockMaster.AddPopupMenuItem('TabPos'+ADLTabPostionNames[tp]+'MenuItem',
                              s,@TabPositionClick,TabPositionSection);
    Item.ShowAlwaysCheckable:=true;
    Item.Checked:=TabPosition=tp;
    Item.Tag:=ord(tp);
  end;

  // close
  ContainsMainForm:=IsParentOf(Application.MainForm);
  if ContainsMainForm then
    s:=Format(adrsQuit, [Application.Title])
  else
    s:=adrsClose;
  DockMaster.AddPopupMenuItem('CloseMenuItem',s,@CloseButtonClick);
end;

procedure TAnchorDockPageControl.CloseButtonClick(Sender: TObject);
var
  Site: TAnchorDockHostSite;
begin
  Site:=GetActiveSite;
  if Site=nil then exit;
  DockMaster.RestoreLayouts.Add(DockMaster.CreateRestoreLayout(Site),true);
  Site.CloseSite;
  DockMaster.SimplifyPendingLayouts;
end;

procedure TAnchorDockPageControl.MoveLeftButtonClick(Sender: TObject);
begin
  if PageIndex>0 then
    Page[PageIndex].PageIndex:=Page[PageIndex].PageIndex-1;
end;

procedure TAnchorDockPageControl.MoveLeftMostButtonClick(Sender: TObject);
begin
  if PageIndex>0 then
    Page[PageIndex].PageIndex:=0;
end;

procedure TAnchorDockPageControl.MoveRightButtonClick(Sender: TObject);
begin
  if PageIndex<PageCount-1 then
    Page[PageIndex].PageIndex:=Page[PageIndex].PageIndex+1;
end;

procedure TAnchorDockPageControl.MoveRightMostButtonClick(Sender: TObject);
begin
  if PageIndex<PageCount-1 then
    Page[PageIndex].PageIndex:=PageCount-1;
end;

procedure TAnchorDockPageControl.TabPositionClick(Sender: TObject);
var
  Item: TMenuItem;
begin
  if not (Sender is TMenuItem) then exit;
  Item:=TMenuItem(Sender);
  TabPosition:=TTabPosition(Item.Tag);
end;

procedure TAnchorDockPageControl.UpdateDockCaption(Exclude: TControl);
begin
  if Exclude=nil then ;
end;

procedure TAnchorDockPageControl.RemoveControl(AControl: TControl);
begin
  inherited RemoveControl(AControl);
  if (not (csDestroying in ComponentState)) then begin
    if (PageCount<=1) and (Parent is TAnchorDockHostSite) then
      DockMaster.NeedSimplify(Parent);
  end;
end;

function TAnchorDockPageControl.GetActiveSite: TAnchorDockHostSite;
var
  CurPage: TCustomPage;
  CurDockPage: TAnchorDockPage;
begin
  Result:=nil;
  CurPage:=ActivePageComponent;
  if not (CurPage is TAnchorDockPage) then exit;
  CurDockPage:=TAnchorDockPage(CurPage);
  Result:=CurDockPage.GetSite;
end;

constructor TAnchorDockPageControl.Create(TheOwner: TComponent);
begin
  PageClass:=DockMaster.PageClass;
  inherited Create(TheOwner);
  PopupMenu:=DockMaster.GetPopupMenu;
end;

{ TAnchorDockPage }

procedure TAnchorDockPage.UpdateDockCaption(Exclude: TControl);
var
  i: Integer;
  Child: TControl;
  NewCaption: String;
begin
  NewCaption:='';
  for i:=0 to ControlCount-1 do begin
    Child:=Controls[i];
    if Child=Exclude then continue;
    if not (Child is TAnchorDockHostSite) then continue;
    if NewCaption<>'' then
      NewCaption:=NewCaption+',';
    NewCaption:=NewCaption+Child.Caption;
  end;
  //debugln(['TAnchorDockPage.UpdateDockCaption ',Caption,' ',NewCaption]);
  if Caption=NewCaption then exit;
  Caption:=NewCaption;
  if Parent is TAnchorDockPageControl then
    TAnchorDockPageControl(Parent).UpdateDockCaption;
end;

procedure TAnchorDockPage.InsertControl(AControl: TControl; Index: integer);
begin
  inherited InsertControl(AControl, Index);
  //debugln(['TAnchorDockPage.InsertControl ',DbgSName(AControl)]);
  if AControl is TAnchorDockHostSite then begin
    if TAnchorDockHostSite(AControl).Header<>nil then
      TAnchorDockHostSite(AControl).Header.Parent:=nil;
    UpdateDockCaption;
  end;
end;

procedure TAnchorDockPage.RemoveControl(AControl: TControl);
begin
  inherited RemoveControl(AControl);
  if (GetSite=nil) and (not (csDestroying in ComponentState))
  and (Parent<>nil) and (not (csDestroying in Parent.ComponentState)) then
    DockMaster.NeedSimplify(Self);
end;

function TAnchorDockPage.GetSite: TAnchorDockHostSite;
begin
  Result:=nil;
  if ControlCount=0 then exit;
  if not (Controls[0] is TAnchorDockHostSite) then exit;
  Result:=TAnchorDockHostSite(Controls[0]);
end;

initialization
  DockMaster:=TAnchorDockMaster.Create(nil);

finalization
  FreeAndNil(DockMaster);

end.

