{-----------------------------------------------------------------------------
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/MPL-1.1.html

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
the specific language governing rights and limitations under the License.

The Original Code is: JvDockControlForm.pas, released on 2003-12-31.

The Initial Developer of the Original Code is luxiaoban.
Portions created by luxiaoban are Copyright (C) 2002,2003 luxiaoban.
All Rights Reserved.

Contributor(s):
  2005-02-08 - Warren Postma - TJvDockServer.CustomPanel.

Last Modified: 2005-02-08

You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
located at http://jvcl.sourceforge.net

Known Issues:
-----------------------------------------------------------------------------}
// $Id: JvDockControlForm.pas 11276 2007-04-24 19:33:37Z remkobonte $

{ Changes:

  2005-02-08 WP: Added TJvDockServer.CustomDock panel (new property),
  and new event TJvDockServer.OnCustomPanel to get
  a custom parent control (such as the form's center area) for the
  CustomPanel. Previously you could only dock at the edges. Created a
  splitter object (to avoid possible access violations) but the splitter
  control is hidden and never used for this custom panel.  Several
  changes were made  to the VID dock style also to make this custom
  panel more useful,allowing an "tabbed MDI" style application.
}

unit JvDockControlForm;

{$I jvcl.inc}

interface

uses
  {$IFDEF USEJVCL}
  {$IFDEF UNITVERSIONING}
  JclUnitVersioning,
  {$ENDIF UNITVERSIONING}
  {$ENDIF USEJVCL}
  Windows, Messages, Classes, Graphics, Controls, Forms, Menus,
  ExtCtrls, ComCtrls,
  {$IFDEF USEJVCL}
  JvComponentBase, JvAppStorage,
  {$ENDIF USEJVCL}
  JvDockTree, JvDockSupportClass, JvDockSupportControl, JvDockAdvTree;

const
  JvDockPositionCount = 5;

  JvDockState_Unknown = 0;
  JvDockState_Docking = 1;
  JvDockState_Floating = 2;

  {$IFNDEF COMPILER9_UP}
  CM_INVALIDATEDOCKHOST = CM_BASE + 70;
  {$ENDIF !COMPILER9_UP}

type
  TJvDockSplitterSize = 0..32767;

  TJvDockBaseControl = class;
  TJvDockServer = class;
  TJvDockClient = class;
  TJvDockConjoinPanel = class;
  TJvDockTabPageControl = class;
  TJvDockConjoinHostForm = class;
  TJvDockTabHostForm = class;

  TJvDockSplitter = class(TJvDockCustomPanelSplitter)
  private
    FDockServer: TJvDockServer;
    function GetSplitterIndex: Integer;
  protected
    function FindControl: TControl; override;
    property DockServer: TJvDockServer read FDockServer write FDockServer;
  public
    constructor Create(AOwner: TComponent); override;
    property SplitterIndex: Integer read GetSplitterIndex;
  end;

  TJvDockSetDockPanelSizeFrom = (sdfDockPanel, sdfClient);

  TJvDockPanel = class(TJvDockCustomPanel)
  private
    FDockServer: TJvDockServer;
    FCustomFlag: Boolean; // Set only if custom area! {NEW}
    function GetPanelIndex: Integer;
    procedure SetDockServer(ADockServer: TJvDockServer);
  protected
    function CreateDockManager: IDockManager; override;
    procedure AddDockServer(ADockServer: TJvDockServer); virtual;
    procedure RemoveDockServer(ADockServer: TJvDockServer); virtual;
    procedure ReloadDockedControl(const AControlName: string;
      var AControl: TControl); override;
    procedure CustomStartDock(var Source: TJvDockDragDockObject); override;
    procedure CustomGetSiteInfo(Source: TJvDockDragDockObject; Client: TControl; var InfluenceRect: TRect;
      MousePos: TPoint; var CanDock: Boolean); override;
    procedure CustomDockOver(Source: TJvDockDragDockObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean); override;
    procedure CustomPositionDockRect(Source: TJvDockDragDockObject; X, Y: Integer); override;
    procedure CustomDockDrop(Source: TJvDockDragDockObject; X, Y: Integer); override;
    procedure CustomEndDock(Target: TObject; X, Y: Integer); override;
    function CustomUnDock(Source: TJvDockDragDockObject; NewTarget: TWinControl; Client: TControl): Boolean; override;
    function DoUnDock(NewTarget: TWinControl; Client: TControl): Boolean; override;
    procedure Resize; override;
  public
    constructor Create(AOwner: TComponent); override;
    procedure DockDrop(Source: TDragDockObject; X, Y: Integer); override;

    procedure ShowDockPanel(MakeVisible: Boolean; Client: TControl;
      PanelSizeFrom: TJvDockSetDockPanelSizeFrom = sdfClient); virtual;
    procedure ResetPosition;
    // GetDockedControls:  NEW! -WPostma.
    // base class doesn't have this capability.
    // see TJvDockAdvPanel for override that implements this!
    procedure GetDockedControls(WinControls: TList); virtual;

    property PanelIndex: Integer read GetPanelIndex;
    property DockServer: TJvDockServer read FDockServer write SetDockServer;
    property CustomFlag: Boolean read FCustomFlag write FCustomFlag; // Set only if custom area! {NEW}
  end;

  TJvDockAdvPanel = class(TJvDockPanel)
  private
    procedure CMUnDockClient(var Msg: TCMUnDockClient); message CM_UNDOCKCLIENT;
  protected
    procedure CustomDockDrop(Source: TJvDockDragDockObject; X, Y: Integer); override;
    function CustomUnDock(Source: TJvDockDragDockObject; NewTarget: TWinControl; Client: TControl): Boolean; override;
    function DoUnDock(NewTarget: TWinControl; Client: TControl): Boolean; override;
  public
    procedure GetDockedControls(WinControls: TList); override;
    procedure DockDrop(Source: TDragDockObject; X, Y: Integer); override;
  end;

  TJvDockPanelClass = class of TJvDockPanel;
  TJvDockSplitterClass = class of TJvDockSplitter;
  TJvDockConjoinPanelClass = class of TJvDockConjoinPanel;
  TJvDockTabClass = class of TJvDockTabPageControl;

  { Maintained by a TJvDockServer; TJvDockServer ensures that FDockServer is
    assigned. FSplitter may be nil }
  TJvDockSplitterStyle = class(TPersistent)
  private
    FSplitter: TJvDockSplitter;
    FDockServer: TJvDockServer;
    FColor: TColor;
    FCursor: TCursor;
    FParentColor: Boolean;
    FResizeStyle: TResizeStyle;
    FSize: TJvDockSplitterSize;
    FMinSize: TJvDockSplitterSize;
    procedure SetColor(const Value: TColor);
    procedure SetCursor(const Value: TCursor);
    procedure SetParentColor(const Value: Boolean);
    procedure SetResizeStyle(const Value: TResizeStyle);
    procedure SetSize(const Value: TJvDockSplitterSize);
    procedure SetMinSize(const Value: TJvDockSplitterSize);
  protected
    procedure AssignTo(Dest: TPersistent); override;
    procedure AssignToSplitter(Dest: TJvDockSplitter);
    procedure SetSplitterStyle;
    property Splitter: TJvDockSplitter read FSplitter write FSplitter;
  public
    constructor Create(ASplitter: TJvDockSplitter; ACursor: TCursor); virtual;
    procedure Assign(Source: TPersistent); override;
  published
    property Color: TColor read FColor write SetColor default clBtnFace;
    property Cursor: TCursor read FCursor write SetCursor;
    property ParentColor: Boolean read FParentColor write SetParentColor default True;
    property ResizeStyle: TResizeStyle read FResizeStyle write SetResizeStyle default rsPattern;
    property Size: TJvDockSplitterSize read FSize write SetSize default 3;
    property MinSize: TJvDockSplitterSize read FMinSize write SetMinSize default 30;
  end;

  TJvDockBasicStyle = class;

  TJvDockBasicStyle = class(TJvDockObservableStyle)
  private
    FDockPanelClass: TJvDockPanelClass;
    FDockSplitterClass: TJvDockSplitterClass;
    FConjoinPanelClass: TJvDockConjoinPanelClass;
    FTabDockClass: TJvDockTabClass;
    FDockPanelTreeClass: TJvDockTreeClass;
    FDockPanelZoneClass: TJvDockZoneClass;
    FConjoinPanelTreeClass: TJvDockTreeClass;
    FConjoinPanelZoneClass: TJvDockZoneClass;
    FDockBaseControls: TList;
    function GetDockBaseControlCount: Integer;
    function GetDockBaseControl(Index: Integer): TJvDockBaseControl;
  protected
    procedure FormStartDock(DockClient: TJvDockClient; var Source: TJvDockDragDockObject); virtual;
    procedure FormGetSiteInfo(Source: TJvDockDragDockObject; DockClient: TJvDockClient; Client: TControl;
      var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean); virtual;
    procedure FormDockOver(DockClient: TJvDockClient; Source: TJvDockDragDockObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean); virtual;
    procedure FormPositionDockRect(DockClient: TJvDockClient; Source: TJvDockDragDockObject); virtual;
    procedure FormDockDrop(DockClient: TJvDockClient; Source: TJvDockDragDockObject; X, Y: Integer); virtual;
    procedure FormEndDock(DockClient: TJvDockClient; Target: TObject; X, Y: Integer); virtual;
    function FormUnDock(DockClient: TJvDockClient; NewTarget: TWinControl; Client: TControl): Boolean; virtual;
    procedure FormGetDockEdge(DockClient: TJvDockClient; Source: TJvDockDragDockObject;
      MousePos: TPoint; var DropAlign: TAlign); virtual;

    procedure SetDockBaseControl(IsCreate: Boolean; DockBaseControl: TJvDockBaseControl); virtual;
    function DockServerWindowProc(DockServer: TJvDockServer; var Msg: TMessage): Boolean; virtual;
    function DockClientWindowProc(DockClient: TJvDockClient; var Msg: TMessage): Boolean; virtual;
    procedure AddDockBaseControl(ADockBaseControl: TJvDockBaseControl); virtual;
    procedure RemoveDockBaseControl(ADockBaseControl: TJvDockBaseControl); virtual;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    function CanSetEnableDocked(ADockBaseControl: TJvDockBaseControl): Boolean; virtual;
    function CanSetLeftDocked(ADockBaseControl: TJvDockBaseControl): Boolean; virtual;
    function CanSetRightDocked(ADockBaseControl: TJvDockBaseControl): Boolean; virtual;
    function CanSetTopDocked(ADockBaseControl: TJvDockBaseControl): Boolean; virtual;
    function CanSetBottomDocked(ADockBaseControl: TJvDockBaseControl): Boolean; virtual;
    function CanSetEachOtherDocked(ADockBaseControl: TJvDockBaseControl): Boolean; virtual;
    {$IFNDEF USEJVCL}
    function GetControlName: string; virtual;
    function GetDockStyleVersion: string; virtual;
    {$ENDIF !USEJVCL}

    procedure ResetCursor(Source: TJvDockDragDockObject); virtual;

    function GetDockState(DockClient: TJvDockClient): Integer; virtual;

    procedure DoShowDockForm(DockWindow: TWinControl); virtual;
    procedure DoHideDockForm(DockWindow: TWinControl); virtual;

    procedure ShowDockForm(ADockClient: TJvDockClient); virtual;
    procedure HideDockForm(ADockClient: TJvDockClient); virtual;
    function GetDockFormVisible(ADockClient: TJvDockClient): Boolean; virtual;

    property DockBaseControlCount: Integer read GetDockBaseControlCount;
    property DockBaseControl[Index: Integer]: TJvDockBaseControl read GetDockBaseControl;

    procedure RestoreClient(DockClient: TJvDockClient); virtual;
    {$IFNDEF USEJVCL}
    property Version: string read GetDockStyleVersion;
    property ControlName: string read GetControlName;
    {$ENDIF !USEJVCL}
    property DockPanelClass: TJvDockPanelClass read FDockPanelClass write FDockPanelClass;
    property DockSplitterClass: TJvDockSplitterClass read FDockSplitterClass write FDockSplitterClass;
    property ConjoinPanelClass: TJvDockConjoinPanelClass read FConjoinPanelClass write FConjoinPanelClass;
    property TabDockClass: TJvDockTabClass read FTabDockClass write FTabDockClass;
    property DockPanelTreeClass: TJvDockTreeClass read FDockPanelTreeClass write FDockPanelTreeClass;
    property DockPanelZoneClass: TJvDockZoneClass read FDockPanelZoneClass write FDockPanelZoneClass;
    property ConjoinPanelTreeClass: TJvDockTreeClass read FConjoinPanelTreeClass write FConjoinPanelTreeClass;
    property ConjoinPanelZoneClass: TJvDockZoneClass read FConjoinPanelZoneClass write FConjoinPanelZoneClass;
  end;

  TJvDockAdvStyle = class(TJvDockBasicStyle)
  protected
    function DockClientWindowProc(DockClient: TJvDockClient; var Msg: TMessage): Boolean; override;
  end;

  {$IFDEF USEJVCL}
  TJvDockBaseControl = class(TJvComponent)
  {$ELSE}
  TJvDockBaseControl = class(TComponent)
  {$ENDIF USEJVCL}
  private
    FEnableDock: Boolean;
    FLeftDock: Boolean; { Can the parent form be docked into the Left dock? }
    FTopDock: Boolean; { Can the parent form be docked into the Top dock? }
    FRightDock: Boolean; { Can the parent form be docked into the Right dock? }
    FBottomDock: Boolean; { Can the parent form be docked into the Bottom dock? }
    FCustomDock: Boolean; {NEW!  Can the parent form be docked into the Custom (center window) dock area? }
    FEachOtherDock: Boolean;
    FDockStyle: TJvDockBasicStyle;
    FOldOnClose: TCloseEvent;
    FOldOnCreate: TNotifyEvent;
    FParentForm: TForm;
    FOldWindowProc: TWndMethod;
    procedure SetDockStyle(ADockStyle: TJvDockBasicStyle);
  protected
    procedure SetParentComponent(Value: TComponent); override;

    function CanSetEnableDocked: Boolean; virtual;
    function CanSetLeftDocked: Boolean; virtual;
    function CanSetRightDocked: Boolean; virtual;
    function CanSetTopDocked: Boolean; virtual;
    function CanSetBottomDocked: Boolean; virtual;
    function CanSetEachOtherDocked: Boolean; virtual;

    procedure DoFormOnClose(Sender: TObject; var Action: TCloseAction); virtual;
    procedure DoFormOnCreate(Sender: TObject); virtual;

    procedure SetBottomDock(const Value: Boolean); virtual;
    procedure SetEachOtherDock(const Value: Boolean); virtual;
    procedure SetEnableDock(const Value: Boolean); virtual;
    procedure SetLeftDock(const Value: Boolean); virtual;
    procedure SetRightDock(const Value: Boolean); virtual;
    procedure SetTopDock(const Value: Boolean); virtual;

    procedure AddDockStyle(ADockStyle: TJvDockBasicStyle); virtual;
    procedure RemoveDockStyle(ADockStyle: TJvDockBasicStyle); virtual;

    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    procedure WindowProc(var Msg: TMessage); virtual;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
    {$IFNDEF USEJVCL}
    function GetDockStyleVersion: string; virtual;
    {$ENDIF !USEJVCL}
    { Owner of this component }
    property ParentForm: TForm read FParentForm;
    property EnableDock: Boolean read FEnableDock write SetEnableDock default True;
    property LeftDock: Boolean read FLeftDock write SetLeftDock default True;
    property TopDock: Boolean read FTopDock write SetTopDock default True;
    property RightDock: Boolean read FRightDock write SetRightDock default True;
    property BottomDock: Boolean read FBottomDock write SetBottomDock default True;
    property EachOtherDock: Boolean read FEachOtherDock write SetEachOtherDock default True;
    property CustomDock: Boolean read FCustomDock write FCustomDock default True; {NEW!}
    property DockStyle: TJvDockBasicStyle read FDockStyle write SetDockStyle;
    {$IFNDEF USEJVCL}
    property Version: string read GetDockStyleVersion;
    {$ENDIF !USEJVCL}
  end;

  TJvDockCustomPanelEvent = procedure(Sender: TJvDockServer;
    var AParent: TWinControl; var Align: TAlign) of object; {NEW!}
  TJvDockCheckDockableEvent = procedure(DockClient: TJvDockClient; DockForm: TForm;
    DockServer: TJvDockServer; DockPanel: TJvDockPanel; var CanDock: Boolean) of object; {NEW!}
  TJvDockTabHostFormCreatedEvent = procedure(DockClient: TJvDockClient;
    TabHost: TJvDockTabHostForm) of object;

  TJvDockGetClientAlignSizeEvent = procedure(Align: TAlign; var Value: Integer) of object;
  TJvDockFinishSetDockPanelSizeEvent = procedure(DockPanel: TJvDockPanel) of object;

  {

    TJvDockServer is the Creator of 4 panels and 4 splitters that are placed on
    the form that contains the TJvDockServer. The type of the panels and splitters
    is determined by the DockStyle.

    o  TJvDockServer maintains the panels and splitters. If the dock server is
       destroyed then the panels and splitters are destroyed.
    o  If the DockStyle is changed then the panels+splitters are destroyed and
       recreated.
    o  If DockStyle is set to nil then the panels+splitters are nil.
  }

  TJvDockPosition = (dpLeft, dpRight, dpTop, dpBottom, dpCustom); {dpCustom NEW!}

  TJvDockServer = class(TJvDockBaseControl)
  private
    FDockPanelClass: TJvDockPanelClass;
    FDockPanels: array [TJvDockPosition] of TJvDockPanel;
    FDockSplitterClass: TJvDockSplitterClass;
    FSplitters: array [TJvDockPosition] of TJvDockSplitter;
    FSplitterStyles: array [TJvDockPosition] of TJvDockSplitterStyle;
    FOnCustomPanel: TJvDockCustomPanelEvent; // Self, AParent, Align
    FOnGetClientAlignSize: TJvDockGetClientAlignSizeEvent;
    FOnFinishSetDockPanelSize: TJvDockFinishSetDockPanelSizeEvent;
    FAutoFocusDockedForm: Boolean;
    procedure CreateDockPanelAndSplitter;
    procedure DestroyDockPanelAndSplitter;
    procedure CreateSplitterStyle;
    procedure DestroySplitterStyle;
    procedure SetSplitterStyles;
    procedure DoGetClientAlignControl(Align: TAlign; var Value: Integer);
    function GetDockPanel(DockPosition: TJvDockPosition): TJvDockPanel;
    function GetDockPanelIndex(const Index: Integer): TJvDockPanel;
    function GetDockPanelWithAlign(Index: TAlign): TJvDockPanel;
    function GetDockSplitterWithAlign(Index: TAlign): TJvDockSplitter;
    function GetSplitter(DockPosition: TJvDockPosition): TJvDockSplitter;
    function GetSplitterIndex(const Index: Integer): TJvDockSplitter;
    function GetSplitterStyle(DockPosition: TJvDockPosition): TJvDockSplitterStyle;
    function GetSplitterStyleIndex(const Index: Integer): TJvDockSplitterStyle;
    procedure SetSplitterStyle(DockPosition: TJvDockPosition; ASplitterStyle: TJvDockSplitterStyle);
    procedure SetSplitterStyleIndex(const Index: Integer; ASplitterStyle: TJvDockSplitterStyle);
  protected
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    procedure DoFinishSetDockPanelSize(DockPanel: TJvDockPanel);
    procedure DoFloatDockClients(DockPanel: TJvDockPanel);
    procedure SetBottomDock(const Value: Boolean); override;
    procedure SetEnableDock(const Value: Boolean); override;
    procedure SetLeftDock(const Value: Boolean); override;
    procedure SetRightDock(const Value: Boolean); override;
    procedure SetTopDock(const Value: Boolean); override;

    procedure AddDockStyle(ADockStyle: TJvDockBasicStyle); override;
    procedure RemoveDockStyle(ADockStyle: TJvDockBasicStyle); override;

    procedure WMActivate(var Msg: TWMActivate);
    procedure WindowProc(var Msg: TMessage); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function GetClientAlignControl(Align: TAlign): Integer;

    property DockPanel[DockPosition: TJvDockPosition]: TJvDockPanel read GetDockPanel;
    property Splitter[DockPosition: TJvDockPosition]: TJvDockSplitter read GetSplitter;
    property SplitterStyle[DockPosition: TJvDockPosition]: TJvDockSplitterStyle read GetSplitterStyle write
      SetSplitterStyle;

    property LeftDockPanel: TJvDockPanel index 0 read GetDockPanelIndex;
    property RightDockPanel: TJvDockPanel index 1 read GetDockPanelIndex;
    property TopDockPanel: TJvDockPanel index 2 read GetDockPanelIndex;
    property BottomDockPanel: TJvDockPanel index 3 read GetDockPanelIndex;
    property CustomDockPanel: TJvDockPanel index 4 read GetDockPanelIndex;

    property LeftSplitter: TJvDockSplitter index 0 read GetSplitterIndex;
    property RightSplitter: TJvDockSplitter index 1 read GetSplitterIndex;
    property TopSplitter: TJvDockSplitter index 2 read GetSplitterIndex;
    property BottomSplitter: TJvDockSplitter index 3 read GetSplitterIndex;

    property DockPanelWithAlign[Index: TAlign]: TJvDockPanel read GetDockPanelWithAlign;
    property DockSplitterWithAlign[Index: TAlign]: TJvDockSplitter read GetDockSplitterWithAlign;
    {$IFNDEF USEJVCL}
    property Version: string read GetDockStyleVersion;
    {$ENDIF !USEJVCL}
  published
    property LeftSplitterStyle: TJvDockSplitterStyle index 0 read GetSplitterStyleIndex write SetSplitterStyleIndex;
    property RightSplitterStyle: TJvDockSplitterStyle index 1 read GetSplitterStyleIndex write SetSplitterStyleIndex;
    property TopSplitterStyle: TJvDockSplitterStyle index 2 read GetSplitterStyleIndex write SetSplitterStyleIndex;
    property BottomSplitterStyle: TJvDockSplitterStyle index 3 read GetSplitterStyleIndex write SetSplitterStyleIndex;
    property AutoFocusDockedForm: Boolean read FAutoFocusDockedForm write FAutoFocusDockedForm default True;
    property EnableDock;
    property LeftDock;
    property TopDock;
    property RightDock;
    property BottomDock;
    property DockStyle;
    property CustomDock;
    property OnGetClientAlignSize: TJvDockGetClientAlignSizeEvent read FOnGetClientAlignSize
      write FOnGetClientAlignSize;
    property OnFinishSetDockPanelSize: TJvDockFinishSetDockPanelSizeEvent read FOnFinishSetDockPanelSize
      write FOnFinishSetDockPanelSize;
    property OnCustomPanel: TJvDockCustomPanelEvent read FOnCustomPanel write FOnCustomPanel; {NEW!}
  end;

  TJvDockMouseStation = (msFloat, msConjoin, msTabPage);

  TJvDockNCButtonEvent = procedure(DockClient: TJvDockClient; Button: TMouseButton;
    X, Y: Smallint; HitTest: Longint; MouseStation: TJvDockMouseStation) of object;
  TJvDockNCButtonDownEvent = TJvDockNCButtonEvent;
  TJvDockNCButtonUpEvent = TJvDockNCButtonEvent;
  TJvDockNCButtonDblClkEvent = TJvDockNCButtonEvent;
  TJvDockNCMouseMoveEvent = procedure(DockClient: TJvDockClient;
    X, Y: Smallint; HitTest: Longint; MouseStation: TJvDockMouseStation) of object;
  TJvDockPaintDockEvent = procedure(Canvas: TCanvas;
    Control: TControl; const ARect: TRect) of object;
  TJvDockPaintDockGrabberEvent = TJvDockPaintDockEvent;
  TJvDockPaintDockSplitterEvent = TJvDockPaintDockEvent;
  TJvDockFormHintEvent = procedure(HTFlag: Integer; var HintStr: string; var CanShow: Boolean) of object;

  TJvDockClient = class(TJvDockBaseControl)
  private
    FConjoinPanelClass: TJvDockConjoinPanelClass;
    FTabDockClass: TJvDockTabClass;
    FParentVisible: Boolean;
    FNCPopupMenu: TPopupMenu;
    FDirectDrag: Boolean;
    FShowHint: Boolean;
    FCanFloat: Boolean;
    FDockLevel: Integer;
    FEnableCloseButton: Boolean;
    FOnNCButtonDown: TJvDockNCButtonDownEvent;
    FOnNCButtonUp: TJvDockNCButtonUpEvent;
    FOnNCMouseMove: TJvDockNCMouseMoveEvent;
    FOnNCButtonDblClk: TJvDockNCButtonDblClkEvent;
    FOnPaintDockGrabber: TJvDockPaintDockGrabberEvent;
    FOnPaintDockSplitter: TJvDockPaintDockSplitterEvent;
    FOnFormShowHint: TJvDockFormHintEvent;
    FOnFormShow: TNotifyEvent;
    FOnFormHide: TNotifyEvent;
    FOnCheckIsDockable: TJvDockCheckDockableEvent; {NEW!}
    FOnTabHostFormCreated: TJvDockTabHostFormCreatedEvent; {NEW!}
    FCurrentDockSite: TWinControl;
    FLastDockSite: TWinControl;
    FUnDockLeft: Integer;
    FUnDockTop: Integer;
    FVSPaneWidth: Integer;
    procedure SetParentVisible(const Value: Boolean);
    function GetLRDockWidth: Integer;
    function GetTBDockHeight: Integer;
    procedure SetLRDockWidth(const Value: Integer);
    procedure SetTBDockHeight(const Value: Integer);
    procedure SetNCPopupMenu(Value: TPopupMenu);
    procedure WMNCLButtonDown(var Msg: TWMNCHitMessage);
    procedure WMNCLButtonUp(var Msg: TWMNCHitMessage);
    procedure WMNCLButtonDblClk(var Msg: TWMNCHitMessage);
    procedure WMNCMButtonDown(var Msg: TWMNCHitMessage);
    procedure WMNCMButtonUp(var Msg: TWMNCHitMessage);
    procedure WMNCMButtonDblClk(var Msg: TWMNCHitMessage);
    procedure WMNCRButtonDown(var Msg: TWMNCHitMessage);
    procedure WMNCRButtonUp(var Msg: TWMNCHitMessage);
    procedure WMNCRButtonDblClk(var Msg: TWMNCHitMessage);
    procedure WMNCMouseMove(var Msg: TWMNCHitMessage);
    procedure CMVisibleChanged(var Msg: TMessage);
    procedure SetCurrentDockSite(const Value: TWinControl);
    procedure SetLastDockSite(ALastDockSite: TWinControl);
    procedure SetVSPaneWidth(const Value: Integer);
    procedure SetUnDockLeft(const Value: Integer);
    procedure SetUnDockTop(const Value: Integer);
    function GetDockState: Integer;
    procedure SetCanFloat(const Value: Boolean);
    procedure SetDockLevel(const Value: Integer);
    procedure SetEnableCloseButton(const Value: Boolean);
  protected
    procedure DoMenuPopup(X, Y: Integer); virtual;
    procedure Deactivate; virtual;
    procedure Activate; virtual;
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    procedure DoFloatDockClients(PanelAlign: TAlign);
    procedure DoFloatDockEachOther;
    procedure SetBottomDock(const Value: Boolean); override;
    procedure SetEachOtherDock(const Value: Boolean); override;
    procedure SetEnableDock(const Value: Boolean); override;
    procedure SetLeftDock(const Value: Boolean); override;
    procedure SetRightDock(const Value: Boolean); override;
    procedure SetTopDock(const Value: Boolean); override;
    procedure DoFormOnClose(Sender: TObject; var Action: TCloseAction); override;
    procedure AddDockStyle(ADockStyle: TJvDockBasicStyle); override;
    procedure RemoveDockStyle(ADockStyle: TJvDockBasicStyle); override;
    procedure WMSize(var Msg: TWMSize);
    procedure WMActivate(var Msg: TWMActivate);
    procedure WindowProc(var Msg: TMessage); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure FormStartDock(var Source: TJvDockDragDockObject); virtual;
    procedure FormPositionDockRect(Source: TJvDockDragDockObject); virtual;
    procedure FormDockOver(Source: TJvDockDragDockObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean); virtual;
    procedure FormDockDrop(Source: TJvDockDragDockObject; X, Y: Integer); virtual;
    procedure FormEndDock(Target: TObject; X, Y: Integer); virtual;
    function FormUnDock(NewTarget: TWinControl; Client: TControl): Boolean; virtual;
    procedure FormGetSiteInfo(Source: TJvDockDragDockObject;
      Client: TControl; var InfluenceRect: TRect; MousePos: TPoint;
      var CanDock: Boolean); virtual;
    procedure FormGetDockEdge(Source: TJvDockDragDockObject; MousePos: TPoint; var DropAlign: TAlign); virtual;
    procedure Assign(Source: TPersistent); override;
    procedure MakeShowEvent;
    procedure MakeHideEvent;
    function CreateConjoinPanelClass(ConjoinHost: TForm): TJvDockConjoinPanel;
    function CreateTabDockClass(TabHost: TForm): TJvDockTabPageControl;
    function CreateConjoinHostAndDockControl(Control1, Control2: TControl;
      DockType: TAlign): TJvDockConjoinHostForm; virtual;
    function CreateTabHostAndDockControl(Control1, Control2: TControl): TJvDockTabHostForm; virtual;

    function FindTabHostForm: TForm;
    // return nil if not found, otherwise, get currently docked parent tabhost form if there is one.

    procedure DoNCButtonDown(Msg: TWMNCHitMessage; Button: TMouseButton;
      MouseStation: TJvDockMouseStation); virtual;
    procedure DoNCButtonUp(Msg: TWMNCHitMessage; Button: TMouseButton;
      MouseStation: TJvDockMouseStation); virtual;
    procedure DoNCMouseMove(Msg: TWMNCHitMessage;
      MouseStation: TJvDockMouseStation); virtual;
    procedure DoNCButtonDblClk(Msg: TWMNCHitMessage; Button: TMouseButton;
      MouseStation: TJvDockMouseStation); virtual;

    procedure DoPaintDockGrabber(Canvas: TCanvas;
      Control: TControl; const ARect: TRect);
    procedure DoPaintDockSplitter(Canvas: TCanvas;
      Control: TControl; const ARect: TRect);
    procedure DoFormShowHint(HTFlag: Integer; var HintStr: string; var CanShow: Boolean);
    procedure ShowParentForm;
    procedure HideParentForm;
    procedure RestoreChild;
    property VSPaneWidth: Integer read FVSPaneWidth write SetVSPaneWidth;
    property ParentVisible: Boolean read FParentVisible write SetParentVisible;
    { (rb) not used? }
    property CurrentDockSite: TWinControl read FCurrentDockSite write SetCurrentDockSite;
    property LastDockSite: TWinControl read FLastDockSite write SetLastDockSite;
    property UnDockLeft: Integer read FUnDockLeft write SetUnDockLeft;
    property UnDockTop: Integer read FUnDockTop write SetUnDockTop;
    property DockState: Integer read GetDockState;
  published
    property LRDockWidth: Integer read GetLRDockWidth write SetLRDockWidth default 100;
    property TBDockHeight: Integer read GetTBDockHeight write SetTBDockHeight default 100;
    property NCPopupMenu: TPopupMenu read FNCPopupMenu write SetNCPopupMenu;
    property DirectDrag: Boolean read FDirectDrag write FDirectDrag;
    property ShowHint: Boolean read FShowHint write FShowHint default True;
    property CanFloat: Boolean read FCanFloat write SetCanFloat default True;
    { Not implemented; intention: only forms with the same DockLevel could be docked together }
    property DockLevel: Integer read FDockLevel write SetDockLevel default 0;
    property EnableCloseButton: Boolean read FEnableCloseButton write SetEnableCloseButton default True;
    property EnableDock;
    property LeftDock;
    property TopDock;
    property RightDock;
    property BottomDock;
    property EachOtherDock;
    property DockStyle;
    property CustomDock;
    property OnFormShow: TNotifyEvent read FOnFormShow write FOnFormShow;
    property OnFormHide: TNotifyEvent read FOnFormHide write FOnFormHide;
    property OnCheckIsDockable: TJvDockCheckDockableEvent read FOnCheckIsDockable write FOnCheckIsDockable; {NEW!}
    property OnTabHostFormCreated: TJvDockTabHostFormCreatedEvent read FOnTabHostFormCreated
      write FOnTabHostFormCreated; {NEW!}
    property OnNCButtonDown: TJvDockNCButtonDownEvent read FOnNCButtonDown write FOnNCButtonDown;
    property OnNCButtonUp: TJvDockNCButtonUpEvent read FOnNCButtonUp write FOnNCButtonUp;
    property OnNCMouseMove: TJvDockNCMouseMoveEvent read FOnNCMouseMove write FOnNCMouseMove;
    property OnNCButtonDblClk: TJvDockNCButtonDblClkEvent read FOnNCButtonDblClk write FOnNCButtonDblClk;
    property OnPaintDockGrabber: TJvDockPaintDockGrabberEvent read FOnPaintDockGrabber write FOnPaintDockGrabber;
    property OnPaintDockSplitter: TJvDockPaintDockSplitterEvent read FOnPaintDockSplitter write FOnPaintDockSplitter;
    property OnFormShowHint: TJvDockFormHintEvent read FOnFormShowHint write FOnFormShowHint;
  end;

  { Maintained by a TJvDockConjoinHostForm; That is (always) the owner, ie.
    that is assumed on multiple places in the code) }
  TJvDockConjoinPanel = class(TJvDockCustomPanel)
  private
    function GetDockClient: TJvDockClient;
    function GetParentForm: TJvDockConjoinHostForm;
    procedure CMUnDockClient(var Msg: TCMUnDockClient); message CM_UNDOCKCLIENT;
  protected
    function CreateDockManager: IDockManager; override;
    procedure ReloadDockedControl(const AControlName: string; var AControl: TControl); override;
    procedure CustomStartDock(var Source: TJvDockDragDockObject); override;
    procedure CustomGetSiteInfo(Source: TJvDockDragDockObject;
      Client: TControl; var InfluenceRect: TRect; MousePos: TPoint;
      var CanDock: Boolean); override;
    procedure CustomDockOver(Source: TJvDockDragDockObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean); override;
    procedure CustomPositionDockRect(Source: TJvDockDragDockObject; X, Y: Integer); override;
    procedure CustomDockDrop(Source: TJvDockDragDockObject; X, Y: Integer); override;
    procedure CustomEndDock(Target: TObject; X, Y: Integer); override;
    function CustomUnDock(Source: TJvDockDragDockObject; NewTarget: TWinControl;
      Client: TControl): Boolean; override;
    procedure DoDockOver(Source: TDragDockObject; X, Y: Integer; State: TDragState;
      var Accept: Boolean); override;
    procedure GetSiteInfo(Client: TControl; var InfluenceRect: TRect;
      MousePos: TPoint; var CanDock: Boolean); override;
    function DoUnDock(NewTarget: TWinControl; Client: TControl): Boolean; override;
  public
    constructor Create(AOwner: TComponent); override;
    procedure DockDrop(Source: TDragDockObject; X, Y: Integer); override;
    { DockClient of the Owner }
    property DockClient: TJvDockClient read GetDockClient;
    { ParentForm is the Owner }
    property ParentForm: TJvDockConjoinHostForm read GetParentForm;
  end;

  TJvDockAdvConjoinPanel = class(TJvDockConjoinPanel)
  private
    procedure CMUnDockClient(var Msg: TCMUnDockClient); message CM_UNDOCKCLIENT;
  protected
    procedure CustomDockDrop(Source: TJvDockDragDockObject; X, Y: Integer); override;
    function CustomUnDock(Source: TJvDockDragDockObject; NewTarget: TWinControl;
      Client: TControl): Boolean; override;
    function DoUnDock(NewTarget: TWinControl; Client: TControl): Boolean; override;
  public
    procedure DockDrop(Source: TDragDockObject; X, Y: Integer); override;
  end;

  { Maintained by a TJvDockTabHostForm; That is (always) the owner, ie.
    that is assumed on multiple places in the code) }
  TJvDockTabPageControl = class(TJvDockPageControl)
  private
    FVersion: Integer;
    FStyleLink: TJvDockStyleLink;
    function GetParentForm: TJvDockTabHostForm;
    procedure DockStyleChanged(Sender: TObject);
    function GetDockStyle: TJvDockObservableStyle;
    function GetActiveDockForm: TCustomForm;
    function GetDockForm(Index: Integer): TCustomForm;
  protected
    procedure AdjustClientRect(var Rect: TRect); override;
    procedure ReloadDockedControl(const AControlName: string;
      var AControl: TControl); override;
    procedure CustomStartDock(var Source: TJvDockDragDockObject); override;
    procedure CustomGetSiteInfo(Source: TJvDockDragDockObject; Client: TControl;
      var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean); override;
    procedure CustomDockOver(Source: TJvDockDragDockObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean); override;
    procedure CustomPositionDockRect(Source: TJvDockDragDockObject; X, Y: Integer); override;
    procedure CustomDockDrop(Source: TJvDockDragDockObject; X, Y: Integer); override;
    procedure CustomEndDock(Target: TObject; X, Y: Integer); override;
    function CustomUnDock(Source: TJvDockDragDockObject; NewTarget: TWinControl;
      Client: TControl): Boolean; override;
    procedure CustomGetDockEdge(Source: TJvDockDragDockObject; MousePos: TPoint;
      var DropAlign: TAlign); override;
    function DoUnDock(NewTarget: TWinControl; Client: TControl): Boolean; override;

    procedure SyncWithStyle; virtual;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure AfterConstruction; override;
    procedure DockDrop(Source: TDragDockObject; X, Y: Integer); override;
    procedure LoadFromStream(Stream: TStream); virtual;
    procedure SaveToStream(Stream: TStream); virtual;

    property ActiveDockForm: TCustomForm read GetActiveDockForm;
    property DockForm[Index: Integer]: TCustomForm read GetDockForm;
    { ParentForm is the Owner }
    property ParentForm: TJvDockTabHostForm read GetParentForm;
    property TabPosition;
    property DockStyle: TJvDockObservableStyle read GetDockStyle;
  end;

  TJvDockAdvTabPageControl = class(TJvDockTabPageControl)
  private
    procedure CMUnDockClient(var Msg: TCMUnDockClient); message CM_UNDOCKCLIENT;
  protected
    procedure CustomDockDrop(Source: TJvDockDragDockObject; X, Y: Integer); override;
    function CustomUnDock(Source: TJvDockDragDockObject; NewTarget: TWinControl;
      Client: TControl): Boolean; override;
    function DoUnDock(NewTarget: TWinControl; Client: TControl): Boolean; override;
  public
    destructor Destroy; override;
    procedure DockDrop(Source: TDragDockObject; X, Y: Integer); override;
  end;

  { A TJvDockableForm is a base class for TJvDockConjoinHostForm
     and TJvDockTabHostForm which are the base classes for the two kinds of
     docked-views possible for handling multiple controls docked to the same
     dock site. }
  TJvDockableForm = class(TForm)
  private
    FDockClient: TJvDockClient;
    FDockableControl: TWinControl;
    FUnDockControl: TControl;
    FFloatingChild: TControl;
    function GetDockableControl: TWinControl;
    procedure SetDockableControl(const Value: TWinControl);
    procedure SetUnDockControl(const Value: TControl);
  protected
    procedure DoClose(var Action: TCloseAction); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    { Fixed DockClient owned by the component }
    property DockClient: TJvDockClient read FDockClient;
    { Either a TJvDockTabPageControl or a TJvDockConjoinPanel, assigned on
      construction of the dockable form }
    property DockableControl: TWinControl read GetDockableControl write SetDockableControl;
    { ?? Probably needs notification }
    property UnDockControl: TControl read FUnDockControl write SetUnDockControl;
    { ?? Probably needs notification }
    property FloatingChild: TControl read FFloatingChild;
  end;

  TJvDockConjoinHostForm = class(TJvDockableForm)
  private
    FPanel: TJvDockConjoinPanel;
  protected
    procedure DoClose(var Action: TCloseAction); override;
  public
    constructor Create(AOwner: TComponent); override;
    property DockClient;
    { Constructed in TJvDockClient.CreateConjoinPanelClass }
    property Panel: TJvDockConjoinPanel read FPanel write FPanel;
  end;

  TJvDockTabHostForm = class(TJvDockableForm)
  private
    FPageControl: TJvDockTabPageControl;
  public
    constructor Create(AOwner: TComponent); override;
    procedure ShowDockedControl(AControl:TWinControl); virtual; // If aControl is docked in PageControl, change PageControl to that page. NEW! WPostma.
    procedure UpdateCaption(AControl:TWinControl); virtual; // update tab host's tabs and title bar when page caption changes.

    // backwards compatibility: Mantis 4100
    function GetActiveDockForm: TForm;
    property DockClient;
    { Constructed in TJvDockClient.CreateTabDockClass }
    property PageControl: TJvDockTabPageControl read FPageControl write FPageControl;
  end;

  TJvGlobalDockManager = class(TJvDockManager)
  private
    FDockServers: TList;
    FDockClients: TList;
    FDockableForms: TList;
    function GetDockClient(const Index: Integer): TJvDockClient;
    function GetDockClientCount: Integer;
    function GetDockServer(const Index: Integer): TJvDockServer;
    function GetDockServerCount: Integer;
    function GetDockableForm(const Index: Integer): TJvDockableForm;
    function GetDockableFormCount: Integer;
  public
    constructor Create; override;
    destructor Destroy; override;
    procedure RegisterDockServer(ADockServer: TJvDockServer);
    procedure RegisterDockClient(ADockClient: TJvDockClient);
    procedure RegisterDockableForm(ADockableForm: TJvDockableForm);
    procedure UnRegisterDockServer(ADockServer: TJvDockServer);
    procedure UnRegisterDockClient(ADockClient: TJvDockClient);
    procedure UnRegisterDockableForm(ADockableForm: TJvDockableForm);
    function FindDockServerForm(const AName: string): TControl;
    function FindDockClientForm(const AName: string): TControl;
    function FindDockControlForm(const AName: string): TControl;

    property DockServer[const Index: Integer]: TJvDockServer read GetDockServer;
    property DockClient[const Index: Integer]: TJvDockClient read GetDockClient;
    property DockableForm[const Index: Integer]: TJvDockableForm read GetDockableForm;
    property DockServerCount: Integer read GetDockServerCount;
    property DockClientCount: Integer read GetDockClientCount;
    property DockableFormCount: Integer read GetDockableFormCount;
  end;

