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

{*******************************************************}
{                                                       }
{       具有类似Visual InterDev的停靠风格               }
{       lbVIDDockStyle 单元                             }
{                                                       }
{       版权 (C) 2002,2003 鲁小班                       }
{                                                       }
{*******************************************************}

{WM_SETTINGCHANGE: 当设置桌面属性的外观后，Windows会给每一个应用程序发送
                   WM_SETTINGCHANGE消息，这时候可以截获这个消息来设置
                   TlbVIDConjoinServerOption中的属性。}

{WM_SYSCOLORCHANGE: 当设置桌面属性的颜色外观后，Windows会给每一个应用程序发送
                   WM_SYSCOLORCHANGE消息，这时候可以截获这个消息来设置
                   TlbVIDConjoinServerOption中的属性的颜色部分。}

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

interface

{$I CnPack.inc}

uses
  Windows, Classes, Controls, Math, Messages, Graphics, ComCtrls, Extctrls,
  ImgList, Forms, SysUtils, Dialogs, CnDockFormControl, CnDockSupportControl,
  CnDockTree, CnConsts, CnCompConsts;

const
  VIDDefaultGrabbersSize   = 18;
  VIDDefaultSplitterWidth = 4;

type
  { VID平铺服务器的选项类 }
  TCnVIDConjoinServerOption = class(TCnBasicConjoinServerOption)
  private
    FTextEllipsis: Boolean;
    FTextAlignment: TAlignment;
    FInactiveTitleEndColor: TColor;
    FInactiveTitleStartColor: TColor;
    FActiveTitleEndColor: TColor;
    FActiveTitleStartColor: TColor;
    FSystemInfo: Boolean;
    FActiveFont: TFont;
    FInactiveFont: TFont;
    procedure SetActiveTitleEndColor(const Value: TColor);
    procedure SetActiveTitleStartColor(const Value: TColor);
    procedure SetInactiveTitleEndColor(const Value: TColor);
    procedure SetInactiveTitleStartColor(const Value: TColor);
    procedure SetTextAlignment(const Value: TAlignment);
    procedure SetTextEllipsis(const Value: Boolean);
    procedure SetSystemInfo(const Value: Boolean);
    procedure SetActiveFont(const Value: TFont);
    procedure SetInactiveFont(const Value: TFont);
  protected
    // 重新设置DockStyle的选项
    procedure ResetDockControlOption; override;
    procedure SetDefaultSystemCaptionInfo; virtual;
  public
    constructor Create(ADockStyle: TCnBasicDockStyle); override;
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
    procedure SetActiveTitleEndColor_WithoutChangeSystemInfo(const Value: TColor);
    procedure SetActiveTitleStartColor_WithoutChangeSystemInfo(const Value: TColor);
    procedure SetInactiveTitleEndColor_WithoutChangeSystemInfo(const Value: TColor);
    procedure SetInactiveTitleStartColor_WithoutChangeSystemInfo(const Value: TColor);
    procedure SetTextAlignment_WithoutChangeSystemInfo(const Value: TAlignment);
    procedure SetTextEllipsis_WithoutChangeSystemInfo(const Value: Boolean);
    procedure SetActiveFont_WithoutChangeSystemInfo(const Value: TFont);
    procedure SetInactiveFont_WithoutChangeSystemInfo(const Value: TFont);
  published
    { 当页面获得焦点的时候的字体颜色 }
    property ActiveFont: TFont read FActiveFont write SetActiveFont;
    { 当页面失去焦点的时候的字体颜色 }
    property InactiveFont: TFont read FInactiveFont write SetInactiveFont;
    { Caption的对齐方式 }
    property TextAlignment: TAlignment read FTextAlignment
      write SetTextAlignment;
    { 当客户窗体获得焦点的时候标题栏的开始部分颜色 }
    property ActiveTitleStartColor: TColor read FActiveTitleStartColor
      write SetActiveTitleStartColor;
    { 当客户窗体获得焦点的时候标题栏的结束部分颜色 }
    property ActiveTitleEndColor: TColor read FActiveTitleEndColor
      write SetActiveTitleEndColor;
    { 当客户窗体失去焦点的时候标题栏的开始部分颜色 }
    property InactiveTitleStartColor: TColor read FInactiveTitleStartColor
      write SetInactiveTitleStartColor;
    { 当客户窗体失去焦点的时候标题栏的结束部分颜色 }
    property InactiveTitleEndColor: TColor read FInactiveTitleEndColor
      write SetInactiveTitleEndColor;
    { 标题栏上的文字是否有省略号 }
    property TextEllipsis: Boolean read FTextEllipsis write SetTextEllipsis;
    { 是否和系统信息是一样的 }
    property SystemInfo: Boolean read FSystemInfo write SetSystemInfo;
  end;

  { VID分页服务器的选项类 }
  TCnVIDTabServerOption = class(TCnBasicTabServerOption)
  private
    FActiveFont: TFont;
    FActiveSheetColor: TColor;
    FHotTrackColor: TColor;
    FInactiveFont: TFont;
    FInactiveSheetColor: TColor;
    FShowTabImages: Boolean;

    function GetActiveFont: TFont;
    function GetActiveSheetColor: TColor;
    function GetHotTrackColor: TColor;
    function GetInactiveFont: TFont;
    function GetInactiveSheetColor: TColor;
    function GetShowTabImages: Boolean;
    procedure SetActiveFont(const Value: TFont);
    procedure SetActiveSheetColor(const Value: TColor);
    procedure SetHotTrackColor(const Value: TColor);
    procedure SetInactiveFont(const Value: TFont);
    procedure SetInactiveSheetColor(const Value: TColor);
    procedure SetShowTabImages(const Value: Boolean);
  protected
    // 重新设置DockStyle的选项
    procedure ResetDockControlOption; override;
    { 重新设置TCnTabPageControl的属性 }
    procedure ResetTabPageControl(APage: TCnTabPageControl); override;
  public
    constructor Create(ADockStyle: TCnBasicDockStyle); override;
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
    procedure SetTabPosition(const Value: TTabPosition); override;
  published
    { 当页面获得焦点的时候的颜色 }
    property ActiveSheetColor: TColor read GetActiveSheetColor write SetActiveSheetColor;
    { 当页面失去焦点的时候的颜色 }
    property InactiveSheetColor: TColor read GetInactiveSheetColor write SetInactiveSheetColor;
    { 当页面获得焦点的时候的字体颜色 }
    property ActiveFont: TFont read GetActiveFont write SetActiveFont;
    { 当页面失去焦点的时候的字体颜色 }
    property InactiveFont: TFont read GetInactiveFont write SetInactiveFont;
    { 高亮显示的颜色 }
    property HotTrackColor: TColor read GetHotTrackColor write SetHotTrackColor;
    { 是否显示图片 }
    property ShowTabImages: Boolean read GetShowTabImages write SetShowTabImages;
  end;

  { 当TCnVIDConjoinServerOption的SystemInfo属性改变的时候触发这个事件 }
  TSystemInfoChange = procedure(Value: Boolean) of object;

  TCnVIDDockStyle = class(TCnAdvDockStyle)
  private
    FSystemInfoChange: TSystemInfoChange;
  protected
    procedure GetComponentInfo(var AName, Author, Email, Comment: string); override;
    function DockClientWindowProc(DockClient: TCnDockClient; var Message: TMessage): Boolean; override;
    procedure ParentFormWindowProc(var Message: TMessage); override;

    procedure FormDockDrop(DockClient: TCnDockClient;
      Source: TCnDragDockObject; X, Y: Integer); override;
    procedure FormGetSiteInfo(Source: TCnDragDockObject; DockClient: TCnDockClient;
      Client: TControl; var InfluenceRect: TRect; MousePos: TPoint;
      var CanDock: Boolean); override;
    procedure FormDockOver(DockClient: TCnDockClient; Source: TCnDragDockObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean); override;
    procedure FormStartDock(DockClient: TCnDockClient;
      var Source: TCnDragDockObject); override;
    procedure FormGetDockEdge(DockClient: TCnDockClient; Source: TCnDragDockObject;
      MousePos: TPoint; var DropAlign: TAlign); override;
    { ------------------------------------------------------------------------ }
    procedure CreateConjoinServerOption(var Option: TCnBasicConjoinServerOption); override;
    procedure CreateTabServerOption(var Option: TCnBasicTabServerOption); override;
    { ------------------------------------------------------------------------ }
    procedure AssignConjoinServerOption(APanel: TCnCustomDockPanel); override;
    procedure AssignTabServerOption(APage: TCnTabPageControl); override;
    { ------------------------------------------------------------------------ }
    procedure DoSystemInfoChange(Value: Boolean);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
//    class function GetControlName: string; override;
    function GetControlName: string; override;
    procedure SetDockBaseControl(IsCreate: Boolean;
      DockBaseControl: TCnDockBaseControl); override;
  published
    property SystemInfoChange: TSystemInfoChange read FSystemInfoChange
      write FSystemInfoChange;
    property ConjoinServerOption;
    property TabServerOption;
    { ------------------------------------------------------------------------ }
  end;

  TCnVIDDockSplitter = class(TCnDockSplitter);

  TCnVIDDockPanel = class(TCnAdvDockPanel)
  protected
    procedure CustomGetSiteInfo(Source: TCnDragDockObject;
      Client: TControl; var InfluenceRect: TRect; MousePos: TPoint;
      var CanDock: Boolean); override;
    procedure CustomStartDock(var Source: TCnDragDockObject); override;
    procedure CustomDockDrop(Source: TCnDragDockObject; X, Y: Integer); override;
    procedure CustomDockOver(Source: TCnDragDockObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean); override;
    procedure CustomGetDockEdge(Source: TCnDragDockObject; MousePos: TPoint;
      var DropAlign: TAlign); override;
    function CreateDockManager: IDockManager; override;
  public
    constructor Create(AOwner: TComponent); override;
    procedure DockDrop(Source: TDragDockObject; X, Y: Integer); override;
  end;

  TCnVIDConjoinPanel = class(TCnAdvConjoinPanel)
  protected
    procedure CustomGetSiteInfo(Source: TCnDragDockObject;
      Client: TControl; var InfluenceRect: TRect; MousePos: TPoint;
      var CanDock: Boolean); override;
    procedure CustomDockOver(Source: TCnDragDockObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean); override;
    procedure CustomGetDockEdge(Source: TCnDragDockObject; MousePos: TPoint; var DropAlign: TAlign); override;
    function CustomUnDock(Source: TCnDragDockObject; NewTarget: TWinControl; Client: TControl): Boolean; override;
    procedure CustomDockDrop(Source: TCnDragDockObject; X, Y: Integer); override;
    function CreateDockManager: IDockManager; override;
  public
    procedure UpdateCaption(Exclude: TControl); override;
    procedure DockDrop(Source: TDragDockObject; X, Y: Integer); override;
  end;

  TCnVIDDockZone = class(TCnAdvDockZone)
  protected
    function GetSplitterLimit(IsMin: Boolean): Integer; override;
  public
    destructor Destroy; override;
    procedure Insert(DockSize: Integer; Hide: Boolean); override;
    procedure Remove(DockSize: Integer; Hide: Boolean); override;
  end;

  TCnVIDDockTree = class(TCnAdvDockTree)
  private
    FDropOnZone: TCnDockZone;
    FLockDropDockSizeCount: Integer;
    // 标题栏文字离左边界的距离
    FCaptionLeftOffset: Integer;
    // 标题栏文字离右边界的距离
    FCaptionRightOffset: Integer;

    procedure LockDropDockSize;
    procedure UnlockDropDockSize;
    procedure SetCaptionLeftOffset(const Value: Integer);
    procedure SetCaptionRightOffset(const Value: Integer);
  protected
    { 当停靠来的控件是TCnConjoinPanel的时候，先把TCnConjoinPanel中的停靠信息
    存储在Stream流中，然后再把这些信息取出来重新构造DockTree的结构，使其符合原来的结构 }
    procedure InsertControlFromConjoinHost(Control: TControl;
      InsertAt: TAlign; DropCtl: TControl); virtual;
    { 忽略掉没有用的信息 }
    procedure IgnoreZoneInfor(Stream: TMemoryStream); virtual;
    { 调整Control控件的大小 }
    procedure AdjustDockRect(Control: TControl; var ARect: TRect); override;
    procedure WindowProc(var Message: TMessage); override;
    procedure SplitterMouseUp; override;

    function GetTopGrabbersHTFlag(const MousePos: TPoint;
      out HTFlag: Integer; Zone: TCnDockZone): TCnDockZone; override;
    function GetGrabbersPosition: TGrabbersPosition; override;
    procedure GetSiteInfo(Client: TControl;
      var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean); override;
    procedure InsertControl(Control: TControl; InsertAt: TAlign;
      DropCtl: TControl); override;
    procedure InsertSibling(NewZone, SiblingZone: TCnDockZone;
        InsertLast, Update: Boolean); override;
    procedure InsertNewParent(NewZone, SiblingZone: TCnDockZone;
      ParentOrientation: TDockOrientation; InsertLast, Update: Boolean); override;
    procedure DrawDockGrabber(Control: TControl; const ARect: TRect); override;
    procedure DrawSplitterRect(const ARect: TRect); override;
    { 重画把手的边缘 }
    procedure PaintDockGrabberRect(Canvas: TCanvas; Control: TControl;
      const ARect: TRect); virtual;
    { 重画关闭按钮 }
    procedure DrawCloseButton(Canvas: TCanvas; Zone: TCnDockZone;
      Left, Top: Integer); virtual;
    procedure ResetBounds(Force: Boolean); override;
    procedure SetActiveControl(const Value: TControl); override;
    procedure DrawDockSiteRect; override;
    procedure PositionDockRect(Client, DropCtl: TControl; DropAlign: TAlign;
      var DockRect: TRect); override;
    function GetDockEdge(DockRect: TRect; MousePos: TPoint;
      var DropAlign: TAlign; Control: TControl): TControl; override;
    procedure RemoveZone(Zone: TCnDockZone; Hide: Boolean = True); override;
    procedure GetCaptionRect(var Rect: TRect); override;
    property CaptionLeftOffset: Integer read FCaptionLeftOffset write SetCaptionLeftOffset;
    property CaptionRightOffset: Integer read FCaptionRightOffset write SetCaptionRightOffset;
  public
    constructor Create(DockSite: TWinControl;
      CnDockZoneClass: TCnDockZoneClass); override;
    destructor Destroy; override;
  end;

  TCnVIDTabPageControl = class;

  TCnVIDDockTabSheet = class(TCnDockTabSheet)
  private
    FTabWidth: Integer;
    FShowTabWidth: Integer;
    FIsSourceDockClient: Boolean;
    procedure SetTabWidth(const Value: Integer);
    procedure WMSETTEXT(var Message: TMessage); message WM_SETTEXT;
    procedure SetSheetSort(CaptionStr: string);
  protected
    procedure SetPageControl(APageControl: TCnDockPageControl); override;
    property TabWidth: Integer read FTabWidth write SetTabWidth;
    property ShowTabWidth: Integer read FShowTabWidth;
    procedure Loaded; override;
    procedure UpdateTabShowing; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

  published
    property BorderWidth;
    property Caption;
    property DragMode;
    property Enabled;
    property Font;
    property Height stored False;
    property Highlighted;
    property ImageIndex;
    property Left stored False;
    property Constraints;
    property PageIndex;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property ShowHint;
    property TabVisible;
    property Top stored False;
    property Visible stored False;
    property Width stored False;
    property OnContextPopup;
    property OnDragDrop;
    property OnDragOver;
    property OnEndDrag;
    property OnEnter;
    property OnExit;
    property OnHide;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
    property OnResize;
    property OnShow;
    property OnStartDrag;
  end;

  TCnTabPanel = class(TCustomControl)
  private
    FPage: TCnVIDTabPageControl;
    FActiveSheetColor: TColor;     //获得焦点的页面的背景颜色
    FHotTrackColor: TColor;        //热点的颜色
    FActiveFont,                   //获得焦点的页面的字体
    FInactiveFont: TFont;          //失去焦点的页面的字体
    FTabLeftOffset: Integer;       //页面左边距
    FTabRightOffset: Integer;      //页面右边距
    FTabTopOffset: Integer;        //页面上边距
    FTabBottomOffset: Integer;     //页面下边距
    FCaptionLeftOffset: Integer;   //标题左边距
    FCaptionRightOffset: Integer;  //标题右边距
    FCaptionTopOffset: Integer;    //标题上边距
    FTabSplitterWidth: Integer;    //页面间距
    FTabHeight: Integer;           //页面高度
    FSortList: TList;              //按照Tab中的文字的长度排列循序的列表
    FSelectSheet: TCnVIDDockTabSheet;//当前被选中的TabSheet
    FTempPages: TList;             //存储临时Tab的列表
    FSelectHotIndex: Integer;      //当前被选中的高亮显示的Tab的索引
    FShowTabImages: Boolean;       //是否显示Tab中的图象
    procedure SetPage(const Value: TCnVIDTabPageControl);
    function GetTotalTabWidth: Integer;
    procedure SetTotalTabWidth(const Value: Integer);
    function GetMinTabWidth: TCnDockTabSheet;
    function GetMaxTabWidth: TCnDockTabSheet;
    procedure SetTabBottomOffset(const Value: Integer);
    procedure SetTabLeftOffset(const Value: Integer);
    procedure SetTabRightOffset(const Value: Integer);
    procedure SetTabTopOffset(const Value: Integer);
    procedure SetCaptionLeftOffset(const Value: Integer);
    procedure SetCaptionRightOffset(const Value: Integer);
    procedure SetCaptionTopOffset(const Value: Integer);
    procedure SetTabSplitterWidth(const Value: Integer);
    function GetSorts(Index: Integer): TCnVIDDockTabSheet;
    function GetPanelHeight: Integer;
    function GetPanelWidth: Integer;
    procedure SetPanelHeight(const Value: Integer);
    function FindSheetWithPos(cX, cY, cTopOffset, cBottomOffset: Integer): Integer;
    function GetDockClientFromPageIndex(Index: Integer): TControl;
    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
    procedure SetShowTabImages(const Value: Boolean);
    procedure SetTabHeight(const Value: Integer);
  protected
    procedure Paint; override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;

    function GetPageIndexFromMousePos(X, Y: Integer): Integer; virtual;
    procedure SetShowTabWidth;
    property TotalTabWidth: Integer read GetTotalTabWidth write SetTotalTabWidth;
    property MinTabWidth: TCnDockTabSheet read GetMinTabWidth;
    property MaxTabWidth: TCnDockTabSheet read GetMaxTabWidth;
    property TabLeftOffset: Integer read FTabLeftOffset write SetTabLeftOffset default 5;
    property TabRightOffset: Integer read FTabRightOffset write SetTabRightOffset default 5;
    property TabTopOffset: Integer read FTabTopOffset write SetTabTopOffset default 2;
    property TabBottomOffset: Integer read FTabBottomOffset write SetTabBottomOffset default 3;
    property TabSplitterWidth: Integer read FTabSplitterWidth write SetTabSplitterWidth default 2;
    property CaptionTopOffset: Integer read FCaptionTopOffset write SetCaptionTopOffset default 0;
    property CaptionLeftOffset: Integer read FCaptionLeftOffset write SetCaptionLeftOffset default 5;
    property CaptionRightOffset: Integer read FCaptionRightOffset write SetCaptionRightOffset default 5;
    property Sorts[Index: Integer]: TCnVIDDockTabSheet read GetSorts;
    property PanelHeight: Integer read GetPanelHeight write SetPanelHeight;
    property PanelWidth: Integer read GetPanelWidth;
    property TabHeight: Integer read FTabHeight write SetTabHeight;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Resize; override;
    procedure DeleteSorts(Sheet: TCnVIDDockTabSheet);
    property Page: TCnVIDTabPageControl read FPage write SetPage;
    property SelectSheet: TCnVIDDockTabSheet read FSelectSheet write FSelectSheet;
    property ShowTabImages: Boolean read FShowTabImages write SetShowTabImages;
  end;

  TCnTabPanelClass = class of TCnTabPanel;

  TCnVIDTabPageControl = class(TCnAdvTabPageControl)
  private
    FCnTabPanelClass: TCnTabPanelClass;
    FPanel: TCnTabPanel;
    FTempSheet: TCnVIDDockTabSheet;
    FTabImageList: TCustomImageList;
    procedure SetActiveSheetColor(const Value: TColor);
    procedure SetInactiveSheetColor(const Value: TColor);
    procedure SetTabBottomOffset(const Value: Integer);
    procedure SetTabLeftOffset(const Value: Integer);
    procedure SetTabRightOffset(const Value: Integer);
    procedure SetTabTopOffset(const Value: Integer);
    procedure SetActiveFont(const Value: TFont);
    procedure SetInactiveFont(const Value: TFont);
    procedure SetHotTrackColor(const Value: TColor);
    function GetTabBottomOffset: Integer;
    function GetTabLeftOffset: Integer;
    function GetTabRightOffset: Integer;
    function GetTabTopOffset: Integer;
    function GetInactiveSheetColor: TColor;
    function GetActiveSheetColor: TColor;
    function GetActiveFont: TFont;
    function GetInactiveFont: TFont;
    function GetVisibleTheetCount: Integer;
    function GetHotTrackColor: TColor;
    function GetShowTabImages: Boolean;
    procedure SetShowTabImages(const Value: Boolean);
    function GetPage(Index: Integer): TCnVIDDockTabSheet;
    function GetActiveVIDPage: TCnVIDDockTabSheet;
    procedure SetActiveVIDPage(const Value: TCnVIDDockTabSheet);
  protected
    procedure AdjustClientRect(var Rect: TRect); override;
    procedure CreatePanel; virtual;
    procedure Change; override;
    procedure CustomDockOver(Source: TCnDragDockObject; X, Y: Integer; State: TDragState;
      var Accept: Boolean); override;
    procedure CustomGetSiteInfo(Source: TCnDragDockObject; Client: TControl; var InfluenceRect: TRect;
      MousePos: TPoint; var CanDock: Boolean); override;
    procedure CustomDockDrop(Source: TCnDragDockObject; X, Y: Integer); override;
    procedure CustomGetDockEdge(Source: TCnDragDockObject; MousePos: TPoint; var DropAlign: TAlign); override;
    function CustomUnDock(Source: TCnDragDockObject; NewTarget: TWinControl; Client: TControl): Boolean; override;
    procedure DrawTab(TabIndex: Integer; const Rect: TRect; Active: Boolean); override;
    procedure CreateParams(var Params: TCreateParams); override;
    function GetDockClientFromMousePos(MousePos: TPoint): TControl; override;
    procedure Paint; override;
    procedure SetActivePage(Page: TCnDockTabSheet); override;
    procedure SetTabHeight(Value: Smallint); override;
    procedure SetTabPosition(Value: TTabPosition); override;
    procedure CreateWnd; override;
    procedure Loaded; override;
    procedure SetHotTrack(Value: Boolean); override;
    procedure SetImages(Value: TCustomImageList); override;
    property CnTabPanelClass: TCnTabPanelClass read FCnTabPanelClass write FCnTabPanelClass;
  public
    constructor Create(AOwner: TComponent); override;
    procedure AfterConstruction; override;
    property ActiveVIDPage: TCnVIDDockTabSheet read GetActiveVIDPage write SetActiveVIDPage;
    destructor Destroy; override;
    procedure DockDrop(Source: TDragDockObject; X, Y: Integer); override;
    procedure UpdateCaption(Exclude: TControl); override;
    procedure Resize; override;
    property Pages[Index: Integer]: TCnVIDDockTabSheet read GetPage;
    property Panel: TCnTabPanel read FPanel;
    property TempSheet: TCnVIDDockTabSheet read FTempSheet write FTempSheet;
    property VisibleTheetCount: Integer read GetVisibleTheetCount;
  published
    { 当页面获得焦点的时候的颜色 }
    property ActiveSheetColor: TColor read GetActiveSheetColor write SetActiveSheetColor;
    { 当页面失去焦点的时候的颜色 }
    property InactiveSheetColor: TColor read GetInactiveSheetColor write SetInactiveSheetColor;
    property TabLeftOffset: Integer read GetTabLeftOffset write SetTabLeftOffset default 5;
    property TabRightOffset: Integer read GetTabRightOffset write SetTabRightOffset default 5;
    property TabTopOffset: Integer read GetTabTopOffset write SetTabTopOffset default 2;
    property TabBottomOffset: Integer read GetTabBottomOffset write SetTabBottomOffset default 3;
    property ActiveFont: TFont read GetActiveFont write SetActiveFont;
    property InactiveFont: TFont read GetInactiveFont write SetInactiveFont;
    property HotTrackColor: TColor read GetHotTrackColor write SetHotTrackColor;
    property ShowTabImages: Boolean read GetShowTabImages write SetShowTabImages;
    property ActivePage;
    property Align;
    property Anchors;
    property BiDiMode;
    property Constraints;
    property DockSite;
    property DragCursor;
    property DragKind;
    property DragMode;
    property Enabled;
    property Font;
    property HotTrack;
    property Images;
    property MultiLine;
    property OwnerDraw;
    property ParentBiDiMode;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property RaggedRight;
    property ScrollOpposite;
    property ShowHint;
    property Style;
    property TabHeight;
    property TabIndex;
    property TabOrder;
    property TabPosition;
    property TabStop;
    property TabWidth;
    property Visible;
    property OnChange;
    property OnChanging;
    property OnContextPopup;
    property OnDockDrop;
    property OnDockOver;
    property OnDragDrop;
    property OnDragOver;
    property OnDrawTab;
    property OnEndDock;
    property OnEndDrag;
    property OnEnter;
    property OnExit;
    property OnGetImageIndex;
    property OnGetSiteInfo;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
    property OnResize;
    property OnStartDock;
    property OnStartDrag;
    property OnUnDock;
  end;

  TCnVIDDragDockObject = class(TCnDragDockObject)
  private
    FOldDropAlign: TAlign;          //前一次停靠位置
    FCurrState,                     //当前的DockOver的State;
    FOldState: TDragState;          //前一次DockOver的State;
    FOldTarget: Pointer;            //前一次停靠服务器
    FSourceDockClientList: TList;
    FDropTabControl: TCnVIDTabPageControl;
