{******************************************************************************}
{                       CnPack For Delphi/C++Builder                           }
{                     中国人自己的开放源码第三方开发包                         }
{                   (C)Copyright 2001-2016 CnPack 开发组                       }
{                   ------------------------------------                       }
{                                                                              }
{            本开发包是开源的自由软件，您可以遵照 CnPack 的发布协议来修        }
{        改和重新发布这一程序。                                                }
{                                                                              }
{            发布这一开发包的目的是希望它有用，但没有任何担保。甚至没有        }
{        适合特定目的而隐含的担保。更详细的情况请参阅 CnPack 发布协议。        }
{                                                                              }
{            您应该已经和开发包一起收到一份 CnPack 发布协议的副本。如果        }
{        还没有，可访问我们的网站：                                            }
{                                                                              }
{            网站地址：http://www.cnpack.org                                   }
{            电子邮件：master@cnpack.org                                       }
{                                                                              }
{******************************************************************************}

{*******************************************************}
{                                                       }
{       管理停靠控件的管理器                            }
{       CnDockTree 单元                                 }
{                                                       }
{       版权 (C) 2002,2003 鲁小班                       }
{                                                       }
{*******************************************************}

unit CnDockTree;
{* |<PRE>
================================================================================
* 软件名称：不可视工具组件包停靠单元
* 单元名称：管理停靠控件的管理器 
* 单元作者：CnPack开发组 周益波（鲁小班）
* 备    注：本单元由原作者授权CnPack开发组移植，已保留原作者版权信息
* 开发平台：
* 兼容测试：PWin9X/2000/XP + Delphi 5/6/7
* 本 地 化：该单元中的字符串均符合本地化处理方式
* 单元标识：$Id$
* 修改记录：2007.07.13 V1.0
*                移植单元
================================================================================
|</PRE>}

interface

{$I CnPack.inc}

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Consts, CnDockSupportClass;

const
  { 鼠标位置在分割条上面 }
  HTSPLITTER = 30;
  { 没有在特定的位置上 }
  HTNONE = 31;

type

  TCnDockTree = class;

  // ICnDockManager接口继承自IDockManager，用户可以在ICnDockManager中再定义一些方法，函数或属性。
  ICnDockManager = interface(IDockManager)
    ['{7B0AACBC-E9BF-42F8-9629-E551067090B2}']
    function GetActiveControl: TControl;               //获得焦点的控件
    procedure SetActiveControl(const Value: TControl); //设置某个控件获得焦点
    function GetGrabberSize: Integer;                  //获得把手的大小
    procedure SetGrabberSize(const Value: Integer);    //设置把手的大小
    function GetSplitterWidth: Integer;                //获得分割条的宽度
    procedure SetSplitterWidth(const Value: Integer);  //设置分割条的宽度
    function GetBorderWidth: Integer;                  //获得边框的宽度
    procedure SetBorderWidth(const Value: Integer);    //设置边框的宽度
    function GetDockRect: TRect;                       //获得停靠矩形
    procedure SetDockRect(const Value: TRect);         //设置停靠矩形

    function GetDockSiteSize: Integer;                 //获得停靠服务器的宽度或者高度
    procedure SetDockSiteSize(const Value: Integer);   //设置停靠服务器的宽度或者高度

    function GetMinSize: Integer;                      //获得停靠客户之间的最小距离

    procedure BeginResizeDockSite;
    procedure EndResizeDockSite;

    function GetDockEdge(DockRect: TRect; MousePos: TPoint;
      var DropAlign: TAlign; Control: TControl): TControl;  //获得停靠预览矩形下面的Control;

    function GetHTFlag(MousePos: TPoint): Integer;

    procedure GetSiteInfo(Client: TControl;
      var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean);

    procedure ShowControl(Control: TControl);           //显示Control
    procedure HideControl(Control: TControl);           //隐藏Control

    procedure ShowAllControl;                           // 显示所有的Control
    procedure HideAllControl;                           // 隐藏所有的Control
    procedure ShowSingleControl(Control: TControl);     // 只显示一个Control，其他的都隐藏
    procedure HideSingleControl(Control: TControl);     // 只隐藏一个Control，其他的都显示

    { 用新的NewControl替换老的OldControl }
    procedure ReplaceZoneChild(OldControl, NewControl: TControl);

    { 根据输入的Control参数查找Zone，如果找到就返回True，否则返回False }
    function HasZoneWithControl(Control: TControl): Boolean;

    { 获得停靠服务器上的停靠客户的极限, 相对于DockSite }
    function GetDockClientLimit(Orient: TDockOrientation; IsMin: Boolean): Integer;

    function GetFrameRect(Control: TControl): TRect;   //获得停靠控件的矩形大小，相对于DockSite。
    function GetFrameRectEx(Control: TControl): TRect; //获得停靠控件的矩形大小，相对于屏幕。
    property ActiveControl: TControl read GetActiveControl
      write SetActiveControl;
    property GrabberSize: Integer read GetGrabberSize
      write SetGrabberSize;
    property SplitterWidth: Integer read GetSplitterWidth
      write SetSplitterWidth;
    property BorderWidth: Integer read GetBorderWidth
      write SetBorderWidth;

    property DockSiteSize: Integer read GetDockSiteSize write SetDockSiteSize;

    property DockRect: TRect read GetDockRect write SetDockRect;

    property MinSize: Integer read GetMinSize;
  end;

  TCnDockZone = class
  private
    FChildControl: TWinControl;     //上面的控件
    FChildZones: TCnDockZone;       //左子女
    FNextSibling: TCnDockZone;      //右兄弟
    FOrientation: TDockOrientation; //停靠的方式，是水平还是垂直，或者没有
    FParentZone: TCnDockZone;       //父亲
    FPrevSibling: TCnDockZone;      //上一个兄弟
    FTree: TCnDockTree;             //属于哪棵树
    FZoneLimit: Integer;            //节点的下边或者右边的坐标
                                    //如果FOrientation是水平的，就表示下边的坐标，
                                    //如果FOrientation是垂直的，就表示右边的坐标。
    FVisibleSize: Integer;          //存储上当Zone可见的时候的ZoneSize的值,
    FVisibled: Boolean;             //是否可见
    FControlVisibled: Boolean;      //ChildControl是否可见
    FIsInside: Boolean;             //是否在DockSite的里面
    function GetFirstSibling: TCnDockZone;//获得最前一个兄弟
    function GetLastSibling: TCnDockZone; //获得最后一个兄弟
    function GetFirstChild: TCnDockZone;  //获得最前一个子女
    function GetLastChild: TCnDockZone;   //获得最后一个子女
    function GetTopLeftArr(Orient: TDockOrientation): Integer;
    function GetHeightWidthArr(Orient: TDockOrientation): Integer;
    function GetAfterClosestVisibleZone: TCnDockZone;
    function GetBeforeClosestVisibleZone: TCnDockZone;
    function GetAfterApoapsisVisibleZone: TCnDockZone;
    function GetBeforeApoapsisVisibleZone: TCnDockZone;
    function GetNextSiblingCount: Integer;
    function GetPrevSiblingCount: Integer;
    procedure SetVisibled(const Value: Boolean);
    procedure SetZoneLimit(const Value: Integer);
    function GetVisibleNextSiblingCount: Integer;
    function GetVisibleNextSiblingTotal: Integer;
    function GetVisiblePrevSiblingCount: Integer;
    function GetVisiblePrevSiblingTotal: Integer;
    function GetFirstVisibleChildZone: TCnDockZone;
    function GetLastVisibleChildZone: TCnDockZone;
    procedure SetIsInside(const Value: Boolean);
  protected
    procedure AdjustZoneLimit(Value: Integer); virtual;//改变FZoneLimit值并且调整她的兄弟
    procedure LButtonDbClkMothed; virtual;    // 当鼠标左键双击的时候，判断Zone上的ChildControl如何动作
    function GetChildCount: Integer;          //获得子女的个数
    function GetVisibleChildCount: Integer;   //获得可见子女的个数
    function GetChildTotal: Integer;          //获得末段子女的总数
    function GetVisibleChildTotal: Integer;   //获得末段可见子女的个数
    function GetLimitBegin: Integer;          //获得节点的上边或者左边的坐标
    function GetLimitSize: Integer;           //获得节点的高度或者宽度
    function GetTopLeft(Orient: Integer{TDockOrientation}): Integer;
    function GetHeightWidth(Orient: Integer{TDockOrientation}): Integer;
    function GetControlName: string;//获得控件的名称，如果没有控件就返回空字符串
    { 获得当前的节点内的子节点的分割条的ZoneLimit, 如果IsMin为True, 说明是取最小值，反之就取最大值 }
    function GetSplitterLimit(IsMin: Boolean): Integer; virtual;
    function DoGetSplitterLimit(Orientation: TDockOrientation;
      IsMin: Boolean; var LimitResult: Integer): Integer; virtual;
    {查找名字为Value的控件，如果找到就把她停靠进FTree中}
    function SetControlName(const Value: string): Boolean;
    procedure DoCustomSetControlName; virtual;
    procedure SetChildControlVisible(Client: TControl; AViisible: Boolean); virtual;
  public
    constructor Create(Tree: TCnDockTree); virtual;
    { 插入一个大小为DockSize的节点 }
    procedure Insert(DockSize: Integer; Hide: Boolean); virtual;
    { 去掉一个大小为DockSize的节点 }
    procedure Remove(DockSize: Integer; Hide: Boolean); virtual;
    { 当有一个控件停靠进来的时候，调用InsertOrRemove重新调整ParentZone上的子节点,
      其中Insert指示是否是插入操作，如果Insert=True就是插入操作，否者就是删除操作 }
    procedure InsertOrRemove(DockSize: Integer; Insert: Boolean; Hide: Boolean); virtual;
    procedure ResetChildren(Exclude: TCnDockZone); virtual;//重新设置子女的属性，如坐标等
    { 更新当前节点(不包括子节点)，调整当前的ChildControl的位置和大小 }
    procedure Update; virtual;
    { 获得这个节点的矩形位置 }
    function GetFrameRect: TRect; virtual;
    { 设置节点的大小 }
    procedure SetZoneSize(Size: Integer; Show: Boolean); virtual;
    { 离本节点最近的可见的前兄弟节点 }
    property BeforeClosestVisibleZone: TCnDockZone read GetBeforeClosestVisibleZone;
    { 离本节点最近的可见的后兄弟节点 }
    property AfterClosestVisibleZone: TCnDockZone read GetAfterClosestVisibleZone;
    { 离本节点最远的可见的前兄弟节点 }
    property BeforeApoapsisVisibleZone: TCnDockZone read GetBeforeApoapsisVisibleZone;
    { 离本节点最远的可见的后兄弟节点 }
    property AfterApoapsisVisibleZone: TCnDockZone read GetAfterApoapsisVisibleZone;
    { 第一个可见的子女节点 }
    property FirstVisibleChildZone: TCnDockZone read GetFirstVisibleChildZone;
    { 第一个可见的子女节点 }
    property LastVisibleChildZone: TCnDockZone read GetLastVisibleChildZone;
    { 子女个数 }
    property ChildCount: Integer read GetChildCount;
    { 末端子女个数 }
    property ChildTotal: Integer read GetChildTotal;
    { 第一个子女 }
    property ChildZones: TCnDockZone read FChildZones write FChildZones;
    { 在节点上的Control控件 }
    property ChildControl: TWinControl read FChildControl write FChildControl;
    { 第一个子女 }
    property FirstChild: TCnDockZone read GetFirstChild;
    { 第一个兄弟 }
    property FirstSibling: TCnDockZone read GetFirstSibling;
    { 高度 }
    property Height: Integer index Ord(doHorizontal) read GetHeightWidth;
    { 根据停靠方向来获得高度和宽度 }
    property HeightWidth[Orient: TDockOrientation]: Integer read GetHeightWidthArr;
    { 最后一个子女 }
    property LastChild: TCnDockZone read GetLastChild;
    { 最后一个兄弟 }
    property LastSibling: TCnDockZone read GetLastSibling;
    { 左边位置 }
    property Left: Integer index Ord(doVertical) read GetTopLeft;
    { 开始位置的坐标 }
    property LimitBegin: Integer read GetLimitBegin;
    { 大小 }
    property LimitSize: Integer read GetLimitSize;
    { 下一个兄弟 }
    property NextSibling: TCnDockZone read FNextSibling write FNextSibling;
    { 获得后兄弟的个数 }
    property NextSiblingCount: Integer read GetNextSiblingCount;
    { 停靠方向 }
    property Orientation: TDockOrientation read FOrientation write FOrientation;
    { 父节点 }
    property ParentZone: TCnDockZone read FParentZone write FParentZone;
    { 上一个兄弟 }
    property PrevSibling: TCnDockZone read FPrevSibling write FPrevSibling;
    { 获得前兄弟的个数 }
    property PrevSiblingCount: Integer read GetPrevSiblingCount;
    { 上边位置 }
    property Top: Integer index Ord(doHorizontal) read GetTopLeft;
    { 根据停靠方向来获得左边和上边位置 }
    property TopLeft[Orient: TDockOrientation]: Integer read GetTopLeftArr;
    { 属于哪个树 }
    property Tree: TCnDockTree read FTree write FTree;
    { 可见子女节点的个数 }
    property VisibleChildCount: Integer read GetVisibleChildCount;
    { 可见末端节点的个数 }
    property VisibleChildTotal: Integer read GetVisibleChildTotal;
    { 可见的上兄弟的个数 }
    property VisiblePrevSiblingCount: Integer read GetVisiblePrevSiblingCount;
    { 可见的上兄弟的末端节点的个数 }
    property VisiblePrevSiblingTotal: Integer read GetVisiblePrevSiblingTotal;
    { 可见的下兄弟的个数 }
    property VisibleNextSiblingCount: Integer read GetVisibleNextSiblingCount;
    { 可见的下兄弟的末端节点的个数 }
    property VisibleNextSiblingTotal: Integer read GetVisibleNextSiblingTotal;
    { 可见的大小 }
    property VisibleSize: Integer read FVisibleSize write FVisibleSize;
    { 宽度 }
    property Width: Integer index Ord(doVertical) read GetHeightWidth;
    { 相对于DockSite的绝对位置 }
    property ZoneLimit: Integer read FZoneLimit write SetZoneLimit;
    { 是否可见 }
    property Visibled: Boolean read FVisibled write SetVisibled;
    { 是否在里面 }
    property IsInside: Boolean read FIsInside write SetIsInside;
  end;

  TCnAdvDockZone = class(TCnDockZone)
  private
    FCloseBtnDown: Boolean;
    FMouseDown: Boolean;
  protected
    procedure LButtonDbClkMothed; override;
  public
    constructor Create(Tree: TCnDockTree); override;
    destructor Destroy; override;
    procedure Insert(DockSize: Integer; Hide: Boolean); override;
    procedure Remove(DockSize: Integer; Hide: Boolean); override;
    property CloseBtnDown: Boolean read FCloseBtnDown write FCloseBtnDown;
    property MouseDown: Boolean read FMouseDown write FMouseDown;
  end;

  { 树的遍历方式，分别是前序，中序和后序遍历 }
  TTreeScanKind = (tskForward, tskMiddle, tskBackward);

  { 树的遍历优先级别，分别是先遍历兄弟，先遍历子女 }
  TTreeScanPriority = (tspSibling, tspChild);

  TCnForEachZoneProc = procedure(Zone: TCnDockZone) of object;

  { 把手的位置，可以有四个位置，分别是上下左右 }
  TGrabbersPosition = (gpTop, gpBottom, gpLeft, gpRight);

  TCnDockZoneClass = class of TCnDockZone;

  TCnDockTree = class(TInterfacedObject, ICnDockManager)
  private

    {节点类的引用}
    FCnDockZoneClass: TCnDockZoneClass;
    {在树中哪个控件获得了焦点}
    FActiveControl: TControl;

    FBorderWidth: Integer;         //边框的宽度

    FSplitterWidth: Integer;       //分割条的宽度
    FBrush: TBrush;                //用来画把手的刷子
    FDockSite: TWinControl;        //停靠的服务控件
    FGrabberSize: Integer;         //把手的大小
    FOldRect: TRect;               //当DockSite的大小调整的时候，
                                   //这个值存储最后一次的DockSite的大小
    FDockRect: TRect;

    FOldWndProc: TWndMethod;
    FReplacementZone: TCnDockZone;
    FResizeCount: Integer;         //调整DockSite大小的计数器
    FScaleBy: Double;              //比例的大小

    { 当进行偏移量调整的时候，用来指示到底是什么停靠方向的Zone需要调整 }
    FShiftScaleOrient: TDockOrientation;
    FShiftBy: Integer;             //偏移量
    FSizePos: TPoint;              //当鼠标点击分割条时，记录下鼠标的坐标
    FSizingDC: HDC;                //画分割条的设备上下文
    FSizingWnd: HWND;
    FSizingZone: TCnDockZone;      //分割条属于哪个节点
    FTopZone: TCnDockZone;         //根节点
    FTopXYLimit: Integer;
    FUpdateCount: Integer;         //更新计数器
    FVersion: Integer;             //版本
    FOldHTFlag: Integer;           //老的鼠标的位置
    FParentLimit: Integer;         //这个值是用来在调整ZoneLimit的时候用的，
                                   //具体公式见ScaleChildZone函数
    FMinSize: Integer;             //停靠控件之间的最小距离

    FCanvas: TControlCanvas;       //用来画DockSite的画布

    procedure SetTopZone(const Value: TCnDockZone);
    procedure SetTopXYLimit(const Value: Integer);
    { 设置TCnDockZone的类引用 }
    procedure SetCnDockZoneClass(const Value: TCnDockZoneClass);

    { 获得分割条的宽度 }
    function GetSplitterWidth: Integer;
    { 获得边框的宽度 }
    function GetBorderWidth: Integer;
    { 设置分割条的宽度 }
    procedure SetSplitterWidth(const Value: Integer);
    { 设置边框的宽度 }
    procedure SetBorderWidth(const Value: Integer);
    function GetDockSiteOrient: TDockOrientation;
    function GetDockSiteSize: Integer;
    procedure SetDockSiteSize(const Value: Integer);
    procedure SetMinSize(const Value: Integer);
    function GetDockSiteBegin: Integer;
    procedure SetDockSiteBegin(const Value: Integer);
    function GetDockSiteSizeA: Integer;
    procedure SetDockSiteSizeA(const Value: Integer);
    procedure SetVersion(const Value: Integer);
    function GetDockSiteSizeWithOrient(Orient: TDockOrientation): Integer;
    procedure SetDockSiteSizeWithOrient(Orient: TDockOrientation;
      const Value: Integer);
    function GetDockRect: TRect;                       //获得停靠矩形
    procedure SetDockRect(const Value: TRect);        //设置停靠矩形
    function GetMinSize: Integer;
  protected
    function HasZoneWithControl(Control: TControl): Boolean;
    { 捕获的DockSite的窗口消息 }
    procedure WindowProc(var Message: TMessage); virtual;
    { ------------------------------------------------------------------------ }
    procedure BeginDrag(Control: TControl;
      Immediate: Boolean; Threshold: Integer = -1); virtual;
    { ------------------------------------------------------------------------ }
    function DoMouseEvent(var Message: TWMMouse;
      var Zone: TCnDockZone; out HTFlag: Integer): TWMNCHitMessage; virtual;
    { ------------------------------------------------------------------------ }
    { 当DockSite上有鼠标移动的时候调用DoMouseMove方法 }
    procedure DoMouseMove(var Message: TWMMouse;
      var Zone: TCnDockZone; out HTFlag: Integer); virtual;
    { 当DockSite上有鼠标左键按下的时候调用DoLButtonDown函数 }
    function DoLButtonDown(var Message: TWMMouse;
      var Zone: TCnDockZone; out HTFlag: Integer): Boolean; virtual;
    { 当DockSite上有鼠标左键释放的时候调用DoLButtonUp方法 }
    procedure DoLButtonUp(var Message: TWMMouse;
      var Zone: TCnDockZone; out HTFlag: Integer); virtual;
    { 当DockSite上有鼠标左键双击的时候调用DoLButtonDbClk方法 }
    procedure DoLButtonDbClk(var Message: TWMMouse;
      var Zone: TCnDockZone; out HTFlag: Integer); virtual;
    { 当DockSite上有鼠标中键按下的时候调用DoLButtonDown函数 }
    procedure DoMButtonDown(var Message: TWMMouse;
      var Zone: TCnDockZone; out HTFlag: Integer); virtual;
    { 当DockSite上有鼠标中键释放的时候调用DoLButtonUp方法 }
    procedure DoMButtonUp(var Message: TWMMouse;
      var Zone: TCnDockZone; out HTFlag: Integer); virtual;
    { 当DockSite上有鼠标左键双击的时候调用DoMButtonDbClk方法 }
    procedure DoMButtonDbClk(var Message: TWMMouse;
      var Zone: TCnDockZone; out HTFlag: Integer); virtual;
    { 当DockSite上有鼠标右键按下的时候调用DoLButtonDown函数 }
    procedure DoRButtonDown(var Message: TWMMouse;
      var Zone: TCnDockZone; out HTFlag: Integer); virtual;
    { 当DockSite上有鼠标右键释放的时候调用DoLButtonUp方法 }
    procedure DoRButtonUp(var Message: TWMMouse;
      var Zone: TCnDockZone; out HTFlag: Integer); virtual;
    { 当DockSite上有鼠标左键双击的时候调用DoRButtonDbClk方法 }
    procedure DoRButtonDbClk(var Message: TWMMouse;
      var Zone: TCnDockZone; out HTFlag: Integer); virtual;
    { ------------------------------------------------------------------------ }
    { 隐藏AZone中的ChildControl }
    procedure DoHideZoneChild(AZone: TCnDockZone); virtual;
    { ------------------------------------------------------------------------ }
    { 当DockSite上要设置光标形状的时候调用DoSetCursor方法 }
    procedure DoSetCursor(var Message: TWMSetCursor;
      var Zone: TCnDockZone; out HTFlag: Integer); virtual;
    { 当DockSite上出现提示框的时候调用DoHintShow方法 }
    procedure DoHintShow(var Message: TCMHintShow;
      var Zone: TCnDockZone; out HTFlag: Integer); virtual;
    { 其他的提示信息 }
    procedure DoOtherHint(Zone: TCnDockZone;
      HTFlag: Integer; var HintStr: string); virtual;
    { ------------------------------------------------------------------------ }
    procedure CustomSaveZone(Stream: TStream;
      Zone: TCnDockZone); virtual;
    procedure CustomLoadZone(Stream: TStream;
      var Zone: TCnDockZone); virtual;
    procedure DoSaveZone(Stream: TStream;
      Zone: TCnDockZone; Level: Integer); virtual;
    procedure DoLoadZone(Stream: TStream); virtual;
    { ------------------------------------------------------------------------ }
    { 调整Control控件的大小 }
    procedure AdjustDockRect(Control: TControl; var ARect: TRect); virtual;
    { 开始调整DockSite的大小，简单的使FResizeCount加一 }
    procedure BeginResizeDockSite;
    { 开始更新，简单的使FUpdateCount加一 }
    procedure BeginUpdate;
    { 计算并且限制分割条的位置 }
    procedure CalcSplitterPos; virtual;
    { 当Control控件的Visible属性改变的时候，调用ControlVisibilityChanged方法 }
    procedure ControlVisibilityChanged(Control: TControl; Visible: Boolean); virtual;
    { 获得Client在DropCtl中的位置 }
    function GetDockAlign(Client: TControl; var DropCtl: TControl): TAlign; virtual;
    { 这个函数是确定光标是在DockSite的什么位置 }
    function DoFindZone(const MousePos: TPoint;
      out HTFlag: Integer; Zone: TCnDockZone): TCnDockZone; virtual;
    { 画分割条移动的时候的外形 }
    procedure DrawSizeSplitter; virtual;
    { 结束调整DockSite的大小，把FResizeCount减一 }
    procedure EndResizeDockSite;
    { 结束更新，把FUpdateCount减一，如果FUpdateCount小于等于零的时候，就调用UpdateAll方法更新 }
    procedure EndUpdate;
    ////////////////////////////////////////////////////////////////////////////
    { 根据输入的Control参数查找到对应的Zone }
    function FindControlZone(Control: TControl; IncludeHide: Boolean = False): TCnDockZone; virtual;
    { 根据输入的Control参数查找到对应的Zone，并且返回这个Zone的Level }
    function FindControlZoneAndLevel(Control: TControl;
      var CtlLevel: Integer; IncludeHide: Boolean = False): TCnDockZone; virtual;
    ////////////////////////////////////////////////////////////////////////////
    { 对整棵树进行遍历 }
    procedure ForEachAt(Zone: TCnDockZone; Proc: TCnForEachZoneProc;
      ScanKind: TTreeScanKind = tskForward; ScanPriority: TTreeScanPriority = tspSibling); virtual;
    { 获得在DockSite中的活动的Control控件 }
    function GetActiveControl: TControl; virtual;
    { 获得把手的大小 }
    function GetGrabberSize: Integer; virtual;
    ////////////////////////////////////////////////////////////////////////////
    function GetBorderHTFlag(const MousePos: TPoint;
      out HTFlag: Integer; Zone: TCnDockZone): TCnDockZone; virtual;
    function GetLeftGrabbersHTFlag(const MousePos: TPoint;
      out HTFlag: Integer; Zone: TCnDockZone): TCnDockZone; virtual;
    function GetRightGrabbersHTFlag(const MousePos: TPoint;
      out HTFlag: Integer; Zone: TCnDockZone): TCnDockZone; virtual;
    function GetTopGrabbersHTFlag(const MousePos: TPoint;
      out HTFlag: Integer; Zone: TCnDockZone): TCnDockZone; virtual;
    function GetBottomGrabbersHTFlag(const MousePos: TPoint;
      out HTFlag: Integer; Zone: TCnDockZone): TCnDockZone; virtual;
    ////////////////////////////////////////////////////////////////////////////
    { 获得停靠预览矩形下面的Control }
    function GetDockEdge(DockRect: TRect; MousePos: TPoint;
      var DropAlign: TAlign; Control: TControl): TControl; virtual;
    ////////////////////////////////////////////////////////////////////////////
    { 获得停靠服务器上的停靠客户的极限, 相对于DockSite }
    function GetDockClientLimit(Orient: TDockOrientation; IsMin: Boolean): Integer; virtual;
    { 获得把手的矩形大小 }
    function GetFrameRect(Control: TControl): TRect; virtual;
    function GetFrameRectEx(Control: TControl): TRect; virtual;
    { 获得分割条的矩形大小 }
    function GetSpiltterRect(Zone: TCnDockZone): TRect; virtual;
    { 设置把手在什么位置 }
    function GetGrabbersPosition: TGrabbersPosition; virtual;
    { 获得Control控件的大小 }
    procedure GetControlBounds(Control: TControl; out CtlBounds: TRect); virtual;
    { 获得分割条的Limit }
    function GetSplitterLimit(AZone: TCnDockZone; IsCurrent, IsMin: Boolean): Integer; virtual;
    procedure DoGetNextLimit(Zone, AZone: TCnDockZone; var LimitResult: Integer); virtual;
    { 获得MousePos位置的HTFlag }
    function GetHTFlag(MousePos: TPoint): Integer; virtual;
    procedure GetSiteInfo(Client: TControl;
      var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean); virtual;
    { 根据输入的鼠标位置判断它下面的Control控件 }
    function HitTest(const MousePos: TPoint; out HTFlag: Integer): TControl; virtual;
    { 根据输入的鼠标位置判断它下面的Zone }
    function InternalHitTest(const MousePos: TPoint;
      out HTFlag: Integer): TCnDockZone; virtual;
    { 插入一个控件，其中InsertAt是插入的位置 }
    procedure InsertControl(Control: TControl; InsertAt: TAlign;
      DropCtl: TControl); virtual;
    { ------------------------------------------------------------------------ }
    { 创建一个新的Zone，然后把NewZone和SiblingZone作为它的ChildZones }
    procedure InsertNewParent(NewZone, SiblingZone: TCnDockZone;
      ParentOrientation: TDockOrientation; InsertLast, Update: Boolean); virtual;
    { 把NewZone作为SiblingZone的兄弟 }
    procedure InsertSibling(NewZone, SiblingZone: TCnDockZone;
        InsertLast, Update: Boolean); virtual;
    { ------------------------------------------------------------------------ }
    { 从流中装载停靠信息 }
    procedure LoadFromStream(Stream: TStream); virtual;
    { 把停靠信息存储到流中 }
    procedure SaveToStream(Stream: TStream); virtual;

    { 以下函数是画图函数 }
    {==========================================================================}
    { 重画整个区域 }
    procedure PaintDockSite; virtual;
    { 重画HostDockSite的矩形外形 }
    procedure DrawDockSiteRect; virtual;
    { 重画每一个节点 }
    procedure DrawZone(Zone: TCnDockZone); virtual;
    { 重画把手 }
    procedure DrawZoneGrabber(Zone: TCnDockZone); virtual;
    procedure DrawDockGrabber(Control: TControl; const ARect: TRect); virtual;
    { 重画分割条 }
    procedure DrawZoneSplitter(Zone: TCnDockZone); virtual;
    procedure DrawSplitterRect(const ARect: TRect); virtual;
    { 重画边框 }
    procedure DrawZoneBorder(Zone: TCnDockZone); virtual;
    { R1为内框大小，R2为外框大小 }
    procedure DrawDockBorder(DockControl: TControl; R1, R2: TRect); virtual;

    {==========================================================================}
    { 得到标题栏的大小 }
    procedure GetCaptionRect(var Rect: TRect); virtual;
    { 定位停靠位置 }
    procedure PositionDockRect(Client, DropCtl: TControl;
      DropAlign: TAlign; var DockRect: TRect); virtual;
    ////////////////////////////////////////////////////////////////////////////
    { 删除全部Zone }
    procedure PruneZone(Zone: TCnDockZone); virtual;
    { 删除单个Zone }
    procedure RemoveZone(Zone: TCnDockZone; Hide: Boolean = True); virtual;
    { 设置Zone的比例 }
    procedure ScaleZone(Zone: TCnDockZone); virtual;

    procedure ScaleChildZone(Zone: TCnDockZone); virtual;   //遍历的时候调用这个函数调整
                                                            //子女Zone的ZoneLimit的值
    procedure ScaleSiblingZone(Zone: TCnDockZone); virtual; //遍历的时候调用这个函数调整
                                                            //兄弟Zone子女Zone的ZoneLimit的值
    { 调整Zone的偏移量 }
    procedure ShiftZone(Zone: TCnDockZone); virtual;
    { 更新Zone }
    procedure UpdateZone(Zone: TCnDockZone); virtual;
    { 画分割条 }
    procedure DrawSplitter(Zone: TCnDockZone); virtual;
    ////////////////////////////////////////////////////////////////////////////
    { 删除Control控件 }
    procedure RemoveControl(Control: TControl); virtual;
    { 设置DockSite中的活动Control控件 }
    procedure SetActiveControl(const Value: TControl); virtual;
    { 设置把手的大小 }
    procedure SetGrabberSize(const Value: Integer); virtual;
    { 设置Zone中ChildControl的大小，包括Zone的ChildZones }
    procedure SetNewBounds(Zone: TCnDockZone); virtual;
    procedure SetReplacingControl(Control: TControl);
    { 当鼠标点击分割条的时候调用SplitterMouseDown方法 }
    procedure SplitterMouseDown(OnZone: TCnDockZone; MousePos: TPoint); virtual;
    { 当鼠标释放分割条的时候调用SplitterMouseUp方法 }
    procedure SplitterMouseUp; virtual;
    { 重新设置范围 }
    procedure ResetBounds(Force: Boolean); virtual;
    { 把控件的名称写到流Stream里面 }
    procedure WriteControlName(Stream: TStream; ControlName: string);
    { 流Stream里面读出控件的名称 }
    procedure ReadControlName(Stream: TStream; var ControlName: string);
    ////////////////////////////////////////////////////////////////////////////
    procedure ShowControl(Control: TControl);           //显示Control
    procedure HideControl(Control: TControl);           //隐藏Control
    procedure ShowAllControl;                           // 显示所有的Control
    procedure HideAllControl;                           // 隐藏所有的Control
    procedure ShowSingleControl(Control: TControl);     // 只显示一个Control，其他的都隐藏
    procedure HideSingleControl(Control: TControl);     // 只隐藏一个Control，其他的都显示
    ////////////////////////////////////////////////////////////////////////////
    { 用新的NewControl替换老的OldControl }
    procedure ReplaceZoneChild(OldControl, NewControl: TControl);
    ////////////////////////////////////////////////////////////////////////////
    property BorderWidth: Integer read GetBorderWidth write SetBorderWidth;
    property Canvas: TControlCanvas read FCanvas;
    property DockSiteSize: Integer read GetDockSiteSize write SetDockSiteSize;
    property DockSiteSizeA: Integer read GetDockSiteSizeA write SetDockSiteSizeA;
    property DockSiteBegin: Integer read GetDockSiteBegin write SetDockSiteBegin;
    property DockSiteSizeWithOrient[Orient: TDockOrientation]: Integer
      read GetDockSiteSizeWithOrient write SetDockSiteSizeWithOrient;
    property GrabberSize: Integer read FGrabberSize write SetGrabberSize;
    property GrabbersPosition: TGrabbersPosition read GetGrabbersPosition;
    property MinSize: Integer read GetMinSize write SetMinSize;
    property DockRect: TRect read GetDockRect write SetDockRect;
    property OldRect: TRect read FOldRect write FOldRect;
    property ParentLimit: Integer read FParentLimit write FParentLimit;
    property ReplacementZone: TCnDockZone read FReplacementZone write FReplacementZone;
    property ResizeCount: Integer read FResizeCount write FResizeCount;
    property ScaleBy: Double read FScaleBy write FScaleBy;
    property ShiftBy: Integer read FShiftBy write FShiftBy;
    property ShiftScaleOrient: TDockOrientation read FShiftScaleOrient write FShiftScaleOrient;
    property SizePos: TPoint read FSizePos write FSizePos;
    property SizingDC: HDC read FSizingDC;
    property SizingWnd: HWND read FSizingWnd;
    property SizingZone: TCnDockZone read FSizingZone write FSizingZone;
    property SplitterWidth: Integer read GetSplitterWidth write SetSplitterWidth;
    property UpdateCount: Integer read FUpdateCount write FUpdateCount;
    property Version: Integer read FVersion write SetVersion;
  public
    SplitterCanvas: TControlCanvas;
    constructor Create(DockSite: TWinControl;
      CnDockZoneClass: TCnDockZoneClass); virtual;
    destructor Destroy; override;
    property DockSite: TWinControl read FDockSite write FDockSite;
    property DockSiteOrient: TDockOrientation read GetDockSiteOrient;
    { 设置分割条的鼠标形状，用户可以重载这个函数来改变鼠标的形状 }
    procedure SetSplitterCursor(CursorIndex: TDockOrientation); virtual;
    { 重画DockSite的界面 }
    procedure PaintSite(DC: HDC); virtual;
    property TopXYLimit: Integer read FTopXYLimit write SetTopXYLimit;
    property TopZone: TCnDockZone read FTopZone write SetTopZone;
    { 更新全部 }
    procedure UpdateAll;
    { 更新当前Zone的子女 }
    procedure UpdateChild(Zone: TCnDockZone);

    property CnDockZoneClass: TCnDockZoneClass read FCnDockZoneClass
      write SetCnDockZoneClass;
  end;

  TCnDockTreeClass = class of TCnDockTree;

  TCnAdvDockTree = class(TCnDockTree)
  private
    FButtonHeight,                  //关闭按钮的高度
    FButtonWidth,                   //关闭按钮的宽度
    FLeftOffset,                    //关闭按钮的左边偏移量
    FRightOffset,                   //关闭按钮的右边偏移量
    FTopOffset,                     //关闭按钮的上边偏移量
    FBottomOffset: Integer;         //关闭按钮的下边偏移量
    FButtonSplitter: Integer;       //按钮之间的间隔
    FCloseBtnZone: TCnAdvDockZone;

    FDropDockSize: Integer;
    FDockHeightWidth: array[TDockOrientation] of Integer;
    FDockRectArr: array[TDockOrientation, Boolean] of Integer;

    procedure SetBottomOffset(const Value: Integer);
    procedure SetButtonHeight(const Value: Integer);
    procedure SetButtonSplitter(const Value: Integer);
    procedure SetButtonWidth(const Value: Integer);
    procedure SetLeftOffset(const Value: Integer);
    procedure SetRightOffset(const Value: Integer);
    procedure SetTopOffset(const Value: Integer);

    function GetDockHeightWidth(Orient: TDockOrientation): Integer;
    procedure SetDockHeightWidth(Orient: TDockOrientation;
      const Value: Integer);

    function GetDockRectFromArr(Orient: TDockOrientation;
      AtLast: Boolean): Integer;
    procedure SetDockRectToArr(Orient: TDockOrientation; AtLast: Boolean;
      const Value: Integer);
    procedure SetDropDockSize(const Value: Integer);

  protected
    function DoLButtonDown(var Message: TWMMouse;
      var Zone: TCnDockZone; out HTFlag: Integer): Boolean; override;
    procedure DoLButtonUp(var Message: TWMMouse;
      var Zone: TCnDockZone; out HTFlag: Integer); override;
    procedure DoMouseMove(var Message: TWMMouse;
      var Zone: TCnDockZone; out HTFlag: Integer); override;
    procedure InsertSibling(NewZone, SiblingZone: TCnDockZone;
      InsertLast, Update: Boolean); override;
    procedure InsertNewParent(NewZone, SiblingZone: TCnDockZone;
      ParentOrientation: TDockOrientation; InsertLast, Update: Boolean); override;
    procedure SetDockHeightWidthArr(NoOrValue, HorValue, VerValue: Integer);
    procedure SetDockRectArr(ARect: TRect);

    procedure ScaleZone(Zone: TCnDockZone); override;
    procedure ScaleChildZone(Zone: TCnDockZone); override;
    procedure ScaleSiblingZone(Zone: TCnDockZone); override;
    procedure ShiftZone(Zone: TCnDockZone); override;

    procedure RemoveZone(Zone: TCnDockZone; Hide: Boolean); override;
  public
    constructor Create(DockSite: TWinControl;
      CnDockZoneClass: TCnDockZoneClass); override;
    property BottomOffset: Integer read FBottomOffset write SetBottomOffset;
    property ButtonHeight: Integer read FButtonHeight write SetButtonHeight;
    property ButtonSplitter: Integer read FButtonSplitter write SetButtonSplitter;
    property ButtonWidth: Integer read FButtonWidth write SetButtonWidth;
    property LeftOffset: Integer read FLeftOffset write SetLeftOffset;
    property RightOffset: Integer read FRightOffset write SetRightOffset;
    property TopOffset: Integer read FTopOffset write SetTopOffset;
    property CloseBtnZone: TCnAdvDockZone read FCloseBtnZone write FCloseBtnZone;
    property DockHeightWidth[Orient: TDockOrientation]: Integer read GetDockHeightWidth write SetDockHeightWidth;
    property DockRectArr[Orient: TDockOrientation; AtLast: Boolean]: Integer read GetDockRectFromArr write SetDockRectToArr;
    property DropDockSize: Integer read FDropDockSize write SetDropDockSize;
  end;
  
var
  //存储树信息的流的结束标志
  TreeStreamEndFlag: Integer = -1;

implementation

uses
  Math, CnDockFormControl, CnDockSupportProc, CnDockGlobal, CnVSNETDockStyle;

type
  TCnWinControlAccess = class(TWinControl);

{ TCnDockZone }

constructor TCnDockZone.Create(Tree: TCnDockTree);
begin
//  FVisibleSize := 0;
  ParentZone := nil;
  PrevSibling := nil;
  NextSibling := nil;
  ChildZones := nil;
  ChildControl := nil;
  FTree := Tree;
  FVisibled := True;
end;

function TCnDockZone.GetChildCount: Integer;
var
  Zone: TCnDockZone;
begin
  Result := 0;
  Zone := ChildZones;
  while Zone <> nil do
  begin
    Zone := Zone.NextSibling;
    Inc(Result);
  end;
end;

function TCnDockZone.GetLimitBegin: Integer;
var
  CheckZone: TCnDockZone;
begin
  if FTree.FTopZone = Self then CheckZone := Self
  else CheckZone := FParentZone;
  if CheckZone.Orientation = doHorizontal then Result := Top
  else if CheckZone.Orientation = doVertical then Result := Left
  else Result := 0;//raise Exception.Create('');
end;

function TCnDockZone.GetLimitSize: Integer;
var
  CheckZone: TCnDockZone;
begin
  if FTree.FTopZone = Self then CheckZone := Self
  else CheckZone := FParentZone;
  if CheckZone.Orientation = doHorizontal then Result := Height
  else if CheckZone.Orientation = doVertical then Result := Width
  else Result := Tree.TopXYLimit; //raise Exception.Create('');
end;

function TCnDockZone.GetTopLeft(Orient: Integer{TDockOrientation}): Integer;
var
  Zone: TCnDockZone;
  R: TRect;
begin
  Zone := Self;
  while Zone <> FTree.FTopZone do
  begin
    // 找到可见的上兄弟。
    if (Zone.VisiblePrevSiblingCount > 0) and (Zone.ParentZone.Orientation = TDockOrientation(Orient)) then
    begin
      Result := Zone.BeforeClosestVisibleZone.ZoneLimit;
      Exit;
    end else Zone := Zone.ParentZone;
  end;
  R := FTree.FDockSite.ClientRect;
  TCnWinControlAccess(FTree.FDockSite).AdjustClientRect(R);
  case TDockOrientation(Orient) of
    doVertical: Result := R.Left;
    doHorizontal: Result := R.Top;
  else
    Result := 0;
  end;
end;

function TCnDockZone.GetHeightWidth(Orient: Integer{TDockOrientation}): Integer;
var
  Zone: TCnDockZone;
  R: TRect;
begin
  if (Self = FTree.FTopZone) or ((FParentZone = FTree.FTopZone) and
    (ChildControl <> nil) and (FTree.FTopZone.ChildCount = 1)) then
  begin
    R := FTree.FDockSite.ClientRect;
    TCnWinControlAccess(FTree.FDockSite).AdjustClientRect(R);
    if TDockOrientation(Orient) = doHorizontal then
      Result := R.Bottom - R.Top
    else
      Result := R.Right - R.Left;
  end
  else begin
    Zone := Self;
    while (Zone <> FTree.FTopZone) and (Zone.ParentZone <> nil) do
    begin
      // 存储
//      BeginLimit := Zone.LimitBegin;
//      while (Zone.NextSibling <> nil) and (not Zone.NextSibling.Visibled) do
//        Zone := Zone.NextSibling;
      if {(Zone.VisiblePrevSiblingCount > 0) and }(Zone.ParentZone.Orientation = TDockOrientation(Orient)) then
      begin
        Result := Zone.ZoneLimit - Zone.LimitBegin;
        Exit;
      end
      else
        Zone := Zone.ParentZone;
    end;
    if FTree.FTopZone.Orientation = TDockOrientation(Orient) then
      Result := FTree.TopXYLimit
    else
      Result := FTree.FTopZone.ZoneLimit;
  end;
end;

procedure TCnDockZone.ResetChildren(Exclude: TCnDockZone);
var
  SumLimit,
  NewLimit,
  FirstChildBegin,
  OldPrevLimit: Integer;
  ChildNode: TCnDockZone;  // 当前的子女节点(可见的)
  PrevNode: TCnDockZone;   // 当前的子女节点的前兄弟(可见的)
begin
  //
  case Orientation of
    doHorizontal: NewLimit := Height;
    doVertical: NewLimit := Width;
  else
    Exit;
  end;
  // 得到第一个可见的子女节点，并且确保它是存在的。
  ChildNode := FirstVisibleChildZone;
  if ChildNode = nil then Exit;
  // 得到平分的ZoneLimit的值
  SumLimit := NewLimit;
  NewLimit := NewLimit div VisibleChildCount;

  FirstChildBegin := ChildNode.LimitBegin;

  Tree.ShiftScaleOrient := Orientation;
  Tree.ParentLimit := 0;
  if ChildNode.ZoneLimit - FirstChildBegin > 0 then
    Tree.ScaleBy := NewLimit / (ChildNode.ZoneLimit - FirstChildBegin)
  else Tree.ScaleBy := 1;
  if (Tree.ScaleBy <> 1) and (ChildNode.VisibleChildCount > 0) then
    Tree.ForEachAt(ChildNode.ChildZones, Tree.ScaleChildZone, tskMiddle, tspChild);

  if ChildNode <> Exclude then
    OldPrevLimit := ChildNode.ZoneLimit
  else OldPrevLimit := FirstChildBegin;


  // 给第一个可见的子女节点的ZoneLimit赋值
  ChildNode.ZoneLimit := FirstChildBegin + NewLimit;
  ChildNode.Update;
  // 保存ChildNode，因为在后面的程序中要用到
  PrevNode := ChildNode;
  ChildNode := ChildNode.AfterClosestVisibleZone;

  // 一直不断的循环，直到最后一个可见的可见的子女节点为止。
  while ChildNode <> nil do
  begin
    if ChildNode.ZoneLimit - OldPrevLimit > 0 then
      Tree.ScaleBy := NewLimit / (ChildNode.ZoneLimit - OldPrevLimit)
    else Tree.ScaleBy := 1;

    Tree.ShiftBy := PrevNode.ZoneLimit - OldPrevLimit;
    if (Tree.ShiftBy <> 0) and (ChildNode.VisibleChildCount > 0){ and (PrevNode <> Exclude) }then
      Tree.ForEachAt(ChildNode.ChildZones, Tree.ShiftZone, tskForward);

    Tree.ParentLimit := PrevNode.ZoneLimit;

    if (Tree.ScaleBy <> 1) and (ChildNode.VisibleChildCount > 0) then
      Tree.ForEachAt(ChildNode.ChildZones, Tree.ScaleChildZone, tskForward);

    if ChildNode <> Exclude then
      OldPrevLimit := ChildNode.ZoneLimit;
//    else OldPrevLimit := PrevNode.ZoneLimit;

    ChildNode.ZoneLimit := PrevNode.ZoneLimit + NewLimit;

    if ChildNode.AfterClosestVisibleZone = nil then
    begin
      // 取消除零错误
      if NewLimit = 0 then
        NewLimit := 1;
      ChildNode.ZoneLimit := ChildNode.ZoneLimit + (SumLimit mod NewLimit);
    end;
    ChildNode.Update;
    PrevNode := ChildNode;
    ChildNode := ChildNode.AfterClosestVisibleZone;
  end;
end;

function TCnDockZone.GetControlName: string;
begin
  Result := '';
  if ChildControl <> nil then
  begin
    if ChildControl.Name = '' then
      raise Exception.CreateRes(@SDockedCtlNeedsName);
    Result := ChildControl.Name;
  end;
end;

function TCnDockZone.SetControlName(const Value: string): Boolean;
var
  Client: TControl;
begin
  Client := nil;
  with FTree do
  begin
    TCnWinControlAccess(FDockSite).ReloadDockedControl(Value, Client);
    Result := Client <> nil;
    if Result then
    begin
      FReplacementZone := Self;
      ChildControl := TWinControl(Client);
      DoCustomSetControlName;
      try
        if IsInside then
        begin
          Client.ManualDock(FDockSite, nil, alNone);
//          CnGlobalDockPresident.CalcDockSizes(Client);
        end;
//        ResetBounds(True);
      finally
        SetChildControlVisible(Client, FControlVisibled);
        FReplacementZone := nil;
      end;
    end;
  end;
end;

procedure TCnDockZone.Update;

  function ParentNotLast: Boolean;
  var
    Parent: TCnDockZone;
  begin
    Result := False;
    Parent := FParentZone;
    while Parent <> nil do
    begin
      if (Parent.VisibleNextSiblingCount > 0) and (Parent.Orientation = ParentZone.Orientation) then
      begin
        Result := True;
        Exit;
      end;
      Parent := Parent.FParentZone;
    end;
  end;

var
  NewWidth, NewHeight: Integer;
  R: TRect;
begin
  if Visibled and (ChildControl <> nil) and (FTree.FUpdateCount = 0) then
  begin
    ChildControl.DockOrientation := FParentZone.Orientation;
    NewWidth := Width;
    NewHeight := Height;
    if ParentNotLast then
    begin
      if FParentZone.Orientation = doHorizontal then
        Dec(NewWidth, FTree.SplitterWidth)
      else
        Dec(NewHeight, FTree.SplitterWidth);
    end;

    if ((NextSibling <> nil) and (VisibleNextSiblingTotal > 0)) or ((FParentZone <> FTree.FTopZone) and
      ((FParentZone.Orientation = FTree.FTopZone.Orientation) and
      (FZoneLimit < FTree.TopXYLimit)) or
      ((FParentZone.Orientation <> FTree.FTopZone.Orientation) and
      (FZoneLimit < FTree.FTopZone.ZoneLimit))) then
    begin
      if FParentZone.Orientation = doHorizontal then
        Dec(NewHeight, FTree.SplitterWidth)
      else
        Dec(NewWidth, FTree.SplitterWidth);
    end;
    R := Bounds(Left, Top, NewWidth, NewHeight);
    FTree.AdjustDockRect(ChildControl, R);
    ChildControl.BoundsRect := R;
  end;
end;

function TCnDockZone.GetFrameRect: TRect;
var
  ALeft, ATop, ARight, ABottom, BorderWidth: Integer;
begin
  ALeft := Left;
  ATop := Top;
  if NextSibling <> nil then
    BorderWidth := Tree.BorderWidth
  else
  BorderWidth := 0;
  ARight := ALeft + Width - BorderWidth;
  ABottom := ATop + Height - BorderWidth;
  Result := Rect(ALeft, ATop, ARight, ABottom);
end;

function TCnDockZone.GetFirstSibling: TCnDockZone;
begin
  Result := Self;
  while Result.PrevSibling <> nil do
    Result := Result.PrevSibling;
end;

function TCnDockZone.GetLastSibling: TCnDockZone;
begin
  Result := Self;
  while (Result <> nil) and (Result.NextSibling <> nil) do
    Result := Result.NextSibling;
end;

function TCnDockZone.GetFirstChild: TCnDockZone;
begin
  Result := ChildZones;
end;

function TCnDockZone.GetLastChild: TCnDockZone;
begin
  Result := ChildZones;
  if Result <> nil then
    Result := Result.LastSibling;
end;

function TCnDockZone.GetTopLeftArr(Orient: TDockOrientation): Integer;
begin
  Result := 0;
  case Orient of
    doHorizontal: Result := Top;
    doVertical: Result := Left;
  else
//    raise Exception.Create('');
  end;
end;

function TCnDockZone.GetHeightWidthArr(Orient: TDockOrientation): Integer;
begin
  Result := 0;
  case Orient of
    doHorizontal: Result := Height;
    doVertical: Result := Width;
  else
//    raise Exception.Create('');
  end;
end;

procedure TCnDockZone.AdjustZoneLimit(Value: Integer);
begin
  FZoneLimit := Value;
  if PrevSibling <> nil then
    PrevSibling.ZoneLimit := PrevSibling.ZoneLimit + Value;
//  else if NextSibling <> nil then
//    NextSibling.ZoneLimit := NextSibling.ZoneLimit
end;

procedure TCnDockZone.SetZoneSize(Size: Integer; Show: Boolean);
begin
  InsertOrRemove(Size, Show, False);
end;

procedure TCnDockZone.InsertOrRemove(DockSize: Integer; Insert: Boolean; Hide: Boolean);
begin
end;

procedure TCnDockZone.Insert(DockSize: Integer; Hide: Boolean);
begin
  InsertOrRemove(DockSize, True, Hide);
  // 如果ParentZone的VisibleChildCount等于0，
  // 说明父节点也是隐藏的，就要调用父节点的Insert函数，
  // 注意，这是一个递归函数，一直调用到父节点可见为止。
  if (ParentZone <> nil) and (ParentZone.VisibleChildCount = 0) then
    ParentZone.Insert(ParentZone.VisibleSize, Hide);

  Visibled := True;
  if ParentZone <> nil then
    ParentZone.ResetChildren(Self);

  // 重新更新ParentZone上的子女节点的位置
  Tree.SetNewBounds(ParentZone);
  Tree.UpdateChild(ParentZone);
end;

procedure TCnDockZone.Remove(DockSize: Integer; Hide: Boolean);
var Zone: TCnDockZone;
begin
  InsertOrRemove(DockSize, False, Hide);
  // 首先设置Visibled。
  Visibled := not Hide;

  // 如果有ParentZone，并且这个ParentZone上没有可见的子女，就把ParentZone也Remove,
  // 这是一个递归函数，目的是使调整从最上层的Zone开始。
  if (ParentZone <> Tree.TopZone) and (ParentZone.VisibleChildCount = 0) then
    ParentZone.Remove(ParentZone.LimitSize, Hide);

  // 如果已经没有可见的后面的兄弟，就把前兄弟的ZoneLimit设成当前Zone的ZoneLimit。
  if AfterClosestVisibleZone = nil then
  begin
    // 找到离当前Zone最近的可见的前兄弟。
    Zone := BeforeClosestVisibleZone;
    if Zone <> nil then
    begin
      // 如果有可见的前兄弟，就调整这个兄弟的所有子女的位置。
      Zone.ZoneLimit := ZoneLimit;
      Tree.SetNewBounds(Zone);
    end;
  end;
  // 因为当前的Zone将被撤消，所以ZoneLimit被设成和LimitBegin一样的。
  ZoneLimit := LimitBegin;
end;

function TCnDockZone.GetVisibleChildCount: Integer;
var
  Zone: TCnDockZone;
begin
  Result := 0;
  Zone := ChildZones;
  while Zone <> nil do
  begin
    if Zone.Visibled then
      Inc(Result);
    Zone := Zone.NextSibling;
  end;
end;

function TCnDockZone.GetChildTotal: Integer;

  procedure DoFindChildCount(Zone: TCnDockZone);
  begin
    if Zone <> nil then
    begin
      DoFindChildCount(Zone.NextSibling);
      DoFindChildCount(Zone.ChildZones);
      {if Zone.Orientation = doNoOrient then}
        Inc(Result);
    end;
  end;

begin
  Result := 0;
  DoFindChildCount(ChildZones);
end;

function TCnDockZone.GetVisibleChildTotal: Integer;

  procedure DoFindVisibleChildCount(Zone: TCnDockZone);
  begin
    if Zone <> nil then
    begin
      DoFindVisibleChildCount(Zone.NextSibling);
      DoFindVisibleChildCount(Zone.ChildZones);
      if {(Zone.Orientation = doNoOrient) and }(Zone.Visibled) then
        Inc(Result);
    end;
  end;

begin
  Result := 0;
  DoFindVisibleChildCount(ChildZones);
end;

function TCnDockZone.GetAfterClosestVisibleZone: TCnDockZone;
begin
  Result := NextSibling;
  while Result <> nil do
  begin
    if Result.Visibled then
      Exit;
    Result := Result.NextSibling;
  end;
end;

function TCnDockZone.GetBeforeClosestVisibleZone: TCnDockZone;
begin
  Result := PrevSibling;
  while Result <> nil do
  begin
    if Result.Visibled then
      Exit;
    Result := Result.PrevSibling;
  end;
end;

function TCnDockZone.GetAfterApoapsisVisibleZone: TCnDockZone;
begin
  Result := LastSibling;
  if Result <> nil then
    Result := Result.BeforeClosestVisibleZone;
  if Self = Result then
    Result := nil;
end;

function TCnDockZone.GetBeforeApoapsisVisibleZone: TCnDockZone;
begin
  Result := ParentZone.ChildZones;
  if Result <> Self then
    Result := Result.AfterClosestVisibleZone;
  if Self = Result then
    Result := nil;
end;

function TCnDockZone.GetNextSiblingCount: Integer;
var AZone: TCnDockZone;
begin
  Result := 0;
  AZone := NextSibling;
  while AZone <> nil do
  begin
    Inc(Result);
    AZone := AZone.NextSibling;
  end;
end;

function TCnDockZone.GetPrevSiblingCount: Integer;
var AZone: TCnDockZone;
begin
  Result := 0;
  AZone := PrevSibling;
  while AZone <> nil do
  begin
    Inc(Result);
    AZone := AZone.PrevSibling;
  end;
end;

procedure TCnDockZone.SetVisibled(const Value: Boolean);
begin
  FVisibled := Value;
  if (not FVisibled) and (Self <> Tree.TopZone) then
  begin
    if ParentZone.Orientation = doNoOrient then
      VisibleSize := Tree.TopXYLimit
    else VisibleSize := LimitSize;
  end else
  begin

  end;
end;

function TCnDockZone.GetVisibleNextSiblingCount: Integer;
var
  Zone: TCnDockZone;
begin
  Result := 0;
  Zone := NextSibling;
  while Zone <> nil do
  begin
    if Zone.Visibled then
      Inc(Result);
    Zone := Zone.NextSibling;
  end;
end;

function TCnDockZone.GetVisibleNextSiblingTotal: Integer;

  procedure DoFindVisibleNextSiblingCount(Zone: TCnDockZone);
  begin
    if Zone <> nil then
    begin
      DoFindVisibleNextSiblingCount(Zone.NextSibling);
      DoFindVisibleNextSiblingCount(Zone.ChildZones);
      if {(Zone.Orientation = doNoOrient) and }(Zone.Visibled) then
        Inc(Result);
    end;
  end;

begin
  Result := 0;
  DoFindVisibleNextSiblingCount(NextSibling);
end;

function TCnDockZone.GetVisiblePrevSiblingCount: Integer;
var
  Zone: TCnDockZone;
begin
  Result := 0;
  Zone := PrevSibling;
  while Zone <> nil do
  begin
    if Zone.Visibled then
      Inc(Result);
    Zone := Zone.PrevSibling;
  end;
end;

function TCnDockZone.GetVisiblePrevSiblingTotal: Integer;

  procedure DoFindVisibleNextSiblingCount(Zone: TCnDockZone);
  begin
    if (Zone <> nil) and (Zone <> Self) then
    begin
      DoFindVisibleNextSiblingCount(Zone.NextSibling);
      DoFindVisibleNextSiblingCount(Zone.ChildZones);
      if {(Zone.Orientation = doNoOrient) and }(Zone.Visibled) then
        Inc(Result);
    end;
  end;

begin
  Result := 0;
  DoFindVisibleNextSiblingCount(ParentZone);
end;

procedure TCnDockZone.SetZoneLimit(const Value: Integer);
begin
  FZoneLimit := Value;
end;

function TCnDockZone.GetFirstVisibleChildZone: TCnDockZone;
begin
  Result := ChildZones;
  while (Result <> nil) and (not Result.Visibled) do
    Result := Result.NextSibling;
end;

function TCnDockZone.GetSplitterLimit(IsMin: Boolean): Integer;
begin
  if IsMin then
    Result := ZoneLimit
  else Result := LimitBegin;
  
  if ChildZones <> nil then
    ChildZones.DoGetSplitterLimit(ParentZone.Orientation, IsMin, Result);
end;

function TCnDockZone.DoGetSplitterLimit(Orientation: TDockOrientation;
  IsMin: Boolean; var LimitResult: Integer): Integer;
begin
  Result := 0;
  if (ParentZone <> nil) and (ParentZone.Orientation = Orientation) and Visibled then
  begin
    if IsMin then
      LimitResult := Min(LimitResult, ZoneLimit)
    else
    begin
      if AfterClosestVisibleZone <> nil then
        LimitResult := Max(LimitResult, ZoneLimit);
    end;
  end;

  if NextSibling <> nil then
    NextSibling.DoGetSplitterLimit(Orientation, IsMin, LimitResult);

  if ChildZones <> nil then
    ChildZones.DoGetSplitterLimit(Orientation, IsMin, LimitResult);
end;

function TCnDockZone.GetLastVisibleChildZone: TCnDockZone;
var Zone: TCnDockZone;
begin
  Result := nil;
  Zone := ChildZones;
  while (Zone <> nil) and Zone.Visibled do
  begin
    Result := Zone;
    Zone := Zone.NextSibling;
  end;
end;

procedure TCnDockZone.DoCustomSetControlName;
begin
  { 没事做 }
end;

procedure TCnDockZone.LButtonDbClkMothed;
begin
  if ChildControl <> nil then
    ChildControl.ManualDock(nil, nil, alTop);
end;

procedure TCnDockZone.SetIsInside(const Value: Boolean);
begin
  FIsInside := Value;
end;

procedure TCnDockZone.SetChildControlVisible(Client: TControl; AViisible: Boolean);
begin
  if Client <> nil then
  begin
    Client.Visible := {(not IsInside) or }FControlVisibled;
  end;
end;

{ TCnDockTree }

constructor TCnDockTree.Create(DockSite: TWinControl;
  CnDockZoneClass: TCnDockZoneClass);
var
  I: Integer;
begin
  FCnDockZoneClass := CnDockZoneClass;
  FBorderWidth := 0;
  FSplitterWidth := 4;
  FDockSite := TWinControl(DockSite);
  FDockSite.ShowHint := True;
  FVersion := gs_BaseDockTreeVersion;
  GrabberSize := 12;
  FMinSize := 12;
  FTopZone := FCnDockZoneClass.Create(Self);
  FBrush := TBrush.Create;
  FBrush.Bitmap := AllocPatternBitmap(clBlack, clWhite);
  // 插入已经存在的控件到树中
  BeginUpdate;
  try
    for I := 0 to DockSite.ControlCount - 1 do
      InsertControl(DockSite.Controls[I], alLeft, nil);
    FTopZone.ResetChildren(nil);
  finally
    EndUpdate;
  end;
  if not (csDesigning in DockSite.ComponentState) then
  begin
    FOldWndProc := FDockSite.WindowProc;
    FDockSite.WindowProc := WindowProc;
  end;
end;

destructor TCnDockTree.Destroy;
begin
  if @FOldWndProc <> nil then
    FDockSite.WindowProc := FOldWndProc;
  PruneZone(FTopZone);
  FBrush.Free;
  inherited Destroy;
end;

procedure TCnDockTree.AdjustDockRect(Control: TControl; var ARect: TRect);
begin
  { 为控件分配空间 }
  { 首先减去边框的宽度 }
  InflateRect(ARect, -BorderWidth, -BorderWidth);
  { 然后再减去把手的宽度 }
  case GrabbersPosition of
    gpTop:
      Inc(ARect.Top, GrabberSize);
    gpBottom:
      Dec(ARect.Bottom, GrabberSize);
    gpLeft:
      Inc(ARect.Left, GrabberSize);
    gpRight:
      Dec(ARect.Right, GrabberSize);
  end;
end;

procedure TCnDockTree.BeginUpdate;
begin
  Inc(FUpdateCount);
end;

procedure TCnDockTree.EndUpdate;
begin
  Dec(FUpdateCount);
  if FUpdateCount <= 0 then
  begin
    FUpdateCount := 0;
    UpdateAll;
  end;
end;

function TCnDockTree.FindControlZone(Control: TControl; IncludeHide: Boolean): TCnDockZone;
var
  CtlZone: TCnDockZone;

  procedure DoFindControlZone(StartZone: TCnDockZone);
  begin
    if (StartZone.ChildControl = Control) and (StartZone.Visibled or IncludeHide) then
      CtlZone := StartZone
    else begin
      // 遍历右兄弟
      if (CtlZone = nil) and (StartZone.NextSibling <> nil) then
        DoFindControlZone(StartZone.NextSibling);
      // 遍历左子女
      if (CtlZone = nil) and (StartZone.ChildZones <> nil) then
        DoFindControlZone(StartZone.ChildZones);
    end;
  end;

begin
  CtlZone := nil;
  if (Control <> nil) and (FTopZone <> nil) then DoFindControlZone(FTopZone);
  Result := CtlZone;
end;

procedure TCnDockTree.ForEachAt(Zone: TCnDockZone; Proc: TCnForEachZoneProc;
  ScanKind: TTreeScanKind; ScanPriority: TTreeScanPriority);

  { 前序遍历 }
  procedure DoForwardForEach(Zone: TCnDockZone);
  begin
    Proc(Zone);
    if ScanPriority = tspSibling then
    begin
      // 遍历右兄弟
      if Zone.NextSibling <> nil then DoForwardForEach(Zone.NextSibling);
      // 遍历左子女
      if Zone.ChildZones <> nil then DoForwardForEach(Zone.ChildZones);
    end else
    begin
      // 遍历左子女
      if Zone.ChildZones <> nil then DoForwardForEach(Zone.ChildZones);
      // 遍历右兄弟
      if Zone.NextSibling <> nil then DoForwardForEach(Zone.NextSibling);
    end;
  end;

  { 中序遍历 }
  procedure DoMiddleForEach(Zone: TCnDockZone);
  begin
    if ScanPriority = tspSibling then
    begin
      // 遍历右兄弟
      if Zone.NextSibling <> nil then DoMiddleForEach(Zone.NextSibling);
    end else
    begin
      // 遍历左子女
      if Zone.ChildZones <> nil then DoMiddleForEach(Zone.ChildZones);
    end;

    Proc(Zone);

    if ScanPriority = tspSibling then
    begin
      // 遍历左子女
      if Zone.ChildZones <> nil then DoMiddleForEach(Zone.ChildZones);
    end else
      // 遍历右兄弟
      if Zone.NextSibling <> nil then DoMiddleForEach(Zone.NextSibling);
  end;

  { 后序遍历 }
  procedure DoBackwardForEach(Zone: TCnDockZone);
  begin
    if ScanPriority = tspSibling then
    begin
      // 遍历右兄弟
      if Zone.NextSibling <> nil then DoBackwardForEach(Zone.NextSibling);
      // 遍历左子女
      if Zone.ChildZones <> nil then DoBackwardForEach(Zone.ChildZones);
    end else
    begin
      // 遍历左子女
      if Zone.ChildZones <> nil then DoForwardForEach(Zone.ChildZones);
      // 遍历右兄弟
      if Zone.NextSibling <> nil then DoForwardForEach(Zone.NextSibling);
    end;
    Proc(Zone);
  end;

begin
  { 如果传过来的参数Zone是nil,就从根目录开始遍历 }
  if Zone = nil then
  begin
    if FTopZone = nil then
      FTopZone := FCnDockZoneClass.Create(Self);
    Zone := FTopZone;
  end;
  { 根据ScanKind参数进行各自的遍历 }
  case ScanKind of
    tskForward: DoForwardForEach(Zone);
    tskMiddle:  DoMiddleForEach(Zone);
    tskBackward:DoBackwardForEach(Zone);
  end;
end;

procedure TCnDockTree.GetControlBounds(Control: TControl; out CtlBounds: TRect);
var
  Z: TCnDockZone;
begin
  Z := FindControlZone(Control);
  if Z = nil then
    FillChar(CtlBounds, SizeOf(CtlBounds), 0)
  else
    with Z do
    begin
      CtlBounds := Bounds(Left, Top, Width, Height);
//      AdjustRect(
    end;
end;

function TCnDockTree.HitTest(const MousePos: TPoint; out HTFlag: Integer): TControl;
var
  Zone: TCnDockZone;
begin
  Zone := InternalHitTest(MousePos, HTFlag);
  if Zone <> nil then Result := Zone.ChildControl
  else Result := nil;
end;

procedure TCnDockTree.InsertControl(Control: TControl; InsertAt: TAlign;
  DropCtl: TControl);
const
{ Delphi6.0 }
{$IFDEF COMPILER6_UP}
  OrientArray: array[TAlign] of TDockOrientation = (doNoOrient, doHorizontal,
    doHorizontal, doVertical, doVertical, doNoOrient, doNoOrient); { alCustom }
  MakeLast: array[TAlign] of Boolean = (False, False, True, False, True, False, False);  { alCustom }
{$ELSE}
{ Delphi5.0 OR LAST }
  OrientArray: array[TAlign] of TDockOrientation = (doNoOrient, doHorizontal,
    doHorizontal, doVertical, doVertical, doNoOrient);
  MakeLast: array[TAlign] of Boolean = (False, False, True, False, True, False);
{$ENDIF}

var
  Sibling,                  // 下一个兄弟
  Me: TCnDockZone;   // 当前正要被创建的节点
  InsertOrientation,        // 插入的方向
  CurrentOrientation: TDockOrientation;// 当前的方向
  NewWidth, NewHeight: Integer;
  R: TRect;                 // 控件的矩形大小
begin
//  if not Control.Visible then Exit;
  if FReplacementZone <> nil then
  begin
    { 如果FReplacementZone <> nil，说明正好在执行装载停靠信息的操作 }
    FReplacementZone.ChildControl := TWinControl(Control);
    FReplacementZone.Update;
    Exit;
  end
  else if FTopZone <> nil then
  begin
    if FTopZone.ChildZones = nil then
    begin
      // 如果树是空的，就要添加第一个子女
      R := FDockSite.ClientRect;
      TCnWinControlAccess(FDockSite).AdjustClientRect(R);
      NewWidth := R.Right - R.Left;
      NewHeight := R.Bottom - R.Top;
      if TCnWinControlAccess(FDockSite).AutoSize then
      begin
        if NewWidth = 0 then NewWidth := Control.UndockWidth;
        if NewHeight = 0 then NewHeight := Control.UndockHeight;
      end;
      R := Bounds(R.Left, R.Top, NewWidth, NewHeight);
      AdjustDockRect(Control, R);
      Control.BoundsRect := R;
      Me := FCnDockZoneClass.Create(Self);
      FTopZone.ChildZones := Me;
      Me.FParentZone := FTopZone;
      Me.ChildControl := TWinControl(Control);
    end
    else begin
      // 默认是停靠到右边
      if InsertAt in [alClient, alNone] then InsertAt := alRight;
      { 查找Control是否已经被停靠进了DockSite中，
        如果是的话就删除这个节点 }
      Me := FindControlZone(Control, True);
      if Me <> nil then RemoveZone(Me, False);
      { 查找到DropCtl所在的节点 }
      Sibling := FindControlZone(DropCtl);
      { 设置插入的方向 }
      InsertOrientation := OrientArray[InsertAt];
      if FTopZone.ChildCount = 1 then
      begin
        // 如果树只有一个子女，并且第二个正在被添加进去，
        // 所以方向和位置必须被重新设置
        FTopZone.Orientation := InsertOrientation;
        case InsertOrientation of
          doHorizontal:
            begin
              FTopZone.ZoneLimit := FTopZone.ChildZones.Width;
              TopXYLimit := FTopZone.ChildZones.Height;
            end;
          doVertical:
            begin
              FTopZone.ZoneLimit := FTopZone.ChildZones.Height;
              TopXYLimit := FTopZone.ChildZones.Width;
            end;
        end;
      end;
      { 创建一个节点，并且把Control赋值给这个节点的ChildControl }
      Me := FCnDockZoneClass.Create(Self);
      Me.ChildControl := TWinControl(Control);
      { 设置CurrentOrientation的方向 }
      if Sibling <> nil then
        { 是DropCtl所在的节点的父节点的方向 }
        CurrentOrientation := Sibling.FParentZone.Orientation
        { 是根节点的方向 }
      else CurrentOrientation := FTopZone.Orientation;
      if InsertOrientation = doNoOrient then
        InsertOrientation := CurrentOrientation;

      // 控件正在被停靠进一个和她相同方向的节点的时候，
      // 需要将自己添加到兄弟的前面或后面
      if InsertOrientation = CurrentOrientation then
        InsertSibling(Me, Sibling, MakeLast[InsertAt], True)
      else
      // 控件正在被停靠进一个和她不同方向的节点的时候，
      // 需要创建一个父节点，并且将自己和兄弟作为这个父节点的子女。
      // 这个父节点的方向和插入方向(InsertOrientation)相同
        InsertNewParent(Me, Sibling, InsertOrientation, MakeLast[InsertAt], True);
    end;
    { 重新画客户区的停靠框架 }
    FDockSite.Invalidate;
  end;

(*  if FTopStoreZone <> nil then
  begin
    if FTopStoreZone.ChildZones = nil then
    begin
      Store := TCnDockZone.Create(Self, True);
      FTopStoreZone.ChildZones := Store;
      Store.FParentZone := FTopStoreZone;
      Store.ChildControl := TWinControl(Control);
    end else
    begin
      // 默认是停靠到右边
      if InsertAt in [alClient, alNone] then InsertAt := alRight;
      { 查找Control是否已经被停靠进了DockSite中，
        如果是的话就删除这个节点 }
      Store := FindControlZone(Control, True);
      if Store <> nil then RemoveZone(Store, True);
      { 查找到DropCtl所在的节点 }
      Sibling := FindControlZone(DropCtl, True);
      { 设置插入的方向 }
      InsertOrientation := OrientArray[InsertAt];
      if FTopStoreZone.ChildCount = 1 then
      begin
        // 如果树只有一个子女，并且第二个正在被添加进去，
        // 所以方向和位置必须被重新设置
        FTopStoreZone.Orientation := InsertOrientation;
      end;

      { 创建一个节点，并且把Control赋值给这个节点的ChildControl }
      Store := TCnDockZone.Create(Self, True);
      Store.ChildControl := TWinControl(Control);
      { 设置CurrentOrientation的方向 }
      if Sibling <> nil then
        { 是DropCtl所在的节点的父节点的方向 }
        CurrentOrientation := Sibling.FParentZone.Orientation
        { 是根节点的方向 }
      else CurrentOrientation := FTopStoreZone.Orientation;
      if InsertOrientation = doNoOrient then
        InsertOrientation := CurrentOrientation;

      // 控件正在被停靠进一个和她相同方向的节点的时候，
      // 需要将自己添加到兄弟的前面或后面
      if InsertOrientation = CurrentOrientation then
        InsertSibling(Store, Sibling, MakeLast[InsertAt], False, True)
      else
      // 控件正在被停靠进一个和她不同方向的节点的时候，
      // 需要创建一个父节点，并且将自己和兄弟作为这个父节点的子女。
      // 这个父节点的方向和插入方向(InsertOrientation)相同
        InsertNewParent(Store, Sibling, InsertOrientation, MakeLast[InsertAt], False, True);
    end;
  end;*)
end;

procedure TCnDockTree.InsertNewParent(NewZone, SiblingZone: TCnDockZone;
  ParentOrientation: TDockOrientation; InsertLast, Update: Boolean);
var
  NewParent: TCnDockZone;
begin
  NewParent := FCnDockZoneClass.Create(Self);

  NewParent.Orientation := ParentOrientation;
  if SiblingZone = nil then
  begin
    // 如果SiblingZone是空的话，我们需要把这个节点作为根节点的子女插入树中
    NewParent.ZoneLimit := TopXYLimit;
    TopXYLimit := FTopZone.ZoneLimit;
    ShiftScaleOrient := ParentOrientation;
    ScaleBy := 0.5;
    if InsertLast then
    begin
      FTopZone.Visibled := FTopZone.VisibleChildCount > 0;
      NewParent.ChildZones := FTopZone;
      FTopZone.ParentZone := NewParent;
      FTopZone.NextSibling := NewZone;
      NewZone.PrevSibling := FTopZone;
      NewZone.ParentZone := NewParent;
      FTopZone := NewParent;
      ForEachAt(NewParent.ChildZones, ScaleZone, tskForward);
    end
    else begin
      NewParent.ChildZones := NewZone;
      FTopZone.ParentZone := NewParent;
      FTopZone.PrevSibling := NewZone;
      NewZone.NextSibling := FTopZone;
      NewZone.ParentZone := NewParent;
      FTopZone := NewParent;

      if ParentOrientation <> FTopZone.Orientation then
        NewZone.ZoneLimit := FTopZone.ZoneLimit div 2
      else NewZone.ZoneLimit := TopXYLimit div 2;

      ForEachAt(NewZone.NextSibling, ScaleZone, tskForward);
      if ParentOrientation <> FTopZone.Orientation then
        ShiftBy := FTopZone.ZoneLimit div 2
      else ShiftBy := TopXYLimit div 2;
      ForEachAt(NewZone.NextSibling, ShiftZone, tskForward);

    end;
    ForEachAt(nil, UpdateZone, tskForward);
  end
  else begin
    // 如果SiblingZone不是空的，我们就要创建一个节点，
    // 这个节点是Me和SiblingZone共同的父亲
    NewParent.ZoneLimit := SiblingZone.ZoneLimit;
    NewParent.ParentZone := SiblingZone.ParentZone;
    NewParent.PrevSibling := SiblingZone.PrevSibling;
    if NewParent.PrevSibling <> nil then
      NewParent.PrevSibling.NextSibling := NewParent;
    NewParent.NextSibling := SiblingZone.NextSibling;
    if NewParent.NextSibling <> nil then
      NewParent.NextSibling.PrevSibling := NewParent;
    if NewParent.ParentZone.ChildZones = SiblingZone then
      NewParent.ParentZone.ChildZones := NewParent;
    NewZone.ParentZone := NewParent;
    SiblingZone.ParentZone := NewParent;
    if InsertLast then
    begin
      // 插入到SiblingZone的后面
      NewParent.ChildZones := SiblingZone;
      SiblingZone.ZoneLimit := NewParent.ParentZone.ZoneLimit;
      SiblingZone.PrevSibling := nil;
      SiblingZone.NextSibling := NewZone;
      NewZone.PrevSibling := SiblingZone;
    end
    else begin
      // 插入到SiblingZone的前面
      NewParent.ChildZones := NewZone;
      SiblingZone.PrevSibling := NewZone;
      SiblingZone.NextSibling := nil;
      NewZone.NextSibling := SiblingZone;
    end;
  end;
  if Update then
  begin
    // 重新设置新子女的范围
    NewParent.ResetChildren(nil);
    ForEachAt(nil, UpdateZone, tskForward);
  end;
end;

procedure TCnDockTree.InsertSibling(NewZone, SiblingZone: TCnDockZone;
  InsertLast, Update: Boolean);
begin
  if (NewZone <> nil) and (SiblingZone <> nil) and
    (NewZone.ChildControl = SiblingZone.ChildControl) then
    SiblingZone := nil;
  if SiblingZone = nil then
  begin
    SiblingZone := FTopZone.ChildZones;
    if InsertLast then
      SiblingZone := SiblingZone.LastSibling;
  end;
  if InsertLast then
  begin
    // 把NewZone插入到SiblingZone后
    NewZone.ParentZone := SiblingZone.ParentZone;
    NewZone.PrevSibling := SiblingZone;
    NewZone.NextSibling := SiblingZone.NextSibling;
    if NewZone.NextSibling <> nil then
      NewZone.NextSibling.PrevSibling := NewZone;
    SiblingZone.NextSibling := NewZone;
  end
  else begin
    // 把NewZone插入到SiblingZone前
    NewZone.NextSibling := SiblingZone;
    NewZone.PrevSibling := SiblingZone.PrevSibling;
    if NewZone.PrevSibling <> nil then
      NewZone.PrevSibling.NextSibling := NewZone;
    SiblingZone.PrevSibling := NewZone;
    NewZone.ParentZone := SiblingZone.ParentZone;
    if NewZone.ParentZone.ChildZones = SiblingZone then
      NewZone.ParentZone.ChildZones := NewZone;
  end;
  if Update then
  begin
    // 重新设置所有的兄弟
    SiblingZone.ParentZone.ResetChildren(nil);
    UpDateChild(SiblingZone.ParentZone);
  end;
end;

function TCnDockTree.DoFindZone(const MousePos: TPoint;
  out HTFlag: Integer; Zone: TCnDockZone): TCnDockZone;
const HTFlagArr : array[Boolean] of Integer = (HTCLIENT, HTSPLITTER);
begin
  Result := nil;
  // 鼠标是否在分割条的底部...
  if (Zone.ParentZone.Orientation = doHorizontal) and
    (Zone.NextSibling <> nil) and
    ((MousePos.Y <= Zone.FZoneLimit) and
    (MousePos.Y >= Zone.FZoneLimit - SplitterWidth)) and
    ((MousePos.X <= Zone.ParentZone.FZoneLimit) and
    (MousePos.X >= Zone.ParentZone.LimitBegin)) then
  begin
    HTFlag := HTFlagArr[Zone.VisibleNextSiblingTotal > 0];
    Result := Zone;
  end

  // 鼠标是否在分割条的左部...
  else if (Zone.FParentZone.Orientation = doVertical) and
    (Zone.NextSibling <> nil) and
    ((MousePos.X <= Zone.FZoneLimit) and
    (MousePos.X >= Zone.FZoneLimit - SplitterWidth)) and
    ((MousePos.Y <= Zone.ParentZone.FZoneLimit) and
    (MousePos.Y >= Zone.ParentZone.LimitBegin)) then
  begin
    HTFlag := HTFlagArr[Zone.VisibleNextSiblingTotal > 0];
    Result := Zone;
  end

  // 鼠标是否在把手内...
  else if Zone.ChildControl <> nil then
  begin
    { 鼠标是否在边框内... }
    case GrabbersPosition of
      gpTop:    Result := GetTopGrabbersHTFlag(MousePos, HTFlag, Zone);
      gpLeft:   Result := GetLeftGrabbersHTFlag(MousePos, HTFlag, Zone);
      gpBottom: Result := GetBottomGrabbersHTFlag(MousePos, HTFlag, Zone);
      gpRight:  Result := GetRightGrabbersHTFlag(MousePos, HTFlag, Zone);
    end;
    { 如果不在把手上，就查找是否在边框内... }
    if Result = nil then
      Result := GetBorderHTFlag(MousePos, HTFlag, Zone);
  end else Result := nil;

  if (Result <> nil) and (not Result.Visibled) then Result := nil;
  
  // 遍历别的节点...
  if (Result = nil) and (Zone.NextSibling <> nil) then
    Result := DoFindZone(MousePos, HTFlag, Zone.NextSibling);
  if (Result = nil) and (Zone.ChildZones <> nil) then
    Result := DoFindZone(MousePos, HTFlag, Zone.ChildZones);
end;

function TCnDockTree.InternalHitTest(const MousePos: TPoint; out HTFlag: Integer): TCnDockZone;
var
  ResultZone: TCnDockZone;

  function FindControlAtPos(const Pos: TPoint): TControl;
  var
    I: Integer;
    P: TPoint;
  begin
    for I := FDockSite.ControlCount - 1 downto 0 do
    begin
      Result := FDockSite.Controls[I];
      with Result do
      begin
        { 控件必须被显示出来... }
        if not Result.Visible or ((Result is TWinControl) and
           not TWinControl(Result).Showing) then continue;
        P := Point(Pos.X - Left, Pos.Y - Top);
        if PtInRect(ClientRect, P) then Exit;
      end;
    end;
    Result := nil;
  end;

var
  CtlAtPos: TControl;
begin
  ResultZone := nil;
  HTFlag := HTNOWHERE;
  CtlAtPos := FindControlAtPos(MousePos);
  if (CtlAtPos <> nil) and (CtlAtPos.HostDockSite = FDockSite) then
  begin
    ResultZone := FindControlZone(CtlAtPos);
    if ResultZone <> nil then HTFlag := HTCLIENT;
  end
  else if (FTopZone <> nil) and (FTopZone.ChildZones <> nil) and (FTopZone.ChildCount >= 1) and
    (CtlAtPos = nil) then
    ResultZone := DoFindZone(MousePos, HTFlag, FTopZone.ChildZones);
  Result := ResultZone;
end;

procedure TCnDockTree.LoadFromStream(Stream: TStream);
var
//  CompName: string;
//  Client: TControl;
//  I, InVisCount: Integer;
  I: Integer;
begin
  PruneZone(FTopZone);
  // 读版本, 如果版本不同,就退出
  Stream.Read(I, SizeOf(I));
  if I <> Version then
    Exit;

  BeginUpdate;
  try
    // 读根节点的数据
    Stream.Read(FTopXYLimit, SizeOf(FTopXYLimit));
    // 读整棵树的节点
    DoLoadZone(Stream);
  finally
    EndUpdate;
  end;
end;

{procedure TCnDockTree.PaintDockGrabber(Canvas: TCanvas; Control: TControl;
  const ARect: TRect);

  procedure DrawCloseButton(Left, Top: Integer);
  begin
    DrawFrameControl(Canvas.Handle, Rect(Left, Top, Left+FGrabberSize-2,
      Top+FGrabberSize-2), DFC_CAPTION, DFCS_CAPTIONCLOSE);
  end;

  procedure DrawGrabberLine(Left, Top, Right, Bottom: Integer);
  begin
    with Canvas do
    begin
      Pen.Color := clBtnHighlight;
      MoveTo(Right, Top);
      LineTo(Left, Top);
      LineTo(Left, Bottom);
      Pen.Color := clBtnShadow;
      LineTo(Right, Bottom);
      LineTo(Right, Top-1);
    end;
  end;

begin
  with ARect do
  begin
    case GrabbersPosition of
      gpLeft:
      begin
        DrawCloseButton(Left+BorderWidth+BorderWidth+1, Top+BorderWidth+BorderWidth+1);
        DrawGrabberLine(Left+BorderWidth+3, Top+FGrabberSize+BorderWidth+1, Left+BorderWidth+5, Bottom+BorderWidth-2);
        DrawGrabberLine(Left+BorderWidth+6, Top+FGrabberSize+BorderWidth+1, Left+BorderWidth+8, Bottom+BorderWidth-2);
      end;
      gpTop:
      begin
        DrawCloseButton(Right-FGrabberSize+BorderWidth+1, Top+BorderWidth+1);
        DrawGrabberLine(Left+BorderWidth+2, Top+BorderWidth+BorderWidth+3, Right-FGrabberSize+BorderWidth-2, Top+BorderWidth+5);
        DrawGrabberLine(Left+BorderWidth+2, Top+BorderWidth+BorderWidth+6, Right-FGrabberSize+BorderWidth-2, Top+BorderWidth+8);
      end;
//      gpBottom:
//      gpRight:
    end;
  end;
end;}

procedure TCnDockTree.PaintSite(DC: HDC);
(*var
  Canvas: TControlCanvas;
  Control: TControl;
  I: Integer;
  R: TRect;
  DockClient: TCnDockClient;
begin
  { 创建一个TControlCanvas对象 }
  Canvas := TControlCanvas.Create;
  try
    { 然后把DockSite赋给Canvas的Control属性，这样Canvas就可以在DockSite上画图案了 }
    Canvas.Control := FDockSite;
    Canvas.Lock;
    try
      Canvas.Handle := DC;
      try
        { 开始循环重画DockSite上的Clients }
        for I := 0 to FDockSite.ControlCount - 1 do
        begin
          Control := FDockSite.Controls[I];
          if Control.Visible and (Control.HostDockSite = FDockSite) then
          begin
            { 查找到Control上面的TCnDockClient类 }
            DockClient := FindDockClient(Control);
            { 获得把手的矩形大小 }
            R := GetFrameRect(Control);
            { 画把手 }
            PaintDockGrabber(Canvas, Control, R);
            { 调用用户自定义画事件 }
            if DockClient <> nil then
              DockClient.DoPaintDockGrabber(Canvas, Control, R);
          end;
        end;
        SplitterCanvas := Canvas;
        SplitterCanvas.Brush.Color := TCnWinControlAccess(DockSite).Color;
        { 画分割条 }
        ForEachAt(nil, DrawSplitter, tskBackward);
        SplitterCanvas := nil;
        { 画DockSite的边框 }
        PaintDockSiteRect(Canvas);
      finally
        Canvas.Handle := 0;
      end;
    finally
      Canvas.Unlock;
    end;
  finally
    Canvas.Free;
  end;*)

begin
  { 创建一个TControlCanvas对象 }
  FCanvas := TControlCanvas.Create;
  try
    { 然后把DockSite赋给Canvas的Control属性，这样Canvas就可以在DockSite上画图案了 }
    FCanvas.Control := FDockSite;
    FCanvas.Lock;
    try
      FCanvas.Handle := DC;
      try
        PaintDockSite;
      finally
        FCanvas.Handle := 0;
      end;
    finally
      FCanvas.Unlock;
    end;
  finally
    FCanvas.Free;
    FCanvas := nil;
  end;

end;

procedure TCnDockTree.PositionDockRect(Client, DropCtl: TControl;
  DropAlign: TAlign; var DockRect: TRect);
var
  VisibleClients,
  NewX, NewY, NewWidth, NewHeight: Integer;
begin
  VisibleClients := FDockSite.VisibleDockClientCount;
  { 当DockSite小于两个停靠控件在她里面，DockRect就应该作为被设置成DockSite的客户区域 }
  if (DropCtl = nil) or (DropCtl.DockOrientation = doNoOrient) or
     {(DropCtl = Client) or }(VisibleClients < 2) then
  begin
    DockRect := Rect(0, 0, FDockSite.ClientWidth, FDockSite.ClientHeight);
    { 当那里有一个停靠客户我们把DockSite的客户区分成一半 }
    if VisibleClients > 0 then
    with DockRect do
      case DropAlign of
        alLeft: Right := Right div 2;
        alRight: Left := Right div 2;
        alTop: Bottom := Bottom div 2;
        alBottom: Top := Bottom div 2;
      end;
  end
  else begin
    { 否者，如果DockSite包含超过一个客户的时候， 根据鼠标下面的控件设置DockRect的坐标}
    NewX := DropCtl.Left;
    NewY := DropCtl.Top;
    NewWidth := DropCtl.Width;
    NewHeight := DropCtl.Height;
    if DropAlign in [alLeft, alRight] then
      NewWidth := DropCtl.Width div 2
    else if DropAlign in [alTop, alBottom] then
      NewHeight := DropCtl.Height div 2;
    case DropAlign of
      alRight: Inc(NewX, NewWidth);
      alBottom: Inc(NewY, NewHeight);
    end;
    DockRect := Bounds(NewX, NewY, NewWidth, NewHeight);
    if DropAlign = alClient then
      DockRect := Bounds(NewX, NewY, NewWidth, NewHeight);
  end;
  MapWindowPoints(FDockSite.Handle, 0, DockRect, 2);
end;

procedure TCnDockTree.PruneZone(Zone: TCnDockZone);

  procedure DoPrune(Zone: TCnDockZone);
  begin
    // 遍历右兄弟
    if Zone.NextSibling <> nil then
      DoPrune(Zone.NextSibling);
    // 遍历左子女
    if Zone.ChildZones <> nil then
      DoPrune(Zone.ChildZones);
    // 删除节点
    Zone.Free;
  end;

begin
  if Zone = nil then Exit;
  // 首先递归的删除子女
  if Zone.ChildZones <> nil then DoPrune(Zone.ChildZones);
  // 修正这个Zone
  if Zone.FPrevSibling <> nil then
    Zone.FPrevSibling.NextSibling := Zone.NextSibling
  else if Zone.FParentZone <> nil then
    Zone.FParentZone.ChildZones := Zone.NextSibling;
  if Zone.NextSibling <> nil then
    Zone.NextSibling.FPrevSibling := Zone.FPrevSibling;
  // 删除这个Zone
  if Zone = FTopZone then FTopZone := nil;
  Zone.Free;
end;

procedure TCnDockTree.RemoveControl(Control: TControl);
var
  Z: TCnDockZone;
begin
  Z := FindControlZone(Control, True);
  if (Z <> nil) then
  begin
    if Z = FReplacementZone then
      Z.ChildControl := nil
    else
    begin
      if (Z.ParentZone.Orientation <> doNoOrient) and Z.Visibled then
        Z.Remove(Z.LimitSize, False);
      RemoveZone(Z, False);
      SetNewBounds(nil);
      UpdateAll;
    end;
    Control.DockOrientation := doNoOrient;
    { 重画DockSite }
    FDockSite.Invalidate;
  end;
end;

procedure TCnDockTree.RemoveZone(Zone: TCnDockZone; Hide: Boolean);
var
  Sibling, LastChild: TCnDockZone;
  VisibleZoneChildCount,
  ZoneChildCount: Integer;
  label LOOP;
begin
  if not Hide then
  begin
    if Zone = nil then
      raise Exception.Create(SDockTreeRemoveError + SDockZoneNotFound);
    if Zone.ChildControl = nil then
      raise Exception.Create(SDockTreeRemoveError + SDockZoneHasNoCtl);
    VisibleZoneChildCount := Zone.ParentZone.VisibleChildCount;
    ZoneChildCount := Zone.ParentZone.ChildCount;
    if VisibleZoneChildCount <= 1 then
    begin
      // 如果当前节点的父节点只有一个可见的节点，就需要把父节点隐藏掉

      // 首先连接前兄弟和后兄弟
      if Zone.PrevSibling = nil then
      begin
        Zone.ParentZone.ChildZones := Zone.NextSibling;
        if Zone.NextSibling <> nil then
          Zone.NextSibling.PrevSibling := nil;
      end else if Zone.NextSibling = nil then
        Zone.PrevSibling.NextSibling := nil
      else
      begin
        Zone.PrevSibling.NextSibling := Zone.NextSibling;
        Zone.NextSibling.PrevSibling := Zone.PrevSibling;
      end;
      // 隐藏掉父节点，注意，这是一个递归函数，如果父节点的父节点的可见子女个数也是0，
      // 就要隐藏掉父节点的父节点
{      if Zone.ParentZone <> TopZone then
        Zone.ParentZone.Remove(Zone.ParentZone.LimitSize, True)
      else}
//      goto LOOP;
//      Exit;
    end;
    if ZoneChildCount = 2 then
    begin
      // 这个节点只有一个兄弟节点
      if Zone.PrevSibling = nil then Sibling := Zone.NextSibling
      else Sibling := Zone.PrevSibling;
      if Sibling.ChildControl <> nil then
      begin
        // 兄弟节点是一个有ChildControl并且没有子节点的节点
        if Zone.ParentZone = FTopZone then
        begin
          // 如果父节点是根节点，就删除这个节点
          FTopZone.ChildZones := Sibling;
          Sibling.PrevSibling := nil;
          Sibling.NextSibling := nil;
          Sibling.ZoneLimit := FTopZone.LimitSize;
          Sibling.Update;
        end
        else begin
          // 否则，就把兄弟节点的ChildControl移动到父节点上，然后删除这个兄弟节点
          Zone.ParentZone.Orientation := doNoOrient;
          Zone.ParentZone.ChildControl := Sibling.ChildControl;
          Zone.ParentZone.ChildZones := nil;
          Sibling.Free;
        end;
        // 更新父节点
        ForEachAt(Zone.ParentZone, UpdateZone, tskForward);
      end
      else begin
        // 兄弟节点是一个有子节点的节点，所以兄弟节点必须被放到根节点上，
        // 或者放到上一级
        if Zone.ParentZone = FTopZone then
        begin
          // 节点是根节点的子节点，所以兄弟节点必须成为根节点
          Sibling.ZoneLimit := TopXYLimit;
          TopXYLimit := FTopZone.ZoneLimit;
          FTopZone.Free;
          FTopZone := Sibling;
          Sibling.NextSibling := nil;
          Sibling.PrevSibling := nil;
          Sibling.ParentZone := nil;
        end
        else
        begin
          // 节点的父节点不是根节点，所以子节点必须被放到父节点上
          Sibling.ChildZones.PrevSibling := Zone.ParentZone.PrevSibling;
          if Sibling.ChildZones.PrevSibling = nil then
            Zone.ParentZone.ParentZone.ChildZones := Sibling.ChildZones
          else
            Sibling.ChildZones.PrevSibling.NextSibling := Sibling.ChildZones;
          LastChild := Sibling.ChildZones;
          LastChild.ParentZone := Zone.ParentZone.ParentZone;
          repeat
            LastChild := LastChild.NextSibling;
            if LastChild <> nil then
              LastChild.ParentZone := Zone.ParentZone.ParentZone
            else
              Break;
          until LastChild.NextSibling = nil;
          if LastChild <> nil then
          begin
          LastChild.NextSibling := Zone.ParentZone.NextSibling;
            if LastChild.NextSibling <> nil then
              LastChild.NextSibling.PrevSibling := LastChild;
            ForEachAt(LastChild.ParentZone, UpdateZone, tskForward);
          end;
          Zone.ParentZone.Free;
          Sibling.Free;
        end;
      end;
    end
    else begin
      // 这个节点有多个兄弟节点
      if Zone.PrevSibling = nil then
      begin
        // 存在父节点的第一个节点，所以使下一个兄弟节点作为父节点的子女，
        // 并且把本身删除掉
        Zone.ParentZone.ChildZones := Zone.NextSibling;
        if Zone.NextSibling <> nil then
        begin
          Zone.NextSibling.PrevSibling := nil;
          Zone.NextSibling.Update;
        end;
      end
      else begin
        // 不存在父节点的第一个节点，所以删除这个节点，并且调整兄弟节点
        Zone.PrevSibling.NextSibling := Zone.NextSibling;
        if Zone.NextSibling <> nil then
          Zone.NextSibling.PrevSibling := Zone.PrevSibling;
        Zone.PrevSibling.ZoneLimit := Zone.ZoneLimit;
        Zone.PrevSibling.Update;
      end;
      ForEachAt(Zone.ParentZone, UpdateZone, tskForward);
    end;
LOOP:
    Zone.Free;
  end;
  SetNewBounds(nil);
  UpdateAll;
end;

procedure TCnDockTree.ResetBounds(Force: Boolean);
var
  R: TRect;
begin
  if not (csLoading in FDockSite.ComponentState) and
    (FTopZone <> nil) and (FDockSite.DockClientCount > 0) then
  begin
    R := FDockSite.ClientRect;
    TCnWinControlAccess(FDockSite).AdjustClientRect(R);
    if Force or (not CompareMem(@R, @FOldRect, SizeOf(TRect))) then
    begin
      FOldRect := R;
      case FTopZone.Orientation of
        doHorizontal:
          begin
            FTopZone.ZoneLimit := R.Right - R.Left;
            // 当R.Bottom = R.Top的时候，说明DockSite的高度为0，
            // 因为程序是在从新设置高度后再调整ZoneLimit的，
            // 所以这条语句是使当高度设置成0的时候不能调整TopXYLimit
            if R.Bottom - R.Top > 0 then
              TopXYLimit := R.Bottom - R.Top;
          end;
        doVertical:
          begin
            FTopZone.ZoneLimit := R.Bottom - R.Top;
            // 当R.Right = R.Left的时候，说明DockSite的宽度为0，
            // 因为程序是在从新设置宽度后再调整ZoneLimit的，
            // 所以这条语句是使当宽度设置成0的时候不能调整TopXYLimit
            if R.Right - R.Left > 0 then
              TopXYLimit := R.Right - R.Left;
          end;
      end;
      SetNewBounds(nil);
      if FUpdateCount = 0 then ForEachAt(nil, UpdateZone, tskForward);
    end;
  end;
end;

procedure TCnDockTree.ScaleZone(Zone: TCnDockZone);
begin
  { ScaleZone的公式是ScaleChildZone公式的特殊情况,
    当FParentLimit是0的时候,这两个公式相等 }
  FParentLimit := 0;
  ScaleChildZone(Zone);
end;

procedure TCnDockTree.SaveToStream(Stream: TStream);
begin
  // 写流的版本
  Stream.Write(FVersion, SizeOf(FVersion));
  // 写根节点数据
  Stream.Write(FTopXYLimit, SizeOf(FTopXYLimit));
  // 从树上写所有节点的数据
  DoSaveZone(Stream, FTopZone, 0);
  Stream.Write(TreeStreamEndFlag, SizeOf(TreeStreamEndFlag));
end;

procedure TCnDockTree.SetNewBounds(Zone: TCnDockZone);

  procedure DoSetNewBounds(Zone: TCnDockZone);
  begin
    if Zone <> nil then
    begin
      if (Zone.VisibleNextSiblingCount = 0) and (Zone <> FTopZone) then
      begin
        if Zone.ParentZone = FTopZone then
          Zone.ZoneLimit := FTopXYLimit
        else
          Zone.ZoneLimit := Zone.ParentZone.ParentZone.FZoneLimit;
      end;
      if Zone.ChildZones <> nil then DoSetNewBounds(Zone.ChildZones);
      if Zone.NextSibling <> nil then DoSetNewBounds(Zone.NextSibling);
    end;
  end;

begin
  if IsLoading then Exit;
  if Zone = nil then Zone := FTopZone.ChildZones;
  DoSetNewBounds(Zone);
  { 重新画停靠框架 }
  FDockSite.Invalidate;
end;

procedure TCnDockTree.SetReplacingControl(Control: TControl);
begin
  FReplacementZone := FindControlZone(Control);
end;

procedure TCnDockTree.ShiftZone(Zone: TCnDockZone);
begin
  if (Zone <> nil) and (Zone <> FTopZone) and
    (Zone.ParentZone.Orientation = FShiftScaleOrient) then
  begin
    Inc(Zone.FZoneLimit, FShiftBy);
    if Zone.LimitSize < FMinSize then
      Zone.FZoneLimit := Zone.LimitBegin + FMinSize;
  end;
end;

procedure TCnDockTree.SplitterMouseDown(OnZone: TCnDockZone; MousePos: TPoint);
begin
  FSizingZone := OnZone;
  Mouse.Capture := FDockSite.Handle;
  FSizingWnd := FDockSite.Handle;
  FSizingDC := GetDCEx(FSizingWnd, 0, DCX_CACHE or DCX_CLIPSIBLINGS or
    DCX_LOCKWINDOWUPDATE);
  FSizePos := MousePos;
  DrawSizeSplitter;
end;

procedure TCnDockTree.SplitterMouseUp;

  procedure SetSiblingZoneSize(PosXY: Integer);
  var AZone: TCnDockZone;
    PrevCount, NextCount: Integer;
  begin
    { 处理PrevSibling }
    PrevCount := FSizingZone.PrevSiblingCount;
    AZone := FSizingZone.ParentZone.ChildZones;
    while (AZone <> nil) and (AZone <> FSizingZone) do
    begin
      if AZone.ZoneLimit >= PosXY - PrevCount * MinSize +
        Integer(AZone.PrevSibling = nil) * (SplitterWidth div 2) then
      begin
        AZone.ZoneLimit := PosXY - PrevCount * MinSize +
          Integer(AZone.PrevSibling = nil) * (SplitterWidth div 2);
        Break;
      end;
      Dec(PrevCount);
      AZone := AZone.NextSibling;
    end;

    AZone := AZone.NextSibling;
    while PrevCount > 0 do
    begin
      Dec(PrevCount);
      AZone.ZoneLimit := AZone.LimitBegin + MinSize;
      AZone := AZone.NextSibling;
    end;

    { 处理NextSibling }
    NextCount := 1;
    AZone := FSizingZone.NextSibling;
    while (AZone <> nil) do
    begin
      if AZone.ZoneLimit <= PosXY + NextCount * MinSize +
        Integer(AZone.NextSibling <> nil) * (SplitterWidth div 2) then
        AZone.ZoneLimit := PosXY + NextCount * MinSize +
          Integer(AZone.NextSibling <> nil) * (SplitterWidth div 2);
      Inc(NextCount);
      AZone := AZone.NextSibling;
    end;
  end;

begin
  Mouse.Capture := 0;
  DrawSizeSplitter;
  ReleaseDC(FSizingWnd, FSizingDC);
  if FSizingZone.ParentZone.Orientation = doHorizontal then
  begin
    FSizingZone.ZoneLimit := FSizePos.y + (SplitterWidth div 2);
    SetSiblingZoneSize(FSizePos.y);
  end else
  begin
    FSizingZone.ZoneLimit := FSizePos.x + (SplitterWidth div 2);
    SetSiblingZoneSize(FSizePos.x);
  end;
  SetNewBounds(FSizingZone.ParentZone);
  ForEachAt(FSizingZone.ParentZone, UpdateZone, tskForward);
  FSizingZone := nil;
end;

procedure TCnDockTree.UpdateAll;
begin
  if (FUpdateCount = 0) and (FDockSite.DockClientCount > 0) then
    ForEachAt(nil, UpdateZone, tskForward);
end;

procedure TCnDockTree.UpdateZone(Zone: TCnDockZone);
begin
  if (FUpdateCount = 0) then
    Zone.Update;
end;

procedure TCnDockTree.DrawSizeSplitter;
var
  R: TRect;
  PrevBrush: HBrush;
begin
  if FSizingZone <> nil then
  begin
    with R do
    begin
      if FSizingZone.ParentZone.Orientation = doHorizontal then
      begin
        Left := FSizingZone.Left;
        Top := FSizePos.Y - (SplitterWidth div 2);
        Right := Left + FSizingZone.Width;
        Bottom := Top + SplitterWidth;
      end
      else begin
        Left := FSizePos.X - (SplitterWidth div 2);
        Top := FSizingZone.Top;
        Right := Left + SplitterWidth;
        Bottom := Top + FSizingZone.Height;
      end;
    end;
    PrevBrush := SelectObject(FSizingDC, FBrush.Handle);
    with R do
      PatBlt(FSizingDC, Left, Top, Right - Left, Bottom - Top, PATINVERT);
    SelectObject(FSizingDC, PrevBrush);
  end;
end;

function TCnDockTree.GetSplitterLimit(AZone: TCnDockZone; IsCurrent, IsMin: Boolean): Integer;
begin
  if IsCurrent then
  begin
    Result := AZone.GetSplitterLimit(False);
  end else
  begin
    if AZone.AfterClosestVisibleZone <> nil then
      Result := AZone.AfterClosestVisibleZone.GetSplitterLimit(True)
    else
      Result := AZone.ZoneLimit + AZone.LimitSize;
  end;
end;

procedure TCnDockTree.ControlVisibilityChanged(Control: TControl;
  Visible: Boolean);
begin
  if Visible then
  begin
    ShowControl(Control);
  end else
    HideControl(Control);
end;

procedure TCnDockTree.WindowProc(var Message: TMessage);
var TempZone: TCnDockZone;
  HitTestValue: Integer;
begin
  case Message.Msg of
    CM_DOCKNOTIFICATION:
      with TCMDockNotification(Message) do
        if (NotifyRec.ClientMsg = CM_VISIBLECHANGED) then
          ControlVisibilityChanged(Client, Boolean(NotifyRec.MsgWParam));
    WM_MOUSEMOVE:
      { 捕获鼠标移动的消息 }
      DoMouseMove(TWMMouse(Message), TempZone, HitTestValue);
    WM_LBUTTONDBLCLK:
      { 捕获到鼠标左键双击的消息 }
      DoLButtonDbClk(TWMMouse(Message), TempZone, HitTestValue);
    WM_LBUTTONDOWN:
      { 捕获到鼠标左键按下的消息 }
      if DoLButtonDown(TWMMouse(Message), TempZone, HitTestValue) then Exit;
    WM_LBUTTONUP:
      { 捕获到鼠标左键放开的消息 }
      DoLButtonUp(TWMMouse(Message), TempZone, HitTestValue);
    WM_MBUTTONDOWN:
      { 捕获到鼠标中键按下的消息 }
      DoMButtonDown(TWMMouse(Message), TempZone, HitTestValue);
    WM_MBUTTONUP:
      { 捕获到鼠标中键放开的消息 }
      DoMButtonUp(TWMMouse(Message), TempZone, HitTestValue);
    WM_RBUTTONDOWN:
      { 捕获到鼠标右键按下的消息 }
      DoRButtonDown(TWMMouse(Message), TempZone, HitTestValue);
    WM_RBUTTONUP:
      { 捕获到鼠标右键放开的消息 }
      DoRButtonUp(TWMMouse(Message), TempZone, HitTestValue);
    WM_SETCURSOR:
      begin
        { 捕获到设置光标的消息 }
        DoSetCursor(TWMSetCursor(Message), TempZone, HitTestValue);
        if Message.Result = 1 then Exit;
      end;
  end;

  { 调用老的WndProc }
  FOldWndProc(Message);

  if Message.Msg = CM_HINTSHOW then
    { 捕获到提示窗体显示的消息 }
    DoHintShow(TCMHintShow(Message), TempZone, HitTestValue);
end;

procedure TCnDockTree.SetGrabberSize(const Value: Integer);
begin
  if FGrabberSize <> Value then
  begin
    FGrabberSize := Value;
    UpdateAll;
    Docksite.Invalidate;
  end;
end;

function TCnDockTree.GetGrabbersPosition: TGrabbersPosition;
begin
  if DockSite.Align in [alTop, alBottom] then
    Result := gpLeft
  else Result := gpTop;
end;

function TCnDockTree.GetBottomGrabbersHTFlag(const MousePos: TPoint;
  out HTFlag: Integer; Zone: TCnDockZone): TCnDockZone;
begin
  Result := nil;
end;

function TCnDockTree.GetBorderHTFlag(const MousePos: TPoint;
  out HTFlag: Integer; Zone: TCnDockZone): TCnDockZone;
var ARect: TRect;
begin
  Result := nil;
  { 获得节点的矩形大小 }
  ARect := Zone.GetFrameRect;
  { 如果鼠标的坐标在矩形内 }
  if PtInRect(ARect, MousePos) then
  begin
    { 减去边框的宽度 }
    InflateRect(ARect, -BorderWidth, -BorderWidth);
    if not PtInRect(ARect, MousePos) then
    begin
      Result := Zone;
      HTFlag := HTBORDER;
    end;
  end;
end;

function TCnDockTree.GetLeftGrabbersHTFlag(const MousePos: TPoint;
  out HTFlag: Integer; Zone: TCnDockZone): TCnDockZone;
begin
  if (MousePos.X >= Zone.Left + BorderWidth) and (MousePos.X <= Zone.Left + BorderWidth + FGrabberSize) and
    (MousePos.Y >= Zone.Top) and (MousePos.Y <= Zone.Top + Zone.Height) then
  begin
    Result := Zone;
    if MousePos.Y < Zone.ChildControl.Top + FGrabberSize + 3 then HTFlag := HTCLOSE
    else HTFlag := HTCAPTION;
  end else Result := nil;
end;

function TCnDockTree.GetRightGrabbersHTFlag(const MousePos: TPoint;
  out HTFlag: Integer; Zone: TCnDockZone): TCnDockZone;
begin
  Result := nil;
end;

function TCnDockTree.GetTopGrabbersHTFlag(const MousePos: TPoint;
  out HTFlag: Integer; Zone: TCnDockZone): TCnDockZone;
begin
  if (MousePos.Y >= Zone.Top + BorderWidth) and (MousePos.Y <= Zone.Top + BorderWidth + FGrabberSize) and
    (MousePos.X >= Zone.Left) and (MousePos.X <= Zone.Left + Zone.Width) then
  begin
    Result := Zone;
    with Zone.ChildControl do
      if MousePos.X > Left + Width - FGrabberSize - 3 then HTFlag := HTCLOSE
      else HTFlag := HTCAPTION;
  end else Result := nil;
end;

function TCnDockTree.GetActiveControl: TControl;
begin
  Result := FActiveControl;
end;

procedure TCnDockTree.SetActiveControl(const Value: TControl);
begin
  FActiveControl := Value;
end;

function TCnDockTree.GetGrabberSize: Integer;
begin
  Result := FGrabberSize;
end;

function TCnDockTree.FindControlZoneAndLevel(Control: TControl;
  var CtlLevel: Integer; IncludeHide: Boolean): TCnDockZone;
var
  CtlZone: TCnDockZone;

  procedure DoFindControlZone(StartZone: TCnDockZone; Level: Integer);
  begin
    if (StartZone.ChildControl = Control) and (StartZone.Visibled or IncludeHide) then
    begin
      CtlZone := StartZone;
      CtlLevel := Level;
    end
    else begin
      // 遍历右兄弟
      if (CtlZone = nil) and (StartZone.NextSibling <> nil) then
        DoFindControlZone(StartZone.NextSibling, Level);
      // 遍历左子女
      if (CtlZone = nil) and (StartZone.ChildZones <> nil) then
        DoFindControlZone(StartZone.ChildZones, Level + 1);
      if (CtlZone <> nil) and (not CtlZone.Visibled) then CtlZone := nil;
    end;
  end;

begin
  CtlZone := nil;
  CtlLevel := 0;
  if (Control <> nil) and (FTopZone <> nil) then DoFindControlZone(FTopZone, 0);
  Result := CtlZone;
end;

procedure TCnDockTree.SetSplitterWidth(const Value: Integer);
begin
  if FSplitterWidth <> Value then
  begin
    FSplitterWidth := Value;
    if FUpdateCount <= 0 then
      UpdateAll;
  end;
end;

procedure TCnDockTree.SetTopZone(const Value: TCnDockZone);
begin
  FTopZone := Value;
end;

procedure TCnDockTree.SetTopXYLimit(const Value: Integer);
begin
  FTopXYLimit := Value;
end;

procedure TCnDockTree.DoMouseMove(var Message: TWMMouse;
  var Zone: TCnDockZone; out HTFlag: Integer);

var Control: TControl;
  DockClient: TCnDockClient;
begin
  { 如果鼠标按下的地方是分割条的位置上 }
  if FSizingZone <> nil then
  begin
    { 画分割条的形状 }
    DrawSizeSplitter;
    { 把鼠标的位置保存起来 }
    FSizePos := SmallPointToPoint(Message.Pos);
    { 计算鼠标的位置，对其做一些限制 }
    CalcSplitterPos;
    { 画分割条的形状 }
    DrawSizeSplitter;
  end;
  { 返回鼠标的位置 }
  Zone := InternalHitTest(SmallPointToPoint(Message.Pos), HTFlag);
  if Zone <> nil then
  begin
    { 首先调用TCnDockClient中的DoNCMouseMove方法 }
    DockClient := FindDockClient(Zone.ChildControl);
    if DockClient <> nil then
      DockClient.DoNCMouseMove(Cn_CreateNCMessage(
        DockSite, WM_NCMOUSEMOVE, HTFlag, FSizePos), msConjoin);
    Control := Zone.ChildControl;
  end
  else Control := nil;
  if (Control <> nil) and (HTFlag <> FOldHTFlag) then
  begin
    { 如果鼠标的位置和原来的位置不一样，就更新提示窗体 }
    Application.HideHint;
    Application.HintMouseMessage(Control, TMessage(Message));
    Application.ActivateHint(SmallPointToPoint(Message.Pos));
    FOldHTFlag := HTFlag;
  end;
end;

function TCnDockTree.DoLButtonDown(var Message: TWMMouse;
  var Zone: TCnDockZone; out HTFlag: Integer): Boolean;
var P: TPoint;
  Msg: TMsg;
//  DockClient: TCnDockClient;
begin
  Result := False;
  P := SmallPointToPoint(Message.Pos);
  { 返回鼠标的位置和当前的节点 }
  Zone := InternalHitTest(P, HTFlag);
  if (Zone <> nil) then
  begin
    if HTFlag = HTSPLITTER then
      { 如果鼠标的位置刚好在分割条的位置上，
      就调用SplitterMouseDown函数进行一些必要的处理 }
      SplitterMouseDown(Zone, P)
    else if (HTFlag = HTCAPTION) or (HTFlag = HTBORDER) then
    begin
      { 如果鼠标的位置是在标题栏上 }
      { 给全局变量GlobalDockClient赋值 }
      GlobalDockClient := FindDockClient(Zone.ChildControl);
      if GlobalDockClient <> nil then
        { 首先调用TCnDockClient中的DoNCButtonDown方法 }
        GlobalDockClient.DoNCButtonDown(Cn_CreateNCMessage(
          DockSite, WM_NCLBUTTONDOWN, HTFlag, P), mbLeft, msConjoin);

      if (not PeekMessage(Msg, FDockSite.Handle, WM_LBUTTONDBLCLK,
        WM_LBUTTONDBLCLK, PM_NOREMOVE)) and
        (Zone.ChildControl is TWinControl) then
        { 使节点上面的控件获得焦点 }
//        if not Zone.ChildControl.Focused then
//          TWinControl(Zone.ChildControl).SetFocus;
        if (GetActiveControl <> Zone.ChildControl) and Zone.ChildControl.CanFocus then
          Zone.ChildControl.SetFocus;
        if (TCnWinControlAccess(Zone.ChildControl).DragKind = dkDock) and
        (TCnWinControlAccess(Zone.ChildControl).DragMode = dmAutomatic)then
        begin
          { 开始拖动 }
          BeginDrag(Zone.ChildControl, True);
        end;
      Result := True;
    end;
  end;
end;

procedure TCnDockTree.DoLButtonUp(var Message: TWMMouse;
  var Zone: TCnDockZone; out HTFlag: Integer);
var P: TPoint;
  DockClient: TCnDockClient;
begin
  if FSizingZone = nil then
  begin
    { 如果鼠标按下的位置不是在分割条上，
    也就是说是在标题栏上面，就判断它具体是在标题栏的哪个具体位置 }
    P := SmallPointToPoint(Message.Pos);
    Zone := InternalHitTest(P, HTFlag);
    if (Zone <> nil) then
    begin
      if (HTFlag <> HTSPLITTER) and (Zone.ChildControl <> nil) then
      begin
        DockClient := FindDockClient(Zone.ChildControl);
        if DockClient <> nil then
          { 首先调用TCnDockClient中的DoNCButtonDown方法 }
          DockClient.DoNCButtonUp(Cn_CreateNCMessage(
            DockSite, WM_NCLBUTTONUP, HTFlag, P), mbLeft, msConjoin);
        if (HTFlag = HTCLOSE) then
        begin
          if (DockClient <> nil) and (not DockClient.EnableCloseBtn) then Exit;
          DoHideZoneChild(Zone);
        end;
      end;
    end;
  end
  else
    { 如果鼠标按下的位置是在分割条上，就调用SplitterMouseUp函数进行一些必要的设置}
    SplitterMouseUp;
end;

procedure TCnDockTree.DoLButtonDbClk(var Message: TWMMouse;
  var Zone: TCnDockZone; out HTFlag: Integer);
var //AControl: TWinControl;
  P: TPoint;
begin
  { 首先判断鼠标的位置是在哪里 }
  P := SmallPointToPoint(Message.Pos);
  Zone := InternalHitTest(P, HTFlag);
  if (Zone <> nil) and (Zone.ChildControl <> nil)
    and (HTFlag = HTCAPTION) or (HTFlag = HTBORDER) then
  begin
    if (HTFlag <> HTSPLITTER) then
      { 首先调用TCnDockClient中的DoNCButtonDown方法 }
      GlobalDockClient.DoNCButtonDblClk(Cn_CreateNCMessage(
        DockSite, WM_NCLBUTTONUP, HTFlag, P), mbLeft, msConjoin);
    if GlobalDockClient.CanFloat then
    begin
      { 如果鼠标的位置是在标题栏上面，就使节点上面的控件浮动 }
      CnGlobalDockPresident.CancelDrag;
      Zone.LButtonDbClkMothed;
    end;
    Zone := nil;
  end;
end;

procedure TCnDockTree.DoSetCursor(var Message: TWMSetCursor;
  var Zone: TCnDockZone; out HTFlag: Integer);
var
  P: TPoint;
begin
  { 获得鼠标的位置并且进行坐标转换 }
  GetCursorPos(P);
  P := FDockSite.ScreenToClient(P);
  with Message do
    if (Smallint(HitTest) = HTCLIENT) and (CursorWnd = FDockSite.Handle)
      and (FDockSite.VisibleDockClientCount > 0) then
    begin
      { 确定鼠标在哪个位置 }
      Zone := InternalHitTest(P, HTFlag);
      if (Zone <> nil) and (HTFlag = HTSPLITTER) then
      begin
        { 如果鼠标是在分割条上，就调用SetSplitterCursor设置光标的形状 }
        SetSplitterCursor(Zone.ParentZone.Orientation);
        Result := 1;
      end;
    end;
end;

procedure TCnDockTree.DoHintShow(var Message: TCMHintShow;
  var Zone: TCnDockZone; out HTFlag: Integer);
var
  Control: TWinControl;
  R: TRect;
  ADockClient: TCnDockClient;
  CanShow: Boolean;
begin
  with Message do
  begin
    if Result = 0 then
    begin
      { 确定鼠标在哪个位置 }
      Zone := InternalHitTest(Message.HintInfo.CursorPos, HTFlag);
      if Zone <> nil then
        Control := Zone.ChildControl
      else Control := nil;
      { 查找到Control上面的TCnDockClient，如果她的ShowHint属性为False，就退出 }
      ADockClient := FindDockClient(Control);
      if (ADockClient <> nil) and (not ADockClient.ShowHint) then
        Exit;

      if HTFlag = HTSPLITTER then
        HintInfo^.HintStr := ''
      else if (Control <> nil){ and (HTFlag in [HTCAPTION, HTCLOSE]) }then
      begin
        { 设置提示窗体的大小 }
        R := GetFrameRect(Control);
        if HTFlag = HTCAPTION then
        begin
          { 标题栏的提示信息 }
          HintInfo^.HintStr := TCnWinControlAccess(Control).Caption;
        end else if HTFlag = HTCLOSE then
          { 关闭按钮的提示信息 }
          HintInfo^.HintStr := gs_CnDockTreeCloseBtnHint
        else DoOtherHint(Zone, HTFlag, HintInfo^.HintStr);

        HintInfo^.CursorRect := R;
        { 调用TCnDockClient的DoFormShowHint方法。}
        CanShow := True;
        if ADockClient <> nil then
          ADockClient.DoFormShowHint(HTFlag, HintInfo^.HintStr, CanShow);
        if not CanShow then
          HintInfo^.HintStr := '';
      end;
    end;
  end;
end;

procedure TCnDockTree.SetSplitterCursor(CursorIndex: TDockOrientation);
const
  SizeCursors: array[TDockOrientation] of TCursor = (crDefault, crVSplit, crHSplit);
begin
  Windows.SetCursor(Screen.Cursors[SizeCursors[CursorIndex]]);
end;

procedure TCnDockTree.SetCnDockZoneClass(const Value: TCnDockZoneClass);
begin
  FCnDockZoneClass := Value;
end;

procedure TCnDockTree.DoMButtonDown(var Message: TWMMouse;
  var Zone: TCnDockZone; out HTFlag: Integer);
var Msg: TWMNCHitMessage;
  DockClient: TCnDockClient;
begin
  Msg := DoMouseEvent(Message, Zone, HTFlag);
  if Msg.Result > 0 then
  begin
    DockClient := FindDockClient(Zone.ChildControl);
    if DockClient <> nil then
      { 查找到控件上的TCnDockClient，并且调用TCnDockClient的DoNCButtonDown方法 }
      DockClient.DoNCButtonDown(Msg, mbMiddle, msConjoin);
  end;
end;

procedure TCnDockTree.DoMButtonUp(var Message: TWMMouse;
  var Zone: TCnDockZone; out HTFlag: Integer);
var Msg: TWMNCHitMessage;
  DockClient: TCnDockClient;
begin
  Msg := DoMouseEvent(Message, Zone, HTFlag);
  if Msg.Result > 0 then
  begin
    DockClient := FindDockClient(Zone.ChildControl);
    if DockClient <> nil then
      { 查找到控件上的TCnDockClient，并且调用TCnDockClient的DoNCButtonUp方法 }
      DockClient.DoNCButtonUp(Msg, mbMiddle, msConjoin);
  end;
end;

procedure TCnDockTree.DoRButtonDown(var Message: TWMMouse;
  var Zone: TCnDockZone; out HTFlag: Integer);
var Msg: TWMNCHitMessage;
  DockClient: TCnDockClient;
begin
  Msg := DoMouseEvent(Message, Zone, HTFlag);
  if Msg.Result > 0 then
  begin
    DockClient := FindDockClient(Zone.ChildControl);
    if DockClient <> nil then
      { 查找到控件上的TCnDockClient，并且调用TCnDockClient的DoNCButtonDown方法 }
      DockClient.DoNCButtonDown(Msg, mbRight, msConjoin);
  end;
end;

procedure TCnDockTree.DoRButtonUp(var Message: TWMMouse;
  var Zone: TCnDockZone; out HTFlag: Integer);
var Msg: TWMNCHitMessage;
  DockClient: TCnDockClient;
begin
  Msg := DoMouseEvent(Message, Zone, HTFlag);
  if Msg.Result > 0 then
  begin
    DockClient := FindDockClient(Zone.ChildControl);
    if DockClient <> nil then
      { 查找到控件上的TCnDockClient，并且调用TCnDockClient的DoNCButtonUp方法 }
      DockClient.DoNCButtonUp(Msg, mbRight, msConjoin);
  end;
end;

function TCnDockTree.DoMouseEvent(var Message: TWMMouse;
  var Zone: TCnDockZone; out HTFlag: Integer): TWMNCHitMessage;
var APoint: TPoint;
begin
  Result.Result := 0;
  APoint := SmallPointToPoint(Message.Pos);
  { 首先检测到鼠标的位置是在哪个控件的标题上 }
  Zone := InternalHitTest(APoint, HTFlag);
  if (Zone <> nil) and (Zone.ChildControl <> nil) and (HTFlag <> HTSPLITTER) then
  begin
    { 创建一个TWMNCHitMessage结构，其中'Message.Msg + WM_NCMOUSEFIRST - WM_MOUSEFIRST'，
      这是因为WM_MOUSExxx和WM_NCMOUSExxx是一一对应的，他们相差了WM_NCMOUSEFIRST - WM_MOUSEFIRST。 }
    Result := Cn_CreateNCMessage(DockSite, Message.Msg + WM_NCMOUSEFIRST - WM_MOUSEFIRST, HTFlag, APoint);
    Result.Result := 1;
  end;
end;

procedure TCnDockTree.DoMButtonDbClk(var Message: TWMMouse;
  var Zone: TCnDockZone; out HTFlag: Integer);
var P: TPoint;
  DockClient: TCnDockClient;
begin
  { 首先判断鼠标的位置是在哪里 }
  P := SmallPointToPoint(Message.Pos);
  Zone := InternalHitTest(P, HTFlag);
  if (Zone <> nil) and (Zone.ChildControl <> nil) and (HTFlag = HTCAPTION) then
  begin
    if (HTFlag <> HTSPLITTER) then
    begin
      DockClient := FindDockClient(Zone.ChildControl);
      if DockClient <> nil then
        { 首先调用TCnDockClient中的DoNCButtonDown方法 }
        DockClient.DoNCButtonDblClk(Cn_CreateNCMessage(
          DockSite, WM_NCLBUTTONUP, HTFlag, P), mbMiddle, msConjoin);
    end;
  end;
end;

procedure TCnDockTree.DoRButtonDbClk(var Message: TWMMouse;
  var Zone: TCnDockZone; out HTFlag: Integer);
var P: TPoint;
  DockClient: TCnDockClient;
begin
  { 首先判断鼠标的位置是在哪里 }
  P := SmallPointToPoint(Message.Pos);
  Zone := InternalHitTest(P, HTFlag);
  if (Zone <> nil) and (Zone.ChildControl <> nil) and (HTFlag = HTCAPTION) then
  begin
    if (HTFlag <> HTSPLITTER) then
    begin
      DockClient := FindDockClient(Zone.ChildControl);
      if DockClient <> nil then
        { 首先调用TCnDockClient中的DoNCButtonDown方法 }
        DockClient.DoNCButtonDblClk(Cn_CreateNCMessage(
          DockSite, WM_NCLBUTTONUP, HTFlag, P), mbRight, msConjoin);
    end;
  end;
end;

function TCnDockTree.GetFrameRect(Control: TControl): TRect;
var ALeft, ATop: Integer;
begin
  if Control <> nil then
  begin
    Result := Control.BoundsRect;
    ALeft := Result.Left;
    Atop := Result.Top;
    AdjustDockRect(Control, Result);
    Dec(Result.Left, 2 * (Result.Left - Control.Left) + 1);
    Dec(Result.Top, 2 * (Result.Top - Control.Top));
    Dec(Result.Right, 2 * (Result.Right - ALeft - Control.Width));
    Dec(Result.Bottom, 2 * (Result.Bottom - ATop - Control.Height));
  end else raise Exception.Create(gs_ControlCannotIsNil);
end;

function TCnDockTree.GetSpiltterRect(Zone: TCnDockZone): TRect;
var A, B, C, D: Integer;
begin
  if (Zone <> nil) and Zone.Visibled and (Zone.ParentZone <> nil)
    and (Zone.VisibleNextSiblingCount >= 1)
    and (Zone.ParentZone.Orientation <> doNoOrient) then
  begin
    A := Zone.ParentZone.LimitBegin;
    B := Zone.ParentZone.ZoneLimit;
    C := Zone.ZoneLimit - SplitterWidth;
    D := C + 1 * SplitterWidth;
    if Zone.ParentZone.Orientation = doHorizontal then
      Result := Rect(A, C, B, D)
    else if Zone.ParentZone.Orientation = doVertical then
      Result := Rect(C, A, D, B);
  end else Result := Rect(0, 0, 0, 0);
end;

{procedure TCnDockTree.PaintDockSplitter(Canvas: TCanvas; Control: TControl;
  const ARect: TRect);
begin
  with Canvas do
  begin
    FillRect(ARect);
  end;
end;}

procedure TCnDockTree.BeginDrag(Control: TControl;
      Immediate: Boolean; Threshold: Integer);
var ADockClient: TCnDockClient;
begin
  ADockClient := FindDockClient(Control);
  { 开始拖动 }
  if ADockClient <> nil then
    CnGlobalDockPresident.BeginDrag(Control, ADockClient.DirectDrag, Threshold);
end;

function TCnDockTree.GetFrameRectEx(Control: TControl): TRect;
begin
  if Control <> nil then
  begin
    Result := GetFrameRect(Control);
    MapWindowPoints(DockSite.Handle, 0, Result, 2);
  end;
end;

procedure TCnDockTree.DrawDockSiteRect;
begin
  { 没事做 }
end;

procedure TCnDockTree.SetBorderWidth(const Value: Integer);
begin
  if FBorderWidth <> Value then
  begin
    FBorderWidth := Value;
    if FUpdateCount <= 0 then
      UpdateAll;
  end;
end;

function TCnDockTree.GetBorderWidth: Integer;
begin
  Result := FBorderWidth;
end;

function TCnDockTree.GetSplitterWidth: Integer;
begin
  Result := FSplitterWidth;
end;

procedure TCnDockTree.DrawSplitter(Zone: TCnDockZone);
var R: TRect;
begin
  { 获得分割条的矩形大小 }
  R := GetSpiltterRect(Zone);
  { 画分割条 }
  DrawSplitterRect(R);
end;

function TCnDockTree.GetDockEdge(DockRect: TRect; MousePos: TPoint;
  var DropAlign: TAlign; Control: TControl): TControl;
begin
  Result := nil;
  { 没事情做 }
end;

function TCnDockTree.GetDockSiteOrient: TDockOrientation;
begin
  Result := Cn_GetControlOrient(DockSite);
end;

procedure TCnDockTree.BeginResizeDockSite;
begin
  Inc(FResizeCount);
end;

procedure TCnDockTree.EndResizeDockSite;
begin
  Dec(FResizeCount);
  if FResizeCount < 0 then
    FResizeCount := 0;
end;

procedure TCnDockTree.ScaleChildZone(Zone: TCnDockZone);
begin
  // 必须是可见的，停靠方向和ShiftScaleOrient要相同。
  if (Zone <> nil) and (Zone.ParentZone <> nil) and Zone.Visibled and
    (Zone.ParentZone.Orientation = ShiftScaleOrient) then
  begin
    // 根据计算公式得到Zone的ZoneLimit。
    Zone.ZoneLimit := Integer(Round(Zone.ZoneLimit * ScaleBy + FParentLimit * (1 - ScaleBy)));
    // Zone的LimitSize不能小于规定的最小尺寸MinSize。
(*    if (Zone.LimitSize < FMinSize) then
      Zone.FZoneLimit := Zone.LimitBegin + FMinSize;
    // Zone的LimitBegin和前一个可见的节点的ZoneLimit的位置不能小于规定的最小尺寸MinSize
    if (Zone.BeforeClosestVisibleZone <> nil) and (Zone.LimitBegin > DockSiteSizeWithOrient[Zone.ParentZone.Orientation] -
      (Zone.VisibleNextSiblingCount + 1) * MinSize + {Integer(Zone.NextSibling <> nil) * }SplitterWidth div 2) then
      Zone.BeforeClosestVisibleZone.ZoneLimit := DockSiteSizeWithOrient[Zone.ParentZone.Orientation] -
        (Zone.VisibleNextSiblingCount + 1) * MinSize + {Integer(Zone.NextSibling <> nil) * }SplitterWidth div 2;
*)  end;
end;

procedure TCnDockTree.ScaleSiblingZone(Zone: TCnDockZone);
begin
  {和ScaleChildZone的计算公式是一样的}
  ScaleChildZone(Zone);
{  if Zone = nil then Exit;
  if (Zone <> nil) and (Zone.ParentZone <> nil) and
    (Zone.ParentZone.Orientation = ShiftScaleOrient) then
      Zone.ZoneLimit := Integer(Round(Zone.ZoneLimit * ScaleBy + FParentLimit * (1 - ScaleBy)));
}
end;

function TCnDockTree.GetDockSiteSize: Integer;
begin
  case DockSiteOrient of
    doVertical: Result := DockSite.Width;
    doHorizontal: Result := DockSite.Height;
  else
    raise Exception.Create(gs_CannotGetValueWithNoOrient);
  end;
end;

procedure TCnDockTree.SetDockSiteSize(const Value: Integer);
begin
  DockSite.Parent.DisableAlign;
  try
    // 如果DockSite是在右边或者下边，就要重新设置DockSiteBegin(Top或者Left),
    // 这样不至于打乱控件之间原来固有的次序。
    if DockSite.Align in [alRight, alBottom] then
      DockSiteBegin := DockSiteBegin - (Value - DockSiteSize);
    case DockSiteOrient of
      doVertical: DockSite.Width := Value;
      doHorizontal: DockSite.Height := Value;
    else
      raise Exception.Create(gs_CannotSetValueWithNoOrient);
    end;
  finally
    DockSite.Parent.EnableAlign;
  end;
end;

procedure TCnDockTree.SetMinSize(const Value: Integer);
begin
  FMinSize := Value;
end;

function TCnDockTree.GetDockSiteBegin: Integer;
begin
  case DockSiteOrient of
    doVertical: Result := DockSite.Left;
    doHorizontal: Result := DockSite.Top;
  else
    raise Exception.Create(gs_CannotGetValueWithNoOrient);
  end;
end;

procedure TCnDockTree.SetDockSiteBegin(const Value: Integer);
begin
  case DockSiteOrient of
    doVertical: DockSite.Left := Value;
    doHorizontal: DockSite.Top := Value;
  else
    raise Exception.Create(gs_CannotSetValueWithNoOrient);
  end;
end;

function TCnDockTree.GetDockSiteSizeA: Integer;
begin
  case DockSiteOrient of
    doVertical: Result := DockSite.Height;
    doHorizontal: Result := DockSite.Width;
  else
    raise Exception.Create(gs_CannotGetValueWithNoOrient);
  end;
end;

procedure TCnDockTree.SetDockSiteSizeA(const Value: Integer);
begin
  case DockSiteOrient of
    doVertical: DockSite.Height := Value;
    doHorizontal: DockSite.Width := Value;
  else
    raise Exception.Create(gs_CannotSetValueWithNoOrient);
  end;
end;

procedure TCnDockTree.CalcSplitterPos;
var
  MinWidth,
  TestLimit: Integer;
begin
  MinWidth := MinSize;
  if (FSizingZone.ParentZone.Orientation = doHorizontal) then
  begin
    TestLimit := GetSplitterLimit(FSizingZone, True, False) + MinWidth;
    if FSizePos.y <= TestLimit then FSizePos.y := TestLimit;
    TestLimit := GetSplitterLimit(FSizingZone, False, True) - MinWidth - SplitterWidth;
    if FSizePos.y >= TestLimit then FSizePos.y := TestLimit;
  end
  else begin
    TestLimit := GetSplitterLimit(FSizingZone, True, False) + MinWidth;
    if FSizePos.x <= TestLimit then FSizePos.x := TestLimit;
    TestLimit := GetSplitterLimit(FSizingZone, False, True) - MinWidth - SplitterWidth;
    if FSizePos.x >= TestLimit then FSizePos.x := TestLimit;
  end;
end;

procedure TCnDockTree.SetVersion(const Value: Integer);
begin
  FVersion := Value;
end;

procedure TCnDockTree.DoSaveZone(Stream: TStream;
  Zone: TCnDockZone; Level: Integer);
begin
  with Stream do
  begin
    { 节点的等级, TopZone为0 }
    Write(Level, SizeOf(Level));
    CustomSaveZone(Stream, Zone);
  end;
  // 遍历左子女
  if Zone.ChildZones <> nil then
    DoSaveZone(Stream, Zone.ChildZones, Level + 1);
  // 遍历右兄弟
  if Zone.NextSibling <> nil then
    DoSaveZone(Stream, Zone.NextSibling, Level);
end;

procedure TCnDockTree.WriteControlName(Stream: TStream; ControlName: string);
var
  NameLen: Integer;
begin
  NameLen := Length(ControlName);
  Stream.Write(NameLen, SizeOf(NameLen));
  if NameLen > 0 then Stream.Write(Pointer(ControlName)^, NameLen * SizeOf(Char));
end;

procedure TCnDockTree.DoLoadZone(Stream: TStream);
var
  Level, LastLevel, I: Integer;
  Zone, LastZone, NextZone: TCnDockZone;
begin
  LastLevel := 0;
  LastZone := nil;
  while True do
  begin
    with Stream do
    begin
      Read(Level, SizeOf(Level));
      if Level = TreeStreamEndFlag then Break;
      Zone := FCnDockZoneClass.Create(Self);
      CustomLoadZone(Stream, Zone);
      if Zone = nil then
        Continue;
    end;
    if Level = 0 then FTopZone := Zone
    else if Level = LastLevel then
    begin
      LastZone.NextSibling := Zone;
      Zone.FPrevSibling := LastZone;
      Zone.FParentZone := LastZone.FParentZone;
    end
    else if Level > LastLevel then
    begin
      LastZone.ChildZones := Zone;
      Zone.FParentZone := LastZone;
    end
    else if Level < LastLevel then
    begin
      NextZone := LastZone;
      for I := 1 to LastLevel - Level do NextZone := NextZone.FParentZone;
      NextZone.NextSibling := Zone;
      Zone.FPrevSibling := NextZone;
      Zone.FParentZone := NextZone.FParentZone;
    end;
    LastLevel := Level;
    LastZone := Zone;
  end;
end;

procedure TCnDockTree.ReadControlName(Stream: TStream;
  var ControlName: string);
var
  Size: Integer;
begin
  ControlName := '';
  Size := 0;
  Stream.Read(Size, SizeOf(Size));
  if Size > 0 then
  begin
    SetLength(ControlName, Size);
    Stream.Read(Pointer(ControlName)^, Size * SizeOf(Char));
  end;
end;

procedure TCnDockTree.CustomLoadZone(Stream: TStream; var Zone: TCnDockZone);
var CompName: string;
begin
  with Stream do
  begin
    Read(Zone.FOrientation, SizeOf(Zone.Orientation));
    Read(Zone.FZoneLimit, SizeOf(Zone.FZoneLimit));
    Read(Zone.FVisibled, SizeOf(Zone.Visibled));
    Read(Zone.FControlVisibled, SizeOf(Zone.FControlVisibled));
    Read(Zone.FVisibleSize, SizeOf(Zone.VisibleSize));
    Read(Zone.FIsInside, SizeOf(Zone.FIsInside));
    ReadControlName(Stream, CompName);
    if CompName <> '' then
    begin
      if not Zone.SetControlName(CompName) then
      begin
        { 如果没有找到这个控件就把节点删除 }
        Zone.Free;
        Zone := nil;
        Exit;
      end;
    end;
  end;
end;

procedure TCnDockTree.CustomSaveZone(Stream: TStream; Zone: TCnDockZone);
var AVisible: Boolean;
begin
  with Stream do
  begin
    { 节点的方向 }
    Write(Zone.Orientation, SizeOf(Zone.Orientation));
    { 节点的绝对坐标 }
    Write(Zone.ZoneLimit, SizeOf(Zone.ZoneLimit));
    { 节点是否可见 }
    if Zone.ChildControl <> nil then
      AVisible := Zone.ChildControl.Visible;
    Write(Zone.Visibled, SizeOf(Zone.Visibled));
    { 控件是否可见 }
    AVisible := False;
    if Zone.ChildControl <> nil then
      AVisible := Zone.ChildControl.Visible;
    Write(AVisible, SizeOf(AVisible));
    { 节点在可见的时候的LimitSize }
    Write(Zone.VisibleSize, SizeOf(Zone.VisibleSize));
    { 是否在DockSite的里面 }
    Zone.IsInside := True;
    if (Zone.ChildControl <> nil) and (Zone.ChildControl.HostDockSite <> DockSite)
      and not (DockSite is TCnVSPopupPanel) then
      Zone.IsInside := False;
    Write(Zone.IsInside, SizeOf(Zone.IsInside));
    { 节点所含有的ChildControl的名字 }
    WriteControlName(Stream, Zone.GetControlName);
  end;
end;

procedure TCnDockTree.SetDockSiteSizeWithOrient(Orient: TDockOrientation;
  const Value: Integer);
begin
  case Orient of
    doVertical: DockSite.Width := Value;
    doHorizontal: DockSite.Height := Value;
  else
    raise Exception.Create(gs_CannotSetValueWithNoOrient);
  end;
end;

procedure TCnDockTree.DoOtherHint(Zone: TCnDockZone;
  HTFlag: Integer; var HintStr: string);
begin
  { 没事做 }
end;

function TCnDockTree.GetHTFlag(MousePos: TPoint): Integer;
var Zone: TCnDockZone;
begin
  Zone := InternalHitTest(MousePos, Result);
  if Zone = nil then Result := HTNONE;
end;

procedure TCnDockTree.GetSiteInfo(Client: TControl;
  var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean);
begin
  GetWindowRect(DockSite.Handle, InfluenceRect);
  InflateRect(InfluenceRect, DefExpandoRect, DefExpandoRect);
end;

function TCnDockTree.GetDockRect: TRect;
begin
  Result := FDockRect;
end;

procedure TCnDockTree.SetDockRect(const Value: TRect);
begin
  FDockRect := Value;
end;

function TCnDockTree.GetDockAlign(Client: TControl; var DropCtl: TControl): TAlign;
var
  CRect, DRect: TRect;
begin
  Result := alRight;
  if DropCtl <> nil then
  begin
    CRect := Client.BoundsRect;
    DRect := DropCtl.BoundsRect;
    if (CRect.Top <= DRect.Top) and (CRect.Bottom < DRect.Bottom) and
       (CRect.Right >= DRect.Right) then
      Result := alTop
    else if (CRect.Left <= DRect.Left) and (CRect.Right < DRect.Right) and
       (CRect.Bottom >= DRect.Bottom) then
      Result := alLeft
    else if CRect.Top >= ((DRect.Top + DRect.Bottom) div 2) then
      Result := alBottom;
  end;
end;

procedure TCnDockTree.HideControl(Control: TControl);
var
  Z: TCnDockZone;
begin
  { 如果ReplacementZone <> nil，就说明是在进行Load操作，不用执行下面的操作，直接退出就可以了 }
  if ReplacementZone <> nil then Exit;
  Z := FindControlZone(Control);
  if (Z <> nil) then
  begin
    if Z = FReplacementZone then
      Z.ChildControl := nil
    else
    begin
      if TopZone.VisibleChildTotal = 1 then
        Z.Remove(TopXYLimit, True)
      else Z.Remove(Z.LimitSize, True);
//      UpdateAll;
    end;
    Control.DockOrientation := doNoOrient;
    SetNewBounds(nil);
    UpdateAll;
    { 重画DockSite }
    FDockSite.Invalidate;
  end;
end;

procedure TCnDockTree.ShowControl(Control: TControl);
var Z: TCnDockZone;
begin
  { 如果ReplacementZone <> nil，就说明是在进行Load操作，不用执行下面的操作，直接退出就可以了 }
  if ReplacementZone <> nil then Exit;
  Z := FindControlZone(Control, True);
  if Z <> nil then
    Z.Insert(Z.VisibleSize, False);

  SetNewBounds(nil);
  UpdateAll;
  DockSite.Invalidate;
end;

procedure TCnDockTree.DoGetNextLimit(Zone, AZone: TCnDockZone; var LimitResult: Integer);
begin
  if (Zone <> AZone) and
    (Zone.ParentZone.Orientation = AZone.ParentZone.Orientation) and
    (Zone.ZoneLimit > AZone.FZoneLimit) and ((Zone.ChildControl = nil) or
    ((Zone.ChildControl <> nil) and (Zone.ChildControl.Visible))) then
    LimitResult := Min(LimitResult, Zone.ZoneLimit);
  if Zone.NextSibling <> nil then DoGetNextLimit(Zone.NextSibling, AZone, LimitResult);
  // 下面这条被注释掉的语句，表示可以忽略掉不是同一个父节点的节点，
  // 使鼠标调节分割条更加自由。
  if (Zone.ChildZones <> nil){ and (Zone = AZone.AfterClosestVisibleZone) }then
    DoGetNextLimit(Zone.ChildZones, AZone, LimitResult);
end;

procedure TCnDockTree.UpdateChild(Zone: TCnDockZone);
begin
  if (FUpdateCount = 0) and (FDockSite.DockClientCount > 0) then
    ForEachAt(Zone, UpdateZone, tskForward);
end;

function TCnDockTree.GetDockClientLimit(Orient: TDockOrientation; IsMin: Boolean): Integer;
var Zone: TCnDockZone;
begin
  Result := 0;
  if TopZone.ChildCount = 1 then
    Result := Integer(not IsMin) * DockSiteSizeWithOrient[Orient]
  else
  begin
    if IsMin then
    begin
      if TopZone.Orientation = Orient then
        Zone := TopZone.LastVisibleChildZone
      else Zone := TopZone;
      if Zone <> nil then
        Result := Zone.LimitBegin;
    end else
    begin
      if TopZone.Orientation = Orient then
        Zone := TopZone.FirstVisibleChildZone
      else Zone := TopZone;
      if Zone <> nil then
        Result := Zone.ZoneLimit;
    end;

    TopZone.DoGetSplitterLimit(Orient, IsMin, Result);
  end;

{  if TopZone <> nil then
  begin
    if TopZone.ChildCount = 1 then
      Result := Integer(not IsMin) * DockSiteSizeWithOrient[Orient]
    else
      TopZone.DoGetSplitterLimit(Orient, IsMin, Result);
  end;}
end;

function TCnDockTree.GetDockSiteSizeWithOrient(
  Orient: TDockOrientation): Integer;
begin
  case Orient of
    doVertical: Result := DockSite.Width;
    doHorizontal: Result := DockSite.Height;
  else
    raise Exception.Create(gs_CannotGetValueWithNoOrient);
  end;
end;

function TCnDockTree.GetMinSize: Integer;
begin
  Result := FMinSize;
end;

procedure TCnDockTree.GetCaptionRect(var Rect: TRect);
begin
  Rect.Left := 0;
  Rect.Top := 0;
  Rect.Right := 0;
  Rect.Bottom := 0;
end;

procedure TCnDockTree.HideAllControl;

  procedure DoHideAllControl(AZone: TCnDockZone);
  begin
    if AZone <> nil then
    begin
      DoHideAllControl(AZone.NextSibling);
      DoHideAllControl(AZone.ChildZones);
      if (AZone.ChildControl <> nil) and (AZone.visibled) then
        AZone.Remove(AZone.LimitSize, True);
    end;
  end;

begin
  { 如果ReplacementZone <> nil，就说明是在进行Load操作，不用执行下面的操作，直接退出就可以了 }
  if ReplacementZone <> nil then Exit;
  DoHideAllControl(TopZone.ChildZones);
  SetNewBounds(nil);
  UpdateAll;
  DockSite.Invalidate;
end;

procedure TCnDockTree.HideSingleControl(Control: TControl);

  procedure DoHideSingleControl(AZone: TCnDockZone);
  begin
    if AZone <> nil then
    begin
      DoHideSingleControl(AZone.NextSibling);
      DoHideSingleControl(AZone.ChildZones);
      if AZone.ChildControl <> nil then
      begin
        if (AZone.ChildControl = Control) then
        begin
          if (AZone.ChildControl.Visible) then
          begin
            AZone.Remove(AZone.LimitSize, True);
            AZone.ChildControl.Visible := False;
          end;
        end else
        begin
          AZone.Insert(AZone.VisibleSize, False);
          AZone.ChildControl.Visible := True;
        end;
      end;
    end;
  end;

begin
  { 如果ReplacementZone <> nil，就说明是在进行Load操作，不用执行下面的操作，直接退出就可以了 }
  if ReplacementZone <> nil then Exit;
  if Control <> nil then
  begin
    DoHideSingleControl(TopZone.ChildZones);
    SetNewBounds(nil);
    UpdateAll;
    DockSite.Invalidate;
  end;
end;

procedure TCnDockTree.ShowAllControl;

  procedure DoShowAllControl(AZone: TCnDockZone);
  begin
    if AZone <> nil then
    begin
      DoShowAllControl(AZone.NextSibling);
      DoShowAllControl(AZone.ChildZones);
      if (AZone.ChildControl <> nil) and (not AZone.visibled) then
        AZone.Insert(AZone.VisibleSize, True);
    end;
  end;

begin
  { 如果ReplacementZone <> nil，就说明是在进行Load操作，不用执行下面的操作，直接退出就可以了 }
  if ReplacementZone <> nil then Exit;
  DoShowAllControl(TopZone.ChildZones);
  SetNewBounds(nil);
  UpdateAll;
  DockSite.Invalidate;
end;

procedure TCnDockTree.ShowSingleControl(Control: TControl);

  procedure DoShowSingleControl(AZone: TCnDockZone);
  begin
    if AZone <> nil then
    begin
      DoShowSingleControl(AZone.NextSibling);
      DoShowSingleControl(AZone.ChildZones);
      if AZone.ChildControl <> nil then
      begin
        if (AZone.ChildControl = Control) then
        begin
          if (not AZone.ChildControl.Visible) then
          begin
            AZone.Insert(AZone.VisibleSize, False);
            AZone.ChildControl.Visible := True;
          end;
        end else
        begin
          AZone.Remove(AZone.LimitSize, True);
          AZone.ChildControl.Visible := False;
        end;
      end;
    end;
  end;

begin
  { 如果ReplacementZone <> nil，就说明是在进行Load操作，不用执行下面的操作，直接退出就可以了 }
  if ReplacementZone <> nil then Exit;
  if Control <> nil then
  begin
    DoShowSingleControl(TopZone.ChildZones);
    SetNewBounds(nil);
    UpdateAll;
    DockSite.Invalidate;
  end;
end;

procedure TCnDockTree.DrawDockBorder(DockControl: TControl; R1, R2: TRect);
begin

end;

procedure TCnDockTree.DrawDockGrabber(Control: TControl;
  const ARect: TRect);
  procedure DrawCloseButton(Left, Top: Integer);
  var ADockClient: TCnDockClient;
  begin
    ADockClient := FindDockClient(Control);
    if (ADockClient <> nil) and (not ADockClient.EnableCloseBtn) then Exit;
    DrawFrameControl(Canvas.Handle, Rect(Left, Top, Left+GrabberSize-2,
      Top+GrabberSize-2), DFC_CAPTION, DFCS_CAPTIONCLOSE);
  end;

  procedure DrawGrabberLine(Left, Top, Right, Bottom: Integer);
  begin
    with Canvas do
    begin
      Pen.Color := clBtnHighlight;
      MoveTo(Right, Top);
      LineTo(Left, Top);
      LineTo(Left, Bottom);
      Pen.Color := clBtnShadow;
      LineTo(Right, Bottom);
      LineTo(Right, Top-1);
    end;
  end;

begin
  with ARect do
  begin
    case GrabbersPosition of
      gpLeft:
      begin
        DrawCloseButton(Left+BorderWidth+BorderWidth+1, Top+BorderWidth+BorderWidth+1);
        DrawGrabberLine(Left+BorderWidth+3, Top+GrabberSize+BorderWidth+1, Left+BorderWidth+5, Bottom+BorderWidth-2);
        DrawGrabberLine(Left+BorderWidth+6, Top+GrabberSize+BorderWidth+1, Left+BorderWidth+8, Bottom+BorderWidth-2);
      end;
      gpTop:
      begin
        DrawCloseButton(Right-GrabberSize+BorderWidth+1, Top+BorderWidth+1);
        DrawGrabberLine(Left+BorderWidth+2, Top+BorderWidth+BorderWidth+3, Right-GrabberSize+BorderWidth-2, Top+BorderWidth+5);
        DrawGrabberLine(Left+BorderWidth+2, Top+BorderWidth+BorderWidth+6, Right-GrabberSize+BorderWidth-2, Top+BorderWidth+8);
      end;
//      gpBottom:
//      gpRight:
    end;
  end;
end;

procedure TCnDockTree.DrawSplitterRect(const ARect: TRect);
begin
  Canvas.Brush.Color := TCnWinControlAccess(DockSite).Color;
  Canvas.FillRect(ARect);
end;

procedure TCnDockTree.DrawZone(Zone: TCnDockZone);
begin
  { 依次调用DrawZoneBorder,DrawGrabber和DrawSplitter方法，
    子类可以重载这个方法改变重画的内容 }
  DrawZoneBorder(Zone);
  DrawZoneGrabber(Zone);
  DrawZoneSplitter(Zone);
  DrawDockSiteRect;
end;

procedure TCnDockTree.DrawZoneBorder(Zone: TCnDockZone);
var ChildControl: TControl;
//  R1, R2: TRect;
begin
  if Zone = nil then Exit;
  ChildControl := Zone.ChildControl;
  if (ChildControl <> nil) and ChildControl.Visible and
    (ChildControl.HostDockSite = DockSite) then
  begin
//    R := GetFrameRect(ChildControl);
//    DrawDockGrabber(ChildControl, R);
  end;
end;

procedure TCnDockTree.DrawZoneGrabber(Zone: TCnDockZone);
var ChildControl: TControl;
  R: TRect;
begin
  if Zone = nil then Exit;
  ChildControl := Zone.ChildControl;
  if (ChildControl <> nil) and ChildControl.Visible and
    (ChildControl.HostDockSite = DockSite) then
  begin
    R := GetFrameRect(ChildControl);
    DrawDockGrabber(ChildControl, R);
  end;
end;

procedure TCnDockTree.DrawZoneSplitter(Zone: TCnDockZone);
var R: TRect;
begin
  { 获得分割条的矩形大小 }
  R := GetSpiltterRect(Zone);
  { 画分割条 }
  DrawSplitterRect(R);
end;

procedure TCnDockTree.PaintDockSite;
begin
  ForEachAt(nil, DrawZone, tskBackward);
end;

function TCnDockTree.HasZoneWithControl(Control: TControl): Boolean;
begin
  Result := FindControlZone(Control, True) <> nil;
end;

procedure TCnDockTree.ReplaceZoneChild(OldControl, NewControl: TControl);
var Zone: TCnDockZone;
begin
  Zone := FindControlZone(OldControl, True);
  if Zone <> nil then
  begin
    Zone.ChildControl := TWinControl(NewControl);
    UpdateAll;
  end;
end;

procedure TCnDockTree.DoHideZoneChild(AZone: TCnDockZone);
var AForm: TCustomForm;
begin
  if (AZone <> nil) and (AZone.ChildControl <> nil) then
  begin
    if AZone.ChildControl.InheritsFrom(TCustomForm) then
    begin
      { 当调用Close函数后，AZone将被释放 }
      AForm := TCustomForm(AZone.ChildControl);
      AForm.Close;
    end else
      AZone.ChildControl.Visible := False;
  end;
end;

{ TCnAdvDockZone }

constructor TCnAdvDockZone.Create(Tree: TCnDockTree);
begin
  inherited;
  FCloseBtnDown := False;
  FMouseDown := False;
end;

destructor TCnAdvDockZone.Destroy;
begin
  if Self = TCnAdvDockTree(Tree).CloseBtnZone then
    TCnAdvDockTree(Tree).CloseBtnZone := nil;
  inherited Destroy;
end;

procedure TCnAdvDockZone.Insert(DockSize: Integer; Hide: Boolean);
begin
  InsertOrRemove(DockSize, True, Hide);
end;

procedure TCnAdvDockZone.LButtonDbClkMothed;
begin
  if GlobalDockClient <> nil then
    GlobalDockClient.RestoreChild;
end;

procedure TCnAdvDockZone.Remove(DockSize: Integer; Hide: Boolean);
begin
  InsertOrRemove(DockSize, False, Hide);
end;

{ TCnAdvDockTree }

constructor TCnAdvDockTree.Create(DockSite: TWinControl;
  CnDockZoneClass: TCnDockZoneClass);
begin
  inherited Create(DockSite, CnDockZoneClass);
  GrabberSize     := 15;
  FButtonHeight   := 12;
  FButtonWidth    := 12;
  FLeftOffset     := 0;
  FRightOffset    := 0;
  FTopOffset      := 0;
  FBottomOffset   := 0;
  FButtonSplitter := 2;
end;

function TCnAdvDockTree.DoLButtonDown(var Message: TWMMouse;
  var Zone: TCnDockZone; out HTFlag: Integer): Boolean;
var TempZone: TCnAdvDockZone;
begin
  Result := inherited DoLButtonDown(Message, Zone, HTFlag);
  if (Zone <> nil) and (HTFlag = HTCLOSE) then
  begin
    TempZone := TCnAdvDockZone(Zone);
    TempZone.CloseBtnDown := True;
    TempZone.MouseDown := True;
    FCloseBtnZone := TempZone;
    DockSite.Invalidate;
  end;
end;

procedure TCnAdvDockTree.DoLButtonUp(var Message: TWMMouse;
  var Zone: TCnDockZone; out HTFlag: Integer);
//var TempZone: TCnAdvDockZone;
begin
  inherited DoLButtonUp(Message, Zone, HTFlag);
  if SizingZone = nil then
  begin
    FCloseBtnZone := nil;
    if (Zone <> nil) and (HTFlag = HTCLOSE) then
    begin
      // 在这里会有地址偏差，TCnAdvDockZone(Zone).CloseBtnDown
      // 会莫名其妙的指向一个类的指针，需要注意。
//      TempZone := TCnAdvDockZone(Zone);
      TCnAdvDockZone(Zone).CloseBtnDown := False;
    end;
  end;
end;

procedure TCnAdvDockTree.DoMouseMove(var Message: TWMMouse;
  var Zone: TCnDockZone; out HTFlag: Integer);
var TempZone: TCnAdvDockZone;
begin
  inherited DoMouseMove(Message, Zone, HTFlag);
  if SizingZone = nil then
  begin
    TempZone := TCnAdvDockZone(Zone);
    if ((TempZone <> nil) and (TempZone.CloseBtnDown <> (HTFlag = HTCLOSE))
      and ((FCloseBtnZone = TempZone) and FCloseBtnZone.MouseDown)) then
    begin
      TempZone.CloseBtnDown := (HTFlag = HTCLOSE) and FCloseBtnZone.MouseDown;
      DockSite.Invalidate;
    end;
  end;
end;

procedure TCnAdvDockTree.InsertSibling(NewZone, SiblingZone: TCnDockZone;
  InsertLast, Update: Boolean);
var
  TempUpdate: Boolean;
begin
  TempUpdate := Update;
  Update := False;
  try
    inherited;
    if NewZone.ChildControl <> nil then
      SetDockHeightWidthArr(0, NewZone.ChildControl.TBDockHeight + BorderWidth,
        NewZone.ChildControl.LRDockWidth + BorderWidth)
    else SetDockHeightWidthArr(0, 0, 0);
  finally
    Update := TempUpdate;
  end;

  if Update then
  begin
    NewZone.Insert(FDropDockSize, False);
    SetNewBounds(NewZone.ParentZone);
    ForEachAt(NewZone.ParentZone, UpdateZone, tskForward);
  end;
end;

procedure TCnAdvDockTree.SetBottomOffset(const Value: Integer);
begin
  FBottomOffset := Value;
end;

procedure TCnAdvDockTree.SetButtonHeight(const Value: Integer);
begin
  FButtonHeight := Value;
end;

procedure TCnAdvDockTree.SetButtonSplitter(const Value: Integer);
begin
  FButtonSplitter := Value;
end;

procedure TCnAdvDockTree.SetButtonWidth(const Value: Integer);
begin
  FButtonWidth := Value;
end;

procedure TCnAdvDockTree.SetLeftOffset(const Value: Integer);
begin
  FLeftOffset := Value;
end;

procedure TCnAdvDockTree.SetRightOffset(const Value: Integer);
begin
  FRightOffset := Value;
end;

procedure TCnAdvDockTree.SetTopOffset(const Value: Integer);
begin
  FTopOffset := Value;
end;

function TCnAdvDockTree.GetDockHeightWidth(
  Orient: TDockOrientation): Integer;
begin
  Result := FDockHeightWidth[Orient];
end;

procedure TCnAdvDockTree.SetDockHeightWidth(Orient: TDockOrientation;
  const Value: Integer);
begin
  FDockHeightWidth[Orient] := Value;
end;

function TCnAdvDockTree.GetDockRectFromArr(Orient: TDockOrientation;
  AtLast: Boolean): Integer;
begin
  Result := FDockRectArr[Orient, Atlast];
end;

procedure TCnAdvDockTree.SetDockRectToArr(Orient: TDockOrientation;
  AtLast: Boolean; const Value: Integer);
begin
  FDockRectArr[Orient, Atlast] := Value;
end;

procedure TCnAdvDockTree.SetDockRectArr(ARect: TRect);
begin
  FDockRectArr[doNoOrient, False] := 0;
  FDockRectArr[doNoOrient, True] := 0;
  FDockRectArr[doHorizontal, False] := ARect.Top;
  FDockRectArr[doHorizontal, True] := ARect.Bottom;
  FDockRectArr[doVertical, False] := ARect.Left;
  FDockRectArr[doVertical, True] := ARect.Right;
end;

procedure TCnAdvDockTree.SetDockHeightWidthArr(NoOrValue, HorValue,
  VerValue: Integer);
begin
  FDockHeightWidth[doNoOrient] := NoOrValue;
  FDockHeightWidth[doHorizontal] := HorValue;
  FDockHeightWidth[doVertical] := VerValue;
end;

procedure TCnAdvDockTree.ScaleChildZone(Zone: TCnDockZone);
begin
  if Zone = ReplacementZone then
    ShiftScaleOrient := doNoOrient;
  inherited ScaleChildZone(Zone);
end;

procedure TCnAdvDockTree.ScaleSiblingZone(Zone: TCnDockZone);
begin
  if Zone = ReplacementZone then
    ShiftScaleOrient := doNoOrient;
  inherited ScaleSiblingZone(Zone);
end;

procedure TCnAdvDockTree.ScaleZone(Zone: TCnDockZone);
begin
  if Zone = ReplacementZone then
    ShiftScaleOrient := doNoOrient;
  inherited ScaleZone(Zone);
end;

procedure TCnAdvDockTree.ShiftZone(Zone: TCnDockZone);
begin
  if Zone = ReplacementZone then
    ShiftScaleOrient := doNoOrient;
  inherited ShiftZone(Zone);
end;

procedure TCnAdvDockTree.InsertNewParent(NewZone, SiblingZone: TCnDockZone;
  ParentOrientation: TDockOrientation; InsertLast, Update: Boolean);
var
  TempUpdate: Boolean;
begin
  TempUpdate := Update;
  Update := False;
  if NewZone.ChildControl <> nil then
    SetDockHeightWidthArr(0, NewZone.ChildControl.TBDockHeight + BorderWidth,
      NewZone.ChildControl.LRDockWidth + BorderWidth)
  else SetDockHeightWidthArr(0, 0, 0);

  if SiblingZone = nil then
  begin
    if InsertLast then
      ReplacementZone := TopZone
    else ReplacementZone := NewZone;
  end;

  try
    inherited;
  finally
    Update := TempUpdate;
    ReplacementZone := nil;
  end;

  if Update then
  begin
    NewZone.Insert(DropDockSize, False);
    ForEachAt(NewZone.ParentZone, UpdateZone, tskForward);
    SetNewBounds(NewZone.ParentZone);
  end;
end;

procedure TCnAdvDockTree.RemoveZone(Zone: TCnDockZone; Hide: Boolean);
begin
  { 调用默认的RemoveZone函数 }
  inherited;
end;

procedure TCnAdvDockTree.SetDropDockSize(const Value: Integer);
begin
  FDropDockSize := Value;
end;

end.