var
  DefaultDockPanelClass: TJvDockPanelClass = TJvDockPanel;
  DefaultDockSplitterClass: TJvDockSplitterClass = TJvDockSplitter;
  DefaultConjoinPanelClass: TJvDockConjoinPanelClass = TJvDockConjoinPanel;
  DefaultTabDockClass: TJvDockTabClass = TJvDockTabPageControl;
  DefaultDockZoneClass: TJvDockZoneClass = TJvDockZone;
  DefaultDockTreeClass: TJvDockTreeClass = TJvDockTree;

procedure ShowDockForm(DockWindow: TWinControl);
procedure HideDockForm(DockWindow: TWinControl);
procedure MakeDockClientEvent(Host: TControl; Visible: Boolean);
function GetFormVisible(DockWindow: TWinControl): Boolean;
procedure SetDockPageControlPopupMenu(Value: TPopupMenu);
procedure SetDockPageControlHotTrack(Value: Boolean);
procedure SetTabDockHostBorderStyle(Value: TFormBorderStyle);
procedure SetConjoinDockHostBorderStyle(Value: TFormBorderStyle);

{$IFDEF USEJVCL}
// Save Layout to JvAppStorage:
procedure SaveDockTreeToAppStorage(AppStorage: TJvCustomAppStorage; AppStoragePath: string = '');
// Load Layout from JvAppStorage:
procedure LoadDockTreeFromAppStorage(AppStorage: TJvCustomAppStorage; AppStoragePath: string = '');
{$ENDIF USEJVCL}

procedure SaveDockTreeToFile(FileName: string);
procedure LoadDockTreeFromFile(FileName: string);
procedure SaveDockTreeToReg(ARootKey: DWORD; RegPatch: string);
procedure LoadDockTreeFromReg(ARootKey: DWORD; RegPatch: string);

function FindDockBaseControl(Client: TControl): TJvDockBaseControl;
function FindDockClient(Client: TControl): TJvDockClient;
function FindDockServer(Client: TControl): TJvDockServer;

function IsDockable(Sender: TWinControl; Client: TControl;
  DropCtl: TControl = nil; DockAlign: TAlign = alNone): Boolean;
function ComputeDockingRect(AControl: TControl;
  var DockRect: TRect; MousePos: TPoint): TAlign;

{ Undocks AControl from Sender; Sender is used to calculate the rectangle in
  which AControl is displayed when it starts floating. }
procedure DoFloat(Sender, AControl: TControl);
procedure SetDockSite(Control: TWinControl; SiteValue: Boolean);
procedure DoFloatForm(DockForm: TControl);
procedure FreeAllDockableForm;
procedure DoFloatAllForm;

function GetClientAlignControlArea(AControl: TWinControl; Align: TAlign; Exclude: TControl = nil): Integer;
procedure ResetDockClient(Control: TControl; NewTarget: TControl); overload;
procedure ResetDockClient(DockClient: TJvDockClient; NewTarget: TControl); overload;

{ Quick way to do tabbed docking programmatically - Added by Warren }
function ManualTabDock(DockSite: TWinControl; Form1, Form2: TForm): TJvDockTabHostForm;
{ Must create the initial tab dock with two pages, using ManualTabDock,
  then you can add more pages with this:}
procedure ManualTabDockAddPage(TabHost: TJvDockTabHostForm; AForm: TForm);
function ManualConjoinDock(DockSite: TWinControl; Form1, Form2: TForm): TJvDockConjoinHostForm;

function DockStateStr(DockState: Integer): string; {return string for a dock state}

procedure BeginDockLoading;
procedure EndDockLoading;
function JvGlobalDockIsLoading: Boolean;

{$IFNDEF COMPILER9_UP}
procedure InvalidateDockHostSiteOfControl(Control: TControl; FocusLost: Boolean);
{$ENDIF !COMPILER9_UP}

{$IFDEF USEJVCL}
{$IFDEF UNITVERSIONING}
const
  UnitVersioning: TUnitVersionInfo = (
    RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_32/run/JvDockControlForm.pas $';
    Revision: '$Revision: 11276 $';
    Date: '$Date: 2007-04-24 21:33:37 +0200 (mar., 24 avr. 2007) $';
    LogPath: 'JVCL\run'
    );
{$ENDIF UNITVERSIONING}
{$ENDIF USEJVCL}

implementation

uses
  SysUtils,
  {$IFDEF USEJVCL}
  JvAppRegistryStorage, JvAppIniStorage, JvTypes,
  {$ELSE}
  IniFiles, Registry,
  {$ENDIF USEJVCL}
  JvDockSupportProc, JvDockGlobals, JvDockInfo, JvDockVSNetStyle;

{$R JvDockableForm.dfm}
{$R JvDockConjoinHost.dfm}
{$R JvDockTabHost.dfm}

type
  TControlAccessProtected = class(TControl);
  TWinControlAccessProtected = class(TWinControl);

  
{$IFNDEF USEJVCL}
const
  cDefaultFormName = '_JVFORM_';
  cDefaultNameSuffix = '_JVDOCK_';
{$ENDIF !USEJVCL}

var
  DockPageControlPopupMenu: TPopupMenu = nil;
  DockPageControlHotTrack: Boolean = False;
  TabDockHostBorderStyle: TFormBorderStyle = bsSizeToolWin;
  ConjoinDockHostBorderStyle: TFormBorderStyle = bsSizeToolWin;

  GDockLoadCount: Integer = 0;
  GShowingChanged: TList;