//    FOldDropTabControl: TCnVIDTabPageControl;
    FIsTabDockOver: Boolean;
    FErase: Boolean;
    function GetSourceDockClient(Index: Integer): TControl;
    function GetSourceDockClientCount: Integer;
    procedure SetOldState(const Value: TDragState);
    procedure SetCurrState(const Value: TDragState);
  protected
    procedure GetBrush_PenSize_DrawRect(
      var ABrush: TBrush; var PenSize: Integer; var DrawRect: TRect; Erase: Boolean); override;
    procedure MouseMsg(var Msg: TMessage); override;
    procedure DefaultDockImage(Erase: Boolean); override;
    function CanLeave(NewTarget: TWinControl): Boolean; override;
  public
    constructor Create(AControl: TControl); override;
    destructor Destroy; override;
    function DragFindWindow(const Pos: TPoint): HWND; override;
    function GetDropCtl: TControl; override;
    property SourceDockClients[Index: Integer]: TControl read GetSourceDockClient;
    property SourceDockClientCount: Integer read GetSourceDockClientCount;
    property CurrState: TDragState read FCurrState write SetCurrState;
    property OldState: TDragState read FOldState write SetOldState;
  end;

procedure PaintGradientBackground(Canvas: TCanvas; ARect: TRect;
  StartColor, EndColor: TColor);

{ 当在TCnVIDTabPageControl上进行停靠预览的时候，这个函数用来实现这个功能 }
procedure SetTabControlPreview(VIDSource: TCnVIDDragDockObject;
  TabControl: TCnVIDTabPageControl;
  State: TDragState; DropAlign: TAlign);

implementation

uses
  CnDockSupportProc, CnDockGlobal;

type
  TCnTempWinControl = class(TWinControl);

var gi_DockRect: TRect;