//=== Local procedures =======================================================

function IsWinXP_UP: Boolean;
begin
  Result := (Win32Platform = VER_PLATFORM_WIN32_NT) and
    ((Win32MajorVersion > 5) or
    (Win32MajorVersion = 5) and (Win32MinorVersion >= 1));
end;

procedure ApplyShowingChanged;
var
  I: Integer;
begin
  if IsWinXP_UP then
    for I := 0 to Screen.FormCount - 1 do
      if GShowingChanged.IndexOf(Screen.Forms[I]) >= 0 then
        Screen.Forms[i].Perform(CM_SHOWINGCHANGED, 0, 0);
  FreeAndNil(GShowingChanged);
end;

procedure UpdateCaption(Source: TWinControl; Exclude: TControl);
var
  I: Integer;
  Host: TJvDockableForm;
begin
  if (Source <> nil) and (Source.Parent is TJvDockableForm) then
  begin
    Host := TJvDockableForm(Source.Parent);
    Host.Caption := '';

    for I := 0 to Source.DockClientCount - 1 do
      if Source.DockClients[I].Visible and (Source.DockClients[I] <> Exclude) then
        Host.Caption := Host.Caption + TCustomForm(Source.DockClients[I]).Caption + RsDockStringSplitter;

    if Host.HostDockSite is TJvDockTabPageControl then
      with TJvDockTabPageControl(Host.HostDockSite) do
        if (ActivePage <> nil) and (ActivePage.Controls[0] = Source) then
          ActivePage.Caption := Host.Caption;
    UpdateCaption(Host.HostDockSite, nil);
  end;
end;

//=== Global procedures ======================================================

procedure BeginDockLoading;
begin
  if GDockLoadCount = 0 then
  begin
    JvDockLockWindow(nil);
  end;
  Inc(GDockLoadCount);
end;

procedure EndDockLoading;
begin
  Dec(GDockLoadCount);
  if GDockLoadCount = 0 then
  begin
    ApplyShowingChanged;
    JvDockUnLockWindow;
  end;
end;

function JvGlobalDockIsLoading: Boolean;
begin
  Result := GDockLoadCount > 0;
end;

{ ahuser: These functions are not call anywhere

function GetActiveControl(AForm: TCustomForm): TWinControl;
var
  AControl: TWinControl;
begin
  Result := nil;
  AControl := AForm.ActiveControl;
  while AControl <> nil do
  begin
    if AControl.HostDockSite <> nil then
    begin
      Result := AControl;
      Break;
    end;
    AControl := AControl.Parent;
  end;
end;

function GetHostDockParent(AControl: TWinControl): TWinControl;
begin
  Result := nil;
  while AControl <> nil do
  begin
    if AControl.HostDockSite <> nil then
    begin
      Result := AControl.HostDockSite;
      Break;
    end;
    AControl := AControl.Parent;
  end;
end;}

function ComputeDockingRect(AControl: TControl; var DockRect: TRect; MousePos: TPoint): TAlign;
var
  DockTopRect, DockLeftRect, DockBottomRect, DockRightRect, DockCenterRect: TRect;
begin
  Result := alNone;

  if AControl = nil then
    Exit;
  with AControl 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);

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

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

    if PtInRect(DockLeftRect, MousePos) then
    begin
      Result := alLeft;
      DockRect := DockLeftRect;
      DockRect.Right := ClientWidth div 2;
    end
    else
    if PtInRect(DockTopRect, MousePos) then
    begin
      Result := alTop;
      DockRect := DockTopRect;
      DockRect.Left := 0;
      DockRect.Right := ClientWidth;
      DockRect.Bottom := ClientHeight div 2;
    end
    else
    if PtInRect(DockRightRect, MousePos) then
    begin
      Result := alRight;
      DockRect := DockRightRect;
      DockRect.Left := ClientWidth div 2;
    end
    else
    if PtInRect(DockBottomRect, MousePos) then
    begin
      Result := alBottom;
      DockRect := DockBottomRect;
      DockRect.Left := 0;
      DockRect.Right := ClientWidth;
      DockRect.Top := ClientHeight div 2;
    end
    else
    if PtInRect(DockCenterRect, MousePos) then
    begin
      Result := alClient;
      DockRect := DockCenterRect;
    end;
    if Result = alNone then
      Exit;

    DockRect.TopLeft := ClientToScreen(DockRect.TopLeft);
    DockRect.BottomRight := ClientToScreen(DockRect.BottomRight);
  end;
end;

function DockStateStr(DockState: Integer): string;
begin
  // (rom) XML strings do not localize
  case DockState of
    JvDockState_Unknown:
      Result := 'Unknown';
    JvDockState_Docking:
      Result := 'Docking';
    JvDockState_Floating:
      Result := 'Floating';
  else
    Result := IntToStr(DockState);
  end;
end;

procedure DoFloat(Sender, AControl: TControl);
var
  ARect: TRect;
  CH, BW: Integer;
begin
  BW := JvDockGetSysBorderWidth;
  CH := JvDockGetSysCaptionHeight;

  ARect.TopLeft := Sender.ClientToScreen(Point(-(BW + 3), -(CH + BW + 1)));
  ARect.BottomRight := Sender.ClientToScreen(
    Point(Sender.UndockWidth - (BW + 3), Sender.UndockHeight - (BW + CH + 1)));
  AControl.ManualFloat(ARect);
  if (AControl.Left <> ARect.Left) or (AControl.Top <> ARect.Top) then
  begin
    AControl.Left := ARect.Left;
    AControl.Top := ARect.Top;
  end;
end;

procedure DoFloatAllForm;
var
  I: Integer;
  TempList: TList;
begin
  TempList := TList.Create;
  try
    for I := 0 to Screen.CustomFormCount - 1 do
      if not (Screen.CustomForms[I] is TJvDockableForm) and
        (Assigned(FinddockClient(Screen.CustomForms[I])) or
         Assigned(FinddockServer(Screen.CustomForms[I]))) then
        TempList.Add(Screen.CustomForms[I]);

    for I := 0 to TempList.Count - 1 do
      DoFloatForm(TempList[I]);
  finally
    TempList.Free;
  end;
  FreeAllDockableForm;
end;

procedure DoFloatForm(DockForm: TControl);
var
  I: TAlign;
  J: Integer;
  ADockServer: TJvDockServer;
  //  ARect: TRect;
  Channel: TJvDockVSChannel;
begin
  if DockForm is TJvDockableForm then
  begin
    with TJvDockableForm(DockForm).DockableControl do
    begin
      for J := DockClientCount - 1 downto 0 do
        DoFloatForm(DockClients[J]);

      DockForm.ManualDock(nil);
    end;
  end
  else
  begin
    ADockServer := FindDockServer(DockForm);
    if ADockServer <> nil then
    begin
      // (rom) better use a Count or introduce one
      // (p3) this is due to the fact that DockPanel returns a dockpanel based on the indices 0 to 3
      //  DockPanelWithAlign uses the TAlign enumeration, however
      for I := alTop to alRight do
        if Assigned(ADockServer.DockPanelWithAlign[I]) then
        begin
          for J := ADockServer.DockPanelWithAlign[I].DockClientCount - 1 downto 0 do
            DoFloatForm(ADockServer.DockPanelWithAlign[I].DockClients[J]);
          if ADockServer.DockPanelWithAlign[I] is TJvDockVSNETPanel then
            with TJvDockVSNETPanel(ADockServer.DockPanelWithAlign[I]).VSChannel do
            begin
              RemoveAllBlock;
              HidePopupPanel(ActiveDockForm);
            end;
        end;
    end
    else
    begin
      if DockForm.HostDockSite <> nil then
      begin
        if (DockForm.HostDockSite.Parent is TJvDockableForm) and
          (DockForm.HostDockSite.DockClientCount <= 2) then
          PostMessage(DockForm.HostDockSite.Parent.Handle, WM_CLOSE, 0, 0);
      end;
      //      else
      //        ARect := DockForm.BoundsRect;

      Channel := RetrieveChannel(DockForm.HostDockSite);
      if Assigned(Channel) then
      begin
        Channel.RemoveDockControl(TWinControl(DockForm));
        DockForm.Dock(nil, Bounds(DockForm.Left, DockForm.Top, DockForm.UndockWidth, DockForm.UndockHeight));
      end
      else
        DockForm.ManualDock(nil);
    end;
  end;
end;

function FindDockBaseControl(Client: TControl): TJvDockBaseControl;
var
  I: Integer;
begin
  Result := nil;
  if Client <> nil then
    for I := 0 to Client.ComponentCount - 1 do
      if Client.Components[I] is TJvDockBaseControl then
      begin
        Result := TJvDockBaseControl(Client.Components[I]);
        Break;
      end;
end;

function FindDockClient(Client: TControl): TJvDockClient;
var
  ADockControl: TJvDockBaseControl;
begin
  ADockControl := FindDockBaseControl(Client);
  if ADockControl is TJvDockClient then
    Result := TJvDockClient(ADockControl)
  else
    Result := nil;
end;

function FindDockServer(Client: TControl): TJvDockServer;
var
  ADockControl: TJvDockBaseControl;
begin
  ADockControl := FindDockBaseControl(Client);
  if ADockControl is TJvDockServer then
    Result := TJvDockServer(ADockControl)
  else
    Result := nil;
end;

procedure FreeAllDockableForm;
var
  I: Integer;
begin
  Assert(JvGlobalDockManager <> nil);
  for I := JvGlobalDockManager.DockableFormCount - 1 downto 0 do
    if JvGlobalDockManager.DockableForm[I].DockableControl.DockClientCount = 0 then
      JvGlobalDockManager.DockableForm[I].Free;
end;

function GetClientAlignControlArea(AControl: TWinControl; Align: TAlign; Exclude: TControl): Integer;
var
  I: Integer;
begin
  Result := 0;
  for I := 0 to AControl.ControlCount - 1 do
    if (AControl.Controls[I].Align = Align) and AControl.Controls[I].Visible and
      (AControl.Controls[I] <> Exclude) and
      not ((AControl.Controls[I] is TJvDockSplitter) or (AControl.Controls[I] is TJvDockPanel)) then
      if Align in [alLeft, alRight] then
        Inc(Result, AControl.Controls[I].Width)
      else
        Inc(Result, AControl.Controls[I].Height);
end;

function GetFormVisible(DockWindow: TWinControl): Boolean;
var
  ADockClient: TJvDockClient;
begin
  Result := True;
  ADockClient := FindDockClient(DockWindow);
  if ADockClient <> nil then
    Result := ADockClient.DockStyle.GetDockFormVisible(ADockClient);
end;

procedure HideDockForm(DockWindow: TWinControl);
var
  ADockClient: TJvDockClient;
begin
  // delegate to style
  ADockClient := FindDockClient(DockWindow);
  if Assigned(ADockClient) and Assigned(ADockClient.DockStyle) then
    ADockClient.DockStyle.DoHideDockForm(DockWindow);
end;

procedure TJvDockBasicStyle.DoHideDockForm(DockWindow: TWinControl);

  procedure HideDockChild(DockWindow: TWinControl);
  var
    I: Integer;
    DockClient: TJvDockClient;
  begin
    if DockWindow = nil then
      Exit;
    if (DockWindow is TJvDockableForm) and (DockWindow.Visible) then
      with TJvDockableForm(DockWindow).DockableControl do
        for I := 0 to DockClientCount - 1 do
          HideDockChild(TWinControl(DockClients[I]));
    DockClient := FindDockClient(DockWindow);
    if (DockWindow is TForm) and (TForm(DockWindow).FormStyle <> fsMDIChild) and
      (DockClient.DockStyle <> nil) then
      DockClient.DockStyle.HideDockForm(DockClient);
  end;

  procedure HideDockParent(DockWindow: TWinControl);
  var
    Host: TWinControl;
    DockClient: TJvDockClient;
  begin
    if (DockWindow <> nil) and (DockWindow.HostDockSite <> nil) then
    begin
      Host := DockWindow.HostDockSite;
      if Host.VisibleDockClientCount = 0 then
        if Host is TJvDockPanel then
          TJvDockPanel(Host).ShowDockPanel(False, nil)
        else
        begin
          if Host.Parent <> nil then
          begin
            DockClient := FindDockClient(Host.Parent);
            if (DockClient <> nil) and (DockClient.DockStyle <> nil) then
              DockClient.DockStyle.HideDockForm(DockClient);
            HideDockParent(Host.Parent);
          end;
        end;
    end;
  end;

begin
  HideDockChild(DockWindow);
  HideDockParent(DockWindow);
  if (DockWindow.HostDockSite is TJvDockCustomControl) then
    TJvDockCustomControl(DockWindow.HostDockSite).UpdateCaption(DockWindow);
end;

{$IFNDEF COMPILER9_UP}
procedure InvalidateDockHostSiteOfControl(Control: TControl; FocusLost: Boolean);
var
  ChildDockSite: TControl;
  ParentWalk: TWinControl;
begin
  { Invalidate the first dock site we come across; its ui may
    need updating to reflect which control has focus. }
  if Control = nil then
    Exit;
  ChildDockSite := Control;
  ParentWalk := Control.Parent;
  while (ChildDockSite.HostDockSite = nil) and (ParentWalk <> nil) do
  begin
    ChildDockSite := ParentWalk;
    ParentWalk := ParentWalk.Parent;
  end;
  if ChildDockSite <> nil then
    TWinControlAccessProtected(ChildDockSite).SendDockNotification(CM_INVALIDATEDOCKHOST,
      Integer(Control), Integer(FocusLost));
end;
{$ENDIF !COMPILER9_UP}

function IsDockable(Sender: TWinControl; Client: TControl; DropCtl: TControl = nil;
  DockAlign: TAlign = alNone): Boolean;
var
  I: Integer;
  ADockClient: TJvDockClient;
  SenderDockStyle, ClientDockStyle: TJvDockBasicStyle;
  SenderStyleName, ClientStyleName: string;
  SenderDocPanel: TJvDockPanel;
  //s:String;
  // (rom) disabled unused
  //label
  // JudgeRelation;
begin
  ADockClient := FindDockClient(Client);
  Result := False;
  if (ADockClient <> nil) and (ADockClient.EnableDock) then
  begin
    if Sender is TJvDockPanel then
    begin
      SenderDocPanel := TJvDockPanel(Sender);
      with SenderDocPanel do
      begin
        {$IFDEF JVDOCK_DEBUG}
        if CustomFlag and (not ADockClient.CustomDock) then
        begin
          OutputDebugString('IsDockable() - Debugging Dock-to-custom-panel problem.');
          ADockClient.CustomDock := True; // BUG WORKAROUND. WPostma.
        end;
        {$ENDIF JVDOCK_DEBUG}
        Result := DockServer.EnableDock and
          (((Align = alLeft) and DockServer.LeftDock and (ADockClient.LeftDock)) or
          ((Align = alTop) and DockServer.TopDock and (ADockClient.TopDock)) or
          ((Align = alRight) and DockServer.RightDock and (ADockClient.RightDock)) or
          ((Align = alBottom) and DockServer.BottomDock and (ADockClient.BottomDock)) or
          ((CustomFlag) and Assigned(DockServer.CustomDockPanel) and (ADockClient.CustomDock))
          );

          //{$IFDEF JVDOCK_DEBUG}
          // XXXXXX very noisy during drag operations:
          //if Result then begin
          //    if Assigned(DropCtl) then
          //        s := DropCtl.Name
          //   else
          //        s := 'nil';
          //    OutputDebugString(PChar( 'JvDockControlForm.pas IsDockable: '+Sender.Name+ ': Dockable. DropCtl='+s ));
          //end;
          //{$ENDIF JVDOCK_DEBUG}
        SenderDockStyle := DockServer.DockStyle;
      end;
    end
    else
    begin
      if (Sender <> nil) and (Sender.Parent is TJvDockableForm) then
        with TJvDockableForm(Sender.Parent).DockableControl do
          for I := 0 to DockClientCount - 1 do
            if DockClients[I] = Client then
              Exit;
      Result := ADockClient.EachOtherDock;
      if Sender <> nil then
        ADockClient := FindDockClient(Sender.Parent);
      if ADockClient <> nil then
        Result := Result and ADockClient.EachOtherDock;

      if ADockClient <> nil then
        SenderDockStyle := ADockClient.DockStyle
      else
        Exit;
    end;

    ADockClient := FindDockClient(Client);
    if ADockClient <> nil then
      ClientDockStyle := ADockClient.DockStyle
    else
      Exit;

    if SenderDockStyle = nil then
      SenderStyleName := ''
    else
      SenderStyleName := SenderDockStyle.ClassName;

    if ClientDockStyle = nil then
      ClientStyleName := ''
    else
      ClientStyleName := ClientDockStyle.ClassName;

    Result := Result and (SenderStyleName = ClientStyleName);

    //JudgeRelation:
  end;
end;

{$IFDEF USEJVCL}

procedure LoadDockTreeFromAppStorage(AppStorage: TJvCustomAppStorage; AppStoragePath: string = '');
var
  JvDockInfoTree: TJvDockInfoTree;
begin
  AppStorage.BeginUpdate;
  try
    HideAllPopupPanel(nil); {This is in JvDockVSNetStyle.pas }

    JvDockInfoTree := TJvDockInfoTree.Create(TJvDockInfoZone);
    try
      BeginDockLoading;
      try
        JvDockInfoTree.AppStorage := AppStorage;
        JvDockInfoTree.AppStoragePath := AppStoragePath;
        JvDockInfoTree.ReadInfoFromAppStorage;
      finally
        EndDockLoading;
      end;
    finally
      JvDockInfoTree.Free;
    end;
  finally
    AppStorage.EndUpdate;
  end;
end;

procedure LoadDockTreeFromFile(FileName: string);
var
  JvAppStorage: TJvAppIniFileStorage;
begin
  JvAppStorage := TJvAppIniFileStorage.Create(nil);
  try
    JvAppStorage.Location := flCustom;
    JvAppStorage.FileName := FileName;
    JvAppStorage.Reload;
    LoadDockTreeFromAppStorage(JvAppStorage);
  finally
    JvAppStorage.Free;
  end;
end;

procedure LoadDockTreeFromReg(ARootKey: DWORD; RegPatch: string);
var
  JvAppStorage: TJvAppRegistryStorage;
begin
  JvAppStorage := TJvAppRegistryStorage.Create(nil);
  try
    // (p3) this seems dangerous but it's the same method as used by TJvAppRegistryStorage
    JvAppStorage.RegRoot := TJvRegKey(HKEY_CLASSES_ROOT + ARootKey);
    JvAppStorage.Path := RegPatch;
    LoadDockTreeFromAppStorage(JvAppStorage);
  finally
    JvAppStorage.Free;
  end;
end;

{$ELSE}

procedure LoadDockTreeFromFile(FileName: string);
var
  JvDockInfoTree: TJvDockInfoTree;
  Form: TForm;
  MemFile: TMemIniFile;
begin
  HideAllPopupPanel(nil);

  { Creating a form with size 0 before locking the desktop supposedly prevents
    some screen flicker }
  Form := TForm.CreateNew(nil);
  Form.BorderStyle := bsNone;
  Form.BoundsRect := Rect(0, 0, 0, 0);
  Form.Visible := True;
  Form.Name := cDefaultFormName;

  JvDockInfoTree := TJvDockInfoTree.Create(TJvDockInfoZone);

  MemFile := TMemIniFile.Create(FileName);
  BeginDockLoading;
  try
    JvDockInfoTree.DockInfoIni := MemFile;
    JvGlobalDockIsLoading := True;
    JvDockInfoTree.ReadInfoFromIni;
  finally
    Form.Free;
    EndDockLoading;
    JvDockInfoTree.Free;
    MemFile.Free;
  end;
end;

procedure LoadDockTreeFromReg(ARootKey: DWORD; RegPatch: string);
var
  JvDockInfoTree: TJvDockInfoTree;
  Form: TForm;
begin
  HideAllPopupPanel(nil);

  { Creating a form with size 0 before locking the desktop supposedly prevents
    some screen flicker }
  Form := TForm.CreateNew(nil);
  Form.BorderStyle := bsNone;
  Form.BoundsRect := Rect(0, 0, 0, 0);
  Form.Visible := True;
  Form.Name := cDefaultFormName;

  JvDockInfoTree := TJvDockInfoTree.Create(TJvDockInfoZone);

  BeginDockLoading;
  try
    JvDockInfoTree.DockInfoReg := TRegistry.Create;
    try
      JvDockInfoTree.DockInfoReg.RootKey := ARootKey;
      JvDockInfoTree.ReadInfoFromReg(RegPatch);
    finally
      JvDockInfoTree.DockInfoReg.Free;
    end;
  finally
    EndDockLoading;
    JvDockInfoTree.Free;
    Form.Free;
  end;
end;

{$ENDIF USEJVCL}

procedure MakeDockClientEvent(Host: TControl; Visible: Boolean);
var
  I: Integer;
  DC: TJvDockClient;
begin
  DC := FindDockClient(Host);
  if DC <> nil then
  begin
    if Visible then
      DC.MakeShowEvent
    else
      DC.MakeHideEvent;
    if (Host is TJvDockableForm) and Host.Visible then
      with TJvDockableForm(Host).DockableControl do
        for I := 0 to DockClientCount - 1 do
          MakeDockClientEvent(DockClients[I], Visible);
  end;
end;

{ Quick way to do conjoined docking programmatically - Added by Warren }

function ManualConjoinDock(DockSite: TWinControl; Form1, Form2: TForm): TJvDockConjoinHostForm;
var
  ConjoinHost: TJvDockConjoinHostForm;
  DockClient1, DockCLient2: TJvDockClient;
begin
  Form1.Show;
  Form2.Show;
  DockClient1 := FindDockClient(Form1);
  Assert(Assigned(DockClient1));
  DockClient2 := FindDockClient(Form2);
  Assert(Assigned(DockClient2));
  ConjoinHost := DockClient1.CreateConjoinHostAndDockControl(DockClient1.ParentForm, Form2, alTop);
  ShowDockForm(Form2);
  ConjoinHost.ManualDock(DockSite);
  Result := ConjoinHost;
end;

{ Quick way to do tabbed docking programmatically - Added by Warren }

function ManualTabDock(DockSite: TWinControl; Form1, Form2: TForm): TJvDockTabHostForm;
var
  TabHost: TJvDockTabHostForm;
// wasVisible1,wasVisible2:Boolean;
  DockClient1, DockCLient2: TJvDockClient;
  ScreenPos: TRect;
begin
  //Form1.Show;
  //Form2.Show;

  DockClient1 := FindDockClient(Form1);
  //wasVisible1 := Form1.Visible;
  Form1.Hide;
  Assert(Assigned(DockClient1));
  if DockClient1.DockState = JvDockState_Docking then
  begin
    ScreenPos := Application.MainForm.ClientRect; // Just making it float temporarily.
    Form1.ManualFloat(ScreenPos);
  end;
  DockClient2 := FindDockClient(Form2);
  //wasVisible2 := Form2.Visible;
  Form2.Hide;

  Assert(Assigned(DockClient2));
  if DockClient2.DockState = JvDockState_Docking then
  begin
    ScreenPos := Application.MainForm.ClientRect; // Just making it float temporarily.
    Form2.ManualFloat(ScreenPos);
  end;

  TabHost := DockClient1.CreateTabHostAndDockControl(Form1, Form2);
    {DockClient1.ParentForm, DockClient2.ParentForm}
  TabHost.Hide;

  TabHost.ManualDock(DockSite);
  if not Form1.Visible then
    Form1.Show;
  if not Form2.Visible then
    Form2.Show;

  TabHost.Show;
  ShowDockForm(Form2);
  Result := TabHost;
end;

{ Must create the initial tab dock with two pages, using ManualTabDock,
  then you can add more pages with this:}

procedure ManualTabDockAddPage(TabHost: TJvDockTabHostForm; AForm: TForm);
begin
  Assert(Assigned(TabHost));
  Assert(Assigned(TabHost.PageControl));
  //AForm.Show;
  AForm.ManualDock(TabHost.PageControl);
  AForm.Show;
end;

procedure ResetDockClient(DockClient: TJvDockClient; NewTarget: TControl);
var
  Pt: TPoint;
begin
  if (DockClient <> nil) and not (csDestroying in DockClient.ParentForm.ComponentState) then
  begin
    if not ((DockClient.ParentForm.HostDockSite is TJvDockPanel) and (NewTarget is TJvDockPanel)) then
    begin
      if (DockClient.LastDockSite is TJvDockPanel) and (NewTarget is TJvDockPanel) and
        (DockClient.LastDockSite <> NewTarget) then
        with TJvDockPanel(DockClient.LastDockSite) do
          if UseDockManager and (JvDockManager <> nil) then
            JvDockManager.RemoveControl(DockClient.ParentForm);

      if DockClient.ParentForm.HostDockSite is TJvDockPanel then
        DockClient.LastDockSite := DockClient.ParentForm.HostDockSite
      else
        DockClient.LastDockSite := nil;

      if DockClient.ParentForm.HostDockSite = nil then
      begin
        DockClient.UnDockLeft := DockClient.ParentForm.BoundsRect.TopLeft.X;
        DockClient.UnDockTop := DockClient.ParentForm.BoundsRect.TopLeft.Y;
      end
      else
      begin
        Pt := DockClient.ParentForm.BoundsRect.TopLeft;
        Pt := DockClient.ParentForm.HostDockSite.ClientToScreen(Pt);
        DockClient.UnDockLeft := Pt.X;
        DockClient.UnDockTop := Pt.Y;
      end;
    end;
  end;
end;

procedure ResetDockClient(Control: TControl; NewTarget: TControl);
begin
  ResetDockClient(FindDockClient(Control), NewTarget);
end;


{$IFDEF USEJVCL}

 // Save Layout to JvAppStorage:
 //
 // Uses Global VCL object Screens to go through all forms, and save layout for
 // all the forms. Use any JvCustomAppStorage descendant to store to, such
 // as registry, ini files, etc.

procedure SaveDockTreeToAppStorage(AppStorage: TJvCustomAppStorage; AppStoragePath: string = '');
var
  JvDockInfoTree: TJvDockInfoTree;
  I: Integer;
begin
  AppStorage.BeginUpdate;
  try
    HideAllPopupPanel(nil); {This is in JvDockVSNetStyle.pas }
    JvDockInfoTree := TJvDockInfoTree.Create(TJvDockInfoZone);
    try
      for I := 0 to Screen.CustomFormCount - 1 do
        if (Screen.CustomForms[I].Parent = nil) and
          ((FindDockClient(Screen.CustomForms[I]) <> nil) or
           (FindDockServer(Screen.CustomForms[I]) <> nil)) then
        begin
          {$IFDEF JVDOCK_DEBUG}
          OutputDebugString(PChar('SaveDockTreeToAppStorage : Form ' + Screen.CustomForms[I].Name));
          {$ENDIF JVDOCK_DEBUG}
          JvDockInfoTree.CreateZoneAndAddInfoFromApp(Screen.CustomForms[I]);
        end;

      JvDockInfoTree.AppStorage := AppStorage;
      JvDockInfoTree.AppStoragePath := AppStoragePath;
      JvDockInfoTree.WriteInfoToAppStorage;
    finally
      JvDockInfoTree.Free;
    end;
  finally
    AppStorage.EndUpdate;
  end;
end;

procedure SaveDockTreeToFile(FileName: string);
var
  JvAppStorage: TJvAppIniFileStorage;
begin
  JvAppStorage := TJvAppIniFileStorage.Create(nil);
  try
    JvAppStorage.Location := flCustom;
    JvAppStorage.FileName := FileName;
    JvAppStorage.Reload;
    SaveDockTreeToAppStorage(JvAppStorage);
  finally
    JvAppStorage.Flush;
    JvAppStorage.Free;
  end;
end;

procedure SaveDockTreeToReg(ARootKey: DWORD; RegPatch: string);
var
  JvAppStorage: TJvAppRegistryStorage;
begin
  JvAppStorage := TJvAppRegistryStorage.Create(nil);
  try
    // (p3) this seems dangerous but it's the same method as used by TJvAppRegistryStorage
    JvAppStorage.RegRoot := TJvRegKey(HKEY_CLASSES_ROOT + ARootKey);
    JvAppStorage.Path := RegPatch;
    SaveDockTreeToAppStorage(JvAppStorage);
  finally
    JvAppStorage.Free;
  end;
end;

{$ELSE}

procedure SaveDockTreeToFile(FileName: string);
var
  JvDockInfoTree: TJvDockInfoTree;
  I: Integer;
  MemFile: TMemIniFile;
begin
  HideAllPopupPanel(nil);
  MemFile := TMemIniFile.Create(FileName);
  try
    JvDockInfoTree := TJvDockInfoTree.Create(TJvDockInfoZone);
    try
      for I := 0 to Screen.CustomFormCount - 1 do
        if (Screen.CustomForms[I].Parent = nil) and
          ((FindDockClient(Screen.CustomForms[I]) <> nil) or
           (FindDockServer(Screen.CustomForms[I]) <> nil)) then
          JvDockInfoTree.CreateZoneAndAddInfoFromApp(Screen.CustomForms[I]);

      JvDockInfoTree.DockInfoIni := MemFile;
      JvDockInfoTree.WriteInfoToIni;
      MemFile.UpdateFile;
    finally
      JvDockInfoTree.Free;
    end;
  finally
    MemFile.Free;
  end;
end;

procedure SaveDockTreeToReg(ARootKey: DWORD; RegPatch: string);
var
  I: Integer;
  JvDockInfoTree: TJvDockInfoTree;
begin
  HideAllPopupPanel(nil);

  JvDockInfoTree := TJvDockInfoTree.Create(TJvDockInfoZone);
  try
    for I := 0 to Screen.CustomFormCount - 1 do
      if (Screen.CustomForms[I].Parent = nil) and
        (FindDockClient(Screen.CustomForms[I]) <> nil) then
        JvDockInfoTree.CreateZoneAndAddInfoFromApp(Screen.CustomForms[I]);

    for I := 0 to Screen.CustomFormCount - 1 do
      if (Screen.CustomForms[I].Parent = nil) and
        (FindDockServer(Screen.CustomForms[I]) <> nil) then
        JvDockInfoTree.CreateZoneAndAddInfoFromApp(Screen.CustomForms[I]);

    JvDockInfoTree.DockInfoReg := TRegistry.Create;
    try
      JvDockInfoTree.DockInfoReg.RootKey := ARootKey;
      JvDockInfoTree.WriteInfoToReg(RegPatch);
    finally
      JvDockInfoTree.DockInfoReg.Free;
    end;
  finally
    JvDockInfoTree.Free;
  end;
end;

{$ENDIF USEJVCL}

procedure SetConjoinDockHostBorderStyle(Value: TFormBorderStyle);
var
  I: Integer;
begin
  ConjoinDockHostBorderStyle := Value;
  for I := 0 to Screen.FormCount - 1 do
    if (Screen.CustomForms[I] is TJvDockConjoinHostForm) and (Screen.CustomForms[I].HostDockSite = nil) then
      TJvDockConjoinHostForm(Screen.CustomForms[I]).BorderStyle := Value;
end;

procedure SetDockPageControlHotTrack(Value: Boolean);
var
  I: Integer;
begin
  DockPageControlHotTrack := Value;
  for I := 0 to Screen.FormCount - 1 do
    if Screen.CustomForms[I] is TJvDockTabHostForm then
      TJvDockTabHostForm(Screen.CustomForms[I]).PageControl.HotTrack := Value;
end;

procedure SetDockPageControlPopupMenu(Value: TPopupMenu);
var
  I: Integer;
begin
  DockPageControlPopupMenu := Value;
  for I := 0 to Screen.FormCount - 1 do
    if Screen.CustomForms[I] is TJvDockTabHostForm then
      TJvDockTabHostForm(Screen.CustomForms[I]).PageControl.PopupMenu := Value;
end;

procedure SetDockSite(Control: TWinControl; SiteValue: Boolean);
begin
  TWinControlAccessProtected(Control).DockSite := SiteValue;
  if (not (csDesigning in Control.ComponentState)) and (JvGlobalDockManager <> nil) then
    JvGlobalDockManager.RegisterDockSite(Control, SiteValue);
end;

procedure SetTabDockHostBorderStyle(Value: TFormBorderStyle);
var
  I: Integer;
begin
  TabDockHostBorderStyle := Value;
  for I := 0 to Screen.FormCount - 1 do
    if (Screen.CustomForms[I] is TJvDockTabHostForm) and (Screen.CustomForms[I].HostDockSite = nil) then
      TJvDockTabHostForm(Screen.CustomForms[I]).BorderStyle := Value;
end;

procedure ShowDockForm(DockWindow: TWinControl);
var
  ADockClient: TJvDockClient;
begin
  // delegate to style
  ADockClient := FindDockClient(DockWindow);
  if Assigned(ADockClient) and Assigned(ADockClient.DockStyle) then
    ADockClient.DockStyle.DoShowDockForm(DockWindow);
end;

procedure TJvDockBasicStyle.DoShowDockForm(DockWindow: TWinControl);

  procedure ShowClient(Client, DockParent: TWinControl);
  var
    ADockClient: TJvDockClient;
    ADockServer: TJvDockServer;
    I: Integer;
  begin
    if (DockParent is TJvDockableForm) and (Client <> nil) then
    begin
      with TJvDockableForm(DockParent).DockableControl do
        for I := 0 to DockClientCount - 1 do
          if DockClients[I] <> Client then
            MakeDockClientEvent(DockClients[I], True);
      if Client.HostDockSite is TJvDockCustomControl then
        TJvDockCustomControl(Client.HostDockSite).UpdateCaption(nil);
    end
    else
    begin
      ADockClient := FindDockClient(DockParent);
      if (ADockClient <> nil) and (ADockClient.DockStyle <> nil) then
      begin
        ADockClient.DockStyle.ShowDockForm(ADockClient);
        ADockServer := FindDockServer(GetParentForm(DockParent));
        if (not Assigned(ADockServer) or ADockServer.AutoFocusDockedForm) and
          DockParent.CanFocus then
          DockParent.SetFocus;
      end;
    end;
    if DockParent.Parent = nil then
      SetForegroundWindow(DockParent.Handle);
  end;

  function ShowDockPanel(Client: TWinControl): TWinControl;
  begin
    Result := Client;
    if Assigned(Client) and (Client.HostDockSite is TJvDockPanel) then
    begin
      ShowClient(nil, Client);
      TJvDockPanel(Client.HostDockSite).ShowDockPanel(True, Client, sdfDockPanel);
      Result := nil;
    end;
  end;

  function ShowTabDockHost(Client: TWinControl): TWinControl;
  var
    I: Integer;
  begin
    Result := Client;
    if Assigned(Client) and (Client.HostDockSite is TJvDockTabPageControl) then
    begin
      ShowClient(nil, Client);

      with TJvDockTabPageControl(Client.HostDockSite) do
        for I := 0 to Count - 1 do
          if Pages[I].Controls[0] = Client then
          begin
            Pages[I].Show;
            Break;
          end;
      if (Client.HostDockSite <> nil) and not (Client.HostDockSite is TJvDockPanel) then
      begin
        Result := Client.HostDockSite.Parent;
        ShowClient(Client, Result);
        if (Result <> nil) and (Result.HostDockSite is TJvDockTabPageControl) then
          { (rb) never called AFAICS }
          Result := ShowTabDockHost(Result)
        else
          ShowClient(nil, Result);
      end;
    end;
  end;

  function ShowConjoinDockHost(Client: TWinControl): TWinControl;
  begin
    Result := Client;
    if Assigned(Client) and Assigned(Client.HostDockSite) and not (Client.HostDockSite is TJvDockPanel) then
    begin
      ShowClient(nil, Client);
      if Client.HostDockSite.Parent <> nil then
      begin
        Result := Client.HostDockSite.Parent;
        ShowClient(Client, Result);
        if (Result <> nil) and (Result.HostDockSite is TJvDockConjoinPanel) then
          { (rb) never called AFAICS }
          Result := ShowConjoinDockHost(Result)
        else
          ShowClient(nil, Result);
      end;
    end;
  end;

begin
  repeat
    { Show single floating window }
    if Assigned(DockWindow) and (DockWindow.HostDockSite = nil) then
      ShowClient(nil, DockWindow);
    DockWindow := ShowTabDockHost(DockWindow);
    DockWindow := ShowConjoinDockHost(DockWindow);
    { Show docked window }
    DockWindow := ShowDockPanel(DockWindow);
  until (DockWindow = nil) or (DockWindow.Parent = nil);
end;

//=== { TJvDockableForm } ====================================================

constructor TJvDockableForm.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  DragKind := dkDock;
  FDockClient := TJvDockClient.Create(Self);
  JvGlobalDockManager.RegisterDockableForm(Self);
  FFloatingChild := nil;
  TBDockHeight := FDockClient.TBDockHeight;
  LRDockWidth := FDockClient.LRDockWidth;
end;

destructor TJvDockableForm.Destroy;
begin
  if JvGlobalDockManager <> nil then
    JvGlobalDockManager.UnRegisterDockableForm(Self);
  { Now handled in destroy of DockClient via TJvDockClient.SetLastDockSite }
  //if DockClient.LastDockSite is TJvDockPanel then
  //  if Assigned(TJvDockPanel(DockClient.LastDockSite).JvDockManager) then
  //    TJvDockPanel(DockClient.LastDockSite).JvDockManager.RemoveControl(Self);
  inherited Destroy;
  // (rom) better comment this
  FFloatingChild := nil;
end;

procedure TJvDockableForm.DoClose(var Action: TCloseAction);
var
  I: Integer;
begin
  if DockableControl.DockClientCount = 1 then
  begin
    FFloatingChild := DockableControl.DockClients[0];

    if HostDockSite <> nil then
      FFloatingChild.Visible := False;

    DoFloat(Self, DockableControl.DockClients[0]);
    Action := caFree;
  end
  else
  if DockableControl.DockClientCount = 0 then
    Action := caFree
  else
  begin
    Action := caHide;
    if (FUnDockControl <> nil) and (DockableControl.DockClientCount = 2) then
      for I := 0 to DockableControl.DockClientCount - 1 do
        if FUnDockControl = DockableControl.DockClients[I] then
        begin
          Action := caNone;
          Break;
        end;
  end;
  if (HostDockSite is TJvDockPanel) and (HostDockSite.VisibleDockClientCount = 1) and
    (FFloatingChild = nil) then
    TJvDockPanel(HostDockSite).ShowDockPanel(False, Self);

  inherited DoClose(Action);
  FUnDockControl := nil;
end;

function TJvDockableForm.GetDockableControl: TWinControl;
begin
  Result := FDockableControl;
end;

procedure TJvDockableForm.SetDockableControl(const Value: TWinControl);
begin
  FDockableControl := Value;
end;

procedure TJvDockableForm.SetUnDockControl(const Value: TControl);
begin
  FUnDockControl := Value;
end;

//=== { TJvDockAdvConjoinPanel } =============================================

procedure TJvDockAdvConjoinPanel.CMUnDockClient(var Msg: TCMUnDockClient);
begin
  inherited;
end;

procedure TJvDockAdvConjoinPanel.CustomDockDrop(Source: TJvDockDragDockObject;
  X, Y: Integer);
begin
  if not JvGlobalDockIsLoading then
    ResetDockClient(Source.Control, Source.TargetControl);
  inherited CustomDockDrop(Source, X, Y);
end;

function TJvDockAdvConjoinPanel.CustomUnDock(Source: TJvDockDragDockObject;
  NewTarget: TWinControl; Client: TControl): Boolean;
begin
  if not JvGlobalDockIsLoading then
    ResetDockClient(Source.Control, NewTarget);
  Result := inherited CustomUnDock(Source, NewTarget, Client);
end;

procedure TJvDockAdvConjoinPanel.DockDrop(Source: TDragDockObject;
  X, Y: Integer);
begin
  if not JvGlobalDockIsLoading then
    ResetDockClient(Source.Control, TControl(Source.DragTarget));
  inherited DockDrop(Source, X, Y);
end;

function TJvDockAdvConjoinPanel.DoUnDock(NewTarget: TWinControl;
  Client: TControl): Boolean;
begin
  if not JvGlobalDockIsLoading then
    ResetDockClient(Client, NewTarget);
  Result := inherited DoUnDock(NewTarget, Client);
end;

//=== { TJvDockAdvPanel } ====================================================

procedure TJvDockAdvPanel.CMUnDockClient(var Msg: TCMUnDockClient);
var
  DockClient: TJvDockClient;
begin
  if JvGlobalDockIsLoading then
    Exit;
  with Msg do
  begin
    Result := 0;
    if UseDockManager and (JvDockManager <> nil) then
    begin
      DockClient := FindDockClient(Client);
      if (NewTarget <> nil) or
        ((Client <> nil) and (csDestroying in Client.ComponentState)) then
      begin
        if DockClient <> nil then
          DockClient.LastDockSite := nil;
        JvDockManager.RemoveControl(Client);
      end
      else
      begin
        if DockClient <> nil then
          DockClient.LastDockSite := Self;
        JvDockManager.HideControl(Client);
      end;
    end;
  end;
end;

procedure TJvDockAdvPanel.CustomDockDrop(Source: TJvDockDragDockObject;
  X, Y: Integer);
begin
  if not JvGlobalDockIsLoading then
    ResetDockClient(Source.Control, Source.TargetControl);
  inherited CustomDockDrop(Source, X, Y);
end;

function TJvDockAdvPanel.CustomUnDock(Source: TJvDockDragDockObject;
  NewTarget: TWinControl; Client: TControl): Boolean;
begin
  if not JvGlobalDockIsLoading then
    ResetDockClient(Source.Control, NewTarget);
  Result := inherited CustomUnDock(Source, NewTarget, Client)
end;

procedure TJvDockAdvPanel.DockDrop(Source: TDragDockObject; X, Y: Integer);
begin
  if not JvGlobalDockIsLoading then
    ResetDockClient(Source.Control, TControl(Source.DragTarget));
  inherited DockDrop(Source, X, Y);
end;

function TJvDockAdvPanel.DoUnDock(NewTarget: TWinControl;
  Client: TControl): Boolean;
begin
  if not JvGlobalDockIsLoading then
    ResetDockClient(Client, NewTarget);
  Result := inherited DoUnDock(NewTarget, Client);
end;

// NEW! -WPostma.
// Calls FADVTree.QueryControls which finds forms containing JvDockClient
// objects that are directly or indirectly docked to this panel.

procedure TJvDockAdvPanel.GetDockedControls(WinControls: TList); //override;
begin
  { (rb) Same result could be get via iterating property TWinControl.DockClients }
  Assert(Assigned(WinControls));
  if Assigned(JvDockManager) then
  begin
    WinControls.Clear;
    {$IFDEF JVDOCK_QUERY}
    JvDockManager.ControlQuery({Docked to self}Self, WinControls);
    {$ENDIF JVDOCK_QUERY}
  end;
end;

//=== { TJvDockAdvStyle } ====================================================

function TJvDockAdvStyle.DockClientWindowProc(DockClient: TJvDockClient;
  var Msg: TMessage): Boolean;
begin
  if (DockClient <> nil) and (Msg.Msg = WM_NCLBUTTONDBLCLK) then
    if DockClient.CanFloat then
      DockClient.RestoreChild;
  Result := inherited DockClientWindowProc(DockClient, Msg);
end;

//=== { TJvDockAdvTabPageControl } ===========================================

destructor TJvDockAdvTabPageControl.Destroy;
//var
//  DockClient: TJvDockClient;
begin
  { Now handled in TJvDockClient.SetLastDockSite }
  //  { Parent is always nil? Self is maintained by TJvDockTabHostForm (=Parent),
  //    Self is destroyed only if TJvDockTabHostForm is destroyed (thus is nil at
  //    this point }
  //  DockClient := FindDockClient(Parent);
  //  if (DockClient <> nil) and (DockClient.LastDockSite is TJvDockPanel) then
  //    with TJvDockPanel(DockClient.LastDockSite) do
  //      if UseDockManager and (JvDockManager <> nil) then
  //        JvDockManager.RemoveControl(Self.Parent);
  inherited Destroy;
end;

procedure TJvDockAdvTabPageControl.CMUnDockClient(var Msg: TCMUnDockClient);
begin
  inherited;
end;

procedure TJvDockAdvTabPageControl.CustomDockDrop(Source: TJvDockDragDockObject;
  X, Y: Integer);
begin
  if not JvGlobalDockIsLoading then
    ResetDockClient(Source.Control, Source.TargetControl);
  inherited CustomDockDrop(Source, X, Y);
end;

function TJvDockAdvTabPageControl.CustomUnDock(Source: TJvDockDragDockObject;
  NewTarget: TWinControl; Client: TControl): Boolean;
begin
  if not JvGlobalDockIsLoading then
    ResetDockClient(Source.Control, NewTarget);
  Result := inherited CustomUnDock(Source, NewTarget, Client)
end;

procedure TJvDockAdvTabPageControl.DockDrop(Source: TDragDockObject;
  X, Y: Integer);
begin
  if not JvGlobalDockIsLoading then
    ResetDockClient(Source.Control, TControl(Source.DragTarget));
  inherited DockDrop(Source, X, Y);
end;

function TJvDockAdvTabPageControl.DoUnDock(NewTarget: TWinControl;
  Client: TControl): Boolean;
begin
  if not JvGlobalDockIsLoading then
    ResetDockClient(Client, NewTarget);
  Result := inherited DoUnDock(NewTarget, Client);
end;

//=== { TJvDockBaseControl } =================================================

constructor TJvDockBaseControl.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  { Dangerous/Dirty }
  FParentForm := TForm(AOwner);
  FEnableDock := True;
  FLeftDock := True;
  FTopDock := True;
  FCustomDock := True; {Allowed to dock in custom area!}
  FRightDock := True;
  FBottomDock := True;
  FEachOtherDock := True;
  FDockStyle := nil;
  if not (csDesigning in ComponentState) then
  begin
    FOldOnClose := FParentForm.OnClose;
    ParentForm.OnClose := DoFormOnClose;
    FOldOnCreate := FParentForm.OnCreate;
    ParentForm.OnCreate := DoFormOnCreate;
    FOldWindowProc := FParentForm.WindowProc;
    FParentForm.WindowProc := WindowProc;
  end;
end;

destructor TJvDockBaseControl.Destroy;
begin
  if not (csDesigning in ComponentState) then
  begin
    if Assigned(FOldWindowProc) then
      FParentForm.WindowProc := FOldWindowProc;
    FOldWindowProc := nil;
    { ?? FDockStyle is always a TJvDockBasicStyle }
    if Assigned(FDockStyle) {and not (FDockStyle is TJvDockBasicStyle)} then
      FDockStyle.SetDockBaseControl(False, Self);
  end;
  DockStyle := nil;
  inherited Destroy;
end;

procedure TJvDockBaseControl.AddDockStyle(ADockStyle: TJvDockBasicStyle);
begin
  { Notification }
end;

procedure TJvDockBaseControl.Assign(Source: TPersistent);
begin
  if Source is TJvDockBaseControl then
  begin
    EnableDock := TJvDockBaseControl(Source).EnableDock;
    LeftDock := TJvDockBaseControl(Source).LeftDock;
    TopDock := TJvDockBaseControl(Source).TopDock;
    RightDock := TJvDockBaseControl(Source).RightDock;
    BottomDock := TJvDockBaseControl(Source).BottomDock;
    CustomDock := TJvDockBaseControl(Source).CustomDock; {NEW!}
    EachOtherDock := TJvDockBaseControl(Source).EachOtherDock;
    DockStyle := TJvDockBaseControl(Source).DockStyle;
  end
  else
    inherited Assign(Source);
end;

function TJvDockBaseControl.CanSetBottomDocked: Boolean;
begin
  if DockStyle <> nil then
    Result := DockStyle.CanSetBottomDocked(Self)
  else
    Result := True;
end;

function TJvDockBaseControl.CanSetEachOtherDocked: Boolean;
begin
  if DockStyle <> nil then
    Result := DockStyle.CanSetEachOtherDocked(Self)
  else
    Result := True;