procedure PaintGradientBackground(Canvas: TCanvas; ARect: TRect;
  StartColor, EndColor: TColor);
{Paints the caption bar's background color(s)}
const
  D = 256;  //will divide the gradient into 256 colors
var
  X, C1, C2, R1, G1, B1, W: Integer;
  DR, DG, DB, DH: Real;

  procedure InitRGBValues(C1, C2: Integer);
  {Sets the initial values for each color}
  begin
    R1:= GetRValue(C1);  //store the red value of FCaptionStartColor
    G1:= GetGValue(C1);  //store the green value of FCaptionStartColor
    B1:= GetBValue(C1);  //store the blue value of FCaptionStartColor
    DR:= (GetRValue(C2) - R1 {+1}) / D;  //store the red increment
    DG:= (GetGValue(C2) - G1 {+1}) / D;  //store the green increment
    DB:= (GetBValue(C2) - B1 {+1}) / D;  //store the blue increment
  end;
begin
  With Canvas do begin
    Lock;   //suspend painting of the canvas
    try
      Brush.Style := bsSolid;  //set the brush style to paint solid strokes

      if StartColor <> EndColor then begin  //colors differ
        C1 := StartColor;//ColorToRgb(FCaptionStartColor);  //get RGB value of Start Color
        C2 := EndColor;//ColorToRgb(FCaptionEndColor);  //get RGB value of End Color

        InitRGBValues(C1, C2);  //get the initial values for the variables

//          If HorizontalGrabber then  //caption bar is horizontal
          DH := (ARect.Right - ARect.Left) / D;  //get width of each small rect
        for X := 0 to 255 do begin  //paint 256 small rects
          Brush.Color := RGB(R1 + Round(DR*X), G1 + Round(DG*X),
                             B1 + Round(DB*X));  //get brush color for this rect
          With ARect do
          begin
//              If HorizontalGrabber then begin  //caption bar is horizontal
              //add five to the width of each to prevent rounding problems
              If Right <= Left + Round((X+1)*DH){ + 5} then  //at the right edge
                W := Right  //set the width to the right edge--won't over-paint
              else  //not at the right edge
                W := Left + Round((X+1)*DH) {+ 5};  //set normal width

              FillRect(Rect(Left + Round(X*DH), Top, W, Bottom))  //paint rect
          end;
        end;
      end
      else begin  //the start and end colors are identical--just paint normally
        Brush.Color := StartColor;  //set the brush's color
        FillRect(ARect);  //paint the rect
      end;
    finally
      Unlock;  //resume painting of the canvas
    end;
  end;
end;

procedure AssignList(FromList, ToList: TList);
var i: Integer;
begin
  ToList.Clear;
  for i := 0 to FromList.Count - 1 do
    ToList.Add(FromList[i]);
end;

{计算停靠的区域}
function ComputeVIDDockingRect(Target, Control: TControl; var DockRect: TRect; MousePos: TPoint): TAlign;
var
  DockTopRect,
  DockLeftRect,
  DockBottomRect,
  DockRightRect,
  DockCenterRect,
  DockTabRect: TRect;
begin
  Result := alNone;
  //划分停靠区域
  if Target = nil then Exit;
  with Target do
  begin
    DockLeftRect.TopLeft := Point(0, 0);
    DockLeftRect.BottomRight := Point(ClientWidth div 5, ClientHeight);

    DockTopRect.TopLeft := Point(ClientWidth div 5, 0);
    DockTopRect.BottomRight := Point(ClientWidth div 5 * 4, ClientHeight div 5);

    DockRightRect.TopLeft := Point(ClientWidth div 5 * 4, 0);
    DockRightRect.BottomRight := Point(ClientWidth, ClientHeight);

    if Target is TCnDockCustomTabControl then
    begin
      DockBottomRect.TopLeft := Point(ClientWidth div 5, ClientWidth div 5 * 4);
      DockBottomRect.BottomRight := Point(ClientWidth div 5 * 4, ClientHeight  -Cn_GetSysCaptionHeight);
    end else
    begin
      DockBottomRect.TopLeft := Point(0, ClientHeight div 5 * 4);
      DockBottomRect.BottomRight := Point(ClientWidth, ClientHeight);
    end;

    DockCenterRect.TopLeft := Point(0, -Cn_GetSysCaptionHeight);
    DockCenterRect.BottomRight := Point(ClientWidth, 0);

    if Target is TCnDockCustomTabControl then
    begin
      DockTabRect.TopLeft := Point(0, ClientHeight - Cn_GetSysCaptionHeight);
      DockTabRect.BottomRight := Point(ClientWidth, ClientHeight);
    end else DockTabRect := Rect(0, 0, 0, 0);

    //发现鼠标在哪个停靠区域
    if PtInRect(DockCenterRect, MousePos) or
      PtInRect(DockTabRect, MousePos) then
    begin
      Result := alClient;
      DockRect := DockCenterRect;
//      DockRect.TopLeft := Point(0, 0);
      DockRect.BottomRight := Point(ClientWidth, ClientHeight);
    end else
    if PtInRect(DockLeftRect, MousePos) then
    begin
      Result := alLeft;
      DockRect := DockLeftRect;
      DockRect.Right := Min(ClientWidth div 2, Control.ClientWidth);
    end else if PtInRect(DockTopRect, MousePos) then
    begin
      Result := alTop;
      DockRect := DockTopRect;
      DockRect.Left := 0;
      DockRect.Right := ClientWidth;
      DockRect.Bottom := Min(ClientHeight div 2, Control.ClientHeight);
    end else if PtInRect(DockRightRect, MousePos) then
    begin
      Result := alRight;
      DockRect := DockRightRect;
      DockRect.Left := Max(ClientWidth div 2, ClientWidth - Control.ClientWidth);
    end else if PtInRect(DockBottomRect, MousePos) then
    begin
      Result := alBottom;
      DockRect := DockBottomRect;
      DockRect.Top := Max(ClientHeight div 2, ClientHeight - Control.ClientHeight);
    end;
    if Result = alNone then Exit;

    //DockRect是屏幕坐标
    DockRect.TopLeft := ClientToScreen(DockRect.TopLeft);
    DockRect.BottomRight := ClientToScreen(DockRect.BottomRight);
  end;
end;

{ 当在TCnVIDTabPageControl上进行停靠预览的时候，这个函数用来实现这个功能 }
procedure SetTabControlPreview(VIDSource: TCnVIDDragDockObject;
  TabControl: TCnVIDTabPageControl;
  State: TDragState; DropAlign: TAlign);

var i: Integer;
  Index: Integer;
begin
  if TabControl <> nil then
  begin
    if (DropAlign = alClient) then
    begin
      { 如果停靠位置为alClient时，就进行预览操作 }
      if (TabControl.FTempSheet = nil) then
      begin
        { TabControl.FTempSheet等于nil，就说明TabControl不是处于预览状态，
          就需要根据VIDSource.Control中的停靠客户来创建TabControl的TabSheet }
        for i := VIDSource.SourceDockClientCount - 1 downto 0 do
        begin
          { 一共创建VIDSource.SourceDockClientCount个TabSheet }
          TabControl.FTempSheet := TCnVIDDockTabSheet.Create(TabControl);
          TabControl.FTempSheet.PageControl := TabControl;
          { 只是创建一个空的TabSheet，里面不放任何东西，但是需要改变TabSheet的Caption属性 }
          TabControl.FTempSheet.Caption := TCnTempWinControl(VIDSource.SourceDockClients[i]).Caption;
          Index := TabControl.FTabImageList.AddIcon(TForm(VIDSource.SourceDockClients[i]).Icon);
          if Index <> -1 then
            TabControl.FTempSheet.ImageIndex := Index;
          { FIsSourceDockClient这个属性用来指示这个TabSheet是否是预览界面时使用的，
            看下面的语句，当State = dsDragLeave的时候，就需要释放这些用来预览的TabSheet，
            这时候就是通过TabSheet的FIsSourceDockClient属性来判断的。 }
          TabControl.FTempSheet.FIsSourceDockClient := True;
        end;
        { 设置TabControl的ActivePage和Panel的SelectSheet，
          SelectSheet是当鼠标按下的时候，鼠标位置所处的那个TabSheet }
        TabControl.ActivePage := TabControl.FTempSheet;
        TabControl.Panel.SelectSheet := TabControl.FTempSheet;
        { Panel.FTempPages是用来保存在鼠标按下之前的PageSheet,
          具体的实现部分见TCnTabPanel的FindSheetWithPos函数 }

{$IFDEF COMPILER6_UP}
        TabControl.Panel.FTempPages.Assign(TabControl.PageSheets);
{$ELSE}
        AssignList(TabControl.PageSheets, TabControl.Panel.FTempPages);
{$ENDIF}

        TabControl.ActivePage.Invalidate;
//        SendMessage(TabControl.ParentForm.Handle, WM_NCPAINT, 0, 0);
      end;
    end;

//    if State = dsDragLeave then
//      State := dsDragLeave;
    if ((State = dsDragLeave) or (VIDSource.DropAlign <> alClient)) and (TabControl.FTempSheet <> nil) then
    begin
      { 当State = dsDragLeave的时候，并且TabControl.FTempSheet <> nil(说明有预览操作)，
        就把一些用来预览的TabSheet删除掉 }
      for i := TabControl.PageCount - 1 downto 0 do
      begin
        if TCnVIDDockTabSheet(TabControl.Pages[i]).FIsSourceDockClient then
        begin
          { 首先找到TabControl.Pages[i]在TabControl.Panel.FTempPages中的位置 }
          Index := TabControl.Panel.FTempPages.IndexOf(TabControl.Pages[i]);
          { 然后删除掉这个指针 }
          if Index >= 0 then
          begin
            TabControl.Panel.FTempPages.Delete(Index);
            if TabControl.FTabImageList.Count > Index then
              TabControl.FTabImageList.Delete(Index);
          end;
          { 删除TabControl.Pages[i] }
          TabControl.Pages[i].Free;
        end;
      end;
      { TabControl.FTempSheet赋值为nil，说明已经没有预览操作 }
      TabControl.FTempSheet := nil;
//      for i := 0 to TWinControl(TabControl.ActivePage.Controls[0]).ControlCount - 1 do
//        TWinControl(TabControl.ActivePage.Controls[0]).Controls[i].Invalidate;
    end;
    { 可以改变TabControl.ParentForm的标题，和TabControl.ActivePage的标题一样 }
    TabControl.ParentForm.Caption := TabControl.ActivePage.Caption;
    { 需要重画这个标题 }
    if TabControl.ParentForm.HostDockSite is TCnCustomDockPanel then
      TabControl.ParentForm.HostDockSite.Invalidate;
  end;
end;


{ TCnVIDDockStyle }

procedure TCnVIDDockStyle.FormDockOver(DockClient: TCnDockClient; Source: TCnDragDockObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
var
  ARect: TRect;
begin
  with DockClient do
  begin
    { 首先得到Accept的值 }
    Accept := EnableDock and EachOtherDock and
      IsDockable(ParentForm, Source.Control, Source.DropOnControl, Source.DropAlign);
    if State = dsDragMove then
    begin
      { 然后计算停靠位置和预览矩形的大小 }
      Source.DropAlign := ComputeVIDDockingRect(ParentForm, Source.Control, ARect, Point(X, Y));
      if Accept and (Source.DropAlign <> alNone) then
      begin
        if Source.DropAlign = alClient then
          { 如果Source.DropAlign = alClient，就要减去矩形的Top一个窗体高度的大小 }
          Inc(ARect.Top, Cn_GetSysCaptionHeightAndBorderWidth + 1);
        { 给Source.DockRect赋值为ARect }
        Source.DockRect := ARect;
      end;
      { 给gi_DockRect赋值，这个gi_DockRect有可能会在别的地方使用到 }
      gi_DockRect := ARect;
    end else if State = dsDragLeave then
      Source.DropAlign := alNone;
    if Source is TCnVIDDragDockObject then
    begin
      TCnVIDDragDockObject(Source).OldState := TCnVIDDragDockObject(Source).CurrState;
      TCnVIDDragDockObject(Source).CurrState := State;
    end;
  end;
end;

procedure TCnVIDDockStyle.FormGetSiteInfo(Source: TCnDragDockObject; DockClient: TCnDockClient;
  Client: TControl; var InfluenceRect: TRect; MousePos: TPoint;
  var CanDock: Boolean);
const
  DefExpandoRect = 20;
var
  CH_BW: Integer;
  ARect: TRect;
begin
  with DockClient do
  begin
    { 获得CanDock的值 }
    CanDock := IsDockable(ParentForm, Client, Source.DropOnControl, Source.DropAlign);
    if CanDock then
    begin
      {获得停靠控件的矩形区域}
      GetWindowRect(ParentForm.Handle, InfluenceRect);
      if ParentForm.HostDockSite is TCnCustomDockPanel then
        { 需要减去GrabberSize(把手的高度) }
        Dec(InfluenceRect.Top, TCnCustomDockPanel(ParentForm.HostDockSite).CnDockManager.GrabberSize);
      if PtInRect(InfluenceRect, MousePos) then
      begin
        ARect := InfluenceRect;
        InflateRect(ARect, -DefExpandoRect, -DefExpandoRect);
        {获得标题栏的高度和边框的宽度}
        CH_BW := Cn_GetSysCaptionHeightAndBorderWidth;
        Inc(ARect.Top, CH_BW + 1);
        if PtInRect(ARect, MousePos) then
        begin
          { 如果鼠标位置在InfluenceRect中，但是不是在InfluenceRect的边缘，
            就告诉系统不能停靠 }
          InfluenceRect := Rect(0, 0, 0, 0);
          CanDock := False;
        end;
      end;
    end;
  end;
end;

constructor TCnVIDDockStyle.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  CnDockPanelClass := TCnVIDDockPanel;
  CnDockSplitterClass := TCnVIDDockSplitter;
  CnConjoinPanelClass := TCnVIDConjoinPanel;
  CnTabDockClass := TCnVIDTabPageControl;
  CnDockPanelTreeClass := TCnVIDDockTree;
  CnDockPanelZoneClass := TCnVIDDockZone;
  CnConjoinPanelTreeClass := TCnVIDDockTree;
  CnConjoinPanelZoneClass := TCnVIDDockZone;
  CnConjoinServerOptionClass := TCnVIDConjoinServerOption;
  CnTabServerOptionClass := TCnVIDTabServerOption;
end;

procedure TCnVIDDockStyle.FormDockDrop(DockClient: TCnDockClient;
  Source: TCnDragDockObject; X, Y: Integer);
var
  ARect,DRect: TRect;
  DockType: TAlign;
  Host: TCnDockableForm;
  APanelDock: TWinControl;
  VIDSource: TCnVIDDragDockObject;
  i: Integer;
begin
  if Source is TCnVIDDragDockObject then
  begin
    TCnVIDDragDockObject(Source).CurrState := dsDragEnter;
    TCnVIDDragDockObject(Source).OldState := dsDragEnter;
  end;

  if IsDockable(DockClient.ParentForm, Source.Control, Source.DropOnControl, Source.DropAlign) then
  begin
    Host := nil;
    { 锁住Windows桌面 }
    if not IsLoading then
      Cn_LockWindow(nil);
    try
      with DockClient do
      begin
        // 调用ComputeVIDDockingRect函数知道停靠的类型
        DockType := ComputeVIDDockingRect(DockClient.ParentForm, Source.Control, ARect, Point(X, Y));

        if (ParentForm.HostDockSite is TCnDockPanel) then
        begin
          // 如果停靠服务器是TDockPanel，就停靠在TDockServer的DockPanel上。
          if DockType = alClient then
          begin
            // 如果停靠类型是alClient
            if Source.Control is TCnTabDockHostForm then
            begin
              // 如果停靠客户是TCnTabDockHostForm，
              // 就先把Parent停靠到TCnTabDockHostForm中，
              // 再把TCnTabDockHostForm停靠到TCnDockPanel中。
              APanelDock := ParentForm.HostDockSite;
              ARect := ParentForm.BoundsRect;
              ParentForm.ManualDock(TCnTabDockHostForm(Source.Control).PageControl, nil, alClient);
              TCnTabDockHostForm(Source.Control).PageControl.ActivePage.PageIndex := 0;
              Source.Control.BoundsRect := ARect;
              Source.Control.ManualDock(APanelDock, nil, alClient);
              if ParentForm.FormStyle = fsStayOnTop then
                TForm(Source.Control).FormStyle := fsStayOnTop;
            end else
            begin
              // 否则就创建TCnTabDockHostForm，
              // 把把Parent停靠到TCnTabDockHostForm中，
              // 再把TCnTabDockHostForm停靠到TCnDockPanel中。
              APanelDock := ParentForm.HostDockSite;
              DRect.TopLeft := ParentForm.HostDockSite.ClientToScreen(Point(0, 0));
              Host := CreateTabHostAndDockControl(ParentForm, Source.Control);
              SetDockSite(ParentForm, False);
              SetDockSite(TWinControl(Source.Control), False);
              Host.Top := DRect.Top;
              Host.Left := DRect.Left;
              Host.Visible := True;
              Host.ManualDock(APanelDock, nil, alClient);
            end;
          end
          else
          begin
            // 如果停靠类型不是alClient,
            // 就把停靠窗体停靠到TCnDockPanel.
            DRect := ParentForm.HostDockSite.BoundsRect;
            Source.Control.ManualDock(ParentForm.HostDockSite, nil, DockType);
            ParentForm.HostDockSite.BoundsRect := DRect;
            SetDockSite(TWinControl(Source.Control), False);
          end;
          Exit;
        end;

        // 创建分页的服务窗体
        if DockType = alClient then
        begin
          if Source.Control is TCnTabDockHostForm then
          begin
            // 如果停靠客户是TCnTabDockHostForm，
            // 就先把Parent停靠到TCnTabDockHostForm中，
            // 再把TCnTabDockHostForm停靠到TCnDockPanel中。
            APanelDock := ParentForm.HostDockSite;
            ARect := ParentForm.BoundsRect;
            ParentForm.ManualDock(TCnTabDockHostForm(Source.Control).PageControl, nil, alClient);
            TCnTabDockHostForm(Source.Control).PageControl.ActivePage.PageIndex := 0;
            Source.Control.BoundsRect := ARect;
            Source.Control.ManualDock(APanelDock, nil, alClient);
            if ParentForm.FormStyle = fsStayOnTop then
              TForm(Source.Control).FormStyle := fsStayOnTop;
            Exit;
          end else
          begin
            if Source is TCnVIDDragDockObject then
            begin
              VIDSource := TCnVIDDragDockObject(Source);
              DoFloatForm(Source.Control);
              FreeAllDockableForm;
              for i := 0 to VIDSource.SourceDockClientCount - 1 do
              begin
                VIDSource.Control := VIDSource.SourceDockClients[i];
                if Host = nil then
                  Host := DockClient.CreateTabHostAndDockControl(DockClient.ParentForm, Source.Control)
                else Source.Control.ManualDock(TCnTabDockHostForm(Host).PageControl, nil, alClient);
              end;
              Host.Visible := True;
  //            CnGlobalDockPresident.DragObject.Control := nil;
            end;
          end;
        end
        else if DockType <> alNone then
        begin
          // 创建平铺的服务窗体
          Host := CreateConjoinHostAndDockControl(ParentForm, Source.Control, DockType);
          SetDockSite(ParentForm, False);
          SetDockSite(TWinControl(Source.Control), False);
          Host.Visible := True;
        end;

        if Host <> nil then
        begin
          Host.LRDockWidth := Source.Control.LRDockWidth;
          Host.TBDockHeight := Source.Control.TBDockHeight;
        end;
      end;
    finally
      { 解锁Windows桌面 }
      if not IsLoading then
        Cn_UnLockWindow;
    end;
  end;
end;

procedure TCnVIDDockStyle.SetDockBaseControl(IsCreate: Boolean;
  DockBaseControl: TCnDockBaseControl);
var ADockClient: TCnDockClient;
begin
  if DockBaseControl is TCnDockClient then
  begin
    ADockClient := TCnDockClient(DockBaseControl);
    if IsCreate then
      ADockClient.DirectDrag := False;
  end;
end;

procedure TCnVIDDockStyle.FormStartDock(DockClient: TCnDockClient;
  var Source: TCnDragDockObject);
begin
  inherited FormStartDock(DockClient, Source);
  { 创建一个TCnVIDDragDockObject返回给系统 }
  Source := TCnVIDDragDockObject.Create(DockClient.ParentForm);
end;

procedure TCnVIDDockStyle.FormGetDockEdge(DockClient: TCnDockClient;
  Source: TCnDragDockObject; MousePos: TPoint; var DropAlign: TAlign);
var ARect: TRect;
begin
  { 获得停靠的位置 }
  DropAlign := ComputeVIDDockingRect(DockClient.ParentForm, Source.Control, ARect, MousePos);
end;

function TCnVIDDockStyle.DockClientWindowProc(DockClient: TCnDockClient;
  var Message: TMessage): Boolean;
begin
  Result := inherited DockClientWindowProc(DockClient, Message);
end;

procedure TCnVIDDockStyle.CreateConjoinServerOption(
  var Option: TCnBasicConjoinServerOption);
begin
  Option := TCnVIDConjoinServerOption.Create(Self);
end;

procedure TCnVIDDockStyle.CreateTabServerOption(
  var Option: TCnBasicTabServerOption);
begin
  Option := TCnVIDTabServerOption.Create(Self);
end;
  
procedure TCnVIDDockStyle.AssignConjoinServerOption(
  APanel: TCnCustomDockPanel);
begin
  inherited AssignConjoinServerOption(APanel);
end;

procedure TCnVIDDockStyle.AssignTabServerOption(APage: TCnTabPageControl);
var TmpPage: TCnVIDTabPageControl;
  TmpOption: TCnVIDTabServerOption;
begin
  inherited AssignTabServerOption(APage);
  if (APage is TCnVIDTabPageControl) and (TabServerOption is TCnVIDTabServerOption) then
  begin
    TmpPage := APage as TCnVIDTabPageControl;
    TmpOption := TabServerOption as TCnVIDTabServerOption;
    TmpPage.ActiveFont.Assign(TmpOption.ActiveFont);
    TmpPage.ActiveSheetColor := TmpOption.ActiveSheetColor;
    TmpPage.InactiveFont.Assign(TmpOption.InactiveFont);
    TmpPage.InactiveSheetColor := TmpOption.InactiveSheetColor;
    TmpPage.HotTrackColor := TmpOption.HotTrackColor;
    TmpPage.ShowTabImages := TmpOption.ShowTabImages;
  end;
end;

procedure TCnVIDDockStyle.ParentFormWindowProc(var Message: TMessage);
begin
  inherited ParentFormWindowProc(Message);
  if (Message.Msg = WM_SETTINGCHANGE) or (Message.Msg = WM_SYSCOLORCHANGE) then
  begin
    ParentForm.Caption := '';
    { 如果是WM_SETTINGCHANGE或者WM_SYSCOLORCHANGE消息，也就是当设置桌面属性的外观后。
      就重新设置ConjoinServerOption的属性 }
    if (ConjoinServerOption is TCnVIDConjoinServerOption) then
    begin
      if TCnVIDConjoinServerOption(ConjoinServerOption).SystemInfo then
        TCnVIDConjoinServerOption(ConjoinServerOption).SetDefaultSystemCaptionInfo;
    end;
  end;
end;

destructor TCnVIDDockStyle.Destroy;
begin
  inherited;

end;

procedure TCnVIDDockStyle.DoSystemInfoChange(Value: Boolean);
begin
  if Assigned(FSystemInfoChange) then
    FSystemInfoChange(Value);
end;

procedure TCnVIDDockStyle.GetComponentInfo(var AName, Author, Email,
  Comment: string);
begin
  AName := SCnVIDDockStyleName;
  Author := SCnPack_LuXiaoban;
  Email := SCnPack_LuXiaobanEmail;
  Comment := SCnVIDDockStyleComment;
end;

{ TCnVIDDockPanel }

constructor TCnVIDDockPanel.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
end;

function TCnVIDDockPanel.CreateDockManager: IDockManager;
var Option: TCnVIDConjoinServerOption;
begin
  Result := inherited CreateDockManager;
  if (DockServer <> nil) and (Result <> nil) then
  begin
    Option := TCnVIDConjoinServerOption(DockServer.DockStyle.ConjoinServerOption);
    (Result as ICnDockManager).GrabberSize := Option.GrabbersSize;
  end;
end;

procedure TCnVIDDockPanel.CustomDockDrop(Source: TCnDragDockObject; X, Y: Integer);
//var
//  VIDSource: TCnVIDDragDockObject;
begin
{  if Source is TCnVIDDragDockObject then
  begin
    VIDSource := TCnVIDDragDockObject(Source);
    SetTabControlPreview(VIDSource, VIDSource.FDropTabControl, dsDragLeave, VIDSource.DropAlign);
  end;}
  if Source.Control is TCnDockableForm then
    { 显示DockPanel }
    ShowDockPanel(True, Source.Control);
  if not ((Source.Control.HostDockSite <> nil) and
    (Source.DropOnControl = Source.Control.HostDockSite.Parent) and
    (Source.DropAlign = alClient)) then
  begin
    inherited CustomDockDrop(Source, X, Y);
    { 因为是平铺风格，所以要设置ActiveControl为当前Source的Control }
    CnDockManager.ActiveControl := Source.Control;
    { 设置Source.Control为获得焦点，这一步一定需要设置，
      不然程序可能会把Source.Control中的控件的内容清空 }
    if (Source.Control is TWinControl) and TWinControl(Source.Control).CanFocus then
      TWinControl(Source.Control).SetFocus;
  end;
end;

procedure TCnVIDDockPanel.CustomDockOver(Source: TCnDragDockObject; X,
  Y: Integer; State: TDragState; var Accept: Boolean);
var DropAlign: TAlign;
//  VIDSource: TCnVIDDragDockObject;
//  DropCtl: TControl;
begin
  { 首先调用父类的CustomDockOver }
  inherited CustomDockOver(Source, X, Y, State, Accept);
  if Accept and (Source is TCnVIDDragDockObject) then
  begin
(*    VIDSource := TCnVIDDragDockObject(Source);
    DropCtl := VIDSource.GetDropCtl;
    if (DropCtl <> VIDSource.DropOnControl) or
      (VIDSource.FOldDropTabControl <> VIDSource.FDropTabControl) then
      SetTabControlPreview(VIDSource, VIDSource.FOldDropTabControl, dsDragLeave, VIDSource.DropAlign);
    { 调用SetTabControlPreview函数用来显示预览界面 }
    SetTabControlPreview(VIDSource, VIDSource.FDropTabControl, State, VIDSource.DropAlign);
    if State = dsDragLeave then
      { 离开的时候要设置FDropTabControl为空 }
      VIDSource.FDropTabControl := nil;
    VIDSource.FOldDropTabControl := VIDSource.FDropTabControl;*)
    if State = dsDragMove then
    begin
      DropAlign := Source.DropAlign;
      { 调用CnDockManager的GetDockEdge来得到停靠的位置 }
      CnDockManager.GetDockEdge(Source.DockRect, Source.DragPos, DropAlign, Source.Control);
    end;// else if (State = dsDragLeave) then
//      if (Source.DropAlign = alClient) and (Source.DropOnControl = nil) then
//      Source.DropAlign := alNone;
  end;
end;

procedure TCnVIDDockPanel.CustomGetDockEdge(Source: TCnDragDockObject;
  MousePos: TPoint; var DropAlign: TAlign);
begin
//  inherited CustomGetDockEdge(Source, MousePos, DropAlign);
end;

procedure TCnVIDDockPanel.CustomGetSiteInfo(Source: TCnDragDockObject;
  Client: TControl; var InfluenceRect: TRect; MousePos: TPoint;
  var CanDock: Boolean);
begin
  { 如果VisibleDockClientCount = 0，也就是说停靠服务器中的TCnVIDDockPanel还没有显示出来，
    就调用默认的CustomGetSiteInfo，否者就要调用CnDockManager.GetSiteInfo来获得InfluenceRect }
  if VisibleDockClientCount = 0 then
    inherited CustomGetSiteInfo(Source, Client, InfluenceRect, MousePos, CanDock)
  else
  begin
    CanDock := IsDockable(Self, Client, Source.DropOnControl, Source.DropAlign);
    if CanDock then
      CnDockManager.GetSiteInfo(Client, InfluenceRect, MousePos, CanDock);
  end;
end;

procedure TCnVIDDockPanel.CustomStartDock(var Source: TCnDragDockObject);
begin
  Source := TCnVIDDragDockObject.Create(Self);
end;

procedure TCnVIDDockPanel.DockDrop(Source: TDragDockObject; X, Y: Integer);
begin
  inherited;

end;

{ TCnVIDDockTree }

constructor TCnVIDDockTree.Create(DockSite: TWinControl;
  CnDockZoneClass: TCnDockZoneClass);
begin
  inherited Create(DockSite, CnDockZoneClass);
  FDropOnZone := nil;
  GrabberSize     := 18;
  ButtonHeight    := 11;
  ButtonWidth     := 13;
  LeftOffset      := 2;
  RightOffset     := 2;
  TopOffset       := 4;
  BottomOffset    := 3;
  ButtonSplitter  := 2;
  BorderWidth     := 0;
  MinSize         := 20;  //节点的最小值为20
  CaptionLeftOffset := 0;
  CaptionRightOffset := 0;
end;

destructor TCnVIDDockTree.Destroy;
begin
  inherited Destroy;
end;

function TCnVIDDockTree.GetGrabbersPosition: TGrabbersPosition;
begin
  { 在这里默认的把手位置都是gpTop }
  Result := gpTop;
end;

function TCnVIDDockTree.GetTopGrabbersHTFlag(const MousePos: TPoint;
  out HTFlag: Integer; Zone: TCnDockZone): TCnDockZone;
begin
  if (MousePos.Y >= Zone.Top) and (MousePos.Y <= Zone.Top + GrabberSize) and
    (MousePos.X >= Zone.Left) and (MousePos.X <= Zone.Left + Zone.Width) then
  begin
    Result := Zone;
    with Zone.ChildControl do
    begin
      if PtInRect(Rect(
        Left + Width - ButtonWidth - RightOffset,
        Top - GrabberSize + TopOffset,
        Left + Width - RightOffset,
        Top - GrabberSize + TopOffset + ButtonHeight), MousePos) then
        HTFlag := HTCLOSE
      else HTFlag := HTCAPTION;
    end;
  end else Result := nil;
end;

procedure TCnVIDDockTree.InsertControl(Control: TControl; InsertAt: TAlign;
  DropCtl: TControl);

  { 根据Client，DropCtl和InsertAt来创建一个TCnTabDockHostForm窗体，
    并且把Client和DropCtl停靠到它的PageControl中 }
  function CreateDockPageControl(Client: TControl): TCnTabDockHostForm;
  var
    Zone: TCnDockZone;          //根据DropCtl查找到的节点
    TempCtl: TControl;          //用来存储原来的DropCtl，因为DropCtl有可能会被改变
    TempPanel: TCnConjoinPanel; //存储TempCtl的HostDockSite
    DockClient: TCnDockClient;  //在DropCtl中的DockClient
    APoint: TPoint;
  begin
    Result := nil;
    Zone := FindControlZone(DropCtl);
    DockClient := FindDockClient(DropCtl);
    if (DockClient <> nil) and (Zone <> nil) then
    begin
      TempCtl := DropCtl;
      { 下面的语句用来给DropCtl和InsertAt赋值 }
      if Zone.ParentZone.Orientation = doHorizontal then
      begin
        if (Zone.PrevSibling = nil) then
        begin
          if Zone.NextSibling <> nil then
            DropCtl := Zone.NextSibling.ChildControl;
          InsertAt := alTop;
        end else 
        begin
          DropCtl := Zone.PrevSibling.ChildControl;
          InsertAt := alBottom;
        end;
      end else if Zone.ParentZone.Orientation = doVertical then
      begin
        if (Zone.PrevSibling = nil) then
        begin
          if Zone.NextSibling <> nil then
            DropCtl := Zone.NextSibling.ChildControl;
          InsertAt := alLeft;
        end else //if Zone.NextSibling = nil then
        begin
          DropCtl := Zone.PrevSibling.ChildControl;
          InsertAt := alRight;
        end;
      end;

      { 考虑到在调用完DockClient的CreateTabHostAndDockControl函数后TempCtl.HostDockSite可能会改变
        所以先把它保存下来，供以后使用 }

      if TempCtl.HostDockSite is TCnConjoinPanel then
        TempPanel := TCnConjoinPanel(TempCtl.HostDockSite)
      else TempPanel := nil;

      { 调用DockClient的CreateTabHostAndDockControl函数，根据TempCtl和Client创建TCnTabDockHostForm }
      Result := DockClient.CreateTabHostAndDockControl(TempCtl, Client);
      if TempPanel <> nil then
        { 这条语句是用在TCnDockableForm的DoClose中的，用来判断UnDockControl是否是将要又被停靠进TempPanel，
          如果是的话，就不把TempPanel.ParentForm释放掉，否者就有可能被释放掉，具体代码请参看TCnDockableForm的DoClose函数 }
        TempPanel.ParentForm.UnDockControl := Result;

      { 设置TempCtl和Control的DockSite属性为False }
      SetDockSite(TWinControl(TempCtl), False);
      SetDockSite(TWinControl(Client), False);

      { 重新设置Result的位置 }
      if DockSite.Align = alBottom then
        APoint := Point(0, -TempCtl.TBDockHeight)
      else if DockSite.Align = alRight then
        APoint := Point(-TempCtl.LRDockWidth, 0)
      else APoint := Point(0, 0);
      APoint :=  DockSite.ClientToScreen(APoint);
      Result.Left := APoint.x;
      Result.Top := APoint.y;
      Result.UndockWidth := TempCtl.UndockWidth;
      Result.UndockHeight := TempCtl.UndockHeight;
      Result.LRDockWidth := TempCtl.LRDockWidth;
      Result.TBDockHeight := TempCtl.TBDockHeight + GrabberSize;

      { 显示TCnTabDockHostForm }
      Result.Visible := True;
    end;
  end;

var i: Integer;
  Host: TCnTabDockHostForm;
  ChildCount: Integer;
  VIDSource: TCnVIDDragDockObject;
  TempControl: TControl;
  ARect: TRect;
  AZone: TCnDockZone;
begin
  { 锁住Windows桌面 }
  if not IsLoading then
    Cn_LockWindow(nil);
  try
    VIDSource := nil;
    if (Control is TCnDockableForm){ and (DockSite is TCnConjoinPanel) }then
    begin
      { 如果将要停靠进来的Control是TCnDockableForm，也就是说Control是一个停靠服务器 }
      if InsertAt in [alClient] then
      begin
        { 如果停靠类型是alClient，就说明要把Control和DropCtl停靠到一个TCnVIDTabPageControl中去，
          然后把TCnVIDTabPageControl的ParentForm--也就是TCnTabDockHostForm停靠到DockSite中 }
        if DropCtl is TCnTabDockHostForm then
        begin
//          IsLoading := True;
          { 如果DropCtl本身就是一个TCnTabDockHostForm，那只要把Control中的所有停靠客户依次停靠到DropCtl的PageControl中去 }
          try
            VIDSource := TCnVIDDragDockObject.Create(Control);
            DoFloatForm(Control);
            FreeAllDockableForm;
            for i := VIDSource.SourceDockClientCount - 1 downto 0 do
            begin
              TempControl := VIDSource.SourceDockClients[i];
              TempControl.ManualDock(TCnTabDockHostForm(DropCtl).PageControl);
              if TempControl is TForm then
              begin
                TForm(TempControl).ActiveControl := nil;
                SetDockSite(TForm(TempControl), False);
              end;
            end;
          finally
//            IsLoading := False;
//            ReshowAllVisibleWindow;
            VIDSource.Free;
            CnGlobalDockPresident.DragObject.Control := nil;
          end;
        end else
        begin
          if (DockSite is TCnCustomDockPanel) and (DockSite.VisibleDockClientCount > 1) and (DropCtl <> nil) then
          begin
//            IsLoading := True;
            try
              VIDSource := TCnVIDDragDockObject.Create(Control);
              DoFloatForm(Control);
              FreeAllDockableForm;
              { 否者，就首先创建一个TCnDockableForm用来作为Control的DockClients[0]和DropCtl的服务器Host }
              Host := CreateDockPageControl(VIDSource.SourceDockClients[0]);//TCnDockableForm(Control).DockableControl.DockClients[0]);
              if Host <> nil then
              begin
                { 然后再把Control中的所有停靠客户依次停靠到DropCtl的PageControl中去 }
                for i := VIDSource.SourceDockClientCount - 1 downto 1 do
                begin
                  TempControl := VIDSource.SourceDockClients[i];
                  TempControl.ManualDock(Host.PageControl);
                  if TempControl is TForm then
                  begin
                    TForm(TempControl).ActiveControl := nil;
                    SetDockSite(TForm(TempControl), False);
                  end;
                end;
                { 最后把Host停靠到DockSite中，注意：这时候DropCtl和InsertAt已经改变了 }
                Host.ManualDock(DockSite, nil, InsertAt);
              end;
            finally
//              IsLoading := False;
//              ReshowAllVisibleWindow;
              VIDSource.Free;
              CnGlobalDockPresident.DragObject.Control := nil;
            end;
          end else
            { 否者调用原来的虚拟函数 }
            inherited InsertControl(Control, InsertAt, DropCtl);
        end;
      end else if Control is TCnConjoinDockHostForm then
      begin
        { 否则如果Control是一个平铺服务器，就要进行特殊的处理，
          调用InsertControlFromConjoinHost函数，把Control中的停靠信息还原到DockSite中 }
        TCnTempWinControl(TCnDockableForm(Control).DockableControl).DockManager.ResetBounds(True);
        InsertControlFromConjoinHost(Control, InsertAt, DropCtl);
      end else
        { 否者调用原来的虚拟函数 }
        inherited InsertControl(Control, InsertAt, DropCtl);
    end else
    begin
      { 否者，也就是说Control是一个普通的窗体 }
      if InsertAt in [alLeft, alTop] then
        { 对DockSize进行必要的调整 }
        DropDockSize := DropDockSize + SplitterWidth div 2;
      if InsertAt in [alClient] then
      begin
        { 如果停靠类型是alClient，就说明要把Control和DropCtl停靠到一个TCnVIDTabPageControl中去，
          然后把TCnVIDTabPageControl的ParentForm--也就是TCnTabDockHostForm停靠到DockSite中 }
        if DropCtl is TCnTabDockHostForm then
        begin
          { 如果DropCtl本身就是一个TCnTabDockHostForm，那只要简单的把Control停靠到DropCtl的PageControl中去 }
          Control.ManualDock(TCnTabDockHostForm(DropCtl).PageControl, nil, alClient);
        end
        else if TopZone.ChildZones <> nil then
        begin
          { 否者如果DockSite本身有停靠客户 }
          ChildCount := TopZone.ChildCount;
          if DropCtl <> nil then
          begin
            ARect := DropCtl.BoundsRect;
            AZone := FindControlZone(DropCtl);
            // 当这个节点紧靠DockSite的右边或者下边的时候，不用给它加偏移量SplitterWidth
            if DropCtl.DockOrientation = doHorizontal then
            begin
              if ((AZone <> nil) and (AZone.ZoneLimit <> DockSite.Height)) then
                ARect.Bottom := ARect.Bottom + SplitterWidth;
            end else
            begin
              if ((AZone <> nil) and (AZone.ZoneLimit <> DockSite.Width)) then
                ARect.Right := ARect.Right + SplitterWidth;
            end;
            DockRect := ARect;
          end
          else DockRect := Rect(0, 0, TopZone.Width, TopZone.Height);
          { 创建一个叫做Host的窗体作为Control和DropCtl的服务器 }
          Host := CreateDockPageControl(Control);
          if Host <> nil then
          begin
            if (ChildCount >= 2) or (DockSite is TCnDockPanel) then
            begin
              { 如果DockSite的客户大于一个，就把Host停靠到DockSite中 }
              if InsertAt in [alLeft, alRight] then
                DropDockSize := DockRect.Right - DockRect.Left
              else DropDockSize := DockRect.Bottom - DockRect.Top + GrabberSize;
              // 锁住DropDockSize
              LockDropDockSize;
              Host.ManualDock(DockSite, DropCtl, InsertAt);
              // 解锁DropDockSize
              UnlockDropDockSize;

            end else
            begin
              { 否者只是简单的设置Host的位置大小 }
              Host.BoundsRect := DockSite.Parent.BoundsRect;
            end;
          end;
        end
        else inherited InsertControl(Control, InsertAt, DropCtl);
      end else
      { 否者调用原来的虚拟函数 }
      inherited InsertControl(Control, InsertAt, DropCtl);
      { 这条语句是用来给DockRect赋值，这是因为有可能InsertControl会调用好几次,
        下次调用InsertControl的时候可能会使用到DockRect }
      DockRect := gi_DockRect;
    end;
    ForEachAt(nil, UpdateZone);
  finally
    { 解锁Windows桌面 }
    if not IsLoading then
      Cn_UnLockWindow;
  end;
end;

procedure TCnVIDDockTree.InsertControlFromConjoinHost(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 }
  ReverseAt: array[TAlign] of TAlign = (alClient, alBottom, alTop, alRight, alLeft, alNone, alCustom); { 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);
  ReverseAt: array[TAlign] of TAlign = (alClient, alBottom, alTop, alRight, alLeft, alNone);
{$ENDIF}

var
  Stream: TMemoryStream;

  TopOrientation,                       //Control中的TopZone的Orientation属性
  InsertOrientation,                    //要插入的方向
  CurrentOrientation: TDockOrientation; //当前的方向
  ZoneLimit: Integer;

  Level, LastLevel, I: Integer;
  Zone, NextZone: TCnDockZone;
  DropCtlZone, LastZone: TCnDockZone;

  OffsetXYLimitArr: array[TDockOrientation] of Integer;  //偏移量
  ControlXYLimitArr: array[TDockOrientation] of Integer; //控件的宽度和高度

  { 读出控件的名称 }
{  procedure ReadControlName(var ControlName: string);
  var
    Size: Integer;
  begin
    ControlName := '';
    Stream.Read(Size, SizeOf(Size));
    if Size > 0 then
    begin
      SetLength(ControlName, Size);
      Stream.Read(Pointer(ControlName)^, Size);
    end;
  end;}

  { 读出Zone并且按要求设置它在DockTree中的位置 }
  procedure ReadZone(SetZone: Boolean);
  var I: Integer;
  begin
    { 读Control中DockTree的Zone }
    with Stream do
    begin
      { 读出Level值，这个值代表了Zone的层次，如果是0就是TopZone，
      如果是1就是TopZone的子女，依次类推 }
      Read(Level, SizeOf(Level));
      if Level = TreeStreamEndFlag then Exit;
      { 创建Zone }
      Zone := CnDockZoneClass.Create(Self);
      { 读出Zone的Orientation属性 }
      CustomLoadZone(Stream, Zone);
      { 读出Zone的ZoneLimit属性，这个值不能直接赋值给Zone，还需要加上偏移量 }
      ZoneLimit := Zone.ZoneLimit;
    end;
    if SetZone then
    begin
      { 如果SetZone等于True，也就是说要设置Zone在DockTree中的关系 }
      if Level = LastLevel then
      begin
        { 如果Level和LastLevel相等，也就是说Zone和LastZone是兄弟关系，就把Zone
        加为LastZone的兄弟。}
        Zone.NextSibling := LastZone.NextSibling;
        if LastZone.NextSibling <> nil then
          LastZone.NextSibling.PrevSibling := Zone;
        LastZone.NextSibling := Zone;
        Zone.PrevSibling := LastZone;
        Zone.ParentZone := LastZone.ParentZone;
      end
      else if Level > LastLevel then
      begin
        { 如果Level大于LastLevel，也就是说Zone是LastZone的子女，就把Zone
        加为LastZone的子女。}
        LastZone.ChildZones := Zone;
        Zone.ParentZone := LastZone;
        InsertOrientation := LastZone.Orientation;
      end
      else if Level < LastLevel then
      begin
        { 如果Level小于LastLevel，就找到和Zone等级相等的NextZone，并且把Zone
        加为NextZone的兄弟。}
        NextZone := LastZone;
        for I := 1 to LastLevel - Level do
          NextZone := NextZone.ParentZone;
        Zone.NextSibling := NextZone.NextSibling;
        if NextZone.NextSibling <> nil then
          NextZone.NextSibling.PrevSibling := Zone;
        NextZone.NextSibling := Zone;
        Zone.PrevSibling := NextZone;
        Zone.ParentZone := NextZone.ParentZone;
        InsertOrientation := Zone.ParentZone.Orientation;
      end;
      { Zone的ZoneLimit要加上偏移量 }
      Zone.ZoneLimit := OffsetXYLimitArr[InsertOrientation] + ZoneLimit;
    end;
    { 把当前的值赋值给Last，进行下一次循环 }
    LastLevel := Level;
    LastZone := Zone;
  end;

begin
  { 控件的长宽 }
  ControlXYLimitArr[doNoOrient] := 0;
  ControlXYLimitArr[doHorizontal] := DockRect.Bottom - DockRect.Top ;//}Control.Height;// - GrabberSize;
  ControlXYLimitArr[doVertical] := DockRect.Right - DockRect.Left;//}Control.Width;// - BorderWidth;

  { 创建并且存储停靠信息到流中 }
  Stream := TMemoryStream.Create;
  if Control is TCnConjoinDockHostForm then
    TCnConjoinDockHostForm(Control).Panel.CnDockManager.SaveToStream(Stream);
  Stream.Position := 0;
  { 开始更新 }
  BeginUpdate;
  try
    { 读版本 }
    Stream.Read(I, SizeOf(I));
    { TopZone中的TopXYLimit(四字节)和Level(四字节)没有用处，被忽略掉 }
    Stream.Position := Stream.Position + 8;

    { 读出停靠窗体的停靠方向 }
    Stream.Read(TopOrientation, SizeOf(TopOrientation));
    { 读出停靠窗体的Limit }
    Stream.Read(ZoneLimit, SizeOf(ZoneLimit));
    
    IgnoreZoneInfor(Stream);

    if (DropCtl = nil) and (TopZone.ChildCount = 1) then
      { 如果TopZone只有一个子女，就把这个子女赋值给DropCtl }
      DropCtl := TopZone.ChildZones.ChildControl;
    { 查找到DropCtl属于哪一个Zone }
    DropCtlZone := FindControlZone(DropCtl);
    { 默认的插入方向是右边 }
    if InsertAt in [alClient, alNone] then InsertAt := alRight;
    InsertOrientation := OrientArray[InsertAt];

    if TopZone.ChildCount = 0 then
    begin
      { DockSite中还没有控件(可视的)，插入方向和Top的方向就设置成和Control中的TopZone一样 }
      TopZone.Orientation := TopOrientation;
      InsertOrientation := TopOrientation;
    end
    else if TopZone.ChildCount = 1 then
    begin
      // 如果树只有一个子女，并且第二个正在被添加进去，
      // 所以方向和位置必须被设置
      TopZone.Orientation := InsertOrientation;
      case InsertOrientation of
        doHorizontal:
          begin
            TopZone.ZoneLimit := TopZone.ChildZones.Width;
            TopXYLimit := TopZone.ChildZones.Height;
          end;
        doVertical:
          begin
            TopZone.ZoneLimit := TopZone.ChildZones.Height;
            TopXYLimit := TopZone.ChildZones.Width;
          end;
      end;
    end;

    { 设置当前的DropCtlZone的方向 }
    if DropCtlZone <> nil then
      CurrentOrientation := DropCtlZone.ParentZone.Orientation
    else
      CurrentOrientation := TopZone.Orientation;

    { 设置DockSize的大小 }
    if InsertOrientation = doHorizontal then
      DropDockSize := DockRect.Bottom - DockRect.Top
    else if InsertOrientation = doVertical then
      DropDockSize := DockRect.Right - DockRect.Left
    else DropDockSize := 0;

    { 设置偏移量 }
    OffsetXYLimitArr[doNoOrient] := 0;
    if DropCtlZone <> nil then
    begin
      { 首先计算水平偏移量 }
      OffsetXYLimitArr[doHorizontal] := DropCtlZone.TopLeft[doHorizontal] +
        Integer(MakeLast[InsertAt]) * (DropCtlZone.HeightWidth[doHorizontal] - ControlXYLimitArr[doHorizontal]);
      { 如果停靠操作是在水平分割条附近，就重新计算水平偏移量 }
      if (FDropOnZone <> nil) and (InsertOrientation = doHorizontal) then
        OffsetXYLimitArr[doHorizontal] := FDropOnZone.ZoneLimit - Round((FDropOnZone.ZoneLimit -
          FDropOnZone.ParentZone.ChildZones.LimitBegin) * (DropDockSize + BorderWidth) / (FDropOnZone.ParentZone.Height));

      { 然后计算垂直偏移量 }
      OffsetXYLimitArr[doVertical] := DropCtlZone.TopLeft[doVertical] +
        Integer(MakeLast[InsertAt]) * (DropCtlZone.HeightWidth[doVertical] - ControlXYLimitArr[doVertical]);
      { 如果停靠操作是在垂直分割条附近，就重新计算垂直偏移量 }
      if (FDropOnZone <> nil) and (InsertOrientation = doVertical) then
        OffsetXYLimitArr[doVertical] := FDropOnZone.ZoneLimit - Round((FDropOnZone.ZoneLimit -
          FDropOnZone.ParentZone.ChildZones.LimitBegin) * (DropDockSize + BorderWidth) / (FDropOnZone.ParentZone.Width));

    end else
    begin
      { 这一项可能没有用处，因为DropCtlZone在这里一直为nil }
      if TopZone.VisibleChildCount = 0 then
//      if TopZone.ChildZones = nil then
      begin
        OffsetXYLimitArr[doHorizontal] := 0;
        OffsetXYLimitArr[doVertical]   := 0;
      end else
      begin
        OffsetXYLimitArr[doHorizontal] := Integer(MakeLast[InsertAt]) * ControlXYLimitArr[doHorizontal];
//        InsertAt := ReverseAt[InsertAt];
        OffsetXYLimitArr[doVertical] := Integer(MakeLast[InsertAt]) * ControlXYLimitArr[doVertical];
      end;
    end;

    if TopOrientation <> InsertOrientation then
    begin
      { TopOrientation和InsertOrientation的方向不同，
      就要先创建一个LastZone作为Control中的Zone父亲 }
      LastZone := CnDockZoneClass.Create(Self);
      if InsertOrientation <> CurrentOrientation then
        { InsertOrientation和CurrentOrientation的方向不同，
        就要创建一个Zone作为LastZone和DropCtlZone的父亲 }
        InsertNewParent(LastZone, DropCtlZone, InsertOrientation, MakeLast[InsertAt], True)
      else
        { 否者就只是简单的使LastZone和DropCtlZone成为兄弟关系 }
        InsertSibling(LastZone, DropCtlZone, MakeLast[InsertAt], True);
      { 调整LastZone的ZoneLimit属性 }
//      LastZone.ZoneLimit := OffsetXYLimitArr[InsertOrientation] + ControlXYLimitArr[InsertOrientation];
      { LastZone的Orientation属性和Control中的TopZone的方向相等 }
      LastZone.Orientation := TopOrientation;
      { LastZone是在根节点 }
      LastLevel := 0;
    end else
    begin
      LastLevel := 1;
      if TopZone.ChildCount > 0 then
      begin
        ReadZone(False);
        if InsertOrientation <> CurrentOrientation then
          InsertNewParent(LastZone, DropCtlZone, InsertOrientation, MakeLast[InsertAt], True)
        else InsertSibling(LastZone, DropCtlZone, MakeLast[InsertAt], True);
        LastZone.ZoneLimit := ZoneLimit + OffsetXYLimitArr[InsertOrientation];
      end else
      begin
        LastLevel := 0;
        LastZone := TopZone;
      end;
      { 设置DropCtlZone的ZoneLimit属性 }
{      if DropCtlZone <> nil then
      begin
        if TopZone.ChildCount = 1 then
          DropCtlZone.ZoneLimit := TopXYLimit - OffsetXYLimitArr[InsertOrientation]
        else
        begin
          if InsertAt in [alRight, alBottom] then
            DropCtlZone.ZoneLimit := OffsetXYLimitArr[InsertOrientation]
          else DropCtlZone.ZoneLimit := DropCtlZone.LimitBegin +
            ControlXYLimitArr[InsertOrientation];
        end;
      end;}
    end;

    { 重新调整OffsetXYLimitArr数组的值 }
    OffsetXYLimitArr[doHorizontal] := LastZone.TopLeft[doHorizontal];
    OffsetXYLimitArr[doVertical] := LastZone.TopLeft[doVertical];

//    Stream.Position := 0;

    { 读数据 }
    while True do
    begin
      ReadZone(True);
      { 如果已经到达了Stream的结尾，就退出这次循环 }
      if Level = TreeStreamEndFlag then
        break;
    end;
  finally
    Stream.Free;
    EndUpdate;
  end;
  { 进行一些必要的设置 }
  SetNewBounds(nil);
end;

procedure TCnVIDDockTree.DrawDockGrabber(Control: TControl; const ARect: TRect);

var Option: TCnVIDConjoinServerOption;

  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;

var DrawRect: TRect;
  uFormat: UINT;
  ActiveControl: TControl;
const
  TextAlignment: array[TAlignment] of UINT = (DT_LEFT, DT_RIGHT, DT_CENTER);
begin
  with ARect do
    if GrabbersPosition = gpLeft then
    begin
    end
    else if GrabbersPosition = gpTop then
    begin
      if DockSite is TCnDockPanel then
        Option := TCnVIDConjoinServerOption(TCnDockPanel(DockSite).DockServer.DockStyle.ConjoinServerOption)
      else if DockSite is TCnConjoinPanel then
        Option := TCnVIDConjoinServerOption(TCnConjoinDockHostForm(TCnConjoinPanel(DockSite).ParentForm).DockClient.DockStyle.ConjoinServerOption)
      else
        Option := nil;
      { 首先调用PaintGradientBackground函数来画标题栏的背景色 }
      ActiveControl := GetActiveControl;
      DrawRect := ARect;
      Inc(DrawRect.Top, 2);
      DrawRect.Bottom := DrawRect.Top + GrabberSize - 3;
      if Option <> nil then
      begin
        if ActiveControl = Control then
          PaintGradientBackground(Canvas, DrawRect, Option.ActiveTitleStartColor, Option.ActiveTitleEndColor)
        else
          PaintGradientBackground(Canvas, DrawRect, Option.InactiveTitleStartColor, Option.InactiveTitleEndColor);
      end;
      PaintDockGrabberRect(Canvas, Control, DrawRect);
      { 设置Canvas的字体和画刷的属性 }
      if ActiveControl = Control then
        Canvas.Font.Assign(Option.ActiveFont)
      else Canvas.Font.Assign(Option.InactiveFont);
      Canvas.Brush.Style := bsClear;
      DrawRect := ARect;
      GetCaptionRect(DrawRect);
      uFormat := DT_SINGLELINE or (UINT(Option.TextEllipsis) * DT_END_ELLIPSIS) or TextAlignment[Option.TextAlignment];
      DrawText(Canvas.Handle, PChar(TForm(Control).Caption), -1, DrawRect, uFormat);
      DrawCloseButton(Canvas, FindControlZone(Control), Right-RightOffset-ButtonWidth, Top+TopOffset);
    end
    else if GrabbersPosition = gpBottom then
    begin

    end
    else if GrabbersPosition = gpRight then
    begin

    end;
end;

procedure TCnVIDDockTree.ResetBounds(Force: Boolean);
var
  R: TRect;
begin
  { 当用户改变DockSite的大小的时候，程序会自动调用这个函数 }
  if not (csLoading in DockSite.ComponentState) and
    (TopZone <> nil) and (DockSite.DockClientCount > 0) then
  begin
    R := DockSite.ClientRect;
    if DockSite is TCnConjoinPanel then
    begin
      { 最好不要使R.Right = R.Left，R.Bottom = R.Top }
      if (R.Right = R.Left) then
        Inc(R.Right, DockSite.Parent.UndockWidth);
      if R.Bottom = R.Top then
        Inc(R.Bottom, DockSite.Parent.UndockHeight);
    end;
    if Force or (not CompareMem(@R, @OldRect, SizeOf(TRect))) then
    begin
      case TopZone.Orientation of
        doHorizontal:
          begin
            if R.Right - R.Left > 0 then
              TopZone.ZoneLimit := R.Right - R.Left;
            if R.Bottom - R.Top > 0 then
              TopXYLimit := R.Bottom - R.Top;
          end;
        doVertical:
          begin
            if R.Bottom - R.Top > 0 then
              TopZone.ZoneLimit := R.Bottom - R.Top;
            if R.Right - R.Left > 0 then
              TopXYLimit := R.Right - R.Left;
          end;
      end;
      if DockSite.DockClientCount > 0 then
      begin
        { 首先确保这个函数不是在装载停靠信息的时候调用的 }
        if not IsLoading then
        begin
          { 然后计算水平方向的缩放比例 }
          if (R.Bottom - R.Top > 0 ) and (OldRect.Bottom - OldRect.Top > 0) then
            ScaleBy := (R.Bottom - R.Top) / (OldRect.Bottom - OldRect.Top)
          else ScaleBy := 1;

          ShiftScaleOrient := doHorizontal;
                             
          { 调用ForEachAt对整个树调整 }
          if (UpdateCount = 0) and (ScaleBy <> 1) then
            ForEachAt(nil, ScaleZone, tskForward);

          { 计算垂直方向的缩放比例 }
          if (R.Right - R.Left > 0) and (OldRect.Right - OldRect.Left > 0) then
            ScaleBy := (R.Right - R.Left) / (OldRect.Right - OldRect.Left)
          else ScaleBy := 1;

          ShiftScaleOrient := doVertical;

          { 调用ForEachAt对整个树调整 }
          if (UpdateCount = 0) and (ScaleBy <> 1) then
            ForEachAt(nil, ScaleZone, tskForward);
        end;

        SetNewBounds(nil);
        if UpdateCount = 0 then ForEachAt(nil, UpdateZone, tskForward);
        { OldRect在下一次调用的时候会用到 }
        OldRect := R;
      end;
    end;
  end;
end;

procedure TCnVIDDockTree.DrawSplitterRect(const ARect: TRect);
begin
  inherited;
  { 不做什么事情 }
end;

procedure TCnVIDDockTree.SetActiveControl(const Value: TControl);
begin
  if GetActiveControl <> Value then
  begin
    inherited SetActiveControl(Value);
    DockSite.Invalidate;
  end;
end;

procedure TCnVIDDockTree.WindowProc(var Message: TMessage);
var AAlign: TAlign;
begin
  if Message.Msg = CM_DOCKCLIENT then
  begin
    { 用来获得DockSize的大小 }
    AAlign := TCMDockClient(Message).DockSource.DropAlign;
    TCMDockClient(Message).DockSource.DockRect := gi_DockRect;
    GetDockEdge(gi_DockRect, TCMDockClient(Message).DockSource.DragPos, AAlign, TCMDockClient(Message).DockSource.Control);
  end;
  inherited WindowProc(Message);
end;

procedure TCnVIDDockTree.SplitterMouseUp;
var OldLimit: Integer;
  Zone: TCnDockZone;
begin
  Mouse.Capture := 0;
  DrawSizeSplitter;
  ReleaseDC(SizingWnd, SizingDC);

  OldLimit := SizingZone.ZoneLimit;
  { 只有是和SizingZone.ParentZone.Orientation相同的Orientation才能够在遍历中执行 }
  ShiftScaleOrient := SizingZone.ParentZone.Orientation;
  if SizingZone.ParentZone.Orientation = doHorizontal then
    SizingZone.ZoneLimit := SizePos.y + (SplitterWidth div 2)
  else
    SizingZone.ZoneLimit := SizePos.x + (SplitterWidth div 2);

  ParentLimit := SizingZone.LimitBegin;
  if OldLimit - ParentLimit > 0 then
    ScaleBy := (SizingZone.ZoneLimit - ParentLimit) / (OldLimit - ParentLimit)
  else ScaleBy := 1;
  { 调整当前的Zone的子女的ZoneLimit }
  if SizingZone.ChildZones <> nil then
    ForEachAt(SizingZone.ChildZones, ScaleChildZone, tskForward);

  Zone := SizingZone;
  while (Zone.NextSibling <> nil) and (not Zone.NextSibling.Visibled) do
  begin
    Zone.NextSibling.ZoneLimit := SizingZone.ZoneLimit;
    Zone := Zone.NextSibling;
  end;

  if SizingZone.NextSibling <> nil then
  begin
    { 如果当前的Zone有后一个兄弟，
    就用当前的Zone的后一个兄弟的ZoneLimit减去当前的Zone的ZoneLimit }
    if SizingZone.NextSibling.ZoneLimit - OldLimit > 0 then
      ScaleBy := (SizingZone.NextSibling.ZoneLimit - SizingZone.ZoneLimit) / (SizingZone.NextSibling.ZoneLimit - OldLimit)
    else ScaleBy := 1;
    ParentLimit := SizingZone.NextSibling.ZoneLimit;
    { 调整当前的Zone的下一个兄弟的子女的ZoneLimit }
    if SizingZone.NextSibling.ChildZones <> nil then
      ForEachAt(SizingZone.NextSibling.ChildZones, ScaleSiblingZone, tskForward);
  end;

  SetNewBounds(SizingZone.ParentZone);
  ForEachAt(SizingZone.ParentZone, UpdateZone, tskForward);
  SizingZone := nil;

end;

procedure TCnVIDDockTree.DrawDockSiteRect;
begin
//  inherited;

end;

procedure TCnVIDDockTree.InsertSibling(NewZone, SiblingZone: TCnDockZone;
  InsertLast, Update: Boolean);
begin
  if (FDropOnZone <> nil) then
    SiblingZone := FDropOnZone;
  inherited;
end;

procedure TCnVIDDockTree.PositionDockRect(Client, DropCtl: TControl;
  DropAlign: TAlign; var DockRect: TRect);

var
  VisibleClients,
  NewX, NewY, NewWidth, NewHeight: Integer;
  Zone: TCnDockZone;
  HTFlag: Integer;
  MousePos: TPoint;
  Scale: Double;
  CtrlRect: TRect;

  procedure DockOverSplitter;
  begin
    NewX := Zone.ParentZone.Left;
    NewY := Zone.ParentZone.Top;
    NewWidth := Zone.ParentZone.Width;
    NewHeight := Zone.ParentZone.Height;
    case Zone.ParentZone.Orientation of
      doHorizontal:
      begin
        Scale := (Zone.ZoneLimit - Zone.ParentZone.ChildZones.LimitBegin) / NewHeight;
        NewHeight := Min(NewHeight div 2, Client.ClientHeight);
        NewY := Zone.ZoneLimit - Round(NewHeight * Scale);
      end;
      doVertical:
      begin
        Scale := (Zone.ZoneLimit - Zone.ParentZone.ChildZones.LimitBegin) / NewWidth;
        NewWidth := Min(NewWidth div 2, Client.ClientWidth);
        NewX := Zone.ZoneLimit - Round(NewWidth * Scale);
      end;
    end;
    DockRect := Bounds(NewX, NewY, NewWidth, NewHeight);
    if Zone.Visibled then
    begin
      if Zone.ParentZone.Orientation = doHorizontal then
        CnGlobalDockPresident.DragObject.DropAlign := alBottom
      else if Zone.ParentZone.Orientation = doVertical then
        CnGlobalDockPresident.DragObject.DropAlign := alRight;
      CnGlobalDockPresident.DragObject.DropOnControl := Zone.ChildControl;
      FDropOnZone := Zone;
    end;
  end;

Label LBDropCtlExist;
begin
  if DropAlign = alNone then
    DropAlign := alClient;
  VisibleClients := DockSite.VisibleDockClientCount;
  FDropOnZone := nil;

  MousePos := CnGlobalDockPresident.DragObject.DragPos;
  MapWindowPoints(0, DockSite.Handle, MousePos, 2);
  Zone := InternalHitTest(MousePos, HTFlag);
  if Zone <> nil then
  begin
    if Zone.ChildControl <> nil then
    begin
      if (HTFlag = HTCaption) or (HTFlag = HTClose) then
      begin
        DockRect := Zone.ChildControl.BoundsRect;
        CnGlobalDockPresident.DragObject.DropAlign := alClient;
        if Zone.ChildControl is TCnTabDockHostForm then
        begin
          if CnGlobalDockPresident.DragObject is TCnVIDDragDockObject then
            TCnVIDDragDockObject(CnGlobalDockPresident.DragObject).FDropTabControl :=
            TCnVIDTabPageControl(TCnTabDockHostForm(Zone.ChildControl).PageControl);
        end else
        begin
          if CnGlobalDockPresident.DragObject is TCnVIDDragDockObject then
            TCnVIDDragDockObject(CnGlobalDockPresident.DragObject).FDropTabControl := nil;
        end;
      end;
    end;
  end;
  { 当DockSite小于两个停靠控件在她里面，DockRect就应该作为被设置成DockSite的客户区域 }
  if (DropCtl = nil)(* or (DropCtl.DockOrientation = doNoOrient) or
     {(DropCtl = Client) or }(VisibleClients < 2) *)then
  begin
    if Zone <> nil then
    begin
      if Zone.ChildControl <> nil then
      begin
        if (HTFlag = HTCaption) or (HTFlag = HTClose) then
        begin
          CnGlobalDockPresident.DragObject.DropOnControl := Zone.ChildControl;
        end else if HTFlag = HTClient then
        begin
          DropCtl := Zone.ChildControl;
          goto LBDropCtlExist;
        end else if HTFlag = HTSplitter then
          DockOverSplitter;
      end else if HTFlag = HTSplitter then
      begin
        DockOverSplitter;
      end else Exit;
    end else
    begin
      DockRect := Rect(0, 0, DockSite.ClientWidth, DockSite.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;
  end
  else begin
LBDropCtlExist:
    { 否者，如果DockSite包含超过一个客户的时候， 根据鼠标下面的控件设置DockRect的坐标}
    Zone := FindControlZone(DropCtl);
    CtrlRect := DockRect;
    MapWindowPoints(0, DockSite.Handle, CtrlRect, 2);
    if Zone <> nil then
    begin
      { 判断鼠标是否在分割条附近，如果是的话，就调用DockOverSplitter函数然后退出，
        否者就计算CtrlRect的大小，在这里CtrlRect是指的是DropOnControl的大小 }
      if Zone.ParentZone.Orientation = doVertical then
      begin
        if (DropAlign = alRight) and (Zone.NextSibling <> nil) then
        begin
          DockOverSplitter;
          MapWindowPoints(DockSite.Handle, 0, DockRect, 2);
          Exit;
        end else if (DropAlign = alLeft) and (Zone.PrevSibling <> nil) then
        begin
          Zone := Zone.PrevSibling;
          DockOverSplitter;
          MapWindowPoints(DockSite.Handle, 0, DockRect, 2);
          Exit;
        end else
        begin
          if DropAlign in [alLeft, alRight] then
            CtrlRect := Bounds(Zone.ParentZone.Left, Zone.ParentZone.Top, Zone.ParentZone.Width, Zone.ParentZone.Height)
          else if (DropAlign in [alTop, alBottom, alClient]) then// or ((DockSite is TCnConjoinPanel) and (DropAlign = alClient)) then
          begin
            CtrlRect := DropCtl.BoundsRect;
            Dec(CtrlRect.Top, GrabberSize);
          end;
          OffsetRect(CtrlRect, 0, GrabberSize);
        end;
      end else if Zone.ParentZone.Orientation = doHorizontal then
      begin
        if (DropAlign = alBottom) and (Zone.NextSibling <> nil) then
        begin
          DockOverSplitter;
          MapWindowPoints(DockSite.Handle, 0, DockRect, 2);
          Exit;
        end else if (DropAlign = alTop) and (Zone.PrevSibling <> nil) then
        begin
          Zone := Zone.PrevSibling;
          DockOverSplitter;
          MapWindowPoints(DockSite.Handle, 0, DockRect, 2);
          Exit;
        end else
        begin
          if DropAlign in [alTop, alBottom] then
            CtrlRect := Bounds(Zone.ParentZone.Left, Zone.ParentZone.Top, Zone.ParentZone.Width, Zone.ParentZone.Height)
          else if (DropAlign in [alLeft, alRight, alClient]) then //or ((DockSite is TCnConjoinPanel) and (DropAlign = alClient)) then
          begin
            CtrlRect := DropCtl.BoundsRect;
            Dec(CtrlRect.Top, GrabberSize);
          end;
          OffsetRect(CtrlRect, 0, GrabberSize);
        end;
      end else
      begin
        CtrlRect := DropCtl.BoundsRect;
        Dec(CtrlRect.Top, GrabberSize);
        OffsetRect(CtrlRect, 0, GrabberSize);
      end;
      { 然后根据CtrlRect和Control自身的位置来计算停靠的预览界面的大小 }
      NewX := CtrlRect.Left;
      NewY := CtrlRect.Top - GrabberSize;
      NewWidth := CtrlRect.Right - CtrlRect.Left;
      NewHeight := CtrlRect.Bottom - CtrlRect.Top;// + GrabberSize;
      if DropAlign in [alLeft, alRight] then
        NewWidth := Min(Client.UndockWidth, NewWidth div 2)
      else if DropAlign in [alTop, alBottom] then
        NewHeight := Min(Client.UndockHeight, NewHeight div 2);
      case DropAlign of
        alRight: Inc(NewX, CtrlRect.Right - CtrlRect.Left - NewWidth);
        alBottom: Inc(NewY, CtrlRect.Bottom - CtrlRect.Top - NewHeight);
      end;
      DockRect := Bounds(NewX, NewY, NewWidth, NewHeight);
      if DropAlign = alClient then
        DockRect := Bounds(NewX, NewY, NewWidth, NewHeight);
      if DropAlign = alNone then
      begin
      end;
    end;
  end;
  MapWindowPoints(DockSite.Handle, 0, DockRect, 2);
end;

function TCnVIDDockTree.GetDockEdge(DockRect: TRect; MousePos: TPoint;
  var DropAlign: TAlign; Control: TControl): TControl;
begin
  Result := inherited GetDockEdge(DockRect, MousePos, DropAlign, Control);
  if FLockDropDockSizeCount = 0 then
  begin
    // 只有在锁打开的时候才能调用这个程序
    if DropAlign in [alLeft, alRight] then
      DropDockSize := DockRect.Right - DockRect.Left
    else if DropAlign in [alTop, alBottom] then
      DropDockSize := DockRect.Bottom - DockRect.Top
    else DropDockSize := 0;
    Self.DockRect := DockRect;
  end;
end;

procedure TCnVIDDockTree.InsertNewParent(NewZone, SiblingZone: TCnDockZone;
  ParentOrientation: TDockOrientation; InsertLast, Update: Boolean);
begin
  if FDropOnZone <> nil then
  begin
    SiblingZone := FDropOnZone;
    InsertSibling(NewZone, SiblingZone, InsertLast, Update);
  end else
    inherited;
end;

procedure TCnVIDDockTree.RemoveZone(Zone: TCnDockZone; Hide: Boolean);
begin
  if (FDropOnZone <> nil) and
    ((FDropOnZone.NextSibling = Zone) or (FDropOnZone = Zone)) then
    FDropOnZone := nil;
  inherited;
end;

procedure TCnVIDDockTree.GetSiteInfo(Client: TControl;
  var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean);
var Zone: TCnDockZone;
  HTFlag: Integer;
  Pos: TPoint;
  Align: TAlign;
begin
  { 根据鼠标位置来判断是否可以进行停靠操作 }
  Pos := DockSite.ScreenToClient(MousePos);
  Zone := InternalHitTest(Pos, HTFlag);
  if Zone <> nil then
  begin
    if HTFlag = HTSPLITTER then
    begin
      InfluenceRect := GetSpiltterRect(Zone);
      MapWindowPoints(DockSite.Handle, 0, InfluenceRect, 2);
    end else
    begin
      Pos := MousePos;
      if Zone.ChildControl <> nil then
        Pos := Zone.ChildControl.ScreenToClient(MousePos);
      Align := ComputeVIDDockingRect(Zone.ChildControl, Client, InfluenceRect, Pos);
      if (Align = alNone) or (Client = Zone.ChildControl) then
      begin
        InfluenceRect := Rect(0, 0, 0, 0);
        CanDock := False;
      end else
      begin
        if Zone.ParentZone.Orientation = doVertical then
        begin
          if (Align = alRight) and (Zone.NextSibling <> nil) and (Zone.NextSibling.Visibled)  then
          begin
            InfluenceRect := GetSpiltterRect(Zone);
            InflateRect(InfluenceRect, DefExpandoRect, 0);
          end else if (Align = alLeft) and (Zone.PrevSibling <> nil) and (Zone.PrevSibling.Visibled) then
          begin
            InfluenceRect := GetSpiltterRect(Zone.PrevSibling);
            InflateRect(InfluenceRect, DefExpandoRect, 0);
          end else
            Exit;
        end else if Zone.ParentZone.Orientation = doHorizontal then
        begin
          if (Align = alBottom) and (Zone.NextSibling <> nil) and (Zone.NextSibling.Visibled) then
          begin
            InfluenceRect := GetSpiltterRect(Zone);
            InflateRect(InfluenceRect, 0, DefExpandoRect);
          end else if (Align = alTop) and (Zone.PrevSibling <> nil) and (Zone.PrevSibling.Visibled) then
          begin
            InfluenceRect := GetSpiltterRect(Zone.PrevSibling);
            InflateRect(InfluenceRect, 0, DefExpandoRect);
          end else
            Exit;
        end else
          Exit;
      end;
      MapWindowPoints(DockSite.Handle, 0, InfluenceRect, 2);
    end;
  end else
  begin
    InfluenceRect := Rect(0, 0, 0, 0);
    CanDock := False;
  end;
end;

procedure TCnVIDDockTree.LockDropDockSize;
begin
  Inc(FLockDropDockSizeCount);
end;

procedure TCnVIDDockTree.UnlockDropDockSize;
begin
  Dec(FLockDropDockSizeCount);
  if FLockDropDockSizeCount < 0 then
    FLockDropDockSizeCount := 0;
end;

procedure TCnVIDDockTree.PaintDockGrabberRect(Canvas: TCanvas;
  Control: TControl; const ARect: TRect);
begin
  { 没事做 }
end;

procedure TCnVIDDockTree.SetCaptionLeftOffset(const Value: Integer);
begin
  FCaptionLeftOffset := Value;
end;

procedure TCnVIDDockTree.SetCaptionRightOffset(const Value: Integer);
begin
  FCaptionRightOffset := Value;
end;

procedure TCnVIDDockTree.DrawCloseButton(Canvas: TCanvas; Zone: TCnDockZone; Left, Top: Integer);
var AZone: TCnAdvDockZone;
  ADockClient: TCnDockClient;
begin
  AZone := TCnAdvDockZone(Zone);
  if AZone <> nil then
  begin
    { 如果EnableCloseBtn属性为False,就不画关闭按钮 }
    ADockClient := FindDockClient(Zone.ChildControl);
    if (ADockClient <> nil) and (not ADockClient.EnableCloseBtn) then Exit;
    DrawFrameControl(Canvas.Handle, Rect(Left, Top, Left+ButtonWidth,
      Top+ButtonHeight), DFC_CAPTION, DFCS_CAPTIONCLOSE or Integer(AZone.CloseBtnDown) * DFCS_PUSHED)
  end;
end;

procedure TCnVIDDockTree.GetCaptionRect(var Rect: TRect);
begin
  Inc(Rect.Left, 2 + CaptionLeftOffset);
  Inc(Rect.Top, 3);
  Dec(Rect.Right, ButtonWidth + CaptionRightOffset - 1);
  Dec(Rect.Bottom, 2);
end;

procedure TCnVIDDockTree.AdjustDockRect(Control: TControl;
  var ARect: TRect);
begin
  if (DockSite.Align <> alClient) or (TopZone.VisibleChildTotal > 1) then
    inherited;
end;

procedure TCnVIDDockTree.IgnoreZoneInfor(Stream: TMemoryStream);
var CompName: string;
begin
  { TopZone中的Visibled(一字节)和VisibleSize(四字节)没有用，被忽略掉 }
  Stream.Position := Stream.Position + 6;
  { 读出控件的名称 }
  ReadControlName(Stream, CompName);
end;

{ TCnVIDConjoinPanel }

function TCnVIDConjoinPanel.CreateDockManager: IDockManager;
var Option: TCnVIDConjoinServerOption;
begin
  Result := inherited CreateDockManager;
  if (ParentForm <> nil) and (ParentForm.DockClient.DockStyle <> nil) and (Result <> nil) then
  begin
    Option := TCnVIDConjoinServerOption(ParentForm.DockClient.DockStyle.ConjoinServerOption);
    (Result as ICnDockManager).GrabberSize := Option.GrabbersSize;
  end;
end;

procedure TCnVIDConjoinPanel.CustomDockDrop(Source: TCnDragDockObject; X,
  Y: Integer);
//var
//  VIDSource: TCnVIDDragDockObject;
begin
{  if Source is TCnVIDDragDockObject then
  begin
    VIDSource := TCnVIDDragDockObject(Source);
    VIDSource.CurrState := dsDragEnter;
    VIDSource.OldState := dsDragEnter;
    SetTabControlPreview(VIDSource, VIDSource.FDropTabControl, dsDragLeave, VIDSource.DropAlign);
  end;}
  if not ((Source.Control.HostDockSite <> nil) and
    (Source.DropOnControl = Source.Control.HostDockSite.Parent) and
    (Source.DropAlign = alClient)) then
  begin
    inherited CustomDockDrop(Source, X, Y);
    ParentForm.Caption := '';
    if CnDockManager <> nil then
      CnDockManager.ActiveControl := Source.Control;
    if (Source.Control is TWinControl) and (Source.Control.Visible)
      and TWinControl(Source.Control).CanFocus then
      TWinControl(Source.Control).SetFocus;
  end;
end;


procedure TCnVIDConjoinPanel.CustomDockOver(Source: TCnDragDockObject; X,
  Y: Integer; State: TDragState; var Accept: Boolean);
var DropAlign: TAlign;
//  VIDSource: TCnVIDDragDockObject;
//  DropCtl: TControl;
begin
  inherited CustomDockOver(Source, X, Y, State, Accept);
  if Accept and (Source is TCnVIDDragDockObject) then
  begin
{    VIDSource := TCnVIDDragDockObject(Source);
    DropCtl := VIDSource.GetDropCtl;
    if (DropCtl <> VIDSource.DropOnControl) or
      (VIDSource.FOldDropTabControl <> VIDSource.FDropTabControl) then
      SetTabControlPreview(VIDSource, VIDSource.FOldDropTabControl, dsDragLeave, VIDSource.DropAlign);
    SetTabControlPreview(VIDSource, VIDSource.FDropTabControl, State, VIDSource.DropAlign);
    if State = dsDragLeave then
      VIDSource.FDropTabControl := nil;
    VIDSource.FOldDropTabControl := VIDSource.FDropTabControl;}
    if State = dsDragMove then
    begin
      DropAlign := Source.DropAlign;
      CnDockManager.GetDockEdge(Source.EraseDockRect, Source.DragPos, DropAlign, Source.Control);
    end;
//    VIDSource.OldState := VIDSource.CurrState;
//    VIDSource.CurrState := State;
  end;
end;

procedure TCnVIDConjoinPanel.CustomGetDockEdge(Source: TCnDragDockObject;
  MousePos: TPoint; var DropAlign: TAlign);
begin
end;

procedure TCnVIDConjoinPanel.CustomGetSiteInfo(Source: TCnDragDockObject; Client: TControl;
  var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean);
begin
  CnDockManager.GetSiteInfo(Client, InfluenceRect, MousePos, CanDock);
  CanDock := IsDockable(Self, Client, Source.DropOnControl, Source.DropAlign);
end;

function TCnVIDConjoinPanel.CustomUnDock(Source: TCnDragDockObject; NewTarget: TWinControl;
  Client: TControl): Boolean;
begin
  Result := inherited CustomUnDock(Source, NewTarget, Client);
end;

procedure TCnVIDConjoinPanel.DockDrop(Source: TDragDockObject; X,
  Y: Integer);
begin
  inherited DockDrop(Source, X, Y);
end;

procedure TCnVIDConjoinPanel.UpdateCaption(Exclude: TControl);
begin
  if VisibleDockClientCount > 1 then
    ParentForm.Caption := ''
  else
    inherited UpdateCaption(Exclude);
end;

{ TCnVIDTabPageControl }

procedure TCnVIDTabPageControl.CustomDockDrop(Source: TCnDragDockObject; X,
  Y: Integer);
var ARect: TRect;
  i: Integer;
  VIDSource: TCnVIDDragDockObject;
  DockClient: TCnDockClient;
  Host: TCnConjoinDockHostForm;
  Index: Integer;
begin
  if Source.DropAlign in [alClient, alNone] then
  begin
    if Source is TCnVIDDragDockObject then
    begin
      Cn_LockWindow(nil);
      IsLoading := True;
      try
        DoFloatForm(Source.Control);
        FreeAllDockableForm;
        VIDSource := TCnVIDDragDockObject(Source);
        //SetTabControlPreview(VIDSource, VIDSource.FDropTabControl, dsDragLeave, VIDSource.DropAlign);
        for i := 0 to VIDSource.SourceDockClientCount - 1 do
        begin
          Source.Control := VIDSource.SourceDockClients[i];
          inherited CustomDockDrop(Source, X, Y);
          if Source.Control is TCustomForm then
          begin
            if FTabImageList <> nil then
            begin
              Index := FTabImageList.AddIcon(TForm(Source.Control).Icon);
              if Index <> -1 then
                ActivePage.ImageIndex := Index;
            end;
          end;
        end;
      finally
        IsLoading := False;
        Cn_UnLockWindow;
        ReshowAllVisibleWindow;
        CnGlobalDockPresident.DragObject.Control := nil;
      end;
    end;
  end else
  begin
    // 创建平铺的服务窗体
    DockClient := FindDockClient(ParentForm);
    if DockClient <> nil then
    begin
      ARect := ParentForm.BoundsRect;
      Host := DockClient.CreateConjoinHostAndDockControl(ParentForm, Source.Control, Source.DropAlign);
      Host.BoundsRect := ARect;
      SetDockSite(ParentForm, False);
      SetDockSite(TWinControl(Source.Control), False);
      Host.Visible := True;
    end;
  end;
  FPanel.SelectSheet := nil;
  ParentForm.Caption := ActivePage.Caption;
end;

procedure TCnVIDTabPageControl.CustomDockOver(Source: TCnDragDockObject; X,
  Y: Integer; State: TDragState; var Accept: Boolean);
var ARect: TRect;
//  VIDSource: TCnVIDDragDockObject;
begin
  {如果停靠客户上面有TDockClient控件，就同意停靠}
  Accept := IsDockable(Self, Source.Control, Source.DropOnControl, Source.DropAlign);
  if Accept then
  begin
    if ParentForm.HostDockSite = nil then
    begin
      Source.DropAlign := ComputeVIDDockingRect(Self, Source.Control, ARect, Point(X, Y));
      if Source.DropAlign = alClient then
        ARect.Top := ARect.Top + Cn_GetSysCaptionHeight;
{      if Source is TCnVIDDragDockObject then
      begin
        VIDSource := TCnVIDDragDockObject(Source);
        SetTabControlPreview(VIDSource, Self, State, VIDSource.DropAlign);
        VIDSource.FDropTabControl := Self;
      end;}
      if Accept and (Source.DropAlign <> alNone) then
      begin
        Source.DockRect := ARect;
        gi_DockRect := ARect;
      end;
    end else
    begin
      if ParentForm.HostDockSite is TCnCustomDockPanel then
      begin
        ARect := Source.DockRect;
        TCnCustomDockPanel(ParentForm.HostDockSite).CnDockManager.PositionDockRect(Source.Control, Source.DropOnControl, Source.DropAlign, ARect);
        Source.DockRect := ARect;
      end;
    end;
  end;
end;

procedure TCnVIDTabPageControl.CustomGetSiteInfo(Source: TCnDragDockObject; Client: TControl;
  var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean);
const
  DefExpandoRect = 20;
var
  CH_BW: Integer;
  ARect: TRect;
begin
  CanDock := IsDockable(Self, Client, Source.DropOnControl, Source.DropAlign);
  if ParentForm.HostDockSite <> nil then
    CanDock := False;
  if CanDock then
  begin
    {获得停靠控件的矩形区域}
    GetWindowRect(Parent.Handle, InfluenceRect);
    if PtInRect(InfluenceRect, MousePos) then
    begin
      ARect := InfluenceRect;
      InflateRect(ARect, -DefExpandoRect, -DefExpandoRect);
      {获得标题栏的高度和边框的宽度}
      CH_BW := Cn_GetSysCaptionHeightAndBorderWidth;
      Inc(ARect.Top, CH_BW + 1);
      Dec(ARect.Bottom, TabHeight);
      if PtInRect(ARect, MousePos) then
        InfluenceRect := Rect(0, 0, 0, 0);
    end;
  end;
end;

procedure TCnVIDTabPageControl.Change;
begin
  // 当PageControl的当前Tab改变的时候，就要改变标题栏的Caption。
  inherited Change;
  ParentForm.Caption := ActivePage.Caption;
  if ParentForm.HostDockSite is TCnCustomDockPanel then
  begin
    // 刷新ParentForm.HostDockSite中的把手部分。
    if ParentForm.Visible and ParentForm.CanFocus then
      ParentForm.SetFocus;
    ParentForm.HostDockSite.Invalidate;
  end;
  if (ActivePage <> nil) and (ActivePage.Visible) and (ActivePage.CanFocus) then
  begin
    if ParentForm.Visible and ParentForm.CanFocus then
      ActivePage.SetFocus;
  end;
end;

procedure TCnVIDTabPageControl.AdjustClientRect(var Rect: TRect);
begin
  // 调整Tab的Client的大小。
  Rect := ClientRect;
  if (Parent is TCnTabDockHostForm) and (VisibleDockClientCount = 1) then Exit;
  case TabPosition of
    tpTop:    Inc(Rect.Top, Panel.FTabHeight - 1);
    tpBottom: Dec(Rect.Bottom, Panel.FTabHeight - 1);
    tpLeft:   Inc(Rect.Left, Panel.FTabHeight - 1);
    tpRight:  Dec(Rect.Right, Panel.FTabHeight - 1);
  end;
end;

procedure TCnVIDTabPageControl.CreateParams(var Params: TCreateParams);
begin
  inherited;
end;

procedure TCnVIDTabPageControl.DrawTab(TabIndex: Integer;
  const Rect: TRect; Active: Boolean);
begin
  inherited DrawTab(TabIndex, Rect, Active);
end;

function TCnVIDTabPageControl.GetActiveFont: TFont;
begin
  Result := FPanel.FActiveFont;
end;

function TCnVIDTabPageControl.GetActiveSheetColor: TColor;
begin
  Result := FPanel.FActiveSheetColor;
end;

function TCnVIDTabPageControl.GetInactiveFont: TFont;
begin
  Result := FPanel.FInactiveFont;
end;

function TCnVIDTabPageControl.GetInactiveSheetColor: TColor;
begin
  Result := FPanel.Color;
end;

function TCnVIDTabPageControl.GetTabBottomOffset: Integer;
begin
  Result := FPanel.TabBottomOffset;
end;

function TCnVIDTabPageControl.GetTabLeftOffset: Integer;
begin
  Result := FPanel.TabLeftOffset;
end;

function TCnVIDTabPageControl.GetTabRightOffset: Integer;
begin
  Result := FPanel.TabRightOffset;
end;

function TCnVIDTabPageControl.GetTabTopOffset: Integer;
begin
  Result := FPanel.TabTopOffset;
end;

procedure TCnVIDTabPageControl.Paint;
begin
  inherited Paint;
end;

procedure TCnVIDTabPageControl.Resize;
begin
  // 当PageControl的大小改变的时候，也需要改变Fpanel的位置。
  inherited Resize;
  if Fpanel = nil then Exit;
  case TabPosition of
    tpLeft:
    begin
      // Fpanel在左边
      FPanel.Left := 0;
      FPanel.Width := Panel.FTabHeight;
      FPanel.Top := 0;
      FPanel.Height := Height;
    end;
    tpRight:
    begin
      // Fpanel在右边
      FPanel.Left := Width - Panel.FTabHeight;
      FPanel.Top := 0;
      FPanel.Width := Panel.FTabHeight;
      FPanel.Height := Height;
    end;
    tpTop:
    begin
      // Fpanel在上边
      FPanel.Left := 0;
      FPanel.Top := 0;
      FPanel.Width := Width;
      FPanel.Height := Panel.FTabHeight;
    end;
    tpBottom:
    begin
      // Fpanel在下边
      FPanel.Left := 0;
      FPanel.Top := Height - Panel.FTabHeight;
      FPanel.Width := Width;
      FPanel.Height := Panel.FTabHeight;
    end;
  end;
end;

procedure TCnVIDTabPageControl.SetActiveFont(const Value: TFont);
begin
  FPanel.FActiveFont.Assign(Value);
  if ActivePage <> nil then
    TCnVIDDockTabSheet(ActivePage).SetSheetSort(ActivePage.Caption);
  FPanel.Invalidate;
end;

procedure TCnVIDTabPageControl.SetActiveSheetColor(const Value: TColor);
begin
  FPanel.FActiveSheetColor := Value;
  FPanel.Invalidate;
end;

procedure TCnVIDTabPageControl.SetInactiveFont(const Value: TFont);
var i: Integer;
begin
  FPanel.FInactiveFont.Assign(Value);
  for i := 0 to PageCount - 1 do
    if Pages[i] <> ActivePage then
      TCnVIDDockTabSheet(Pages[i]).SetSheetSort(Pages[i].Caption);
  FPanel.Invalidate;
end;

procedure TCnVIDTabPageControl.SetInactiveSheetColor(const Value: TColor);
begin
  if FPanel.Color <> Value then
  begin
    FPanel.Color := Value;
    FPanel.Invalidate;
  end;
end;

procedure TCnVIDTabPageControl.SetTabBottomOffset(const Value: Integer);
begin
  if FPanel.TabBottomOffset <> Value then
  begin
    FPanel.TabBottomOffset := Value;
    FPanel.Invalidate;
  end;
end;

procedure TCnVIDTabPageControl.SetTabHeight(Value: Smallint);
begin
  inherited SetTabHeight(Value);
  if Panel.FTabHeight <> Value then
  begin
    Panel.FTabHeight := Value;
    FPanel.Invalidate;
  end;
end;

procedure TCnVIDTabPageControl.SetTabLeftOffset(const Value: Integer);
begin
  if FPanel.TabLeftOffset <> Value then
  begin
    FPanel.TabLeftOffset := Value;
    FPanel.Invalidate;
  end;
end;

procedure TCnVIDTabPageControl.SetTabPosition(Value: TTabPosition);
begin
  Assert(Value in [tpTop, tpBottom], gs_CannotSetTabPosition);
  inherited SetTabPosition(Value);
  Resize;
end;

procedure TCnVIDTabPageControl.SetTabRightOffset(const Value: Integer);
begin
  if FPanel.TabRightOffset <> Value then
  begin
    FPanel.TabRightOffset := Value;
    FPanel.Invalidate;
  end;
end;

procedure TCnVIDTabPageControl.SetTabTopOffset(const Value: Integer);
begin
  if FPanel.TabTopOffset <> Value then
  begin
    FPanel.TabTopOffset := Value;
    FPanel.Invalidate;
  end;
end;

constructor TCnVIDTabPageControl.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FPanel := nil;
  TabWidth := 1;
  MultiLine := True;
  CnDockTabSheetClass := TCnVIDDockTabSheet;
  CnTabPanelClass := TCnTabPanel;
  FTempSheet := nil;
  TabPosition := tpBottom;
  FTabImageList := nil;
  Images := nil;
  if AOwner is TCnTabDockHostForm then
  begin
    FTabImageList := TCustomImageList.Create(AOwner);
    Images := FTabImageList;
  end;
end;

destructor TCnVIDTabPageControl.Destroy;
begin
  if FTabImageList <> nil then
  begin
    FTabImageList.Free;
    FTabImageList := nil;
  end;
  if FPanel <> nil then
  begin
    FPanel.Free;
    FPanel := nil;
  end;
  inherited;
end;

procedure TCnVIDTabPageControl.Loaded;
begin
  inherited;
  CreatePanel;
end;

procedure TCnVIDTabPageControl.CreatePanel;
begin
  if FPanel = nil then
  begin
    FPanel := CnTabPanelClass.Create(Self);
    FPanel.Page := Self;
    FPanel.Parent := Self;
    FPanel.TabLeftOffset := 5;
    FPanel.TabRightOffset := 5;
    FPanel.TabTopOffset := 3;
    FPanel.TabBottomOffset := 3;
    ActiveSheetColor := clBtnFace;
    InactiveSheetColor := clBtnShadow;
  end;
  Resize;
end;

procedure TCnVIDTabPageControl.CreateWnd;
begin
  inherited;
end;

procedure TCnVIDTabPageControl.SetActivePage(Page: TCnDockTabSheet);
begin
  inherited SetActivePage(Page);
  FPanel.Invalidate;
end;

procedure TCnVIDTabPageControl.DockDrop(Source: TDragDockObject; X,
  Y: Integer);
var Index: Integer;
begin
  inherited DockDrop(Source, X, Y);
  FPanel.SelectSheet := nil;
  ParentForm.Caption := ActivePage.Caption;
  if Source.Control is TCustomForm then
  begin
    if Source.Control.Visible and (Source.Control.Parent is TCnDockTabSheet) then
      ActivePage := TCnDockTabSheet(Source.Control.Parent);
    if FTabImageList <> nil then
    begin
      Index := FTabImageList.AddIcon(TForm(Source.Control).Icon);
      if (Index <> -1) and (ActivePage <> nil) then
        ActivePage.ImageIndex := Index;
    end;
  end;
end;

function TCnVIDTabPageControl.GetDockClientFromMousePos(
  MousePos: TPoint): TControl;
var PageIndex: Integer;
begin
  Result := nil;
  case TabPosition of
  tpTop:
    PageIndex := Panel.FindSheetWithPos(MousePos.X, MousePos.y, 0, Panel.Height - TabBottomOffset);
  tpBottom:
    PageIndex := Panel.FindSheetWithPos(MousePos.x, MousePos.y, TabBottomOffset, Panel.Height);
  tpLeft:
    PageIndex := Panel.FindSheetWithPos(MousePos.y, MousePos.x, 0, Panel.Height - TabBottomOffset);
  tpRight:
    PageIndex := Panel.FindSheetWithPos(MousePos.y, MousePos.x, TabBottomOffset, Panel.Height);
  else
    PageIndex := -1;
  end;
  if PageIndex >= 0 then
  begin
    Result := Pages[PageIndex].Controls[0];
    if Result.HostDockSite <> Self then Result := nil;
  end;
end;

procedure TCnVIDTabPageControl.CustomGetDockEdge(Source: TCnDragDockObject;
  MousePos: TPoint; var DropAlign: TAlign);
var ARect: TRect;
begin
  DropAlign := ComputeVIDDockingRect(Self, Source.Control, ARect, MousePos);
end;

function TCnVIDTabPageControl.GetVisibleTheetCount: Integer;
var i: Integer;
begin
  Result := 0;
  for i := 0 to PageCount - 1 do
    if Pages[i].TabVisible then
      Inc(Result);
end;

procedure TCnVIDTabPageControl.UpdateCaption(Exclude: TControl);
begin
  ParentForm.Caption := ActivePage.Caption;
end;

procedure TCnVIDTabPageControl.SetHotTrack(Value: Boolean);
begin
  inherited SetHotTrack(Value);

end;

procedure TCnVIDTabPageControl.SetImages(Value: TCustomImageList);
begin
  inherited SetImages(Value);
  if Panel <> nil then
  begin
    Panel.ShowTabImages := Value <> nil;
    Panel.Invalidate;
  end;
end;

function TCnVIDTabPageControl.GetHotTrackColor: TColor;
begin
  Result := Panel.FHotTrackColor;
end;

procedure TCnVIDTabPageControl.SetHotTrackColor(const Value: TColor);
begin
  if Panel.FHotTrackColor <> Value then
  begin
    Panel.FHotTrackColor := Value;
    Panel.Invalidate;
  end;
end;

function TCnVIDTabPageControl.GetShowTabImages: Boolean;
begin
  Result := FPanel.FShowTabImages;
end;

procedure TCnVIDTabPageControl.SetShowTabImages(const Value: Boolean);
begin
  FPanel.ShowTabImages := Value;
end;

function TCnVIDTabPageControl.CustomUnDock(Source: TCnDragDockObject;
  NewTarget: TWinControl; Client: TControl): Boolean;
var CurrPage: TCnDockTabSheet;
  i: Integer;
begin
  if not ((Source.Control.HostDockSite <> nil) and
    (Source.DropOnControl = Source.Control.HostDockSite.Parent) and
    (Source.DropAlign = alClient)) then
  begin
    CurrPage := GetPageFromDockClient(Client);
    if (CurrPage <> nil) then
    begin
      if (FTabImageList <> nil) and ShowTabImages and
        (FTabImageList.Count > CurrPage.ImageIndex) then
      begin
        FTabImageList.Delete(CurrPage.ImageIndex);
        for i := 0 to PageCount - 1 do
          if Pages[i].ImageIndex > CurrPage.ImageIndex then
            Pages[i].ImageIndex := Pages[i].ImageIndex - 1;
      end;
    end;
    Result := inherited CustomUnDock(Source, NewTarget, Client);
  end else Result := True;
end;

procedure TCnVIDTabPageControl.AfterConstruction;
begin
  inherited;
  CreatePanel;
end;

function TCnVIDTabPageControl.GetPage(Index: Integer): TCnVIDDockTabSheet;
begin
  Result := TCnVIDDockTabSheet(inherited Pages[Index]);
end;

function TCnVIDTabPageControl.GetActiveVIDPage: TCnVIDDockTabSheet;
begin
  Result := TCnVIDDockTabSheet(inherited ActivePage);
end;

procedure TCnVIDTabPageControl.SetActiveVIDPage(
  const Value: TCnVIDDockTabSheet);
begin
  ActivePage := Value;
end;

{ TCnTabPanel }

constructor TCnTabPanel.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  Page := nil;
  FCaptionTopOffset := 0;
  FCaptionLeftOffset := 5;
  FCaptionRightOffset := 5;
  FTabBottomOffset := 3;
  FTabSplitterWidth := 3;
  FTabHeight := 22;
  FSortList := TList.Create;
  FActiveFont := TFont.Create;
  FActiveFont.Color := clBlack;
  FInactiveFont := TFont.Create;
  FInactiveFont.Color := clWhite;
  FHotTrackColor := clBlue;
  FTempPages := TList.Create;
  FSelectHotIndex := -1;
  FShowTabImages := False;
  FSelectSheet := nil;
end;

procedure TCnTabPanel.DeleteSorts(Sheet: TCnVIDDockTabSheet);
var SheetIndex: Integer;
begin
  SheetIndex := FSortList.IndexOf(Sheet);
  if SheetIndex >= 0 then
    FSortList.Delete(SheetIndex);
  if Sheet <> nil then
    Sheet.TabVisible := False;
  SetShowTabWidth;
  Page.Invalidate;
end;

destructor TCnTabPanel.Destroy;
begin
  FActiveFont.Free;
  FInactiveFont.Free;
  FSortList.Free;
  FTempPages.Free;
  inherited;
end;

function TCnTabPanel.FindSheetWithPos(cX, cY, cTopOffset, cBottomOffset: Integer): Integer;
var i: Integer;
  CompleteWidth, CurrTabWidth: Integer;
  Pages: TList;
begin
  Result := -1;
  if (cY > cBottomOffset) or (cY < cTopOffset) then Exit;
  CompleteWidth := 0;
  if FSelectSheet = nil then
    Pages := Page.PageSheets
  else Pages := FTempPages;
  for i := 0 to Pages.Count - 1 do
  begin
    if not TCnVIDDockTabSheet(Pages[i]).TabVisible then Continue;
    CurrTabWidth := TCnVIDDockTabSheet(Pages[i]).ShowTabWidth;
    if (cX >= FTabLeftOffset + CompleteWidth) and (cX <= FTabLeftOffset + CurrTabWidth + CompleteWidth + FTabSplitterWidth) then
    begin
      Result := i;
      Exit;
    end;
    Inc(CompleteWidth, CurrTabWidth + FTabSplitterWidth);
  end;
end;

function TCnTabPanel.GetPageIndexFromMousePos(X, Y: Integer): Integer;
begin
  Result := -1;
  case Page.TabPosition of
  tpTop:
    Result := FindSheetWithPos(X, y, 0, Height - TabBottomOffset);
  tpBottom:
    Result := FindSheetWithPos(x, y, TabBottomOffset, Height);
  tpLeft:
    Result := FindSheetWithPos(y, x, 0, Height - TabBottomOffset);
  tpRight:
    Result := FindSheetWithPos(y, x, TabBottomOffset, Height);
  end;
end;

function TCnTabPanel.GetMaxTabWidth: TCnDockTabSheet;
var i: Integer;
  MaxWidth, CurrWidth: Integer;
begin
  Result := nil;
  MaxWidth := 0;
  if Page = nil then Exit;
  for i := 0 to Page.PageCount - 1 do
  begin
    CurrWidth := Canvas.TextWidth(Page.Tabs[i]);
    if MaxWidth < CurrWidth then
    begin
      Result := Page.Pages[i];
      MaxWidth := CurrWidth;
    end;
  end;
end;

function TCnTabPanel.GetMinTabWidth: TCnDockTabSheet;
var i: Integer;
  MinWidth, CurrWidth: Integer;
begin
  Result := nil;
  MinWidth := 0;
  for i := 0 to Page.PageCount - 1 do
  begin
    CurrWidth := Canvas.TextWidth(Page.Tabs[i]);
    if MinWidth > CurrWidth then
    begin
      Result := Page.Pages[i];
      MinWidth := CurrWidth;
    end;
  end;
end;

function TCnTabPanel.GetPanelHeight: Integer;
begin
  Result := 0;
  case Page.TabPosition of
    tpLeft, tpRight:
      Result := Width;
    tpTop, tpBottom:
      Result := Height;
  end;
end;

function TCnTabPanel.GetPanelWidth: Integer;
begin
  Result := 0;
  case Page.TabPosition of
    tpLeft, tpRight:
      Result := Height;
    tpTop, tpBottom:
      Result := Width;
  end;
end;

function TCnTabPanel.GetSorts(Index: Integer): TCnVIDDockTabSheet;
begin
  Result := FSortList[Index];
end;

function TCnTabPanel.GetTotalTabWidth: Integer;
var i: Integer;
begin
  Result := 0;
  if FSortList = nil then Exit;
  for i := 0 to FSortList.Count - 1 do
    Inc(Result, Sorts[i].TabWidth + Integer(i <> FSortList.Count - 1) * FTabSplitterWidth);
end;

procedure TCnTabPanel.MouseDown(Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
var Ctrl: TControl;
  Index: Integer;
  Msg: TWMMouse;
  Sheet: TCnVIDDockTabSheet;
begin
  inherited MouseDown(Button, Shift, X, Y);
  if Page = nil then Exit;
  { 首先根据鼠标的坐标确定是哪个Sheet }
  Index := GetPageIndexFromMousePos(X, Y);
  if (Index >= 0) then
  begin
    if Index <> Page.ActivePageIndex then
    begin
      Sheet := Page.ActiveVIDPage;
      Page.ActivePageIndex := Index;
      Sheet.SetSheetSort(Sheet.Caption);
      Page.ActiveVIDPage.SetSheetSort(Page.ActiveVIDPage.Caption);
      Page.Change;
      Invalidate;
    end;

    if Button = mbLeft then
    begin
      // 只有在鼠标左键操作的时候才可以移动Tab。
      FSelectSheet := TCnVIDDockTabSheet(Page.ActivePage);
      { Delphi6.0以上版本 }
      {$IFDEF COMPILER6_UP}
      FTempPages.Assign(Page.PageSheets);
      {$ELSE}
      { Delphi5.0以下版本 }
      AssignList(Page.PageSheets, FTempPages);
      {$ENDIF}

    end;

    Ctrl := GetDockClientFromPageIndex(Index);
    if Ctrl <> nil then
    begin
      { 查找到DockCtl上的TCnDockClient，然后把她赋值给全局变量GlobalDockClient }
      GlobalDockClient := FindDockClient(Ctrl);

      if GlobalDockClient <> nil then
      begin
        Msg.Msg := WM_NCLBUTTONDOWN + Integer(Button) * 3 + Integer(ssDouble in Shift) * 2;
        Msg.Pos.x := X;
        Msg.Pos.y := Y;
        if not (ssDouble in Shift) then
          // 单击鼠标
          GlobalDockClient.DoNCButtonDown(Page.DoMouseEvent(Msg, Page), Button, msTabPage)
        else
        begin
          // 双击鼠标
          GlobalDockClient.DoNCButtonDblClk(Page.DoMouseEvent(Msg, Page), Button, msTabPage);
          if (Button = mbLeft) and GlobalDockClient.CanFloat then
            Ctrl.ManualDock(nil, nil, alNone);
        end;
      end;
    end;
  end;
end;

procedure TCnTabPanel.MouseMove(Shift: TShiftState; X, Y: Integer);
var Index: Integer;
  Ctrl: TControl;
  ARect: TRect;
begin
  inherited MouseMove(Shift, X, Y);

  Index := GetPageIndexFromMousePos(X, Y);

  if Page.HotTrack and (Index <> FSelectHotIndex) then
  begin
    FSelectHotIndex := Index;
    Invalidate;
  end;

  if Assigned(FSelectSheet) then
  begin
    Index := GetPageIndexFromMousePos(X, Y);
    if Index >= 0 then
    begin
      if (Index <> Page.ActivePageIndex) and (Page.PageCount > Index) then
      begin
        FSelectSheet.PageIndex := Index;
        Invalidate;
      end;
    end else
    begin
      case Page.TabPosition of
      tpTop:
        ARect := Rect(0, 0, Width, Height - FTabBottomOffset);
      tpBottom:
        ARect := Rect(0, FTabBottomOffset, Width, Height);
      tpLeft:
        ARect := Rect(0, 0, Width - FTabBottomOffset, Height);
      tpRight:
        ARect := Rect(FTabBottomOffset, 0, Width, Height);
      else
        ARect := Rect(0, 0, 0, 0);
      end;
      if PtInRect(ARect, Point(X, Y)) then Exit;
      if Page.FTempSheet = nil then
      begin
        Ctrl := GetDockClientFromPageIndex(FSelectSheet.PageIndex);
        if Ctrl <> nil then
          { 如果条件都满足，就调用CnGlobalDockPresident的BeginDrag方法，开始停靠操作 }
          CnGlobalDockPresident.BeginDrag(Ctrl, False, 1);
      end;
    end;
  end;
end;

procedure TCnTabPanel.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
var Ctrl: TControl;
  Index: Integer;
  Msg: TWMMouse;
begin
  inherited;
  FSelectSheet := nil;
  if Page = nil then Exit;
  { 首先根据鼠标的坐标确定是哪个Sheet }
  Index := GetPageIndexFromMousePos(X, Y);
  Ctrl := GetDockClientFromPageIndex(Index);
  if Ctrl <> nil then
  begin
    { 查找到DockCtl上的TCnDockClient，然后把她赋值给全局变量GlobalDockClient }
    GlobalDockClient := FindDockClient(Ctrl);

    if (GlobalDockClient <> nil) then
    begin
      Msg.Msg := WM_NCLBUTTONUP + Integer(Button) * 3 + Integer(ssDouble in Shift) * 2;
      Msg.Pos := PointToSmallPoint(Page.ScreenToClient(ClientToScreen(Point(X, Y))));
      if not (ssDouble in Shift) then
        // 单击鼠标
        GlobalDockClient.DoNCButtonUp(Page.DoMouseEvent(Msg, Page), Button, msTabPage);
    end;
  end;
end;

procedure TCnTabPanel.Paint;
var ARect: TRect;
  CurrTabWidth: Integer;
  i, CompleteWidth: Integer;
  ImageWidth: Integer;
  CaptionString: string;
//  LogFont : TLogFont;
begin
  inherited Paint;
  if Page = nil then Exit;

  if (Page.Images <> nil) and (Page.ShowTabImages) then
    ImageWidth := Page.Images.Width
  else ImageWidth := 0;

  { 首先填充整个Panel的颜色 }
  Canvas.Brush.Color := Page.ActiveSheetColor;
  case Page.TabPosition of
    tpLeft:   Canvas.FillRect(Rect(PanelHeight - FTabBottomOffset, 0, PanelHeight, PanelWidth));
    tpRight:  Canvas.FillRect(Rect(0, 0, FTabBottomOffset, PanelWidth));
    tpTop:    Canvas.FillRect(Rect(0, PanelHeight - FTabBottomOffset, PanelWidth, PanelHeight));
    tpBottom: Canvas.FillRect(Rect(0, 0, PanelWidth, FTabBottomOffset));
  end;

  { 再画一条黑色的阴影线 }
  case Page.TabPosition of
    tpTop, tpLeft: Canvas.Pen.Color := clWhite;
    tpBottom, tpRight: Canvas.Pen.Color := clBlack;
  end;

  case Page.TabPosition of
    tpLeft:
    begin
      Canvas.MoveTo(PanelHeight - FTabBottomOffset, 0);
      Canvas.LineTo(PanelHeight - FTabBottomOffset, PanelWidth);
    end;
    tpRight:
    begin
      Canvas.MoveTo(FTabBottomOffset, 0);
      Canvas.LineTo(FTabBottomOffset, PanelWidth);
    end;
    tpTop:
    begin
      Canvas.MoveTo(0, PanelHeight - FTabBottomOffset);
      Canvas.LineTo(PanelWidth, PanelHeight - FTabBottomOffset);
    end;
    tpBottom:
    begin
      Canvas.MoveTo(0, FTabBottomOffset);
      Canvas.LineTo(PanelWidth, FTabBottomOffset);
    end;
  end;

  CompleteWidth := 0;

  Canvas.Brush.Style := bsClear;

  for i := 0 to Page.PageCount - 1 do
  begin
    if not Page.Pages[i].TabVisible then Continue;

    { 获得当前页面的宽度, 宽度 = 字符串的宽度加上左边距和右边距 }
    CurrTabWidth := TCnVIDDockTabSheet(Page.Pages[i]).ShowTabWidth;// + ImageWidth;

    if Page.ActivePageIndex = i then
    begin
      { 画一个被选中的页面 }
      Canvas.Brush.Color := Page.ActiveSheetColor;
      case Page.TabPosition of
        tpLeft:   Canvas.FillRect(Rect(FTabTopOffset, CompleteWidth + FTabLeftOffset,
                    PanelHeight, CompleteWidth + FTabLeftOffset + CurrTabWidth));
        tpRight:  Canvas.FillRect(Rect(FTabBottomOffset, CompleteWidth + FTabLeftOffset,
                    PanelHeight - FTabTopOffset, CompleteWidth + FTabLeftOffset + CurrTabWidth));
        tpTop:    Canvas.FillRect(Rect(CompleteWidth + FTabLeftOffset, FTabTopOffset,
                    CompleteWidth + FTabLeftOffset + CurrTabWidth, PanelHeight));
        tpBottom: Canvas.FillRect(Rect(CompleteWidth + FTabLeftOffset, FTabBottomOffset,
                    CompleteWidth + FTabLeftOffset + CurrTabWidth, PanelHeight - FTabTopOffset));
      end;

      { 画两条被照亮的白线 }
      Canvas.Pen.Color := clWhite;
      case Page.TabPosition of
        tpLeft:
        begin
          Canvas.MoveTo(PanelHeight - FTabBottomOffset, CompleteWidth + FTabLeftOffset);
          Canvas.LineTo(FTabTopOffset, CompleteWidth + FTabLeftOffset);
          Canvas.LineTo(FTabTopOffset, CompleteWidth + FTabLeftOffset + CurrTabWidth);
          Canvas.Pen.Color := clBlack;
          Canvas.LineTo(PanelHeight - FTabBottomOffset, CompleteWidth + FTabLeftOffset + CurrTabWidth);
        end;
        tpRight:
        begin
          Canvas.MoveTo(FTabTopOffset, CompleteWidth + FTabLeftOffset);
          Canvas.LineTo(PanelHeight - FTabBottomOffset, CompleteWidth + FTabLeftOffset);
          Canvas.Pen.Color := clBlack;
          Canvas.LineTo(PanelHeight - FTabBottomOffset, CompleteWidth + FTabLeftOffset + CurrTabWidth);
          Canvas.LineTo(FTabTopOffset, CompleteWidth + FTabLeftOffset + CurrTabWidth);
        end;
        tpTop:
        begin
          Canvas.MoveTo(CompleteWidth + FTabLeftOffset, PanelHeight - FTabBottomOffset);
          Canvas.LineTo(CompleteWidth + FTabLeftOffset, FTabTopOffset);
          Canvas.LineTo(CompleteWidth + FTabLeftOffset + CurrTabWidth, FTabTopOffset);
          Canvas.Pen.Color := clBlack;
          Canvas.LineTo(CompleteWidth + FTabLeftOffset + CurrTabWidth, PanelHeight - FTabTopOffset);
        end;
        tpBottom:
        begin
          Canvas.MoveTo(CompleteWidth + FTabLeftOffset, FTabBottomOffset);
          Canvas.LineTo(CompleteWidth + FTabLeftOffset, PanelHeight - FTabTopOffset);
          Canvas.Pen.Color := clBlack;
          Canvas.LineTo(CompleteWidth + FTabLeftOffset + CurrTabWidth, PanelHeight - FTabTopOffset);
          Canvas.LineTo(CompleteWidth + FTabLeftOffset + CurrTabWidth, FTabBottomOffset);
        end;
      end;

      { 字体为焦点字体 }
      Canvas.Font.Assign(FActiveFont);
    end else
    begin
      { 画页面之间的分割线 }
      if (i < Page.ActivePageIndex - 1) or (i > Page.ActivePageIndex) then
      begin
        Canvas.Pen.Color := Page.InactiveFont.Color;
        case Page.TabPosition of
          tpLeft, tpRight:
          begin
            Canvas.MoveTo(PanelHeight - FTabBottomOffset - 3, CompleteWidth + FTabLeftOffset + CurrTabWidth);
            Canvas.LineTo(FTabTopOffset + 2, CompleteWidth + FTabLeftOffset + CurrTabWidth);
          end;
          tpTop, tpBottom:
          begin
            Canvas.MoveTo(CompleteWidth + FTabLeftOffset + CurrTabWidth , PanelHeight - FTabBottomOffset - 3);
            Canvas.LineTo(CompleteWidth + FTabLeftOffset + CurrTabWidth , FTabTopOffset + 2);
          end;
        end;
      end;
      Canvas.Brush.Color := Page.InactiveSheetColor;
      { 字体为非焦点字体 }
      Canvas.Font.Assign(FInactiveFont);
    end;

    if FSelectHotIndex = i then
      Canvas.Font.Color := FHotTrackColor;

    case Page.TabPosition of
      tpLeft:   ARect := Rect(FTabTopOffset + FCaptionTopOffset + 1,
                  CompleteWidth + FTabLeftOffset + FCaptionLeftOffset,
                  PanelHeight,
                  CompleteWidth + FTabLeftOffset + CurrTabWidth - FCaptionRightOffset);

      tpRight:  ARect := Rect(FTabBottomOffset + FCaptionTopOffset + 1,
                  CompleteWidth + FTabLeftOffset + FCaptionLeftOffset,
                  PanelHeight,
                  CompleteWidth + FTabLeftOffset + CurrTabWidth - FCaptionRightOffset);

      tpTop:    ARect := Rect(CompleteWidth + FTabLeftOffset + FCaptionLeftOffset + Integer(FShowTabImages) * (ImageWidth + FCaptionLeftOffset),
                  FTabTopOffset + FCaptionTopOffset + 1,
                  CompleteWidth + FTabLeftOffset + CurrTabWidth - FCaptionRightOffset,// + Integer(FShowTabImages) * FCaptionRightOffset,
                  PanelHeight);

      tpBottom: ARect := Rect(CompleteWidth + FTabLeftOffset + FCaptionLeftOffset + Integer(FShowTabImages) * (ImageWidth + FCaptionLeftOffset),
                  FTabBottomOffset + FCaptionTopOffset + 1,
                  CompleteWidth + FTabLeftOffset + CurrTabWidth - FCaptionRightOffset,// + Integer(FShowTabImages) * FCaptionRightOffset,
                  PanelHeight);
    end;

    CaptionString := Page.Pages[i].Caption;

    { 画文字 }
    DrawText(Canvas.Handle, PChar(CaptionString), Length(CaptionString),
        ARect, DT_LEFT or DT_SINGLELINE or DT_END_ELLIPSIS);
    { 画图象 }
    if FShowTabImages and (Page.Images <> nil) and (CurrTabWidth > ImageWidth + 2 * FCaptionLeftOffset) then
      Page.Images.Draw(Canvas, CompleteWidth + FTabLeftOffset + FCaptionLeftOffset,
        FTabBottomOffset + FCaptionTopOffset + 1, Page.Pages[i].ImageIndex, True);

    Inc(CompleteWidth, CurrTabWidth + FTabSplitterWidth);
  end;

  { 最后画一个边框 }
  Canvas.Brush.Color := Page.ActiveSheetColor;
  ARect := ClientRect;
  Canvas.FrameRect(ARect);
end;

procedure TCnTabPanel.Resize;
begin
  inherited Resize;
  SetShowTabWidth;
end;

procedure TCnTabPanel.SetCaptionLeftOffset(const Value: Integer);
begin
  if FCaptionLeftOffset <> Value then
  begin
    FCaptionLeftOffset := Value;
    Invalidate;
  end;
end;

procedure TCnTabPanel.SetCaptionRightOffset(const Value: Integer);
begin
  if FCaptionRightOffset <> Value then
  begin
    FCaptionRightOffset := Value;
    Invalidate;
  end;
end;

procedure TCnTabPanel.SetCaptionTopOffset(const Value: Integer);
begin
  if FCaptionTopOffset <> Value then
  begin
    FCaptionTopOffset := Value;
    Invalidate;
  end;
end;

procedure TCnTabPanel.SetPage(const Value: TCnVIDTabPageControl);
begin
  FPage := Value;
end;

procedure TCnTabPanel.SetPanelHeight(const Value: Integer);
begin
  if PanelHeight <> Value then
  begin
    case Page.TabPosition of
      tpLeft, tpRight: Width := Value;
      tpTop, tpBottom: Height := Value;
    end;
    SetShowTabWidth;
  end;
end;

procedure TCnTabPanel.SetTabBottomOffset(const Value: Integer);
begin
  if FTabBottomOffset <> Value then
  begin
    FTabBottomOffset := Value;
    Invalidate;
  end;
end;

procedure TCnTabPanel.SetTabLeftOffset(const Value: Integer);
begin
  if FTabLeftOffset <> Value then
  begin
    FTabLeftOffset := Value;
    Invalidate;
  end;
end;

procedure TCnTabPanel.SetTabRightOffset(const Value: Integer);
begin
  if FTabRightOffset <> Value then
  begin
    FTabRightOffset := Value;
    Invalidate;
  end;
end;

procedure TCnTabPanel.SetTabSplitterWidth(const Value: Integer);
begin
  if FTabSplitterWidth <> Value then
  begin
    FTabSplitterWidth := Value;
    Invalidate;
  end;
end;

procedure TCnTabPanel.SetTabTopOffset(const Value: Integer);
begin
  if FTabTopOffset <> Value then
  begin
    FTabTopOffset := Value;
    Invalidate;
  end;
end;

procedure TCnTabPanel.SetTotalTabWidth(const Value: Integer);
begin
end;

function TCnTabPanel.GetDockClientFromPageIndex(Index: Integer): TControl;
begin
  Result := nil;
  if Index >= 0 then
  begin
    if Page.Pages[Index].ControlCount = 1 then
    begin
      Result := Page.Pages[Index].Controls[0];
      if Result.HostDockSite <> Page then Result := nil;
    end;
  end;
end;

procedure TCnTabPanel.SetShowTabWidth;
var i, j, TempWidth: Integer;
  PanelWidth, VisibleCount: Integer;
  ImageWidth: Integer;
begin
  if Page = nil then Exit;
  if FSortList = nil then Exit;
  PanelWidth := 0;
  case Page.TabPosition of
  tpTop, tpBottom:
    PanelWidth := Width;
  tpLeft, tpRight:
    PanelWidth := Height;
  end;
  // 总共的宽度
  TempWidth := PanelWidth - FCaptionLeftOffset - FCaptionRightOffset;
  if Page.ShowTabImages then
    ImageWidth := Page.Images.Width + FCaptionLeftOffset
  else ImageWidth := 0;
  VisibleCount := Page.VisibleTheetCount;
  j := 0;
  for i := 0 to FSortList.Count - 1 do
  begin
    // 只有可见的Tab才能调整Tab的宽度
    if not Sorts[i].TabVisible then Continue;
    // 只有当Tab能够显示的宽度小于Tab本身的宽度TabWidth的时候才重新设置Tab的ShowTabWidth，
    // 否者就把Tab的ShowTabWidth设置成Tab本身的宽度TabWidth。
    if (VisibleCount - j) * (Sorts[i].TabWidth + FTabSplitterWidth + ImageWidth) > TempWidth then
    begin
      Sorts[i].FShowTabWidth := TempWidth div (VisibleCount - j) - FTabSplitterWidth;
    end else
      Sorts[i].FShowTabWidth := Sorts[i].TabWidth + ImageWidth;
    // TempWidth需要减去显示宽度ShowTabWidth和分割宽度TabSplitterWidth。
    Dec(TempWidth, Sorts[i].FShowTabWidth + FTabSplitterWidth);
    // 可见的Tab+1。
    Inc(j);
  end;
end;

procedure TCnTabPanel.CMMouseLeave(var Message: TMessage);
begin
  inherited;
  if FSelectHotIndex <> -1 then
  begin
    FSelectHotIndex := -1;
    Invalidate;
  end;
end;

procedure TCnTabPanel.SetShowTabImages(const Value: Boolean);
begin
  if FShowTabImages <> Value then
  begin
    FShowTabImages := Value;
    SetShowTabWidth;
    Invalidate;
  end;
end;

procedure TCnTabPanel.SetTabHeight(const Value: Integer);
begin
  FTabHeight := Value;
  Height := FTabHeight + FTabTopOffset + FTabBottomOffset;
end;

{ TCnVIDDockTabSheet }

constructor TCnVIDDockTabSheet.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FIsSourceDockClient := False;
end;

destructor TCnVIDDockTabSheet.Destroy;
begin
  if (PageControl is TCnVIDTabPageControl) and (PageControl <> nil) then
    TCnVIDTabPageControl(PageControl).Panel.DeleteSorts(Self);
  inherited Destroy;
end;

procedure TCnVIDDockTabSheet.Loaded;
begin
  inherited;
  SetSheetSort(Caption);
end;

procedure TCnVIDDockTabSheet.SetPageControl(
  APageControl: TCnDockPageControl);
begin
  inherited;

end;

procedure TCnVIDDockTabSheet.SetSheetSort(CaptionStr: string);
var TempWidth: Integer;

  procedure DoSetSheetSort;
  var i: Integer;
  begin
    TCnVIDTabPageControl(PageControl).Panel.FSortList.Remove(Self);
    for i := 0 to TCnVIDTabPageControl(PageControl).Panel.FSortList.Count - 1 do
    begin
      if TCnVIDTabPageControl(PageControl).Panel.Sorts[i].TabWidth > TempWidth then
      begin
        TCnVIDTabPageControl(PageControl).Panel.FSortList.Insert(i, Self);
        Exit;
      end;
    end;
    TCnVIDTabPageControl(PageControl).Panel.FSortList.Add(Self);
  end;

var TabPanel: TCnTabPanel;

begin
  if (PageControl is TCnVIDTabPageControl) and (PageControl <> nil) then
  begin
    TabPanel := TCnVIDTabPageControl(PageControl).Panel;
    if PageControl.ActivePage = Self then
      TabPanel.Canvas.Font.Assign(TabPanel.Page.ActiveFont)
    else TabPanel.Canvas.Font.Assign(TabPanel.Page.InactiveFont);
    TempWidth := TabPanel.Canvas.TextWidth(
      CaptionStr) + TabPanel.CaptionLeftOffset + TabPanel.CaptionRightOffset;
    if TempWidth <> FTabWidth then
    begin
      DoSetSheetSort;
      FTabWidth := TempWidth;
      TabPanel.SetShowTabWidth;
      TabPanel.Invalidate;
    end;
  end;
end;

procedure TCnVIDDockTabSheet.SetTabWidth(const Value: Integer);
begin
  FTabWidth := Value;
end;

procedure TCnVIDDockTabSheet.UpdateTabShowing;
begin
  inherited UpdateTabShowing;
  TCnVIDTabPageControl(PageControl).Panel.SetShowTabWidth;
end;

procedure TCnVIDDockTabSheet.WMSETTEXT(var Message: TMessage);
begin
  inherited;
  SetSheetSort(PChar(Message.LParam));
end;

function TCnVIDDockStyle.GetControlName: string;
begin
  Result := Format(gs_LikeVIDStyle, [inherited GetControlName]);
end;

{ TCnVIDDragDockObject }

constructor TCnVIDDragDockObject.Create(AControl: TControl);

  procedure DoGetSourceDockClients(Control: TControl);
  var i: Integer;
    DockableControl: TWinControl;
  begin
    if (Control is TCnDockableForm) then
    begin
      DockableControl := TCnDockableForm(Control).DockableControl;
      for i := 0 to DockableControl.DockClientCount - 1 do
      begin
//        if not DockableControl.DockClients[i].Visible then Continue;
        DoGetSourceDockClients(DockableControl.DockClients[i]);
      end;
    end else
      FSourceDockClientList.Add(Control);
  end;

begin
  inherited Create(AControl);
  FSourceDockClientList := TList.Create;
  DoGetSourceDockClients(AControl);
  FDropTabControl := nil;
  FIsTabDockOver := False;
  CurrState := dsDragEnter;
  OldState := CurrState;
end;

procedure TCnVIDDragDockObject.GetBrush_PenSize_DrawRect(
  var ABrush: TBrush; var PenSize: Integer; var DrawRect: TRect; Erase: Boolean);
begin
  if DragTarget = nil then DropAlign := alNone;
  inherited GetBrush_PenSize_DrawRect(ABrush, PenSize, DrawRect, Erase);
  FIsTabDockOver := ((FOldDropAlign = alClient) and FErase) or
    ((DropAlign = alClient) and not FErase);
  FOldDropAlign := DropAlign;
  FOldTarget := DragTarget;
end;

{$J+}
procedure TCnVIDDragDockObject.DefaultDockImage(Erase: Boolean);
var
  DesktopWindow: HWND;
  DC: HDC;
  OldBrush: HBrush;
  DrawRect: TRect;
  PenSize: Integer;
  ABrush: TBrush;

  ButtomOffset: Integer;
  MaxTabWidth: Integer;

const
  LeftOffset = 4;

  procedure DoDrawDefaultImage;
  begin
    with DrawRect do
    begin
      PatBlt(DC, Left + PenSize, Top, Right - Left - PenSize, PenSize, PATINVERT);
      PatBlt(DC, Right - PenSize, Top + PenSize, PenSize, Bottom - Top - PenSize, PATINVERT);
      PatBlt(DC, Left, Bottom - PenSize, Right - Left - PenSize, PenSize, PATINVERT);
      PatBlt(DC, Left, Top, PenSize, Bottom - Top - PenSize, PATINVERT);
    end;
  end;

  procedure DoDrawTabImage;
  begin
    with DrawRect do
    begin
      ButtomOffset := 15;
      MaxTabWidth := 30;

      PatBlt(DC, Left + PenSize, Top, Right - Left - PenSize, PenSize, PATINVERT);
      PatBlt(DC, Right - PenSize, Top + PenSize, PenSize, Bottom - Top - 2 * PenSize - ButtomOffset, PATINVERT);

      if DrawRect.Right - DrawRect.Left - 2 * PenSize < LeftOffset + 2 * PenSize + 2 * MaxTabWidth then
        MaxTabWidth := (DrawRect.Right - DrawRect.Left - 4 * PenSize - LeftOffset) div 2;

      if DrawRect.Bottom - DrawRect.Top - 2 * PenSize < 2 * ButtomOffset then
        ButtomOffset := Max((DrawRect.Bottom - DrawRect.Top - 2 * PenSize) div 2, 0);

      PatBlt(DC, Left, Bottom - PenSize - ButtomOffset, 2*PenSize + LeftOffset, PenSize, PATINVERT);
      PatBlt(DC, Left + PenSize + LeftOffset, Bottom - ButtomOffset, PenSize, ButtomOffset, PATINVERT);
      PatBlt(DC, Left + 2*PenSize + LeftOffset, Bottom - PenSize, MaxTabWidth, PenSize, PATINVERT);
      PatBlt(DC, Left + 2*PenSize + LeftOffset + MaxTabWidth, Bottom - PenSize - ButtomOffset, PenSize, PenSize + ButtomOffset, PATINVERT);
      PatBlt(DC, Left + 3*PenSize + LeftOffset + MaxTabWidth, Bottom - PenSize - ButtomOffset, Right - Left - 3*PenSize - LeftOffset - MaxTabWidth, PenSize, PATINVERT);

      PatBlt(DC, Left, Top, PenSize, Bottom - Top - PenSize - ButtomOffset, PATINVERT);
    end;
  end;

begin
  { 获得画刷句柄，画笔宽度和绘画区域 }
  FErase := Erase;
  GetBrush_PenSize_DrawRect(ABrush, PenSize, DrawRect, Erase);

  DesktopWindow := GetDesktopWindow;
  DC := GetDCEx(DesktopWindow, 0, DCX_CACHE or DCX_LOCKWINDOWUPDATE);
  try
    OldBrush := SelectObject(DC, ABrush.Handle);
    if not FIsTabDockOver then
      DoDrawDefaultImage
    else DoDrawTabImage;
    SelectObject(DC, OldBrush);
  finally
    ReleaseDC(DesktopWindow, DC);
  end;
end;
{$J-}

destructor TCnVIDDragDockObject.Destroy;
begin
  FDropTabControl := nil;
  FSourceDockClientList.Free;
  inherited Destroy;
end;

function TCnVIDDragDockObject.DragFindWindow(const Pos: TPoint): HWND;
begin
  Result := 0;
end;

function TCnVIDDragDockObject.GetDropCtl: TControl;
var ARect: TRect;
  i: Integer;
begin
  Result := inherited GetDropCtl;
  if (Result = nil) and (TargetControl is TCnCustomDockPanel) then
  begin
    for i := 0 to TargetControl.DockClientCount - 1 do
    begin
      if TargetControl.DockClients[i].Visible then
      begin
        ARect := TCnCustomDockPanel(DragTarget).CnDockManager.GetFrameRectEx(TargetControl.DockClients[i]);
        if PtInRect(ARect, DragPos) then
        begin
          Result := TargetControl.DockClients[i];
          Exit;
        end;
      end;
    end;
  end;
end;

function TCnVIDDragDockObject.GetSourceDockClient(
  Index: Integer): TControl;
begin
  Result := TControl(FSourceDockClientList[Index]);
end;

function TCnVIDDragDockObject.GetSourceDockClientCount: Integer;
begin
  Result := FSourceDockClientList.Count;
end;

procedure TCnVIDDragDockObject.MouseMsg(var Msg: TMessage);
var APos: TPoint;
  Page: TCnVIDTabPageControl;
begin
  inherited MouseMsg(Msg);
  case Msg.Msg of
    WM_CAPTURECHANGED:
    begin
      if GlobalDockClient.ParentForm.HostDockSite is TCnVIDTabPageControl then
        TCnVIDTabPageControl(GlobalDockClient.ParentForm.HostDockSite).Panel.MouseUp(mbLeft, [], 0, 0)
      else if TWinControl(CnGlobalDockPresident.DragObject.DragTarget) is TCnVIDTabPageControl then
        TCnVIDTabPageControl(CnGlobalDockPresident.DragObject.TargetControl).Panel.MouseUp(mbLeft, [], 0, 0);
    end;
    WM_MOUSEMOVE:
    begin
      if CnGlobalDockPresident.DragObject.TargetControl is TCnVIDTabPageControl then
      begin
        Page := TCnVIDTabPageControl(CnGlobalDockPresident.DragObject.TargetControl);
        if Page.FTempSheet <> nil then
        begin
          APos := Point(TWMMouse(Msg).XPos, TWMMouse(Msg).YPos);
          APos := Page.Panel.ScreenToClient(APos);
          Page.Panel.MouseMove([], APos.X, APos.Y);
        end;
      end;
    end;
  end;
end;

procedure TCnVIDDragDockObject.SetOldState(const Value: TDragState);
begin
  FOldState := Value;
end;

procedure TCnVIDDragDockObject.SetCurrState(const Value: TDragState);
begin
  FCurrState := Value;
end;

function TCnVIDDragDockObject.CanLeave(NewTarget: TWinControl): Boolean;
begin
  Result := inherited CanLeave(NewTarget);
end;

{ TCnVIDDockZone }

{constructor TCnVIDDockZone.Create(Tree: TCnDockTree);
begin
  inherited;
end;}

destructor TCnVIDDockZone.Destroy;
begin
  inherited;
end;

function TCnVIDDockZone.GetSplitterLimit(IsMin: Boolean): Integer;
begin
  if IsMin then
    Result := ZoneLimit
  else Result := LimitBegin;
end;

procedure TCnVIDDockZone.Insert(DockSize: Integer; Hide: Boolean);
var PrevShift,
    NextShift: Integer;
    TempSize: Integer;
    BorderSize: Integer;
    BeforeVisibleZone,
    AfterVisibleZone: TCnDockZone;
    BeginSize: Integer;
begin
  if (ParentZone <> nil) and (ParentZone.VisibleChildCount = 0) then
    ParentZone.Insert(ParentZone.VisibleSize, Hide);

  if (ParentZone = nil) or ((ParentZone = Tree.TopZone) and (ParentZone.ChildCount <= 1)) then
  begin
    Visibled := True;
    Exit;
  end;

  if (ParentZone <> nil) and (ParentZone.ChildZones <> nil) then
    BeginSize := ParentZone.ChildZones.LimitBegin
  else BeginSize := 0;

  BeforeVisibleZone := BeforeClosestVisibleZone;
  AfterVisibleZone  := AfterClosestVisibleZone;

  BorderSize := TCnVIDDockTree(Tree).BorderWidth * Integer(AfterClosestVisibleZone <> nil) div 2;

  TempSize := ParentZone.HeightWidth[ParentZone.Orientation] + BorderSize;

  Visibled := False;

  if DockSize >= TempSize - (ParentZone.VisibleChildCount) * TCnVIDDockTree(Tree).MinSize then
    DockSize := (TempSize - (ParentZone.VisibleChildCount) * TCnVIDDockTree(Tree).MinSize) div 2;

  if DockSize < TCnVIDDockTree(Tree).MinSize then
    DockSize := TempSize div 2;

  if (BeforeVisibleZone = nil) and (AfterVisibleZone = nil) then
  begin
    PrevShift := 0;
    NextShift := 0;
    ZoneLimit := TempSize + BeginSize;
  end else
  if BeforeVisibleZone = nil then
  begin
    { 要插入的节点是在父节点的最前面 }
    PrevShift := 0;
    NextShift := DockSize + BorderSize;
    ZoneLimit := DockSize + LimitBegin + BorderSize;
    if ParentZone.VisibleChildCount = 1 then
      AfterVisibleZone.ZoneLimit := TempSize + BeginSize;
  end else if AfterVisibleZone = nil then
  begin
    { 要插入的节点是在父节点的最后面 }
    PrevShift := DockSize + BorderSize;
    NextShift := 0;
    if (ParentZone.VisibleChildCount = 1) and (ParentZone = Tree.TopZone) then
      BeforeVisibleZone.ZoneLimit := Tree.TopXYLimit - PrevShift
    else
      BeforeVisibleZone.ZoneLimit := BeforeVisibleZone.ZoneLimit - PrevShift;
    ZoneLimit := TempSize + BeginSize;
  end else
  begin
    { 要插入的节点是在父节点的中间 }
    PrevShift := Round((BeforeVisibleZone.ZoneLimit - BeginSize) * (DockSize + BorderSize) / TempSize);
    NextShift := DockSize - PrevShift;
    if (ParentZone.VisibleChildCount = 1) and (ParentZone = Tree.TopZone) then
      BeforeVisibleZone.ZoneLimit := Tree.TopXYLimit - PrevShift
    else
      BeforeVisibleZone.ZoneLimit := BeforeVisibleZone.ZoneLimit - PrevShift;
    ZoneLimit := BeforeVisibleZone.ZoneLimit + DockSize;
  end;

  { 如果新节点有上一个兄弟节点 }
  if PrevShift <> 0 then
  begin
    with TCnVIDDockTree(Tree) do
    begin
      { 当遍历到新节点的上一个兄弟节点的时候，就停止遍历 }
      ReplacementZone := BeforeVisibleZone;
      try
        if (BeforeVisibleZone.ZoneLimit - BeginSize) * (BeforeVisibleZone.ZoneLimit - BeginSize + PrevShift) <> 0 then
          ScaleBy := (BeforeVisibleZone.ZoneLimit - BeginSize) / (BeforeVisibleZone.ZoneLimit - BeginSize + PrevShift)
        else ScaleBy := 1;
        ParentLimit := BeginSize;
        ShiftScaleOrient := ParentZone.Orientation;
        if ScaleBy <> 1 then
          ForEachAt(ParentZone.ChildZones, ScaleChildZone, tskMiddle, tspChild);
      finally
        ReplacementZone := nil;
      end;
    end;
    { 对PrevSibling的ZoneLimit进行调整 }
    if BeforeVisibleZone.LimitSize < TCnVIDDockTree(Tree).MinSize then
      BeforeVisibleZone.ZoneLimit := BeforeVisibleZone.LimitBegin + TCnVIDDockTree(Tree).MinSize;
  end;


  { 如果新节点有下一个兄弟节点 }
  if NextShift <> 0 then
  begin
    with TCnVIDDockTree(Tree) do
    begin
      if (TempSize + BeginSize - LimitBegin - NextShift) * (TempSize + BeginSize - LimitBegin) <> 0 then
        ScaleBy := (TempSize + BeginSize - LimitBegin - NextShift) / (TempSize + BeginSize - LimitBegin)
      else ScaleBy := 1;
      ParentLimit := TempSize + BeginSize;
      ShiftScaleOrient := ParentZone.Orientation;
      if ScaleBy <> 1 then
        ForEachAt(AfterVisibleZone, ScaleSiblingZone, tskForward);
    end;
  end;
  Visibled := True;
end;

procedure TCnVIDDockZone.Remove(DockSize: Integer; Hide: Boolean);
var PrevShift,
    NextShift: Integer;
    TempSize: Integer;
    BorderSize: Integer;
    BeforeVisibleZone,
    AfterVisibleZone: TCnDockZone;
    BeginSize: Integer;
begin
  if (ParentZone <> nil) and (ParentZone.VisibleChildCount = 1) and (ParentZone <> Tree.TopZone) then
    ParentZone.Remove(ParentZone.LimitSize, Hide);

  if (ParentZone = nil) or ((ParentZone = Tree.TopZone) and (ParentZone.ChildCount <= 1)) then
  begin
    Visibled := False;
    Exit;
  end;

  if (ParentZone <> nil) and (ParentZone.ChildZones <> nil) then
    BeginSize := ParentZone.ChildZones.LimitBegin
  else BeginSize := 0;

  BeforeVisibleZone := BeforeClosestVisibleZone;
  AfterVisibleZone  := AfterClosestVisibleZone;

  BorderSize := TCnVIDDockTree(Tree).BorderWidth * Integer(AfterClosestVisibleZone <> nil) div 2;

  TempSize := ParentZone.HeightWidth[ParentZone.Orientation] + BorderSize;

  if DockSize > TempSize - (ParentZone.VisibleChildCount-1) * TCnVIDDockTree(Tree).MinSize then
    DockSize := TempSize - (ParentZone.VisibleChildCount-1) * TCnVIDDockTree(Tree).MinSize;

  if DockSize = 0 then
    DockSize := TempSize div 2;

  Visibled := False;

  if (BeforeVisibleZone = nil) and (AfterVisibleZone = nil) then
    Exit;

  if BeforeVisibleZone = nil then
  begin
    { 要插入的节点是在父节点的最前面 }
    PrevShift := 0;
    NextShift := -DockSize + BorderSize;
    ZoneLimit := -DockSize + BorderSize + BeginSize;
  end else if AfterVisibleZone = nil then
  begin
    { 要插入的节点是在父节点的最后面 }
    PrevShift := -DockSize + BorderSize;
    NextShift := 0;
    BeforeVisibleZone.ZoneLimit := BeforeVisibleZone.ZoneLimit - PrevShift;
    ZoneLimit := TempSize + BeginSize;
  end else
  begin
    { 要插入的节点是在父节点的中间 }
    PrevShift := -Round((BeforeVisibleZone.ZoneLimit - BeginSize) * (DockSize + BorderSize) / (TempSize - (DockSize + BorderSize)));
    NextShift := -DockSize - PrevShift;
    BeforeVisibleZone.ZoneLimit := BeforeVisibleZone.ZoneLimit - PrevShift;
    ZoneLimit := BeforeVisibleZone.ZoneLimit;
  end;

  { 如果新节点有上一个兄弟节点 }
  if PrevShift <> 0 then
  begin
    with TCnVIDDockTree(Tree) do
    begin
      { 当遍历到新节点的上一个兄弟节点的时候，就停止遍历 }
      ReplacementZone := BeforeVisibleZone;
      try
        if (BeforeVisibleZone.ZoneLimit - BeginSize)*(BeforeVisibleZone.ZoneLimit - BeginSize + PrevShift) <> 0 then
          ScaleBy := (BeforeVisibleZone.ZoneLimit - BeginSize) / (BeforeVisibleZone.ZoneLimit - BeginSize + PrevShift)
        else ScaleBy := 1;
        ParentLimit := BeginSize;
        ShiftScaleOrient := ParentZone.Orientation;
        if ScaleBy <> 1 then
          ForEachAt(ParentZone.ChildZones, ScaleChildZone, tskMiddle, tspChild);
      finally
        ReplacementZone := nil;
      end;
    end;
    { 对PrevSibling的ZoneLimit进行调整 }
    if BeforeVisibleZone.LimitSize < TCnVIDDockTree(Tree).MinSize then
      BeforeVisibleZone.ZoneLimit := BeforeVisibleZone.LimitBegin + TCnVIDDockTree(Tree).MinSize;
  end;


  { 如果新节点有下一个兄弟节点 }
  if NextShift <> 0 then
  begin
    with TCnVIDDockTree(Tree) do
    begin
      if (TempSize + BeginSize - LimitBegin) * (TempSize + BeginSize - LimitBegin + NextShift) <> 0 then
        ScaleBy := (TempSize + BeginSize - LimitBegin) / (TempSize + BeginSize - LimitBegin + NextShift)
      else ScaleBy := 1;
      ParentLimit := TempSize + BeginSize;
      ShiftScaleOrient := ParentZone.Orientation;
      if ScaleBy <> 1 then
        ForEachAt(AfterVisibleZone, ScaleSiblingZone, tskForward);
    end;
  end;
end;

{ TCnVIDTabServerOption }

procedure TCnVIDTabServerOption.Assign(Source: TPersistent);
begin
  if Source is TCnVIDTabServerOption then
  begin
    FActiveFont.Assign(TCnVIDTabServerOption(Source).FActiveFont);
    FActiveSheetColor := TCnVIDTabServerOption(Source).FActiveSheetColor;
    FHotTrackColor := TCnVIDTabServerOption(Source).FHotTrackColor;
    FInactiveFont.Assign(TCnVIDTabServerOption(Source).FInactiveFont);
    FInactiveSheetColor := TCnVIDTabServerOption(Source).FInactiveSheetColor;
    FShowTabImages := TCnVIDTabServerOption(Source).FShowTabImages;
  end;
  inherited Assign(Source);
end;

constructor TCnVIDTabServerOption.Create(ADockStyle: TCnBasicDockStyle);
begin
  inherited Create(ADockStyle);
  TabPosition := tpBottom;
  FActiveFont := TFont.Create;
  FActiveSheetColor := clBtnFace;
  FHotTrackColor := clBlue;
  FInactiveFont := TFont.Create;
  FInactiveFont.Color := clWhite;
  FInactiveSheetColor := clBtnShadow;
  FShowTabImages := False;
end;

destructor TCnVIDTabServerOption.Destroy;
begin
  FActiveFont.Free;
  FInactiveFont.Free;
  inherited;
end;

function TCnVIDTabServerOption.GetActiveFont: TFont;
begin
  Result := FActiveFont;
end;

function TCnVIDTabServerOption.GetActiveSheetColor: TColor;
begin
  Result := FActiveSheetColor;
end;

function TCnVIDTabServerOption.GetHotTrackColor: TColor;
begin
  Result := FHotTrackColor;
end;

function TCnVIDTabServerOption.GetInactiveFont: TFont;
begin
  Result := FInactiveFont;
end;

function TCnVIDTabServerOption.GetInactiveSheetColor: TColor;
begin
  Result := FInactiveSheetColor;
end;

function TCnVIDTabServerOption.GetShowTabImages: Boolean;
begin
  Result := FShowTabImages;
end;

procedure TCnVIDTabServerOption.ResetDockControlOption;
begin
  inherited;

end;

procedure TCnVIDTabServerOption.ResetTabPageControl(
  APage: TCnTabPageControl);
begin
  inherited;
  if APage is TCnVIDTabPageControl then
  begin
    TCnVIDTabPageControl(APage).ActiveFont := ActiveFont;
    TCnVIDTabPageControl(APage).ActiveSheetColor := ActiveSheetColor;
    TCnVIDTabPageControl(APage).HotTrackColor := HotTrackColor;
    TCnVIDTabPageControl(APage).InactiveFont := InactiveFont;
    TCnVIDTabPageControl(APage).InactiveSheetColor := InactiveSheetColor;
    TCnVIDTabPageControl(APage).ShowTabImages := ShowTabImages;
    TCnVIDTabPageControl(APage).TabPosition := TabPosition;
  end;
end;

procedure TCnVIDTabServerOption.SetActiveFont(const Value: TFont);
begin
  FActiveFont.Assign(Value);
  ResetDockControlOption;
end;

procedure TCnVIDTabServerOption.SetActiveSheetColor(const Value: TColor);
begin
  if FActiveSheetColor <> Value then
  begin
    FActiveSheetColor := Value;
    ResetDockControlOption;
  end;
end;

procedure TCnVIDTabServerOption.SetHotTrackColor(const Value: TColor);
begin
  if FHotTrackColor <> Value then
  begin
    FHotTrackColor := Value;
    ResetDockControlOption;
  end;
end;

procedure TCnVIDTabServerOption.SetInactiveFont(const Value: TFont);
begin
  FInactiveFont.Assign(Value);
  ResetDockControlOption;
end;

procedure TCnVIDTabServerOption.SetInactiveSheetColor(const Value: TColor);
begin
  if FInactiveSheetColor <> Value then
  begin
    FInactiveSheetColor := Value;
    ResetDockControlOption;
  end;
end;

procedure TCnVIDTabServerOption.SetShowTabImages(const Value: Boolean);
begin
  if FShowTabImages <> Value then
  begin
    FShowTabImages := Value;
    ResetDockControlOption;
  end;
end;

procedure TCnVIDTabServerOption.SetTabPosition(const Value: TTabPosition);
begin
  if Value = tpBottom then
    inherited SetTabPosition(Value)
  else
    raise Exception.Create(gs_TabPositionMustBetpBottom);
end;

{ TCnVIDConjoinServerOption }

procedure TCnVIDConjoinServerOption.Assign(Source: TPersistent);
begin
  if Source is TCnVIDConjoinServerOption then
  begin
    FTextEllipsis := TCnVIDConjoinServerOption(Source).FTextEllipsis;
    FTextAlignment := TCnVIDConjoinServerOption(Source).FTextAlignment;
    FInactiveTitleEndColor := TCnVIDConjoinServerOption(Source).FInactiveTitleEndColor;
    FInactiveTitleStartColor := TCnVIDConjoinServerOption(Source).FInactiveTitleStartColor;
    FActiveTitleEndColor := TCnVIDConjoinServerOption(Source).FActiveTitleEndColor;
    FActiveTitleStartColor := TCnVIDConjoinServerOption(Source).FActiveTitleStartColor;
    FActiveFont.Assign(TCnVIDConjoinServerOption(Source).FActiveFont);
    FInactiveFont.Assign(TCnVIDConjoinServerOption(Source).FInactiveFont);
    FSystemInfo := TCnVIDConjoinServerOption(Source).FSystemInfo;
  end;
  inherited Assign(Source);
end;

constructor TCnVIDConjoinServerOption.Create(
  ADockStyle: TCnBasicDockStyle);
begin
  inherited Create(ADockStyle);
  GrabbersSize := 18;
  FActiveFont := TFont.Create;
  FInactiveFont := TFont.Create;
  SystemInfo := True;
end;

destructor TCnVIDConjoinServerOption.Destroy;
begin
  FActiveFont.Free;
  FInactiveFont.Free;
  inherited;
end;

procedure TCnVIDConjoinServerOption.SetActiveTitleEndColor(
  const Value: TColor);
begin
  if FActiveTitleEndColor <> Value then
  begin
    FActiveTitleEndColor := Value;
    FSystemInfo := False;
    ResetDockControlOption;
  end;
end;

procedure TCnVIDConjoinServerOption.SetActiveTitleStartColor(
  const Value: TColor);
begin
  if FActiveTitleStartColor <> Value then
  begin
    FActiveTitleStartColor := Value;
    FSystemInfo := False;
    ResetDockControlOption;
  end;
end;

procedure TCnVIDConjoinServerOption.SetInactiveTitleEndColor(
  const Value: TColor);
begin
  if FInactiveTitleEndColor <> Value then
  begin
    FInactiveTitleEndColor := Value;
    FSystemInfo := False;
    ResetDockControlOption;
  end;
end;

procedure TCnVIDConjoinServerOption.SetInactiveTitleStartColor(
  const Value: TColor);
begin
  if FInactiveTitleStartColor <> Value then
  begin
    FInactiveTitleStartColor := Value;
    FSystemInfo := False;
    ResetDockControlOption;
  end;
end;

procedure TCnVIDConjoinServerOption.SetSystemInfo(const Value: Boolean);
begin
  if FSystemInfo <> Value then
  begin
    FSystemInfo := Value;
    if FSystemInfo then
      SetDefaultSystemCaptionInfo;

    ResetDockControlOption;
  end;
end;

procedure TCnVIDConjoinServerOption.SetTextAlignment(
  const Value: TAlignment);
begin
  if FTextAlignment <> Value then
  begin
    FTextAlignment := Value;
    FSystemInfo := False;
    ResetDockControlOption;
  end;
end;

procedure TCnVIDConjoinServerOption.SetTextEllipsis(const Value: Boolean);
begin
  if FTextEllipsis <> Value then
  begin
    FTextEllipsis := Value;
    FSystemInfo := False;
    ResetDockControlOption;
  end;
end;

procedure TCnVIDConjoinServerOption.SetDefaultSystemCaptionInfo;
begin
  FActiveTitleStartColor    := Cn_GetActiveTitleBeginColor;
  FActiveTitleEndColor      := Cn_GetActiveTitleEndColor;
  FInactiveTitleStartColor  := Cn_GetInactiveTitleBeginColor;
  FInactiveTitleEndColor    := Cn_GetInactiveTitleEndColor;
  FTextAlignment            := taLeftJustify;
  FTextEllipsis             := True;
  FActiveFont.Assign(Cn_GetTitleFont);
  FActiveFont.Style         := FActiveFont.Style + [fsBold];
  FInactiveFont.Assign(FActiveFont);
  FActiveFont.Color         := Cn_GetActiveTitleFontColor;
  FInactiveFont.Color       := Cn_GetInactiveTitleFontColor;
  GrabbersSize              := VIDDefaultGrabbersSize;
  SplitterWidth             := VIDDefaultSplitterWidth;
end;

procedure TCnVIDConjoinServerOption.SetActiveFont(const Value: TFont);
begin
  FActiveFont.Assign(Value);
  FSystemInfo := False;
  ResetDockControlOption;
end;

procedure TCnVIDConjoinServerOption.SetInactiveFont(const Value: TFont);
begin
  FInactiveFont.Assign(Value);
  FSystemInfo := False;
  ResetDockControlOption;
end;

procedure TCnVIDConjoinServerOption.ResetDockControlOption;
begin
  inherited ResetDockControlOption;
  FSystemInfo := FSystemInfo and (GrabbersSize = VIDDefaultGrabbersSize)
                  and (SplitterWidth = VIDDefaultSplitterWidth);
  TCnVIDDockStyle(DockStyle).DoSystemInfoChange(FSystemInfo);
end;

procedure TCnVIDConjoinServerOption.SetActiveFont_WithoutChangeSystemInfo(
  const Value: TFont);
begin
  FActiveFont.Assign(Value);
end;

procedure TCnVIDConjoinServerOption.SetActiveTitleEndColor_WithoutChangeSystemInfo(
  const Value: TColor);
begin
  FActiveTitleEndColor := Value;
end;

procedure TCnVIDConjoinServerOption.SetActiveTitleStartColor_WithoutChangeSystemInfo(
  const Value: TColor);
begin
  FActiveTitleStartColor := Value;
end;

procedure TCnVIDConjoinServerOption.SetInactiveFont_WithoutChangeSystemInfo(
  const Value: TFont);
begin
  FInactiveFont.Assign(Value);
end;

procedure TCnVIDConjoinServerOption.SetInactiveTitleEndColor_WithoutChangeSystemInfo(
  const Value: TColor);
begin
  FInactiveTitleEndColor := Value;
end;

procedure TCnVIDConjoinServerOption.SetInactiveTitleStartColor_WithoutChangeSystemInfo(
  const Value: TColor);
begin
  FInactiveTitleStartColor := Value;
end;

procedure TCnVIDConjoinServerOption.SetTextAlignment_WithoutChangeSystemInfo(
  const Value: TAlignment);
begin
  FTextAlignment := Value;
end;

procedure TCnVIDConjoinServerOption.SetTextEllipsis_WithoutChangeSystemInfo(
  const Value: Boolean);
begin
  FTextEllipsis := Value;
end;

end.