end;

function TJvDockBaseControl.CanSetEnableDocked: Boolean;
begin
  if DockStyle <> nil then
    Result := DockStyle.CanSetEnableDocked(Self)
  else
    Result := True;
end;

function TJvDockBaseControl.CanSetLeftDocked: Boolean;
begin
  if DockStyle <> nil then
    Result := DockStyle.CanSetLeftDocked(Self)
  else
    Result := True;
end;

function TJvDockBaseControl.CanSetRightDocked: Boolean;
begin
  if DockStyle <> nil then
    Result := DockStyle.CanSetRightDocked(Self)
  else
    Result := True;
end;

function TJvDockBaseControl.CanSetTopDocked: Boolean;
begin
  if DockStyle <> nil then
    Result := DockStyle.CanSetTopDocked(Self)
  else
    Result := True;
end;

procedure TJvDockBaseControl.DoFormOnClose(Sender: TObject;
  var Action: TCloseAction);
begin
  { Code is moved to AddDockStyle, RemoveDockStyle; if a user assigns OnClose,
    OnCreate handlers things would go wrong.
  }
  if Assigned(FOldOnClose) then
    FOldOnClose(Sender, Action);
end;

procedure TJvDockBaseControl.DoFormOnCreate(Sender: TObject);
begin
  { Code is moved to AddDockStyle, RemoveDockStyle; if a user assigns OnClose,
    OnCreate handlers things would go wrong.
  }
  if Assigned(FOldOnCreate) then
    FOldOnCreate(Sender);
end;

{$IFNDEF USEJVCL}
function TJvDockBaseControl.GetDockStyleVersion: string;
begin
  Result := RsDockManagerVersion;
end;
{$ENDIF !USEJVCL}

procedure TJvDockBaseControl.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if Operation = opRemove then
    if AComponent = FDockStyle then
      DockStyle := nil;
end;

procedure TJvDockBaseControl.RemoveDockStyle(ADockStyle: TJvDockBasicStyle);
begin
  { Notification }
end;

procedure TJvDockBaseControl.SetBottomDock(const Value: Boolean);
begin
  if CanSetBottomDocked then
    FBottomDock := Value;
end;

procedure TJvDockBaseControl.SetDockStyle(ADockStyle: TJvDockBasicStyle);
begin
  {$IFDEF JVDOCK_DEBUG}
  OutputDebugString('TJvDockBaseControl.SetDockStyle');
  {$ENDIF JVDOCK_DEBUG}
  if ADockStyle <> FDockStyle then
  begin
    ParentForm.DisableAlign;
    try
      if FDockStyle <> nil then
      begin
        FDockStyle.RemoveFreeNotification(Self);

        { Remove Self from the internal list of the dock style component }
        FDockStyle.RemoveDockBaseControl(Self);

        { Give the ancestors a change to respond }
        RemoveDockStyle(FDockStyle);
      end;

      FDockStyle := ADockStyle;

      if FDockStyle <> nil then
      begin
        { Let the style initialize the TJvDockClient/TJvDockServer }
        FDockStyle.SetDockBaseControl([csLoading, csDesigning] * ComponentState <> [], Self);

        { Add Self to the internal list of the dock style component }
        FDockStyle.AddDockBaseControl(Self);

        { Give the ancestors a change to respond }
        AddDockStyle(FDockStyle);

        FDockStyle.FreeNotification(Self);
      end;
    finally
      ParentForm.EnableAlign;
    end;
  end;
end;

procedure TJvDockBaseControl.SetEachOtherDock(const Value: Boolean);
begin
  if CanSetEachOtherDocked then
    FEachOtherDock := Value;
end;

procedure TJvDockBaseControl.SetEnableDock(const Value: Boolean);
begin
  if CanSetEnableDocked then
    FEnableDock := Value;
end;

procedure TJvDockBaseControl.SetLeftDock(const Value: Boolean);
begin
  if CanSetLeftDocked then
    FLeftDock := Value;
end;

procedure TJvDockBaseControl.SetParentComponent(Value: TComponent);
var
  DockBaseControl: TJvDockBaseControl;
begin
  DockBaseControl := FindDockBaseControl(ParentForm);
  if Assigned(DockBaseControl) and (DockBaseControl <> Self) then
    raise EInvalidOperation.CreateResFmt(@RsEDockCannotLayAnother, [DockBaseControl.ClassName, ClassName]);
  inherited SetParentComponent(Value);
end;

procedure TJvDockBaseControl.SetRightDock(const Value: Boolean);
begin
  if CanSetRightDocked then
    FRightDock := Value;
end;

procedure TJvDockBaseControl.SetTopDock(const Value: Boolean);
begin
  if CanSetTopDocked then
    FTopDock := Value;
end;

procedure TJvDockBaseControl.WindowProc(var Msg: TMessage);
begin
  if not (csDesigning in ComponentState) then
    if Assigned(FOldWindowProc) then
      FOldWindowProc(Msg);
end;

//=== { TJvDockBasicStyle } ==================================================

constructor TJvDockBasicStyle.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  DockPanelClass := DefaultDockPanelClass;
  DockSplitterClass := DefaultDockSplitterClass;
  ConjoinPanelClass := DefaultConjoinPanelClass;
  TabDockClass := DefaultTabDockClass;
  DockPanelTreeClass := DefaultDockTreeClass;
  DockPanelZoneClass := DefaultDockZoneClass;
  ConjoinPanelTreeClass := DefaultDockTreeClass;
  ConjoinPanelZoneClass := DefaultDockZoneClass;
  FDockBaseControls := TList.Create;
end;

destructor TJvDockBasicStyle.Destroy;
begin
  FDockBaseControls.Free;
  FreeServerOption;
  inherited Destroy;
end;

procedure TJvDockBasicStyle.AddDockBaseControl(ADockBaseControl: TJvDockBaseControl);
begin
  {$IFDEF JVDOCK_DEBUG}
  OutputDebugString('TJvDockBasicStyle.AddDockBaseControl');
  {$ENDIF JVDOCK_DEBUG}
  if ADockBaseControl = nil then
    Exit;
  FDockBaseControls.Add(ADockBaseControl);
end;

function TJvDockBasicStyle.CanSetBottomDocked(ADockBaseControl: TJvDockBaseControl): Boolean;
begin
  Result := True;
end;

function TJvDockBasicStyle.CanSetEachOtherDocked(ADockBaseControl: TJvDockBaseControl): Boolean;
begin
  Result := True;
end;

function TJvDockBasicStyle.CanSetEnableDocked(ADockBaseControl: TJvDockBaseControl): Boolean;
begin
  Result := True;
end;

function TJvDockBasicStyle.CanSetLeftDocked(ADockBaseControl: TJvDockBaseControl): Boolean;
begin
  Result := True;
end;

function TJvDockBasicStyle.CanSetRightDocked(ADockBaseControl: TJvDockBaseControl): Boolean;
begin
  Result := True;
end;

function TJvDockBasicStyle.CanSetTopDocked(ADockBaseControl: TJvDockBaseControl): Boolean;
begin
  Result := True;
end;

function TJvDockBasicStyle.DockClientWindowProc(DockClient: TJvDockClient; var Msg: TMessage): Boolean;
begin
  Result := False;
end;

function TJvDockBasicStyle.DockServerWindowProc(DockServer: TJvDockServer;
  var Msg: TMessage): Boolean;
begin
  Result := False;
end;

procedure TJvDockBasicStyle.FormDockDrop(DockClient: TJvDockClient;
  Source: TJvDockDragDockObject; X, Y: Integer);
var
  ARect, DRect: TRect;
  DockType: TAlign;
  Host: TCustomForm;
  APanelDock: TWinControl;
begin
  if IsDockable(DockClient.ParentForm, Source.Control, Source.DropOnControl, Source.DropAlign) then
  begin
    Host := nil;

    if not JvGlobalDockIsLoading then
      JvDockLockWindow(nil);
    try
      with DockClient do
      begin
        DockType := ComputeDockingRect(DockClient.ParentForm, ARect, Point(X, Y));

        if ParentForm.HostDockSite is TJvDockPanel then
        begin
          if DockType = alClient then
          begin
            if Source.Control is TJvDockTabHostForm then
            begin
              APanelDock := ParentForm.HostDockSite;
              ARect := ParentForm.BoundsRect;
              ParentForm.ManualDock(TJvDockTabHostForm(Source.Control).PageControl, nil, alClient);
              TJvDockTabHostForm(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
              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.ManualDock(APanelDock, nil, alClient);
              Host.Visible := True;
            end;
          end
          else
          begin
            DRect := ParentForm.HostDockSite.BoundsRect;
            Source.Control.ManualDock(ParentForm.HostDockSite, nil, DockType);
            ParentForm.HostDockSite.BoundsRect := DRect;
          end;
          Exit;
        end;

        if DockType = alClient then
        begin
          Host := CreateTabHostAndDockControl(ParentForm, Source.Control);
          SetDockSite(ParentForm, False);
          SetDockSite(TWinControl(Source.Control), False);
          Host.Visible := True;
        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
      if not JvGlobalDockIsLoading then
        JvDockUnLockWindow;
    end;
  end;
end;

procedure TJvDockBasicStyle.FormDockOver(DockClient: TJvDockClient;
  Source: TJvDockDragDockObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
var
  ARect: TRect;
begin
  with DockClient do
  begin
    Accept := EnableDock and EachOtherDock and
      IsDockable(ParentForm.HostDockSite, Source.Control, Source.DropOnControl, Source.DropAlign);
    if Accept and (State = dsDragMove) and
      (ComputeDockingRect(ParentForm, ARect, Point(X, Y)) <> alNone) then
      Source.DockRect := ARect;
  end;
end;

procedure TJvDockBasicStyle.FormEndDock(DockClient: TJvDockClient;
  Target: TObject; X, Y: Integer);
begin
end;

procedure TJvDockBasicStyle.FormGetDockEdge(DockClient: TJvDockClient;
  Source: TJvDockDragDockObject; MousePos: TPoint; var DropAlign: TAlign);
begin
  DropAlign := TControlAccessProtected(DockClient.ParentForm).GetDockEdge(MousePos);
end;

procedure TJvDockBasicStyle.FormGetSiteInfo(Source: TJvDockDragDockObject;
  DockClient: TJvDockClient; Client: TControl; var InfluenceRect: TRect;
  MousePos: TPoint; var CanDock: Boolean);
begin
  with DockClient do
    CanDock := EnableDock and EachOtherDock and
      IsDockable(ParentForm, Client, Source.DropOnControl, Source.DropAlign);
end;

procedure TJvDockBasicStyle.FormPositionDockRect(DockClient: TJvDockClient;
  Source: TJvDockDragDockObject);
var
  NewWidth, NewHeight: Integer;
  TempX, TempY: Double;
  R: TRect;
begin
  with Source do
  begin
    if (DragTarget = nil) or (not TWinControlAccessProtected(DragTarget).UseDockManager) then
    begin
      NewWidth := Control.UndockWidth;
      NewHeight := Control.UndockHeight;

      TempX := DragPos.X - ((NewWidth) * MouseDeltaX);
      TempY := DragPos.Y - ((NewHeight) * MouseDeltaY);
      with DockRect do
      begin
        Left := Round(TempX);
        Top := Round(TempY);
        Right := Left + NewWidth;
        Bottom := Top + NewHeight;
      end;

      AdjustDockRect(DockRect);
    end
    else
    begin
      GetWindowRect(TargetControl.Handle, R);
      DockRect := R;
      if TWinControlAccessProtected(DragTarget).UseDockManager then
        if TargetControl is TJvDockCustomPanel then
          if (TJvDockCustomPanel(DragTarget).JvDockManager <> nil) then
          begin
            R := DockRect;
            TJvDockCustomPanel(DragTarget).JvDockManager.PositionDockRect(Control,
              DropOnControl, DropAlign, R);
            DockRect := R;
          end;
    end;
  end;
end;

procedure TJvDockBasicStyle.FormStartDock(DockClient: TJvDockClient;
  var Source: TJvDockDragDockObject);
begin
end;

function TJvDockBasicStyle.FormUnDock(DockClient: TJvDockClient; NewTarget: TWinControl;
  Client: TControl): Boolean;
begin
  Result := True;
end;

{$IFNDEF USEJVCL}
function TJvDockBasicStyle.GetControlName: string;
begin
  Result := RsDockStyleName;
end;
{$ENDIF !USEJVCL}

function TJvDockBasicStyle.GetDockBaseControlCount: Integer;
begin
  Result := FDockBaseControls.Count;
end;

function TJvDockBasicStyle.GetDockBaseControl(Index: Integer): TJvDockBaseControl;
begin
  Result := TJvDockBaseControl(FDockBaseControls[Index]);
end;

function TJvDockBasicStyle.GetDockFormVisible(ADockClient: TJvDockClient): Boolean;
begin
  Result := True;
  if ADockClient <> nil then
  begin
    if ADockClient.ParentForm.Visible then
    begin
      if ADockClient.ParentForm.HostDockSite <> nil then
      begin
        if ADockClient.ParentForm.HostDockSite is TJvDockPanel then
          Result := ADockClient.ParentForm.HostDockSite.Width * ADockClient.ParentForm.HostDockSite.Height > 0
        else
          Result := GetFormVisible(ADockClient.ParentForm.HostDockSite.Parent);
      end;
    end
    else
      Result := False;
  end;
end;

function TJvDockBasicStyle.GetDockState(DockClient: TJvDockClient): Integer;
begin
  Result := JvDockState_Unknown;
  if (DockClient <> nil) and (DockClient.ParentForm <> nil) then
    if DockClient.ParentForm.Floating then
      Result := JvDockState_Floating
    else
      Result := JvDockState_Docking;
end;

{$IFNDEF USEJVCL}
function TJvDockBasicStyle.GetDockStyleVersion: string;
begin
  Result := RsDockStyleVersion;
end;
{$ENDIF !USEJVCL}

procedure TJvDockBasicStyle.HideDockForm(ADockClient: TJvDockClient);
begin
  if ADockClient <> nil then
  begin
    ADockClient.ParentForm.Visible := False;
    ADockClient.MakeHideEvent;
  end;
end;

procedure TJvDockBasicStyle.RemoveDockBaseControl(ADockBaseControl: TJvDockBaseControl);
begin
  FDockBaseControls.Remove(ADockBaseControl);
end;

procedure TJvDockBasicStyle.ResetCursor(Source: TJvDockDragDockObject);
begin
  if (Source.TargetControl = nil) and (Source.Control <> nil) and (Source.Control.Floating) then
    Windows.SetCursor(Screen.Cursors[crDefault])
  else
  if (Source.TargetControl = nil) and (not JvGlobalDockClient.CanFloat) then
    Windows.SetCursor(Screen.Cursors[crNo])
  else
    Windows.SetCursor(Screen.Cursors[crDefault]);
end;

procedure TJvDockBasicStyle.RestoreClient(DockClient: TJvDockClient);
var
  TmpLastDockSite: TWinControl;
  TmpUnDockLeft, TmpUnDockTop: Integer;
  I: Integer;
  ADockClient: TJvDockClient;
  ADockServer: TJvDockServer;
  ARect: TRect;

  procedure DoFloatParentForm;
  begin
    with DockClient do
      if (ParentForm.HostDockSite <> nil) then
      begin
        ARect := Bounds(TmpUnDockLeft, TmpUnDockTop, ParentForm.UndockWidth, ParentForm.UndockHeight);
        ParentForm.ManualFloat(ARect);
        if (ParentForm.Left <> ARect.Left) or (ParentForm.Top <> ARect.Top) then
        begin
          ParentForm.Left := ARect.Left;
          ParentForm.Top := ARect.Top;
        end;
      end;
  end;

begin
  if DockClient = nil then
    Exit;
  if not DockClient.CanFloat then
    Exit;
  with DockClient do
  begin
    if not EnableDock then
      Exit;
    if LastDockSite is TJvDockPanel then
    begin
      with TJvDockPanel(LastDockSite) do
      begin
        if ((not LeftDock) and (Align = alLeft)) or
          ((not RightDock) and (Align = alRight)) or
          ((not TopDock) and (Align = alTop)) or
          ((not BottomDock) and (Align = alBottom)) then
        begin
          DoFloatParentForm;
          Exit;
        end;

        ADockServer := DockServer;
        if ADockServer <> nil then
          if (not ADockServer.EnableDock) or
            ((not ADockServer.LeftDock) and (Align = alLeft)) or
            ((not ADockServer.RightDock) and (Align = alRight)) or
            ((not ADockServer.TopDock) and (Align = alTop)) or
            ((not ADockServer.BottomDock) and (Align = alBottom)) then
          begin
            DoFloatParentForm;
            Exit;
          end;
      end;
    end;

    if ParentForm is TJvDockConjoinHostForm then
    begin
      with TJvDockConjoinHostForm(ParentForm).Panel do
        for I := DockClientCount - 1 downto 0 do
        begin
          ADockClient := FindDockClient(DockClients[I]);
          if (ADockClient <> nil) and (ADockClient.LastDockSite is TJvDockPanel) then
            ADockClient.RestoreChild;
        end;
      Exit;
    end;

    TmpLastDockSite := LastDockSite;
    TmpUnDockLeft := UnDockLeft;
    TmpUnDockTop := UnDockTop;

    ResetDockClient(DockClient, nil);

    DoFloatParentForm;

    if TmpLastDockSite is TJvDockPanel then
    begin
      with TJvDockPanel(TmpLastDockSite) do
      begin
        if UseDockManager and (JvDockManager <> nil) then
        begin
          if not JvDockManager.HasZoneWithControl(ParentForm) then
            Exit;
          DisableAlign;
          try
            ParentForm.Dock(TmpLastDockSite, Rect(0, 0, 0, 0));

            JvDockManager.ShowControl(ParentForm);

            ParentForm.ActiveControl := nil;
            SetDockSite(ParentForm, False);

            if ParentForm.Visible and ParentForm.CanFocus then
              ParentForm.SetFocus;
            ShowDockPanel(True, ParentForm, sdfDockPanel);
          finally
            EnableAlign;
          end;
        end;
      end;
    end;
  end;
end;

procedure TJvDockBasicStyle.SetDockBaseControl(IsCreate: Boolean;
  DockBaseControl: TJvDockBaseControl);
begin
end;

procedure TJvDockBasicStyle.ShowDockForm(ADockClient: TJvDockClient);
begin
  if ADockClient <> nil then
  begin
    ADockClient.ParentForm.Visible := True;
    ADockClient.MakeShowEvent;
  end;
end;

//=== { TJvDockClient } ======================================================

constructor TJvDockClient.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FParentVisible := ParentForm.Visible;
  ParentForm.DragKind := dkDock;
  ParentForm.DragMode := dmAutomatic;
  ParentForm.UseDockManager := False;
  if not (ParentForm is TJvDockableForm) then
    SetDockSite(ParentForm, True);
  LRDockWidth := 100;
  TBDockHeight := 100;
  if JvGlobalDockClient = nil then
    JvGlobalDockClient := Self;
  FDirectDrag := False;
  FShowHint := True;
  FCanFloat := True;
  FDockLevel := 0;
  EnableCloseButton := True;
end;

destructor TJvDockClient.Destroy;
begin
  if not (ParentForm is TJvDockableForm) then
    SetDockSite(ParentForm, False);
  ParentForm.DragKind := dkDrag;
  ParentForm.DragMode := dmManual;
  LastDockSite := nil;
  inherited Destroy;
end;

procedure TJvDockClient.Activate;
begin
  {$IFNDEF COMPILER9_UP}
  if ParentForm.HostDockSite is TJvDockCustomPanel then
    InvalidateDockHostSiteOfControl(ParentForm, False);
  {$ENDIF !COMPILER9_UP}
end;

procedure TJvDockClient.AddDockStyle(ADockStyle: TJvDockBasicStyle);
begin
  JvGlobalDockManager.RegisterDockClient(Self);

  if Assigned(ADockStyle) and Assigned(ADockStyle.ConjoinPanelClass) then
    FConjoinPanelClass := ADockStyle.ConjoinPanelClass
  else
    FConjoinPanelClass := DefaultConjoinPanelClass;

  if Assigned(ADockStyle) and Assigned(ADockStyle.TabDockClass) then
    FTabDockClass := ADockStyle.TabDockClass
  else
    FTabDockClass := DefaultTabDockClass;
end;

procedure TJvDockClient.Assign(Source: TPersistent);
begin
  if Source is TJvDockClient then
  begin
    FConjoinPanelClass := TJvDockClient(Source).FConjoinPanelClass;
    FTabDockClass := TJvDockClient(Source).FTabDockClass;
    ParentVisible := TJvDockClient(Source).ParentVisible;
    NCPopupMenu := TJvDockClient(Source).NCPopupMenu;
    DirectDrag := TJvDockClient(Source).DirectDrag;
    ShowHint := TJvDockClient(Source).ShowHint;
    CanFloat := TJvDockClient(Source).CanFloat;
    // (rom) either bug or needs comment
    FDockLevel := TJvDockClient(Source).DockLevel;
    CustomDock := TJvDockClient(Source).CustomDock; {NEW!}
    DockStyle := TJvDockClient(Source).DockStyle;
  end;
  inherited Assign(Source); // Also copy base class properties!
end;

procedure TJvDockClient.CMVisibleChanged(var Msg: TMessage);
begin
end;

function TJvDockClient.CreateConjoinHostAndDockControl(Control1, Control2: TControl;
  DockType: TAlign): TJvDockConjoinHostForm;
var
  APanel: TJvDockConjoinPanel;
  OldDockWidth, OldDockHeight: Integer;
begin
  Result := TJvDockConjoinHostForm.Create(Application);

  { CreateConjoinPanelClass implicitly sets Result.DockClient.DockStyle via the
    assign in that function }
  APanel := CreateConjoinPanelClass(Result);

  Result.BoundsRect := Control1.BoundsRect;

  Result.Width := Control1.UndockWidth;
  Result.Height := Control1.UndockHeight;

  OldDockWidth := Control1.LRDockWidth;
  OldDockHeight := Control1.TBDockHeight;
  Control1.ManualDock(APanel, nil, alNone);
  Control1.LRDockWidth := OldDockWidth;
  Control1.TBDockHeight := OldDockHeight;

  OldDockWidth := Control2.LRDockWidth;
  OldDockHeight := Control2.TBDockHeight;
  Control2.ManualDock(APanel, nil, DockType);
  Control2.LRDockWidth := OldDockWidth;
  Control2.TBDockHeight := OldDockHeight;

  SetDockSite(Result, False);
end;

function TJvDockClient.CreateConjoinPanelClass(ConjoinHost: TForm): TJvDockConjoinPanel;
begin
  Result := nil;
  TJvDockConjoinHostForm(ConjoinHost).DockClient.Assign(Self);
  if (FConjoinPanelClass <> nil) and
    (FConjoinPanelClass <> TJvDockConjoinPanelClass(ClassType)) then
  begin
    Result := FConjoinPanelClass.Create(ConjoinHost);
    Result.Align := alClient;
    TJvDockConjoinHostForm(ConjoinHost).DockableControl := Result;
    TJvDockConjoinHostForm(ConjoinHost).Panel := Result;
    SetDockSite(Result, True);
  end;
end;

{ CreateTabDockClass creates a TJvDockTabPageControl subclass. The actual subclass
  used is stored in  FTabDockClass which is assigned by the Docking Style object,
  so this is truly polymorphic.

  We need a TJvDockTabPageControl to do tabbed-docking inside a container form.
  The typical container this is placed inside of is either a
  TJvDockConjoinHostForm or TJvDockTabHostForm.
}

function TJvDockClient.CreateTabDockClass(TabHost: TForm): TJvDockTabPageControl;
begin
  Result := nil;
  { copy properties of the JvTabDockPageControl's DockClient
    from the form it contains. }
  TJvDockTabHostForm(TabHost).DockClient.Assign(Self);

// Next line commented out, it seems it is useless. See Mantis 3900.
//  Assert(TJvDockTabHostForm(TabHost).DockClient.CustomDock, 'DEBUG HELPER: Not working!');

  if (FTabDockClass <> nil) and (FTabDockClass <> TJvDockTabClass(ClassType)) then
  begin
    Result := FTabDockClass.Create(TabHost);
    Result.Align := alClient;
    TJvDockTabHostForm(TabHost).DockableControl := Result;
    TJvDockTabHostForm(TabHost).PageControl := Result;
    SetDockSite(Result, True);
  end;
end;

{ This is the main function for creating a tabbed dock, given two non-tab-docked
 controls (usually these are actually Forms), we create a TJvDockTabHostForm,
 and then inside it we create a TabDock container.  A TJvDockTabHostForm is
 an empty TForm containing a DockClient object, and then it contains a set of
 tabbed pages inside the TJvDockTabPageControl object (Page). }

function TJvDockClient.CreateTabHostAndDockControl(Control1, Control2: TControl): TJvDockTabHostForm;
var
  Page: TJvDockTabPageControl;
  OldDockWidth, OldDockHeight: Integer;
begin
  Result := TJvDockTabHostForm.Create(Application);
  Result.Name := 'TJvDockTabHostForm_' + Control1.Name + '_' + Control2.Name + '_' +
    IntToHex(Integer(Result), 8);

  { CreateTabDockClass implicitly sets Result.DockClient.DockStyle via the
    assign in that function }
  Page := CreateTabDockClass(Result);
  Page.Name := Page.ClassName + '_' + Control1.Name + '_' + Control2.Name; // debug!

  Result.BoundsRect := Control1.BoundsRect;

  Result.Width := Control1.UndockWidth;
  Result.Height := Control1.UndockHeight;

  OldDockWidth := Control1.LRDockWidth;
  OldDockHeight := Control1.TBDockHeight;
  Control1.ManualDock(Page, nil, alClient);
  Control1.LRDockWidth := OldDockWidth;
  Control1.TBDockHeight := OldDockHeight;

  OldDockWidth := Control2.LRDockWidth;
  OldDockHeight := Control2.TBDockHeight;
  Control2.ManualDock(Page, nil, alClient);
  Control2.LRDockWidth := OldDockWidth;
  Control2.TBDockHeight := OldDockHeight;

  SetDockSite(Result, False);
  //TJvDockTabHostFormCreatedEvent:
  if Assigned(FOnTabHostFormCreated) then
    FOnTabHostFormCreated(Self, {TabHost:TJvDockTabHostForm} Result);
end;

procedure TJvDockClient.Deactivate;
begin
  {$IFNDEF COMPILER9_UP}
  if ParentForm.HostDockSite is TJvDockCustomPanel then
    InvalidateDockHostSiteOfControl(ParentForm, True);
  {$ENDIF !COMPILER9_UP}
end;

procedure TJvDockClient.DoFloatDockClients(PanelAlign: TAlign);
begin
  if not (csDestroying in ParentForm.ComponentState) and
    (ParentForm.HostDockSite is TJvDockPanel) and
    (PanelAlign = ParentForm.HostDockSite.Align) then
    RestoreChild;
end;

procedure TJvDockClient.DoFloatDockEachOther;
begin
  if not (csDestroying in ParentForm.ComponentState) and
    (ParentForm.HostDockSite <> nil) and
    (ParentForm.HostDockSite.Parent is TJvDockableForm) then
    RestoreChild;
end;

procedure TJvDockClient.DoFormOnClose(Sender: TObject; var Action: TCloseAction);
begin
  if Action = caHide then
  begin
    HideDockForm(ParentForm);
    FParentVisible := True;
  end;
  inherited DoFormOnClose(Sender, Action);
end;

procedure TJvDockClient.DoFormShowHint(HTFlag: Integer; var HintStr: string;
  var CanShow: Boolean);
begin
  if Assigned(FOnFormShowHint) then
    FOnFormShowHint(HTFlag, HintStr, CanShow);
end;

procedure TJvDockClient.DoMenuPopup(X, Y: Integer);
begin
  if FNCPopupMenu <> nil then
  begin
    FNCPopupMenu.PopupComponent := ParentForm;
    FNCPopupMenu.Popup(X, Y);
  end;
end;

procedure TJvDockClient.DoNCButtonDblClk(Msg: TWMNCHitMessage; Button: TMouseButton;
  MouseStation: TJvDockMouseStation);
begin
  if Assigned(FOnNCButtonDblClk) then
    FOnNCButtonDblClk(Self, Button, Msg.XCursor, Msg.YCursor,
      Msg.HitTest, MouseStation);
end;

procedure TJvDockClient.DoNCButtonDown(Msg: TWMNCHitMessage;
  Button: TMouseButton; MouseStation: TJvDockMouseStation);
begin
  if Assigned(FOnNCButtonDown) then
    FOnNCButtonDown(Self, Button, Msg.XCursor, Msg.YCursor,
      Msg.HitTest, MouseStation);
end;

procedure TJvDockClient.DoNCButtonUp(Msg: TWMNCHitMessage;
  Button: TMouseButton; MouseStation: TJvDockMouseStation);
begin
  if Assigned(FOnNCButtonUp) then
    FOnNCButtonUp(Self, Button, Msg.XCursor, Msg.YCursor,
      Msg.HitTest, MouseStation);
  if Button = mbRight then
    DoMenuPopup(Msg.XCursor, Msg.YCursor);
end;

procedure TJvDockClient.DoNCMouseMove(Msg: TWMNCHitMessage;
  MouseStation: TJvDockMouseStation);
begin
  if Assigned(FOnNCMouseMove) then
    FOnNCMouseMove(Self, Msg.XCursor, Msg.YCursor,
      Msg.HitTest, MouseStation);
end;

procedure TJvDockClient.DoPaintDockGrabber(Canvas: TCanvas;
  Control: TControl; const ARect: TRect);
begin
  if Assigned(FOnPaintDockGrabber) then
    FOnPaintDockGrabber(Canvas, Control, ARect);
end;

procedure TJvDockClient.DoPaintDockSplitter(Canvas: TCanvas;
  Control: TControl; const ARect: TRect);
begin
  if Assigned(FOnPaintDockSplitter) then
    FOnPaintDockSplitter(Canvas, Control, ARect);
end;

// return nil if not found, otherwise, get currently docked parent tabhost form if there is one.

function TJvDockClient.FindTabHostForm: TForm;
var
  OwnerWin: TWinControl;
  TabSheet: TJvDockTabSheet;
  CheckForm: TComponent;
begin
  Result := nil;
  if not Assigned(Owner) then
    Exit;
  if not (Owner is TWinControl) then
    Exit;
  OwnerWin := TWinControl(Owner);
  if Self.DockState <> JvDockState_Docking then
    Exit;
  if not Assigned(OwnerWin.Parent) then
    Exit;
  if OwnerWin.Parent is TJvDockTabSheet then
  begin
    TabSheet := TJvDockTabSheet(OwnerWin.Parent);
    if TabSheet.Owner is TJvDockPageControl then
    begin
      CheckForm := TJvDockPageControl(TabSheet.Owner).Owner;
      if CheckForm is TJvDockTabHostForm then
        Result := TForm(CheckForm);
    end;
  end;
end;

procedure TJvDockClient.FormDockDrop(Source: TJvDockDragDockObject;
  X, Y: Integer);
begin
  if Assigned(DockStyle) then
    DockStyle.FormDockDrop(Self, Source, X, Y);
end;

procedure TJvDockClient.FormDockOver(Source: TJvDockDragDockObject;
  X, Y: Integer; State: TDragState; var Accept: Boolean);
begin
  FormPositionDockRect(Source);
  if Assigned(DockStyle) then
    DockStyle.FormDockOver(Self, Source, X, Y, State, Accept);
end;

procedure TJvDockClient.FormEndDock(Target: TObject; X, Y: Integer);
begin
  if Assigned(DockStyle) then
    DockStyle.FormEndDock(Self, Target, X, Y);
end;

procedure TJvDockClient.FormGetDockEdge(Source: TJvDockDragDockObject;
  MousePos: TPoint; var DropAlign: TAlign);
begin
  if Assigned(DockStyle) then
    DockStyle.FormGetDockEdge(Self, Source, MousePos, DropAlign)
  else
    DropAlign := alNone;
end;

procedure TJvDockClient.FormGetSiteInfo(Source: TJvDockDragDockObject; Client: TControl;
  var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean);
begin
  GetWindowRect(ParentForm.Handle, InfluenceRect);
  InflateRect(InfluenceRect, -4, -4);
  if Assigned(DockStyle) then
    DockStyle.FormGetSiteInfo(Source, Self, Client, InfluenceRect, MousePos, CanDock);
end;

procedure TJvDockClient.FormPositionDockRect(Source: TJvDockDragDockObject);
begin
  if Assigned(DockStyle) then
    DockStyle.FormPositionDockRect(Self, Source);
end;

procedure TJvDockClient.FormStartDock(var Source: TJvDockDragDockObject);
begin
  if Assigned(DockStyle) then
    DockStyle.FormStartDock(Self, Source);
end;

function TJvDockClient.FormUnDock(NewTarget: TWinControl;
  Client: TControl): Boolean;
begin
  if Assigned(DockStyle) then
    Result := DockStyle.FormUnDock(Self, NewTarget, Client)
  else
    Result := False;
end;

function TJvDockClient.GetDockState: Integer;
begin
  Result := JvDockState_Unknown;
  if DockStyle <> nil then
    Result := DockStyle.GetDockState(Self);
end;

function TJvDockClient.GetLRDockWidth: Integer;
begin
  Result := ParentForm.LRDockWidth;
end;

function TJvDockClient.GetTBDockHeight: Integer;
begin
  Result := ParentForm.TBDockHeight;
end;

procedure TJvDockClient.HideParentForm;
begin
  HideDockForm(ParentForm);
end;

procedure TJvDockClient.MakeHideEvent;
begin
  ParentVisible := False;
  if Assigned(FOnFormHide) then
    FOnFormHide(Self);
end;

procedure TJvDockClient.MakeShowEvent;
begin
  if ParentForm.Visible then
  begin
    if Assigned(FOnFormShow) then
      FOnFormShow(Self);
    ParentVisible := True;
  end;
end;

procedure TJvDockClient.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if Operation = opRemove then
  begin
    if AComponent = FLastDockSite then
      LastDockSite := nil
    else
    if AComponent = NCPopupMenu then
      NCPopupMenu := nil;
  end;
end;

procedure TJvDockClient.RemoveDockStyle(ADockStyle: TJvDockBasicStyle);
begin
  DoFloatDockClients(alTop);
  DoFloatDockClients(alBottom);
  DoFloatDockClients(alLeft);
  DoFloatDockClients(alRight);
  DoFloatDockEachOther;

  FConjoinPanelClass := nil;
  FTabDockClass := nil;

  if JvGlobalDockManager <> nil then
    JvGlobalDockManager.UnRegisterDockClient(Self);
end;

procedure TJvDockClient.RestoreChild;
begin
  DockStyle.RestoreClient(Self);
end;

procedure TJvDockClient.SetBottomDock(const Value: Boolean);
begin
  if not Value then
    DoFloatDockClients(alBottom);
  inherited SetBottomDock(Value);
end;

procedure TJvDockClient.SetCanFloat(const Value: Boolean);
begin
  FCanFloat := Value;
end;

procedure TJvDockClient.SetCurrentDockSite(const Value: TWinControl);
begin
  FCurrentDockSite := Value;
end;

procedure TJvDockClient.SetDockLevel(const Value: Integer);
begin
  if not ParentForm.Floating then
    if FDockLevel <> Value then
      DoFloatForm(ParentForm);
  FDockLevel := Value;
end;

procedure TJvDockClient.SetEachOtherDock(const Value: Boolean);
begin
  if not Value then
    DoFloatDockEachOther;
  inherited SetEachOtherDock(Value);
end;

procedure TJvDockClient.SetEnableCloseButton(const Value: Boolean);
begin
  FEnableCloseButton := Value;
end;

procedure TJvDockClient.SetEnableDock(const Value: Boolean);
begin
  if not Value then
  begin
    DoFloatDockClients(alTop);
    DoFloatDockClients(alBottom);
    DoFloatDockClients(alLeft);
    DoFloatDockClients(alRight);
    DoFloatDockEachOther;
  end;
  if ParentForm <> nil then
    if Value then
      ParentForm.DragKind := dkDock
    else
      ParentForm.DragKind := dkDrag;
  inherited SetEnableDock(Value);
end;

procedure TJvDockClient.SetLastDockSite(ALastDockSite: TWinControl);
var
  JvDockManager: IJvDockManager;
begin
  if ALastDockSite <> FLastDockSite then
  begin
    if FLastDockSite <> nil then
    begin
      FLastDockSite.RemoveFreeNotification(Self);

      if TWinControlAccessProtected(FLastDockSite).UseDockManager and
        Supports(TWinControlAccessProtected(FLastDockSite).DockManager, IJvDockManager, JvDockManager) then
        JvDockManager.RemoveControl(Self.ParentForm);
    end;

    FLastDockSite := ALastDockSite;

    if FLastDockSite <> nil then
      FLastDockSite.FreeNotification(Self);
  end;
end;

procedure TJvDockClient.SetLeftDock(const Value: Boolean);
begin
  if not Value then
    DoFloatDockClients(alLeft);
  inherited SetLeftDock(Value);
end;

procedure TJvDockClient.SetLRDockWidth(const Value: Integer);
begin
  if ParentForm.LRDockWidth <> Value then
    ParentForm.LRDockWidth := Value;
end;

procedure TJvDockClient.SetNCPopupMenu(Value: TPopupMenu);
begin
  FNCPopupMenu := Value;
  if Value <> nil then
    Value.FreeNotification(Self);
end;

procedure TJvDockClient.SetParentVisible(const Value: Boolean);
begin
  FParentVisible := Value;
end;

procedure TJvDockClient.SetRightDock(const Value: Boolean);
begin
  if not Value then
    DoFloatDockClients(alRight);
  inherited SetRightDock(Value);
end;

procedure TJvDockClient.SetTBDockHeight(const Value: Integer);
begin
  if ParentForm.TBDockHeight <> Value then
    ParentForm.TBDockHeight := Value;
end;

procedure TJvDockClient.SetTopDock(const Value: Boolean);
begin
  if not Value then
    DoFloatDockClients(alTop);
  inherited SetTopDock(Value);
end;

procedure TJvDockClient.SetUnDockLeft(const Value: Integer);
begin
  FUnDockLeft := Value;
end;

procedure TJvDockClient.SetUnDockTop(const Value: Integer);
begin
  FUnDockTop := Value;
end;

procedure TJvDockClient.SetVSPaneWidth(const Value: Integer);
begin
  FVSPaneWidth := Value;
end;

procedure TJvDockClient.ShowParentForm;
begin
  ShowDockForm(ParentForm);
end;

procedure TJvDockClient.WindowProc(var Msg: TMessage);
var
  OldOrient: TDockOrientation;
begin
  if Assigned(FDockStyle) then
    if FDockStyle.DockClientWindowProc(Self, Msg) then
      Exit;
  if not (csDesigning in ComponentState) then
  begin
    case Msg.Msg of
      CM_SHOWINGCHANGED:
        if IsWinXP_UP and JvGlobalDockIsLoading then
        begin
          if GShowingChanged = nil then
            GShowingChanged := TList.Create;
          GShowingChanged.Add(ParentForm);
          Exit;
        end;
      WM_NCLBUTTONDOWN:
        begin
          WMNCLButtonDown(TWMNCHitMessage(Msg));
          if Msg.Result = 1 then
            Exit;
        end;
      WM_NCLBUTTONUP:
        WMNCLButtonUp(TWMNCHitMessage(Msg));
      WM_NCLBUTTONDBLCLK:
        WMNCLButtonDblClk(TWMNCHitMessage(Msg));
      WM_NCMBUTTONDOWN:
        WMNCMButtonDown(TWMNCHitMessage(Msg));
      WM_NCMBUTTONUP:
        WMNCMButtonUp(TWMNCHitMessage(Msg));
      WM_NCMBUTTONDBLCLK:
        WMNCMButtonDblClk(TWMNCHitMessage(Msg));
      WM_NCRBUTTONDOWN:
        begin
          WMNCRButtonDown(TWMNCHitMessage(Msg));
          if FNCPopupMenu <> nil then
            Exit;
        end;
      WM_NCRBUTTONUP:
        WMNCRButtonUp(TWMNCHitMessage(Msg));
      WM_NCRBUTTONDBLCLK:
        WMNCRButtonDblClk(TWMNCHitMessage(Msg));
      WM_NCMOUSEMOVE:
        WMNCMouseMove(TWMNCHitMessage(Msg));
      WM_SIZE:
        WMSize(TWMSize(Msg));
      WM_ACTIVATE:
        WMActivate(TWMActivate(Msg));
      WM_WINDOWPOSCHANGED:
        begin
          ParentForm.ControlState := ParentForm.ControlState + [csDocking];
          OldOrient := ParentForm.DockOrientation;
          ParentForm.DockOrientation := doNoOrient;
          try
            inherited WindowProc(Msg);
          finally
            ParentForm.ControlState := ParentForm.ControlState - [csDocking];
            ParentForm.DockOrientation := OldOrient;
          end;
          Exit;
        end;
      CM_ENTER:
        Activate;
      CM_EXIT:
        Deactivate;
      CM_VISIBLECHANGED:
        CMVisibleChanged(Msg);
    end;
  end;

  inherited WindowProc(Msg);

  if Msg.Msg = WM_SETTEXT then
    if ParentForm.HostDockSite is TJvDockCustomControl then
      TJvDockCustomControl(ParentForm.HostDockSite).UpdateCaption(ParentForm);
end;

procedure TJvDockClient.WMActivate(var Msg: TWMActivate);
begin
  {$IFNDEF COMPILER9_UP}
  InvalidateDockHostSiteOfControl(ParentForm.ActiveControl, Msg.Active = WA_INACTIVE);
  {$ENDIF !COMPILER9_UP}
end;

procedure TJvDockClient.WMNCLButtonDblClk(var Msg: TWMNCHitMessage);
begin
  DoNCButtonDblClk(Msg, mbLeft, msFloat);
end;

procedure TJvDockClient.WMNCLButtonDown(var Msg: TWMNCHitMessage);
begin
  DoNCButtonDown(Msg, mbLeft, msFloat);

  JvGlobalDockClient := Self;

  if (Msg.HitTest = HTCAPTION) and (ParentForm.DragKind = dkDock) and not
    (csDesigning in ComponentState) and not IsIconic(ParentForm.Handle) then
  begin
    SetWindowPos(ParentForm.Handle, 0, 0, 0, 0, 0, SWP_NOZORDER or SWP_NOMOVE or SWP_NOSIZE);
    PostMessage(ParentForm.Handle, WM_NCLBUTTONUP, TMessage(Msg).WParam, TMessage(Msg).LParam);
    if ParentForm.Active then
      JvGlobalDockManager.BeginDrag(ParentForm, DirectDrag, Integer(DirectDrag) * 2 - 1);
    Msg.Result := 1;
  end
  else
    Msg.Result := 0;
end;

procedure TJvDockClient.WMNCLButtonUp(var Msg: TWMNCHitMessage);
begin
  DoNCButtonUp(Msg, mbLeft, msFloat);
end;

procedure TJvDockClient.WMNCMButtonDblClk(var Msg: TWMNCHitMessage);
begin
  DoNCButtonDblClk(Msg, mbMiddle, msFloat);
end;

procedure TJvDockClient.WMNCMButtonDown(var Msg: TWMNCHitMessage);
begin
  DoNCButtonDown(Msg, mbMiddle, msFloat);
end;

procedure TJvDockClient.WMNCMButtonUp(var Msg: TWMNCHitMessage);
begin
  DoNCButtonUp(Msg, mbMiddle, msFloat);
end;

procedure TJvDockClient.WMNCMouseMove(var Msg: TWMNCHitMessage);
begin
  DoNCMouseMove(Msg, msFloat);
end;

procedure TJvDockClient.WMNCRButtonDblClk(var Msg: TWMNCHitMessage);
begin
  DoNCButtonDblClk(Msg, mbRight, msFloat);
end;

procedure TJvDockClient.WMNCRButtonDown(var Msg: TWMNCHitMessage);
begin
  DoNCButtonDown(Msg, mbRight, msFloat);
end;

procedure TJvDockClient.WMNCRButtonUp(var Msg: TWMNCHitMessage);
begin
  DoNCButtonUp(Msg, mbRight, msFloat);
end;

procedure TJvDockClient.WMSize(var Msg: TWMSize);
begin
  inherited;
end;

//=== { TJvDockConjoinHostForm } =============================================

constructor TJvDockConjoinHostForm.Create(AOwner: TComponent);
begin
  {$IFDEF JVDOCK_DEBUG}
  OutputDebugString('TJvDockConjoinHostForm.Create');
  {$ENDIF JVDOCK_DEBUG}
  inherited Create(AOwner);
  BorderStyle := ConjoinDockHostBorderStyle;
end;

procedure TJvDockConjoinHostForm.DoClose(var Action: TCloseAction);
begin
  inherited DoClose(Action);
end;

//=== { TJvDockConjoinPanel } ================================================

constructor TJvDockConjoinPanel.Create(AOwner: TComponent);
begin
  {$IFDEF JVDOCK_DEBUG}
  OutputDebugString('TJvDockConjoinPanel.Create');
  {$ENDIF JVDOCK_DEBUG}
  inherited Create(AOwner);
  Parent := TWinControl(AOwner);
  Align := alClient;
  BevelOuter := bvNone;
  DoubleBuffered := True;
  ParentFont := False;
  Caption := '';
end;

procedure TJvDockConjoinPanel.CMUnDockClient(var Msg: TCMUnDockClient);
begin
  inherited;
  { Panel can be closed when
     * DockClientCount <= 1:
        One hidden form left that is freed. Call originates from TControl.Destroy
     * DockClientCount=2 and VisibleDockClientCount=1
        Two forms left, one is freed. Call originates from TControl.Destroy or
        TJvDockManager.DoUnDock etc.
  }
  if (DockClientCount <= 1) or ((DockClientCount = 2) and (VisibleDockClientCount = 1)) then
    PostMessage(ParentForm.Handle, WM_CLOSE, 0, 0);
  if VisibleDockClientCount <= 2 then
    JvDockControlForm.UpdateCaption(Self, Msg.Client);
  if UseDockManager and (JvDockManager <> nil) then
    JvDockManager.ResetBounds(True);
end;

procedure TJvDockConjoinPanel.CustomDockDrop(Source: TJvDockDragDockObject;
  X, Y: Integer);
begin
  inherited CustomDockDrop(Source, X, Y);
  if Source.Control is TForm then
  begin
    ParentForm.ActiveControl := nil;
    if TForm(Source.Control).FormStyle = fsStayOnTop then
      TForm(Parent).FormStyle := fsStayOnTop;
  end;
  UpdateCaption(nil);
end;

procedure TJvDockConjoinPanel.CustomDockOver(Source: TJvDockDragDockObject;
  X, Y: Integer; State: TDragState; var Accept: Boolean);
begin
  inherited CustomDockOver(Source, X, Y, State, Accept);
  Accept := IsDockable(Self, Source.Control, Source.DropOnControl, Source.DropAlign);
end;

procedure TJvDockConjoinPanel.CustomEndDock(Target: TObject; X, Y: Integer);
begin
  inherited CustomEndDock(Target, X, Y);
end;

procedure TJvDockConjoinPanel.CustomGetSiteInfo(Source: TJvDockDragDockObject;
  Client: TControl; var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean);
begin
  CanDock := IsDockable(Self, Client, Source.DropOnControl, Source.DropAlign);
  inherited CustomGetSiteInfo(Source, Client, InfluenceRect, MousePos, CanDock);
end;

procedure TJvDockConjoinPanel.CustomPositionDockRect(Source: TJvDockDragDockObject;
  X, Y: Integer);
begin
  inherited CustomPositionDockRect(Source, X, Y);
end;

procedure TJvDockConjoinPanel.CustomStartDock(var Source: TJvDockDragDockObject);
begin
  ParentForm.FUnDockControl := nil;
  inherited CustomStartDock(Source);
end;

function TJvDockConjoinPanel.CustomUnDock(Source: TJvDockDragDockObject;
  NewTarget: TWinControl; Client: TControl): Boolean;
begin
  ParentForm.FUnDockControl := Client;

  if not (Client is TJvDockableForm) then
    SetDockSite(TForm(Client), True);
  if ((VisibleDockClientCount = 1) or
    (DockClientCount <= 2)) and (NewTarget <> ParentForm.DockableControl) then
    PostMessage(Parent.Handle, WM_CLOSE, 0, 0);
  UpdateCaption(Client);
  Result := inherited CustomUnDock(Source, NewTarget, Client);
end;

procedure TJvDockConjoinPanel.DockDrop(Source: TDragDockObject; X, Y: Integer);
begin
  if Perform(CM_DOCKCLIENT, Integer(Source), Integer(SmallPoint(X, Y))) >= 0 then
  begin
    if Source.Control is TForm then
    begin
      ParentForm.ActiveControl := nil;
      TForm(Source.Control).ActiveControl := nil;

      SetDockSite(TForm(Source.Control), False);
      if TForm(Source.Control).FormStyle = fsStayOnTop then
        TForm(Parent).FormStyle := fsStayOnTop;
    end;
    UpdateCaption(nil);
  end;
end;

procedure TJvDockConjoinPanel.DoDockOver(Source: TDragDockObject;
  X, Y: Integer; State: TDragState; var Accept: Boolean);
begin
  Accept := IsDockable(Self, Source.Control, Source.DropOnControl, Source.DropAlign);
end;

function TJvDockConjoinPanel.DoUnDock(NewTarget: TWinControl;
  Client: TControl): Boolean;
begin
  ParentForm.FUnDockControl := Client;

  if not (Client is TJvDockableForm) then
    SetDockSite(TForm(Client), True);
  if (VisibleDockClientCount = 1) or
    (DockClientCount <= 2) then
    { It's possible that 2 WM_CLOSE are send to the parent form  }
    PostMessage(Parent.Handle, WM_CLOSE, 0, 0);
  UpdateCaption(Client);
  Result := Perform(CM_UNDOCKCLIENT, Integer(NewTarget), Integer(Client)) = 0;
end;

function TJvDockConjoinPanel.GetDockClient: TJvDockClient;
begin
  if Assigned(ParentForm) then
    Result := ParentForm.FDockClient
  else
    Result := nil;
end;

function TJvDockConjoinPanel.GetParentForm: TJvDockConjoinHostForm;
begin
  { Dirty }
  Result := TJvDockConjoinHostForm(Parent);
end;

procedure TJvDockConjoinPanel.GetSiteInfo(Client: TControl;
  var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean);
begin
  CanDock := IsDockable(Self, Client);
  if CanDock then
    GetWindowRect(Handle, InfluenceRect);
end;

procedure TJvDockConjoinPanel.ReloadDockedControl(const AControlName: string;
  var AControl: TControl);
begin
  AControl := JvDockFindDockFormWithName(AControlName);
end;

function TJvDockConjoinPanel.CreateDockManager: IDockManager;
var
  TreeClass: TJvDockTreeClass;
  ADockStyle: TJvDockBasicStyle;
begin
  Result := nil;

  if (DockManager = nil) and DockSite and UseDockManager then
  begin
    if Assigned(DockClient) then
    begin
      ADockStyle := DockClient.DockStyle;
      if Assigned(ADockStyle) then
      begin
        TreeClass := ADockStyle.ConjoinPanelTreeClass;
        if Assigned(TreeClass) and (TreeClass <> TJvDockTree) then
          Result := TreeClass.Create(Self, ADockStyle.ConjoinPanelZoneClass, ADockStyle) as IJvDockManager;
      end;
    end;
  end;
  if Result = nil then
    Result := DockManager;
  DoubleBuffered := DoubleBuffered or (Result <> nil);
end;

//=== { TJvDockPanel } =======================================================

constructor TJvDockPanel.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  DoubleBuffered := True;
  BevelOuter := bvNone;
  Width := 10;
  Height := 10;
end;

procedure TJvDockPanel.AddDockServer(ADockServer: TJvDockServer);
begin
  { Notification }
end;

procedure TJvDockPanel.CustomDockDrop(Source: TJvDockDragDockObject;
  X, Y: Integer);
begin
  inherited CustomDockDrop(Source, X, Y);

  if Source.Control <> nil then
    ShowDockPanel(True, Source.Control);
end;

procedure TJvDockPanel.CustomDockOver(Source: TJvDockDragDockObject;
  X, Y: Integer; State: TDragState; var Accept: Boolean);
begin
  Accept := IsDockable(Self, Source.Control, Source.DropOnControl, Source.DropAlign);
  if Accept then
    inherited CustomDockOver(Source, X, Y, State, Accept);
end;

procedure TJvDockPanel.CustomEndDock(Target: TObject; X, Y: Integer);
begin
  inherited CustomEndDock(Target, X, Y);
end;

procedure TJvDockPanel.CustomGetSiteInfo(Source: TJvDockDragDockObject; Client: TControl;
  var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean);
begin
  inherited CustomGetSiteInfo(Source, Client, InfluenceRect, MousePos, CanDock);
  CanDock := IsDockable(Self, Client, Source.DropOnControl, Source.DropAlign);
end;

procedure TJvDockPanel.CustomPositionDockRect(Source: TJvDockDragDockObject; X, Y: Integer);
var
  ARect: TRect;
begin
  inherited CustomPositionDockRect(Source, X, Y);
  if (VisibleDockClientCount = 0) and (JvGlobalDockClient <> nil) then
  begin
    case Align of
      alTop:
        begin
          ARect.TopLeft := ClientToScreen(Point(0, 0));
          ARect.BottomRight := ClientToScreen(Point(Width, Source.Control.TBDockHeight));
        end;
      alBottom:
        begin
          ARect.TopLeft := ClientToScreen(Point(0, -Source.Control.TBDockHeight));
          ARect.BottomRight := ClientToScreen(Point(Width, 0));
        end;
      alLeft:
        begin
          ARect.TopLeft := ClientToScreen(Point(0, 0));
          ARect.BottomRight := ClientToScreen(Point(Source.Control.LRDockWidth, Height));
        end;
      alRight:
        begin
          ARect.TopLeft := ClientToScreen(Point(-Source.Control.LRDockWidth, 0));
          ARect.BottomRight := ClientToScreen(Point(Width, Height));
        end;
      alClient:
        begin
          ARect.TopLeft := ClientToScreen(Point(0, 0));
          ARect.BottomRight := ClientToScreen(Point(Width, Height));
        end;
    end;
    Source.DockRect := ARect;
  end;
end;

procedure TJvDockPanel.CustomStartDock(var Source: TJvDockDragDockObject);
begin
  inherited CustomStartDock(Source);
end;

function TJvDockPanel.CustomUnDock(Source: TJvDockDragDockObject; NewTarget: TWinControl;
  Client: TControl): Boolean;
begin
  ShowDockPanel(False, nil);
  Result := inherited CustomUnDock(Source, NewTarget, Client);
  if not (Client is TJvDockableForm) and (Client is TWinControl) then
    SetDockSite(TWinControl(Client), True);
end;

procedure TJvDockPanel.DockDrop(Source: TDragDockObject; X, Y: Integer);
begin
  if Perform(CM_DOCKCLIENT, Integer(Source), Integer(SmallPoint(X, Y))) >= 0 then
  begin
    if Source.Control is TForm then
    begin
      TForm(Source.Control).ActiveControl := nil;
      SetDockSite(TForm(Source.Control), False);
    end;
    UpdateCaption(nil);
  end;
  ShowDockPanel(TWinControl(Source.DragTarget).VisibleDockClientCount > 0, Source.Control);
end;

function TJvDockPanel.DoUnDock(NewTarget: TWinControl; Client: TControl): Boolean;
begin
  Result := IsDockable(Self, Client);
  ShowDockPanel(False, nil);
  Result := Result and (Perform(CM_UNDOCKCLIENT, Integer(NewTarget), Integer(Client)) = 0);
  if Result then
    if not (Client is TJvDockableForm) then
      SetDockSite(TForm(Client), True);
end;

procedure TJvDockPanel.GetDockedControls(WinControls: TList);
begin
  // THE BASE CLASS DOESN'T SUPPORT THIS. JUST RETURN QUIETLY.
  // See TJvDockAdvPanel.GetDockedControls for the actual implementation.
end;

function TJvDockPanel.GetPanelIndex: Integer;
begin
  case Align of
    alTop:
      Result := 0;
    alBottom:
      Result := 1;
    alLeft:
      Result := 2;
    alRight:
      Result := 3;
  else
    if FCustomFlag then
      Result := 4 {NEW!}
    else
      Result := -1; {unknown.}
  end;
end;

procedure TJvDockPanel.ReloadDockedControl(const AControlName: string;
  var AControl: TControl);
begin
  AControl := JvDockFindDockFormWithName(AControlName);
end;

procedure TJvDockPanel.RemoveDockServer(ADockServer: TJvDockServer);
begin
  { Notification }
end;

procedure TJvDockPanel.ResetPosition;
begin
  case Align of
    alLeft:
      Left := GetClientAlignControlArea(Parent, Align) + 1;
    alRight:
      Left := Parent.ClientWidth - GetClientAlignControlArea(Parent, Align) - Width - 1;
    alTop:
      Top := GetClientAlignControlArea(Parent, Align) + 1;
    alBottom:
      Top := Parent.ClientHeight - GetClientAlignControlArea(Parent, Align) - Height - 1;
  end;
end;

procedure TJvDockPanel.Resize;
begin
  inherited Resize;
end;

procedure TJvDockPanel.SetDockServer(ADockServer: TJvDockServer);
begin
  if ADockServer <> FDockServer then
  begin
    if FDockServer <> nil then
      RemoveDockServer(FDockServer);

    FDockServer := ADockServer;

    if FDockServer <> nil then
      AddDockServer(FDockServer);
  end;
end;

procedure TJvDockPanel.ShowDockPanel(MakeVisible: Boolean;
  Client: TControl; PanelSizeFrom: TJvDockSetDockPanelSizeFrom);
const
  DefaultDockSize = 100;
var
  DockHeight, DockWidth: Integer;
begin
  if (not MakeVisible and (VisibleDockClientCount > 1)) or
    (JvGlobalDockClient = nil) then
    Exit;

  if Assigned(DockServer.DockSplitterWithAlign[Align]) then
    DockServer.DockSplitterWithAlign[Align].Visible := MakeVisible;

  if MakeVisible and (Client <> nil) then
  begin
    if Width * Height = 0 then
    begin
      if (PanelSizeFrom = sdfDockPanel) or (Client = nil) then
      begin
        DockHeight := TBDockHeight;
        DockWidth := LRDockWidth;
      end
      else
      begin
        DockHeight := Client.TBDockHeight;
        DockWidth := Client.LRDockWidth;
      end;

      if DockHeight = 0 then
        DockHeight := DefaultDockSize;
      if DockWidth = 0 then
        DockWidth := DefaultDockSize;

      Parent.DisableAlign;
      try
        case Align of
          alTop:
            begin
              Top := DockServer.GetClientAlignControl(alTop);
              Height := DockHeight;
              DockServer.TopSplitter.Top := Top + Height;
            end;
          alBottom:
            begin
              Top := Parent.ClientHeight - DockServer.GetClientAlignControl(alBottom) - DockHeight + 1;
              Height := DockHeight;
              DockServer.BottomSplitter.Top := Top + DockServer.BottomSplitter.Height;
            end;
          alLeft:
            begin
              Left := DockServer.GetClientAlignControl(alLeft);
              Width := DockWidth;
              DockServer.LeftSplitter.Left := Left + Width;
            end;
          alRight:
            begin
              Width := DockWidth;
              Left := Parent.ClientWidth - DockServer.GetClientAlignControl(alRight) - DockWidth + 1;
              DockServer.RightSplitter.Left := Left - DockServer.RightSplitter.Width;
            end;
        end;
      finally
        Parent.EnableAlign;
        if UseDockManager and (JvDockManager <> nil) then
          JvDockManager.ResetBounds(True);
      end;
      DockServer.DoFinishSetDockPanelSize(Self);
    end;
  end
  else
  begin
    if (PanelSizeFrom = sdfDockPanel) or (Client = nil) then
    begin
      if Height > 0 then
        TBDockHeight := Height;
      if Width > 0 then
        LRDockWidth := Width;
    end
    else
    begin
      if Height > 0 then
        Client.TBDockHeight := Height;
      if Width > 0 then
        Client.LRDockWidth := Width;
    end;
    if Align in [alLeft, alRight] then
      Width := 0
    else
      Height := 0;

    ResetPosition;
  end;

  if MakeVisible and (Client <> nil) then
  begin
    if not Client.Visible then
      Client.Show;

    if DockServer.AutoFocusDockedForm and (Client is TWinControl) and
      not TWinControl(Client).Focused and TWinControl(Client).CanFocus then
      TWinControl(Client).SetFocus;
  end;
end;

function TJvDockPanel.CreateDockManager: IDockManager;
var
  ADockStyle: TJvDockBasicStyle;
  TreeClass: TJvDockTreeClass;
begin
  Result := nil;
  if (DockManager = nil) and DockSite and UseDockManager then
  begin
    if Assigned(DockServer) then
    begin
      ADockStyle := DockServer.DockStyle;
      if Assigned(ADockStyle) then
      begin
        TreeClass := ADockStyle.DockPanelTreeClass;
        if Assigned(TreeClass) and (TreeClass <> TJvDockTree) then
          Result := TreeClass.Create(Self, ADockStyle.DockPanelZoneClass, ADockStyle) as IJvDockManager;
      end;
    end;
  end;

  if Result = nil then
    Result := DockManager;
  DoubleBuffered := DoubleBuffered or (Result <> nil);
end;

//=== { TJvDockServer } ======================================================

constructor TJvDockServer.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FAutoFocusDockedForm := True;
  CreateSplitterStyle;
end;

destructor TJvDockServer.Destroy;
begin
  DestroySplitterStyle;
  inherited Destroy;
end;

procedure TJvDockServer.AddDockStyle(ADockStyle: TJvDockBasicStyle);
begin
  JvGlobalDockManager.RegisterDockServer(Self);

  if Assigned(ADockStyle) and Assigned(ADockStyle.DockPanelClass) then
    FDockPanelClass := ADockStyle.DockPanelClass
  else
    FDockPanelClass := DefaultDockPanelClass;

  if Assigned(ADockStyle) and Assigned(ADockStyle.DockSplitterClass) then
    FDockSplitterClass := ADockStyle.DockSplitterClass
  else
    FDockSplitterClass := DefaultDockSplitterClass;

  if not (csDesigning in ComponentState) then
  begin
    CreateDockPanelAndSplitter;
    SetSplitterStyles;
  end;
end;

procedure TJvDockServer.CreateDockPanelAndSplitter;
//var
//  ControlList: TList;

  function CreatePanel(AParent: TWinControl; Align: TAlign; Name: string): TJvDockPanel;
  begin
    if (FDockPanelClass <> nil) and
      (FDockPanelClass <> TJvDockPanelClass(ClassType)) then
    begin
      Result := FDockPanelClass.Create({Owner} Self);
      // (ahuser) Delphi 5's TComponent.Notification() will fail if Owner=Self.Owner
      Result.Parent := AParent; {ParentForm;}
      Result.Name := Name;
      Result.Caption := '';
      Result.Align := Align;
      Result.DockServer := Self;
      Result.ResetPosition;
      if Align in [alTop, alBottom] then
        Result.Height := 0
      else
      if Align in [alLeft, alRight] then
        Result.Width := 0;
      SetDockSite(Result, True);

      Result.FreeNotification(Self);
    end
    else
      Result := nil;
  end;

  function CreateSplitter(AParent: TWinControl; Align: TAlign; Name: string): TJvDockSplitter;
  begin
    if (FDockSplitterClass <> nil) and
      (FDockSplitterClass <> TJvDockSplitterClass(ClassType)) then
    begin
      Result := FDockSplitterClass.Create({Owner}Self);
        // (ahuser) Delphi 5's TComponent.Notification() will fail if Owner=Self.Owner
      Result.Parent := AParent; {ParentForm;}
      Result.Name := Name;
      Result.Visible := False;
      Result.Align := Align;
      Result.DockServer := Self;
      Result.FreeNotification(Self);
    end
    else
      Result := nil;
  end;

  {NEW! Docking panel in center area, or other part of form than the built in left/top/right/bottom panels! }

  procedure CustomDockPanel;
  var
    AParent: TWinControl;
    LAlign: TAlign;
  begin
    AParent := PArentForm;
    LAlign := alClient;
    if Assigned(FOnCustomPanel) then
      //TJvDockCustomPanelEvent:
      FOnCustomPanel(Self, AParent, LAlign)
    else
    begin
      FDockPanels[dpCustom] := nil;
      FSplitters[dpCustom] := nil;
      Exit;
    end;
    if (LAlign <> alNone) and Assigned(AParent) then
    begin // Don't create if align is alNone or parent is NIL.
      FDockPanels[dpCustom] := CreatePanel(AParent, LAlign, {NOTRANSLATE} 'CustomDockPanel');
      FDockPanels[dpCustom].CustomFlag := True;
      if AParent is TPanel then
        FDockPanels[dpCustom].Color := TPanel(AParent).Color;
      FSplitters[dpCustom] := nil; //CreateSplitter(ParentForm, alTop, 'CustomSplitter' + cDefaultNameSuffix);
        //FSplitters[dpCustom].Visible := False;
    end
    else
      FDockPanels[dpCustom] := nil;
    FSplitters[dpCustom] := nil;
  end;

begin
  //  ControlList := TList.Create;
  //  try
  FDockPanels[dpLeft] := CreatePanel(ParentForm, alLeft, {NOTRANSLATE} 'LeftDockPanel');
  FSplitters[dpLeft] := CreateSplitter(ParentForm, alLeft, {NOTRANSLATE} 'LeftSplitter');
  FDockPanels[dpRight] := CreatePanel(ParentForm, alRight, {NOTRANSLATE} 'RightDockPanel');
  FSplitters[dpRight] := CreateSplitter(ParentForm, alRight, {NOTRANSLATE} 'RightSplitter');
  FDockPanels[dpTop] := CreatePanel(ParentForm, alTop, {NOTRANSLATE} 'TopDockPanel');
  FSplitters[dpTop] := CreateSplitter(ParentForm, alTop, {NOTRANSLATE} 'TopSplitter');
  FDockPanels[dpBottom] := CreatePanel(ParentForm, alBottom, {NOTRANSLATE} 'BottomDockPanel');
  FSplitters[dpBottom] := CreateSplitter(ParentForm, alBottom, {NOTRANSLATE} 'BottomSplitter');

  CustomDockPanel;

//  FSplitters[dpCustom] := nil; // NOT USED!
  //  finally
  //    ControlList.Free;
  //  end;
end;

procedure TJvDockServer.CreateSplitterStyle;
const
  cCursor: array [TJvDockPosition] of TCursor =
    (crHSplit, crHSplit, crVSplit, crVSplit, crNone);
var
  Position: TJvDockPosition;
begin
  for Position := Low(TJvDockPosition) to High(TJvDockPosition) do
  begin
    FSplitterStyles[Position] := TJvDockSplitterStyle.Create(Splitter[Position], cCursor[Position]);
    FSplitterStyles[Position].FDockServer := Self;
  end;
end;

procedure TJvDockServer.DestroyDockPanelAndSplitter;
var
  Position: TJvDockPosition;
begin
  for Position := Low(TJvDockPosition) to High(TJvDockPosition) do
  begin
    { SplitterStyles may already be destroyed }
    if Assigned(SplitterStyle[Position]) then
      SplitterStyle[Position].Splitter := nil;
    FreeAndNil(FDockPanels[Position]);
    FreeAndNil(FSplitters[Position]);
  end;
end;

procedure TJvDockServer.DestroySplitterStyle;
var
  Position: TJvDockPosition;
begin
  for Position := Low(TJvDockPosition) to High(TJvDockPosition) do
    FreeAndNil(FSplitterStyles[Position]);
end;

procedure TJvDockServer.DoFinishSetDockPanelSize(DockPanel: TJvDockPanel);
begin
  if Assigned(FOnFinishSetDockPanelSize) then
    FOnFinishSetDockPanelSize(DockPanel);
end;

procedure TJvDockServer.DoFloatDockClients(DockPanel: TJvDockPanel);
var
  I: Integer;
  DC: TJvDockClient;
begin
  if not (csDesigning in ComponentState) and (DockPanel <> nil) then
    for I := DockPanel.DockClientCount - 1 downto 0 do
    begin
      DC := FindDockClient(DockPanel.DockClients[I]);
      if DC <> nil then
        DC.RestoreChild;
    end;
end;

procedure TJvDockServer.DoGetClientAlignControl(Align: TAlign; var Value: Integer);
begin
  if Assigned(FOnGetClientAlignSize) then
    FOnGetClientAlignSize(Align, Value);
end;

function TJvDockServer.GetClientAlignControl(Align: TAlign): Integer;
begin
  Result := GetClientAlignControlArea(ParentForm, Align);
  DoGetClientAlignControl(Align, Result);
end;

function TJvDockServer.GetDockPanel(DockPosition: TJvDockPosition): TJvDockPanel;
begin
  Result := FDockPanels[DockPosition];
end;

function TJvDockServer.GetDockPanelIndex(const Index: Integer): TJvDockPanel;
begin
  Result := FDockPanels[TJvDockPosition(Index)];
end;

function TJvDockServer.GetDockPanelWithAlign(Index: TAlign): TJvDockPanel;
begin
  Result := nil;
  case Index of
    alLeft:
      Result := LeftDockPanel;
    alRight:
      Result := RightDockPanel;
    alTop:
      Result := TopDockPanel;
    alBottom:
      Result := BottomDockPanel;
  end;
end;

function TJvDockServer.GetDockSplitterWithAlign(Index: TAlign): TJvDockSplitter;
begin
  Result := nil;
  case Index of
    alLeft:
      Result := LeftSplitter;
    alRight:
      Result := RightSplitter;
    alTop:
      Result := TopSplitter;
    alBottom:
      Result := BottomSplitter;
  end;
end;

function TJvDockServer.GetSplitter(DockPosition: TJvDockPosition): TJvDockSplitter;
begin
  Result := FSplitters[DockPosition];
end;

function TJvDockServer.GetSplitterIndex(const Index: Integer): TJvDockSplitter;
begin
  Result := FSplitters[TJvDockPosition(Index)];
end;

function TJvDockServer.GetSplitterStyle(DockPosition: TJvDockPosition): TJvDockSplitterStyle;
begin
  Result := FSplitterStyles[DockPosition];
end;

function TJvDockServer.GetSplitterStyleIndex(const Index: Integer): TJvDockSplitterStyle;
begin
  Result := FSplitterStyles[TJvDockPosition(Index)];
end;

procedure TJvDockServer.Notification(AComponent: TComponent;
  Operation: TOperation);
var
  Position: TJvDockPosition;
begin
  inherited Notification(AComponent, Operation);

  if Operation = opRemove then
  begin
    for Position := Low(TJvDockPosition) to High(TJvDockPosition) do
      if AComponent = DockPanel[Position] then
      begin
        FDockPanels[Position] := nil;
        DestroyDockPanelAndSplitter;
      end
      else
      if AComponent = Splitter[Position] then
      begin
        FSplitters[Position] := nil;
        if Assigned(SplitterStyle[Position]) then
          SplitterStyle[Position].Splitter := nil;
        DestroyDockPanelAndSplitter;
      end;
  end;
end;

procedure TJvDockServer.RemoveDockStyle(ADockStyle: TJvDockBasicStyle);
begin
  DoFloatDockClients(TopDockPanel);
  DoFloatDockClients(BottomDockPanel);
  DoFloatDockClients(LeftDockPanel);
  DoFloatDockClients(RightDockPanel);

  DestroyDockPanelAndSplitter;

  FDockPanelClass := nil;
  FDockSplitterClass := nil;

  { !! can be nil }
  if JvGlobalDockManager <> nil then
    JvGlobalDockManager.UnRegisterDockServer(Self);
end;

procedure TJvDockServer.SetBottomDock(const Value: Boolean);
begin
  if not Value then
    DoFloatDockClients(BottomDockPanel);
  inherited SetBottomDock(Value);
end;

procedure TJvDockServer.SetEnableDock(const Value: Boolean);
begin
  if not Value then
  begin
    DoFloatDockClients(TopDockPanel);
    DoFloatDockClients(BottomDockPanel);
    DoFloatDockClients(LeftDockPanel);
    DoFloatDockClients(RightDockPanel);
  end;
  inherited SetEnableDock(Value);
end;

procedure TJvDockServer.SetLeftDock(const Value: Boolean);
begin
  if not Value then
    DoFloatDockClients(LeftDockPanel);
  inherited SetLeftDock(Value);
end;

procedure TJvDockServer.SetRightDock(const Value: Boolean);
begin
  if not Value then
    DoFloatDockClients(RightDockPanel);
  inherited SetRightDock(Value);
end;

procedure TJvDockServer.SetSplitterStyle(DockPosition: TJvDockPosition;
  ASplitterStyle: TJvDockSplitterStyle);
begin
  FSplitterStyles[DockPosition].Assign(ASplitterStyle);
end;

procedure TJvDockServer.SetSplitterStyleIndex(const Index: Integer;
  ASplitterStyle: TJvDockSplitterStyle);
begin
  FSplitterStyles[TJvDockPosition(Index)].Assign(ASplitterStyle);
end;

procedure TJvDockServer.SetSplitterStyles;
var
  Position: TJvDockPosition;
begin
  for Position := Low(TJvDockPosition) to High(TJvDockPosition) do
  begin
    SplitterStyle[Position].Splitter := Splitter[Position];
    SplitterStyle[Position].SetSplitterStyle;
  end;
end;

procedure TJvDockServer.SetTopDock(const Value: Boolean);
begin
  if not Value then
    DoFloatDockClients(TopDockPanel);
  inherited SetTopDock(Value);
end;

procedure TJvDockServer.WindowProc(var Msg: TMessage);
begin
  if Assigned(FDockStyle) then
    if FDockStyle.DockServerWindowProc(Self, Msg) then
      Exit;
  if not (csDesigning in ComponentState) then
    if Msg.Msg = WM_ACTIVATE then
      WMActivate(TWMActivate(Msg));
  inherited WindowProc(Msg);
end;

procedure TJvDockServer.WMActivate(var Msg: TWMActivate);
{$IFNDEF COMPILER9_UP}
var
  Control: TWinControl;
{$ENDIF !COMPILER9_UP}
begin
  {$IFNDEF COMPILER9_UP}
  if Msg.Active = WA_INACTIVE then
  begin
    Control := ParentForm.ActiveControl;
    if Assigned(Control) then
      InvalidateDockHostSiteOfControl(Control, True);
  end
  else
  begin
    Control := ParentForm.ActiveControl;
    if Assigned(Control) then
    begin
      //      { ?? }
      //      if AutoFocusDockedForm and Control.CanFocus then
      //        Control.SetFocus;
      InvalidateDockHostSiteOfControl(Control, False);
    end;
  end;
  {$ENDIF !COMPILER9_UP}
end;

//=== { TJvDockSplitter } ====================================================

constructor TJvDockSplitter.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  AutoSnap := False;
end;

function TJvDockSplitter.FindControl: TControl;
begin
  if DockServer <> nil then
    Result := DockServer.GetDockPanelWithAlign(Align)
  else
    Result := inherited FindControl;
end;

function TJvDockSplitter.GetSplitterIndex: Integer;
begin
  case Align of
    alTop:
      Result := 0;
    alBottom:
      Result := 1;
    alLeft:
      Result := 2;
    alRight:
      Result := 3;
  else
    Result := -1;
  end;
end;

//=== { TJvDockSplitterStyle } ===============================================

constructor TJvDockSplitterStyle.Create(ASplitter: TJvDockSplitter; ACursor: TCursor);
begin
  inherited Create;
  FSplitter := ASplitter;
  Color := clBtnFace;
  Cursor := ACursor;
  ParentColor := False;
  ResizeStyle := rsPattern;
  FSize := 3;
  FMinSize := 30;
end;

procedure TJvDockSplitterStyle.Assign(Source: TPersistent);
begin
  if Source is TJvDockSplitterStyle then
  begin
    Color := TJvDockSplitterStyle(Source).Color;
    Cursor := TJvDockSplitterStyle(Source).Cursor;
    ParentColor := TJvDockSplitterStyle(Source).ParentColor;
    ResizeStyle := TJvDockSplitterStyle(Source).ResizeStyle;
    Size := TJvDockSplitterStyle(Source).Size;
  end
  else
    inherited Assign(Source);
end;

procedure TJvDockSplitterStyle.AssignTo(Dest: TPersistent);
begin
  if Dest is TJvDockSplitterStyle then
    with TJvDockSplitterStyle(Dest) do
    begin
      Color := Self.Color;
      Cursor := Self.Cursor;
      ParentColor := Self.ParentColor;
      ResizeStyle := Self.ResizeStyle;
      Size := Self.Size;
    end
  else
    inherited AssignTo(Dest);
end;

procedure TJvDockSplitterStyle.AssignToSplitter(Dest: TJvDockSplitter);
begin
  Dest.Color := Color;
  Dest.Cursor := Cursor;
  Dest.ParentColor := ParentColor;
  Dest.ResizeStyle := ResizeStyle;
  if Dest.Align in [alTop, alBottom] then
    Dest.Height := Size
  else
    Dest.Width := Size;
end;

procedure TJvDockSplitterStyle.SetColor(const Value: TColor);
begin
  if FColor <> Value then
  begin
    FColor := Value;
    ParentColor := False;
    if Assigned(FSplitter) then
      FSplitter.Color := Value;
  end;
end;

procedure TJvDockSplitterStyle.SetCursor(const Value: TCursor);
begin
  FCursor := Value;
  if Assigned(FSplitter) then
    FSplitter.Cursor := Value;
end;

procedure TJvDockSplitterStyle.SetMinSize(const Value: TJvDockSplitterSize);
begin
  FMinSize := Value;
  if Assigned(FSplitter) then
    FSplitter.MinSize := Value;
end;

procedure TJvDockSplitterStyle.SetParentColor(const Value: Boolean);
begin
  if FParentColor <> Value then
  begin
    FParentColor := Value;
    if Value then
      FColor := FDockServer.ParentForm.Color;
    if Assigned(FSplitter) then
      FSplitter.ParentColor := Value;
  end;
end;

procedure TJvDockSplitterStyle.SetResizeStyle(const Value: TResizeStyle);
begin
  FResizeStyle := Value;
  if Assigned(FSplitter) then
    FSplitter.ResizeStyle := Value;
end;

procedure TJvDockSplitterStyle.SetSize(const Value: TJvDockSplitterSize);
begin
  FSize := Value;
  if Assigned(FSplitter) then
  begin
    if FSplitter.Align in [alTop, alBottom] then
      FSplitter.Height := Value
    else
      FSplitter.Width := Value;
  end;
end;

procedure TJvDockSplitterStyle.SetSplitterStyle;
begin
  if Assigned(FSplitter) then
    AssignToSplitter(FSplitter);
end;

//=== { TJvDockTabHostForm } =================================================

constructor TJvDockTabHostForm.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  BorderStyle := TabDockHostBorderStyle;
end;

function TJvDockTabHostForm.GetActiveDockForm: TForm;
begin
  if PageControl.ActiveDockForm is TForm then
    Result := TForm(PageControl.ActiveDockForm)
  else
    Result := nil;
end;

//------------------------------------------------------------------------
// ShowDockedControl:
//
// If under program control you need a tabdock to switch pages to show
// a particular docked form, you call DockTabHostForm.ShowControl(aForm):
//
// If aControl is docked in DockTabhostForm.PageControl, this will
// change the PageControl.ActivePage to that page.
//------------------------------------------------------------------------

procedure TJvDockTabHostForm.ShowDockedControl(AControl: TWinControl); // NEW! WPostma.
var
  t: Integer;
  TabSheet: TJvDockTabSheet;
begin
  Assert(Assigned(AControl));
  if not Assigned(AControl.Parent) then
    exit;
  if not (AControl.Parent is TJvDockTabSheet) then
    exit;
  TabSheet := TJvDockTabSheet(AControl.Parent);
  // Now go through the pages and find it!
  for t := 0 to FPageControl.Count - 1 do
  begin
    if FPageControl.Pages[t] = TabSheet then
    begin
      FPageControl.ActivePage := TabSheet; { set page!}
      if AControl is TForm then
        TabSheet.Caption := TForm(AControl).Caption;
      Self.Caption := TabSheet.Caption;
      exit;
    end;
  end;
end;

// If while docked, you need a docked form's caption to update,
// and this update needs to be shown on the tabs on the screen,
// and possibly also the title bar of the docktabhost form needs
// updating also, then you need to call this function, like this:
//   DockTabHostForm.UpdateCaption(MyForm)

procedure TJvDockTabHostForm.UpdateCaption(AControl: TWinControl); //virtual;
var
  t: Integer;
  TabSheet: TJvDockTabSheet;
begin
  Assert(Assigned(AControl));
  if not Assigned(AControl.Parent) then
    exit;
  if not (AControl.Parent is TJvDockTabSheet) then
    exit;
  TabSheet := TJvDockTabSheet(AControl.Parent);
  // Now go through the pages and find the one page that needs
  // its caption updated.
  for t := 0 to FPageControl.Count - 1 do
  begin
    if FPageControl.Pages[t] = TabSheet then
    begin
      if AControl is TForm then
        TabSheet.Caption := TForm(AControl).Caption;
      if FPageControl.ActivePage = TabSheet then
        Self.Caption := TabSheet.Caption; // tabhost's form caption needs updating.
      exit;
    end;
  end;
end;

//=== { TJvDockTabPageControl } ==============================================

constructor TJvDockTabPageControl.Create(AOwner: TComponent);
var
  ADockClient: TJvDockClient;
begin
  inherited Create(AOwner);
  if AOwner is TWinControl then
    Parent := TWinControl(AOwner);
  FStyleLink := TJvDockStyleLink.Create;
  { First set DockStyle then OnStyleChanged so no OnStyleChanged is fired;
    we do it ourself in AfterContruction }
  ADockClient := FindDockClient(Parent);
  if Assigned(ADockClient) then
    FStyleLink.DockStyle := ADockClient.DockStyle;
  FStyleLink.OnStyleChanged := DockStyleChanged;
  SetDockSite(Self, True);
  PopupMenu := DockPageControlPopupMenu;
  HotTrack := DockPageControlHotTrack;
  DoubleBuffered := True;
  Caption := '';
  FVersion := $00040000;
end;

destructor TJvDockTabPageControl.Destroy;
begin
  FStyleLink.Free;
  SetDockSite(Self, False);
  inherited Destroy;
end;

procedure TJvDockTabPageControl.AdjustClientRect(var Rect: TRect);
begin
  inherited AdjustClientRect(Rect);
  case TabPosition of
    tpLeft:
      Inc(Rect.Left, 2);
    tpRight:
      Dec(Rect.Right, 2);
    tpBottom:
      begin
        Dec(Rect.Top, 1);
        Dec(Rect.Bottom, 2);
      end;
  end;
end;

procedure TJvDockTabPageControl.AfterConstruction;
begin
  inherited AfterConstruction;
  FStyleLink.StyleChanged;
end;

procedure TJvDockTabPageControl.CustomDockDrop(Source: TJvDockDragDockObject;
  X, Y: Integer);
var
  DragDockObject: TDragDockObject;
begin
  if Source.DropAlign in [alClient, alNone] then
  begin
    DragDockObject := TDragDockObject.Create(Source.Control);
    try
      DragDockObject.DockRect := Source.DockRect;
      DragDockObject.Control := Source.Control;
      Perform(CM_DOCKCLIENT, Integer(DragDockObject), Integer(SmallPoint(X, Y)));
      UpdateCaption(nil);
    finally
      DragDockObject.Free;
    end;
  end
  else
    inherited CustomDockDrop(Source, X, Y);
  if Source.Control is TForm then
  begin
    TForm(Source.Control).ActiveControl := nil;
    SetDockSite(TForm(Source.Control), False);
  end;
end;

procedure TJvDockTabPageControl.CustomDockOver(Source: TJvDockDragDockObject;
  X, Y: Integer; State: TDragState; var Accept: Boolean);
var
  Rect: TRect;
begin
  inherited CustomDockOver(Source, X, Y, State, Accept);

  Accept := IsDockable(Self, Source.Control, Source.DropOnControl, Source.DropAlign);

  if Accept then
  begin
    ComputeDockingRect(Self, Rect, Point(ClientWidth div 2, ClientHeight div 2));
    Source.DockRect := Rect;
  end;
end;

procedure TJvDockTabPageControl.CustomEndDock(Target: TObject; X, Y: Integer);
begin
  inherited CustomEndDock(Target, X, Y);
end;

procedure TJvDockTabPageControl.CustomGetDockEdge(Source: TJvDockDragDockObject;
  MousePos: TPoint; var DropAlign: TAlign);
var
  ARect: TRect;
begin
  ARect := Source.DockRect;
  DropAlign := ComputeDockingRect(Source.Control, ARect, MousePos);
end;

procedure TJvDockTabPageControl.CustomGetSiteInfo(Source: TJvDockDragDockObject;
  Client: TControl; var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean);
begin
  inherited CustomGetSiteInfo(Source, Client, InfluenceRect, MousePos, CanDock);
  CanDock := IsDockable(Self, Client, Source.DropOnControl, Source.DropAlign);
end;

procedure TJvDockTabPageControl.CustomPositionDockRect(Source: TJvDockDragDockObject;
  X, Y: Integer);
begin
  inherited CustomPositionDockRect(Source, X, Y);
end;

procedure TJvDockTabPageControl.CustomStartDock(var Source: TJvDockDragDockObject);
begin
  inherited CustomStartDock(Source);
end;

function TJvDockTabPageControl.CustomUnDock(Source: TJvDockDragDockObject;
  NewTarget: TWinControl; Client: TControl): Boolean;
begin
  if not (Client is TJvDockableForm) then
    SetDockSite(TForm(Client), True);
  if (VisibleDockClientCount = 1) or (DockClientCount <= 2) then
    PostMessage(Parent.Handle, WM_CLOSE, 0, 0);
  UpdateCaption(Client);
  Result := Perform(CM_UNDOCKCLIENT, Integer(NewTarget), Integer(Client)) = 0;
end;

procedure TJvDockTabPageControl.DockDrop(Source: TDragDockObject; X,
  Y: Integer);
begin
  if Perform(CM_DOCKCLIENT, Integer(Source), Integer(SmallPoint(X, Y))) >= 0 then
  begin
    if Source.Control is TForm then
    begin
      TForm(Source.Control).ActiveControl := nil;
      SetDockSite(TWinControl(Source.Control), False);
      if TForm(Source.Control).FormStyle = fsStayOnTop then
        TForm(Parent).FormStyle := fsStayOnTop;
    end;
    UpdateCaption(nil);
  end;
end;

procedure TJvDockTabPageControl.DockStyleChanged(Sender: TObject);
begin
  SyncWithStyle;
end;

function TJvDockTabPageControl.DoUnDock(NewTarget: TWinControl;
  Client: TControl): Boolean;
begin
  if not (Client is TJvDockableForm) then
    SetDockSite(TForm(Client), True);
  if (VisibleDockClientCount = 1) or
    (DockClientCount <= 2) then
    PostMessage(Parent.Handle, WM_CLOSE, 0, 0);
  UpdateCaption(Client);
  Result := Perform(CM_UNDOCKCLIENT, Integer(NewTarget), Integer(Client)) = 0;
end;

function TJvDockTabPageControl.GetActiveDockForm: TCustomForm;
begin
  Result := DockForm[ActivePageIndex];
end;

function TJvDockTabPageControl.GetDockForm(Index: Integer): TCustomForm;
var
  Page: TJvDockTabSheet;
begin
  Result := nil;

  if (Index > -1) and (Index < Count) then
  begin
    Page := Pages[Index];
    if Assigned(Page) and (Page.ControlCount = 1) and (Page.Controls[0] is TCustomForm) then
    begin
      Result := TCustomForm(Page.Controls[0]);
    end
  end;
end;

function TJvDockTabPageControl.GetDockStyle: TJvDockObservableStyle;
begin
  Result := FStyleLink.DockStyle;
end;

function TJvDockTabPageControl.GetParentForm: TJvDockTabHostForm;
begin
  if Parent is TJvDockTabHostForm then
    Result := TJvDockTabHostForm(Parent)
  else
    Result := nil;
end;

procedure TJvDockTabPageControl.LoadFromStream(Stream: TStream);
var
  I, ACount, NameLen, SheetVisible, ActiveSheetIndex: Integer;
  ControlName: string;
  AControl: TControl;
  Index: Integer;
begin
  Stream.Read(I, SizeOf(I));

  Stream.Read(ACount, SizeOf(ACount));
  Index := 0;
  for I := 0 to ACount - 1 do
  begin
    ControlName := '';

    Stream.Read(NameLen, SizeOf(NameLen));
    if NameLen > 0 then
    begin
      SetLength(ControlName, NameLen);
      Stream.Read(Pointer(ControlName)^, NameLen);
    end;

    Stream.Read(SheetVisible, SizeOf(SheetVisible));

    if ControlName <> '' then
    begin
      ReloadDockedControl(ControlName, AControl);
      if AControl <> nil then
      begin
        AControl.ManualDock(Self, nil, alClient);
        { DockClients[Index] is always AControl? }
        DockClients[Index].Visible := Boolean(SheetVisible);
        Inc(Index);
      end;
    end;
  end;

  Stream.Read(ActiveSheetIndex, SizeOf(ActiveSheetIndex));
  ActivePageIndex := ActiveSheetIndex;
  Change;
end;

procedure TJvDockTabPageControl.ReloadDockedControl(const AControlName: string;
  var AControl: TControl);
begin
  AControl := JvDockFindDockFormWithName(AControlName);
end;

procedure TJvDockTabPageControl.SaveToStream(Stream: TStream);
var
  I, ACount, NameLen, SheetVisible, ActiveSheetIndex: Integer;
  ControlName: string;
  CurrentControl: TControl;
  TabPageStreamEndFlag: Integer;
begin
  Stream.Write(FVersion, SizeOf(FVersion));
  ACount := Count;

  Stream.Write(ACount, SizeOf(ACount));
  for I := 0 to ACount - 1 do
  begin
    if Pages[I].ControlCount > 0 then
    begin
      CurrentControl := Pages[I].Controls[0];
      ControlName := CurrentControl.Name;
      NameLen := Length(ControlName);

      Stream.Write(NameLen, SizeOf(NameLen));

      if NameLen > 0 then
        Stream.Write(Pointer(ControlName)^, NameLen);
      SheetVisible := 0;
      if (Self is TJvDockVSNETTabPageControl) and (ParentForm.HostDockSite is TJvDockPanel) then
        SheetVisible := Integer(TJvDockVSNETTabSheet(Pages[I]).OldVisible)
      else
        SheetVisible := SheetVisible + Integer(CurrentControl.Visible);

      Stream.Write(SheetVisible, SizeOf(SheetVisible));
    end;
  end;
  ActiveSheetIndex := ActivePageIndex;

  Stream.Write(ActiveSheetIndex, SizeOf(ActiveSheetIndex));

  TabPageStreamEndFlag := -10;
  Stream.Write(TabPageStreamEndFlag, SizeOf(TabPageStreamEndFlag));
end;

procedure TJvDockTabPageControl.SyncWithStyle;
begin
  HotTrack := DockStyle.TabServerOption.HotTrack;
  TabPosition := DockStyle.TabServerOption.TabPosition;
end;

//=== { TJvGlobalDockManager } ===============================================

constructor TJvGlobalDockManager.Create;
begin
  inherited Create;
  FDockServers := TList.Create;
  FDockClients := TList.Create;
  FDockableForms := TList.Create;
end;

destructor TJvGlobalDockManager.Destroy;
begin
  FDockableForms.Free;
  FDockServers.Free;
  FDockClients.Free;
  inherited Destroy;
end;

function TJvGlobalDockManager.FindDockClientForm(
  const AName: string): TControl;
var
  I: Integer;
begin
  for I := 0 to DockClientCount - 1 do
  begin
    Result := DockClient[I].ParentForm;
    if Result.Name = AName then
      Exit;
  end;
  Result := nil;
end;

function TJvGlobalDockManager.FindDockControlForm(
  const AName: string): TControl;
begin
  Result := FindDockServerForm(AName);
  if Result = nil then
    FindDockClientForm(AName);
end;

function TJvGlobalDockManager.FindDockServerForm(
  const AName: string): TControl;
var
  I: Integer;
begin
  for I := 0 to DockServerCount - 1 do
  begin
    Result := DockServer[I].ParentForm;
    if Result.Name = AName then
      Exit;
  end;
  Result := nil;
end;

function TJvGlobalDockManager.GetDockableForm(
  const Index: Integer): TJvDockableForm;
begin
  Result := TJvDockableForm(FDockableForms[Index]);
end;

function TJvGlobalDockManager.GetDockableFormCount: Integer;
begin
  Result := FDockableForms.Count;
end;

function TJvGlobalDockManager.GetDockClient(
  const Index: Integer): TJvDockClient;
begin
  Result := TJvDockClient(FDockClients[Index]);
end;

function TJvGlobalDockManager.GetDockClientCount: Integer;
begin
  Result := FDockClients.Count;
end;

function TJvGlobalDockManager.GetDockServer(
  const Index: Integer): TJvDockServer;
begin
  Result := TJvDockServer(FDockServers[Index]);
end;

function TJvGlobalDockManager.GetDockServerCount: Integer;
begin
  Result := FDockServers.Count;
end;

procedure TJvGlobalDockManager.RegisterDockableForm(
  ADockableForm: TJvDockableForm);
var
  Index: Integer;
begin
  Index := FDockableForms.IndexOf(ADockableForm);
  if Index < 0 then
    FDockableForms.Add(ADockableForm);
end;

procedure TJvGlobalDockManager.RegisterDockClient(
  ADockClient: TJvDockClient);
var
  Index: Integer;
begin
  Index := FDockClients.IndexOf(ADockClient);
  if Index < 0 then
    FDockClients.Add(ADockClient);
end;

procedure TJvGlobalDockManager.RegisterDockServer(
  ADockServer: TJvDockServer);
var
  Index: Integer;
begin
  Index := FDockServers.IndexOf(ADockServer);
  if Index < 0 then
    FDockServers.Add(ADockServer);
end;

procedure TJvGlobalDockManager.UnRegisterDockableForm(
  ADockableForm: TJvDockableForm);
begin
  FDockableForms.Remove(ADockableForm);
end;

procedure TJvGlobalDockManager.UnRegisterDockClient(
  ADockClient: TJvDockClient);
begin
  FDockClients.Remove(ADockClient);
end;

procedure TJvGlobalDockManager.UnRegisterDockServer(
  ADockServer: TJvDockServer);
begin
  FDockServers.Remove(ADockServer);
end;

procedure InitDockManager;
begin
  try
    JvGlobalDockManager.Free;
    JvGlobalDockManager := nil;
    JvGlobalDockManager := TJvGlobalDockManager.Create;
  except
  end;
end;

procedure DoneDockManager;
begin
  JvGlobalDockManager.Free;
  JvGlobalDockManager := nil;
end;

initialization
  {$IFDEF USEJVCL}
  {$IFDEF UNITVERSIONING}
  RegisterUnitVersion(HInstance, UnitVersioning);
  {$ENDIF UNITVERSIONING}
  {$ENDIF USEJVCL}
  InitDockManager;

finalization
  DoneDockManager;
  {$IFDEF USEJVCL}
  {$IFDEF UNITVERSIONING}
  UnregisterUnitVersion(HInstance);
  {$ENDIF UNITVERSIONING}
  {$ENDIF USEJVCL}

end.

