git-svn-id: https://192.168.0.254/svn/Componentes.Terceros.DevExpressVCL@55 05c56307-c608-d34a-929d-697000501d7a
1585 lines
46 KiB
ObjectPascal
1585 lines
46 KiB
ObjectPascal
{********************************************************************}
|
|
{ }
|
|
{ Developer Express Visual Component Library }
|
|
{ ExpressLayoutControl main components }
|
|
{ }
|
|
{ Copyright (c) 2001-2009 Developer Express Inc. }
|
|
{ ALL RIGHTS RESERVED }
|
|
{ }
|
|
{ The entire contents of this file is protected by U.S. and }
|
|
{ International Copyright Laws. Unauthorized reproduction, }
|
|
{ reverse-engineering, and distribution of all or any portion of }
|
|
{ the code contained in this file is strictly prohibited and may }
|
|
{ result in severe civil and criminal penalties and will be }
|
|
{ prosecuted to the maximum extent possible under the law. }
|
|
{ }
|
|
{ RESTRICTIONS }
|
|
{ }
|
|
{ THIS SOURCE CODE AND ALL RESULTING INTERMEDIATE FILES }
|
|
{ (DCU, OBJ, DLL, ETC.) ARE CONFIDENTIAL AND PROPRIETARY TRADE }
|
|
{ SECRETS OF DEVELOPER EXPRESS INC. THE REGISTERED DEVELOPER IS }
|
|
{ LICENSED TO DISTRIBUTE THE EXPRESSLAYOUTCONTROL AND ALL }
|
|
{ ACCOMPANYING VCL CONTROLS AS PART OF AN EXECUTABLE PROGRAM }
|
|
{ ONLY. }
|
|
{ }
|
|
{ THE SOURCE CODE CONTAINED WITHIN THIS FILE AND ALL RELATED }
|
|
{ FILES OR ANY PORTION OF ITS CONTENTS SHALL AT NO TIME BE }
|
|
{ COPIED, TRANSFERRED, SOLD, DISTRIBUTED, OR OTHERWISE MADE }
|
|
{ AVAILABLE TO OTHER INDIVIDUALS WITHOUT EXPRESS WRITTEN CONSENT }
|
|
{ AND PERMISSION FROM DEVELOPER EXPRESS INC. }
|
|
{ }
|
|
{ CONSULT THE END USER LICENSE AGREEMENT FOR INFORMATION ON }
|
|
{ ADDITIONAL RESTRICTIONS. }
|
|
{ }
|
|
{********************************************************************}
|
|
|
|
unit dxLayoutImport;
|
|
|
|
interface
|
|
|
|
uses
|
|
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
|
|
Dialogs, StdCtrls, cxClasses, dxLayoutControl, cxControls, cxStyles,
|
|
cxGraphics, ExtCtrls, cxContainer, cxLabel, cxLookAndFeelPainters, cxGroupBox,
|
|
Types, cxLookAndFeels, cxSplitter, Menus, cxDropDownEdit, cxCheckBox, cxButtons,
|
|
cxRadioGroup, cxEdit, cxTextEdit, cxMaskEdit, cxSpinEdit, ComCtrls, cxPC,
|
|
dxLayoutcxEditAdapters, dxLayoutLookAndFeels;
|
|
|
|
type
|
|
TdxAutoLayout = class;
|
|
|
|
{ TdxAutoLayoutItem }
|
|
|
|
TdxAutoLayoutItem = class
|
|
private
|
|
FAlign: TAlign;
|
|
FAutoLayout: TdxAutoLayout;
|
|
FBoundsRect: TRect;
|
|
FCaption: string;
|
|
FCaptionLayout: TdxCaptionLayout;
|
|
FControl: TControl;
|
|
FLabeled: Boolean;
|
|
FLabeledBoundsRect: TRect;
|
|
FLayoutControlItem: TdxCustomLayoutItem;
|
|
procedure ProcessAnchors;
|
|
public
|
|
constructor Create(AAutoLayout: TdxAutoLayout; AControl: TControl);
|
|
procedure Apply(AProcessAnchors: Boolean = True);
|
|
procedure Assign(Source: TdxAutoLayoutItem);
|
|
procedure CreateLayoutControlItem(AGroup: TdxLayoutGroup);
|
|
function CanResize: Boolean;
|
|
|
|
function CreateGroup(AGroup: TdxLayoutGroup): TdxLayoutGroup;
|
|
function CreateItem(AGroup: TdxLayoutGroup): TdxCustomLayoutItem;
|
|
function CreateSeparator(AGroup: TdxLayoutGroup): TdxCustomLayoutItem;
|
|
function CreateSplitter(AGroup: TdxLayoutGroup): TdxCustomLayoutItem;
|
|
function CreateTabbedGroup(AGroup: TdxLayoutGroup): TdxCustomLayoutItem;
|
|
|
|
function GetGroupCaption: string;
|
|
function GetLabelCaption: string;
|
|
function IsAligned: Boolean;
|
|
function IsButton: Boolean;
|
|
function IsGroup: Boolean;
|
|
function IsHiddenGroup: Boolean;
|
|
function IsLabel: Boolean;
|
|
function IsSeparator: Boolean;
|
|
function IsSplitter: Boolean;
|
|
function IsPageControl: Boolean;
|
|
function IsStandaloneLabel: Boolean;
|
|
function GetFocusControl: TWinControl;
|
|
procedure SetLabelInfo(ALabel: TControl);
|
|
|
|
property Align: TAlign read FAlign;
|
|
property BoundsRect: TRect read FBoundsRect;
|
|
property Caption: string read FCaption;
|
|
property CaptionLayout: TdxCaptionLayout read FCaptionLayout;
|
|
property Control: TControl read FControl;
|
|
property Labeled: Boolean read FLabeled;
|
|
property LayoutControlItem: TdxCustomLayoutItem read FLayoutControlItem;
|
|
end;
|
|
|
|
{ TdxAutoLayoutItemList }
|
|
|
|
TdxAutoLayoutItemList = class(TList)
|
|
private
|
|
function GetItem(Index: Integer): TdxAutoLayoutItem;
|
|
public
|
|
function CanConvert(ASide: TcxBorder): Boolean;
|
|
function First: TdxAutoLayoutItem;
|
|
procedure FreeItems;
|
|
function IsButtons: Boolean;
|
|
function Last: TdxAutoLayoutItem;
|
|
|
|
property Items[Index: Integer]: TdxAutoLayoutItem read GetItem; default;
|
|
end;
|
|
|
|
{ TdxAutoLayoutOwnedItemList }
|
|
|
|
TdxAutoLayoutOwnedItemList = class(TcxObjectList)
|
|
private
|
|
FAutoLayout: TdxAutoLayout;
|
|
function GetItem(Index: Integer): TdxAutoLayoutItem;
|
|
public
|
|
constructor Create(AAutoLayout: TdxAutoLayout);
|
|
function Add(AControl: TControl): TdxAutoLayoutItem;
|
|
procedure DeleteItems(AItems: TdxAutoLayoutItemList);
|
|
procedure Extract(AIndex: Integer);
|
|
function Find(AControl: TControl): TdxAutoLayoutItem;
|
|
function Remove(AControl: TControl): Boolean;
|
|
procedure RemoveItems(AItems: TdxAutoLayoutItemList);
|
|
|
|
property Items[Index: Integer]: TdxAutoLayoutItem read GetItem; default;
|
|
end;
|
|
|
|
{ TdxAutoLayoutHelper }
|
|
|
|
TdxAutoLayoutHelper = class
|
|
private
|
|
FAlignedItems: TdxAutoLayoutOwnedItemList;
|
|
FAligns: set of TAlign;
|
|
FBottommostItem: TdxAutoLayoutItem;
|
|
FContainer: TWinControl;
|
|
FItems: TdxAutoLayoutOwnedItemList;
|
|
FLeftmostItem: TdxAutoLayoutItem;
|
|
FNonAlignedBoundsRect: TRect;
|
|
FOwner: TdxAutoLayout;
|
|
FRightmostItem: TdxAutoLayoutItem;
|
|
FRoot: TdxLayoutGroup;
|
|
FTopmostItem: TdxAutoLayoutItem;
|
|
function CanExport(AControl: TControl): Boolean;
|
|
function FindBottommostItem: TdxAutoLayoutItem;
|
|
function FindLeftmostItem: TdxAutoLayoutItem;
|
|
function FindRightmostItem: TdxAutoLayoutItem;
|
|
function FindTopmostItem: TdxAutoLayoutItem;
|
|
function GetControlAtPos(X, Y: Integer): TControl;
|
|
function GetFocusControl(AItem: TdxAutoLayoutItem): TWinControl;
|
|
function GetItemCount: Integer;
|
|
function HasItemsAtRect(const R: TRect): Boolean;
|
|
procedure MakeNewRoot;
|
|
procedure PopulateItems;
|
|
procedure PopulateItemsAtRect(AItems: TdxAutoLayoutItemList; const R: TRect);
|
|
procedure ProcessLabels;
|
|
function UpdateUtmostItems: TRect;
|
|
protected
|
|
procedure CheckRootDirection(ADirection: TdxLayoutDirection);
|
|
procedure ConvertGroup(AItems: TdxAutoLayoutItemList; ASide: TcxBorder);
|
|
procedure DoAlignedControls;
|
|
procedure DoNonAlignedControls;
|
|
procedure DoExecute; virtual;
|
|
procedure GenerateAligned(AAlign: TAlign);
|
|
procedure GenerateAlignedLayout; virtual;
|
|
procedure GenerateNonAlignedLayout; virtual;
|
|
procedure GetSideItemGroup(AItems: TdxAutoLayoutItemList; ASide: TcxBorder);
|
|
procedure Initialize(AContainer: TWinControl; ARoot: TdxLayoutGroup); virtual;
|
|
|
|
property Owner: TdxAutoLayout read FOwner;
|
|
property Root: TdxLayoutGroup read FRoot;
|
|
public
|
|
constructor Create(AOwner: TdxAutoLayout);
|
|
destructor Destroy; override;
|
|
procedure Execute(AContainer: TWinControl; ARoot: TdxLayoutGroup);
|
|
|
|
property Container: TWinControl read FContainer;
|
|
property ItemCount: Integer read GetItemCount;
|
|
property Items: TdxAutoLayoutOwnedItemList read FItems;
|
|
end;
|
|
|
|
{ TdxAutoLayout }
|
|
|
|
TdxAutoLayout = class
|
|
private
|
|
FFocusControlDistanceX: Byte;
|
|
FFocusControlDistanceY: Byte;
|
|
FControl: TdxCustomLayoutControl;
|
|
FConvertPageControls: Boolean;
|
|
FRecursive: Boolean;
|
|
FSmartFindFocusControl: Boolean;
|
|
FUseLabeledItems: Boolean;
|
|
protected
|
|
procedure CorrectAlign(AGroup: TdxLayoutGroup);
|
|
procedure DoExecute(AContainer: TWinControl; ARecursive: Boolean = True); virtual;
|
|
|
|
property Control: TdxCustomLayoutControl read FControl;
|
|
property Recursive: Boolean read FRecursive;
|
|
public
|
|
constructor Create(AControl: TdxCustomLayoutControl);
|
|
procedure Execute(AContainer: TWinControl; ARecursive: Boolean = True);
|
|
|
|
property ConvertPageControls: Boolean read FConvertPageControls write FConvertPageControls default True;
|
|
property FocusControlDistanceX: Byte read FFocusControlDistanceX write FFocusControlDistanceX default 48;
|
|
property FocusControlDistanceY: Byte read FFocusControlDistanceY write FFocusControlDistanceY default 16;
|
|
property SmartFindFocusControl: Boolean read FSmartFindFocusControl write FSmartFindFocusControl default True;
|
|
property UseLabeledItems: Boolean read FUseLabeledItems write FUseLabeledItems default True;
|
|
end;
|
|
|
|
{ TfmImport }
|
|
|
|
TfmImport = class(TForm)
|
|
seDeltaX: TcxSpinEdit;
|
|
seDeltaY: TcxSpinEdit;
|
|
btnImport: TcxButton;
|
|
btnCancel: TcxButton;
|
|
cbContainers: TcxComboBox;
|
|
cbUseLabeledItems: TcxCheckBox;
|
|
cbAssociate: TcxCheckBox;
|
|
cbConvertPageControls: TcxCheckBox;
|
|
dxLayoutControl1Group_Root: TdxLayoutGroup;
|
|
dxLayoutControl1: TdxLayoutControl;
|
|
dxLayoutControl1Item1: TdxLayoutItem;
|
|
dxLayoutControl1Item2: TdxLayoutItem;
|
|
dxLayoutControl1Item3: TdxLayoutItem;
|
|
dxLayoutControl1Group2: TdxLayoutGroup;
|
|
dxLayoutControl1Item4: TdxLayoutItem;
|
|
dxLayoutControl1Item5: TdxLayoutItem;
|
|
dxLayoutControl1Group4: TdxLayoutGroup;
|
|
dxLayoutControl1Item6: TdxLayoutItem;
|
|
dxLayoutControl1Item7: TdxLayoutItem;
|
|
dxLayoutControl1Item8: TdxLayoutItem;
|
|
dxLayoutControl1LabeledItem1: TdxLayoutLabeledItem;
|
|
dxLayoutControl1LabeledItem2: TdxLayoutLabeledItem;
|
|
dxLayoutControl1Group5: TdxLayoutGroup;
|
|
dxLayoutLookAndFeelList1: TdxLayoutLookAndFeelList;
|
|
dxLayoutCxLookAndFeel2: TdxLayoutCxLookAndFeel;
|
|
procedure cbAssociateClick(Sender: TObject);
|
|
public
|
|
end;
|
|
|
|
procedure ImportLayout(ALayoutControl: TdxLayoutControl);
|
|
|
|
implementation
|
|
|
|
uses
|
|
cxGeometry, Math;
|
|
|
|
{$R *.dfm}
|
|
|
|
type
|
|
TControlAccess = class(TControl);
|
|
TWinControlAccess = class(TWinControl);
|
|
TLabelAccess = class(TCustomLabel);
|
|
TcxLabelAccess = class(TcxCustomLabel);
|
|
TdxLayoutGroupAcsess = class(TdxLayoutGroup);
|
|
TdxLayoutControlAccess = class(TdxLayoutControl);
|
|
TdxCustomLayoutItemAccess = class(TdxCustomLayoutItem);
|
|
TdxLayoutItemAccess = class(TdxLayoutItem);
|
|
TStaticTextAccess = class(TCustomStaticText);
|
|
TcxGroupBoxAccess = class(TcxCustomGroupBox);
|
|
TGroupBoxAccess = class(TCustomGroupBox);
|
|
TPanelAccess = class(TCustomPanel);
|
|
|
|
{ TdxSideItem }
|
|
|
|
TdxSideItem = class
|
|
private
|
|
FItems: TdxAutoLayoutItemList;
|
|
FSide: TcxBorder;
|
|
function GetDirection: TdxLayoutDirection;
|
|
protected
|
|
FOrder: Integer;
|
|
public
|
|
constructor Create(AItems: TdxAutoLayoutItemList; ASide: TcxBorder);
|
|
destructor Destroy; override;
|
|
|
|
property Direction: TdxLayoutDirection read GetDirection;
|
|
property Items: TdxAutoLayoutItemList read FItems write FItems;
|
|
property Side: TcxBorder read FSide write FSide;
|
|
end;
|
|
|
|
{ TdxSideItem }
|
|
|
|
TdxSideItemList = class(TcxObjectList)
|
|
private
|
|
FCurrentOrder: Integer;
|
|
FHelper: TdxAutoLayoutHelper;
|
|
function GetItem(Index: Integer): TdxSideItem;
|
|
protected
|
|
procedure SetCreationOrder(AFirst, ALast: Integer);
|
|
property Helper: TdxAutoLayoutHelper read FHelper;
|
|
public
|
|
constructor Create(AHelper: TdxAutoLayoutHelper);
|
|
function Add(AItems: TdxAutoLayoutItemList; ASide: TcxBorder): TdxSideItem;
|
|
procedure SortCreationOrder;
|
|
procedure Build;
|
|
|
|
property Items[Index: Integer]: TdxSideItem read GetItem; default;
|
|
end;
|
|
|
|
function CompareTop(Item1, Item2: Pointer): Integer;
|
|
begin
|
|
Result := TdxAutoLayoutItem(Item1).BoundsRect.Top - TdxAutoLayoutItem(Item2).BoundsRect.Top;
|
|
end;
|
|
|
|
function CompareBottom(Item1, Item2: Pointer): Integer;
|
|
begin
|
|
Result := TdxAutoLayoutItem(Item2).BoundsRect.Bottom - TdxAutoLayoutItem(Item1).BoundsRect.Bottom;
|
|
end;
|
|
|
|
function CompareLeft(Item1, Item2: Pointer): Integer;
|
|
begin
|
|
Result := TdxAutoLayoutItem(Item1).BoundsRect.Left - TdxAutoLayoutItem(Item2).BoundsRect.Left;
|
|
end;
|
|
|
|
function CompareRight(Item1, Item2: Pointer): Integer;
|
|
begin
|
|
Result := TdxAutoLayoutItem(Item2).BoundsRect.Right - TdxAutoLayoutItem(Item1).BoundsRect.Right;
|
|
end;
|
|
|
|
{ TdxSideItem }
|
|
|
|
constructor TdxSideItem.Create(AItems: TdxAutoLayoutItemList; ASide: TcxBorder);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
FSide := ASide;
|
|
FItems := TdxAutoLayoutItemList.Create;
|
|
for I := 0 to AItems.Count - 1 do
|
|
FItems.Add(AItems[I]);
|
|
end;
|
|
|
|
destructor TdxSideItem.Destroy;
|
|
begin
|
|
FItems.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TdxSideItem.GetDirection: TdxLayoutDirection;
|
|
begin
|
|
if FSide in [bTop, bBottom] then
|
|
Result := ldHorizontal
|
|
else
|
|
Result := ldVertical;
|
|
end;
|
|
|
|
{ TdxSideItemList }
|
|
|
|
constructor TdxSideItemList.Create(AHelper: TdxAutoLayoutHelper);
|
|
begin
|
|
FHelper := AHelper;
|
|
end;
|
|
|
|
function TdxSideItemList.Add(AItems: TdxAutoLayoutItemList;
|
|
ASide: TcxBorder): TdxSideItem;
|
|
begin
|
|
Result := TdxSideItem.Create(AItems, ASide);
|
|
inherited Add(Result);
|
|
Helper.Items.DeleteItems(AItems);
|
|
end;
|
|
|
|
procedure TdxSideItemList.Build;
|
|
var
|
|
I: Integer;
|
|
ADirection: TdxLayoutDirection;
|
|
begin
|
|
if Count = 0 then Exit;
|
|
SortCreationOrder;
|
|
ADirection := Items[0].Direction;
|
|
Helper.CheckRootDirection(ADirection);
|
|
for I := 0 to Count - 1 do
|
|
begin
|
|
if ADirection <> Items[I].Direction then
|
|
begin
|
|
ADirection := Items[I].Direction;
|
|
Helper.CheckRootDirection(ADirection);
|
|
end;
|
|
Helper.ConvertGroup(Items[I].Items, Items[I].Side);
|
|
Application.ProcessMessages;
|
|
end;
|
|
end;
|
|
|
|
procedure TdxSideItemList.SetCreationOrder(AFirst, ALast: Integer);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
for I := AFirst to ALast do
|
|
if Items[I].Side in [bTop, bLeft] then
|
|
begin
|
|
Items[I].FOrder := FCurrentOrder;
|
|
Inc(FCurrentOrder);
|
|
end;
|
|
for I := ALast downto AFirst do
|
|
if Items[I].Side in [bRight, bBottom] then
|
|
begin
|
|
Items[I].FOrder := FCurrentOrder;
|
|
Inc(FCurrentOrder);
|
|
end;
|
|
end;
|
|
|
|
function CompareOrder(Item1, Item2: Pointer): Integer;
|
|
begin
|
|
Result := TdxSideItem(Item1).FOrder - TdxSideItem(Item2).FOrder;
|
|
end;
|
|
|
|
procedure TdxSideItemList.SortCreationOrder;
|
|
var
|
|
ADirection: TdxLayoutDirection;
|
|
AIndex, AFirst: Integer;
|
|
begin
|
|
if Count < 2 then
|
|
begin
|
|
SetCreationOrder(0, Count - 1);
|
|
Exit;
|
|
end;
|
|
FCurrentOrder := 0;
|
|
AIndex := 0;
|
|
repeat
|
|
AFirst := AIndex;
|
|
ADirection := Items[AFirst].Direction;
|
|
while (AIndex < Count) and (ADirection = Items[AIndex].Direction) do
|
|
Inc(AIndex);
|
|
SetCreationOrder(AFirst, AIndex - 1);
|
|
until AIndex = Count;
|
|
Sort(CompareOrder);
|
|
end;
|
|
|
|
function TdxSideItemList.GetItem(Index: Integer): TdxSideItem;
|
|
begin
|
|
Result := TdxSideItem(inherited Items[Index]);
|
|
end;
|
|
|
|
{ TdxAutoLayoutControl }
|
|
|
|
constructor TdxAutoLayoutItem.Create(AAutoLayout: TdxAutoLayout; AControl: TControl);
|
|
begin
|
|
FAutoLayout := AAutoLayout;
|
|
FControl := AControl;
|
|
FBoundsRect := Control.BoundsRect;
|
|
FLabeledBoundsRect := FBoundsRect;
|
|
FAlign := Control.Align;
|
|
end;
|
|
|
|
procedure TdxAutoLayoutItem.Assign(Source: TdxAutoLayoutItem);
|
|
begin
|
|
FControl := Source.Control;
|
|
FBoundsRect := Source.BoundsRect;
|
|
FLabeledBoundsRect := Source.FLabeledBoundsRect;
|
|
FAlign := Source.Align;
|
|
end;
|
|
|
|
procedure TdxAutoLayoutItem.CreateLayoutControlItem(AGroup: TdxLayoutGroup);
|
|
begin
|
|
if IsPageControl then
|
|
FLayoutControlItem := CreateTabbedGroup(AGroup)
|
|
else if IsGroup then
|
|
FLayoutControlItem := CreateGroup(AGroup)
|
|
else if IsSplitter then
|
|
FLayoutControlItem := CreateSplitter(AGroup)
|
|
else if IsSeparator then
|
|
FLayoutControlItem := CreateSeparator(AGroup)
|
|
else
|
|
FLayoutControlItem := CreateItem(AGroup);
|
|
end;
|
|
|
|
function TdxAutoLayoutItem.CanResize: Boolean;
|
|
begin
|
|
Result := not IsButton;
|
|
end;
|
|
|
|
function TdxAutoLayoutItem.CreateGroup(AGroup: TdxLayoutGroup): TdxLayoutGroup;
|
|
begin
|
|
Result := AGroup.CreateGroup;
|
|
if AGroup.LayoutDirection = ldVertical then
|
|
Result.AlignHorz := ahClient
|
|
else
|
|
Result.AlignVert := avClient;
|
|
Result.Caption := GetGroupCaption;
|
|
Result.Hidden := IsHiddenGroup;
|
|
if FAutoLayout.Recursive and (TWinControl(Control).ControlCount > 0) then
|
|
begin
|
|
with TdxAutoLayoutHelper.Create(FAutoLayout) do
|
|
try
|
|
Execute(TWinControl(Control), Result);
|
|
finally
|
|
Free;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TdxAutoLayoutItem.CreateItem(AGroup: TdxLayoutGroup): TdxCustomLayoutItem;
|
|
begin
|
|
if FAutoLayout.UseLabeledItems and IsStandaloneLabel then
|
|
begin
|
|
Result := AGroup.CreateItem(TdxLayoutLabeledItem);
|
|
Result.Caption := GetLabelCaption;
|
|
end
|
|
else
|
|
Result := AGroup.CreateItemForControl(Control);
|
|
end;
|
|
|
|
function TdxAutoLayoutItem.CreateSeparator(AGroup: TdxLayoutGroup): TdxCustomLayoutItem;
|
|
begin
|
|
Result := AGroup.CreateItem(TdxLayoutSeparatorItem);
|
|
end;
|
|
|
|
function TdxAutoLayoutItem.CreateSplitter(AGroup: TdxLayoutGroup): TdxCustomLayoutItem;
|
|
begin
|
|
Result := AGroup.CreateItem(TdxLayoutSplitterItem);
|
|
end;
|
|
|
|
function TdxAutoLayoutItem.CreateTabbedGroup(AGroup: TdxLayoutGroup): TdxCustomLayoutItem;
|
|
var
|
|
AControlGroup: TdxLayoutGroup;
|
|
AcxPageControl: TcxPageControl;
|
|
APageControl: TPageControl;
|
|
I, APageIndex: Integer;
|
|
begin
|
|
AControlGroup := AGroup.CreateGroup;
|
|
if AGroup.LayoutDirection = ldVertical then
|
|
AControlGroup.AlignHorz := ahClient
|
|
else
|
|
AControlGroup.AlignVert := avClient;
|
|
if not FAutoLayout.ConvertPageControls then
|
|
begin
|
|
Result := CreateItem(AControlGroup);
|
|
Exit;
|
|
end;
|
|
AControlGroup.LayoutDirection := ldTabbed;
|
|
if FAutoLayout.Recursive then
|
|
begin
|
|
if Control is TPageControl then
|
|
begin
|
|
APageControl := TPageControl(Control);
|
|
APageIndex := APageControl.ActivePageIndex;
|
|
for I := 0 to APageControl.PageCount - 1 do
|
|
begin
|
|
APageControl.ActivePageIndex := I;
|
|
Application.ProcessMessages;
|
|
AGroup := AControlGroup.CreateGroup;
|
|
AGroup.Caption := APageControl.Pages[I].Caption;
|
|
with TdxAutoLayoutHelper.Create(FAutoLayout) do
|
|
try
|
|
Execute(APageControl.Pages[I], AGroup);
|
|
finally
|
|
Free;
|
|
end;
|
|
end;
|
|
AControlGroup.ItemIndex := APageIndex;
|
|
end
|
|
else
|
|
if Control is TcxPageControl then
|
|
begin
|
|
AcxPageControl := TcxPageControl(Control);
|
|
APageIndex := AcxPageControl.ActivePageIndex;
|
|
for I := 0 to AcxPageControl.PageCount - 1 do
|
|
begin
|
|
AcxPageControl.ActivePageIndex := I;
|
|
Application.ProcessMessages;
|
|
AGroup := AControlGroup.CreateGroup;
|
|
AGroup.Caption := AcxPageControl.Pages[I].Caption;
|
|
with TdxAutoLayoutHelper.Create(FAutoLayout) do
|
|
try
|
|
Execute(AcxPageControl.Pages[I], AGroup);
|
|
finally
|
|
Free;
|
|
end;
|
|
end;
|
|
AControlGroup.ItemIndex := APageIndex;
|
|
end
|
|
end;
|
|
Result := AControlGroup;
|
|
end;
|
|
|
|
function TdxAutoLayoutItem.GetGroupCaption: string;
|
|
begin
|
|
if Control is TCustomGroupBox then
|
|
Result := TGroupBoxAccess(Control).Caption
|
|
else if Control is TcxCustomGroupBox then
|
|
Result := TcxGroupBox(Control).Caption
|
|
else if Control is TCustomPanel then
|
|
Result := TPanelAccess(Control).Caption
|
|
else
|
|
Result := '';
|
|
end;
|
|
|
|
function TdxAutoLayoutItem.GetLabelCaption: string;
|
|
begin
|
|
if Control is TCustomLabel then
|
|
Result := TLabelAccess(Control).Caption
|
|
else if Control is TCustomStaticText then
|
|
Result := TStaticTextAccess(Control).Caption
|
|
else if Control is TcxCustomLabel then
|
|
Result := TcxCustomLabel(Control).Caption
|
|
else
|
|
Result := '';
|
|
end;
|
|
|
|
function TdxAutoLayoutItem.IsAligned: Boolean;
|
|
begin
|
|
Result := Align in [alLeft, alTop, alRight, alBottom];
|
|
end;
|
|
|
|
function TdxAutoLayoutItem.IsButton: Boolean;
|
|
begin
|
|
Result := (Control is TcxCustomButton) or (Control is TButton);
|
|
end;
|
|
|
|
function TdxAutoLayoutItem.IsGroup: Boolean;
|
|
begin
|
|
Result := ((Control is TCustomGroupBox) or (Control is TCustomPanel) or
|
|
(Control is TcxCustomGroupBox)) //and (TWinControl(Control).ControlCount > 0);
|
|
end;
|
|
|
|
function TdxAutoLayoutItem.IsHiddenGroup: Boolean;
|
|
begin
|
|
if Control is TCustomGroupBox then
|
|
Result := False
|
|
else if Control is TCustomPanel then
|
|
with TPanelAccess(Control) do
|
|
Result := (BorderStyle = bsNone) and (BevelInner = bvNone) and
|
|
(BevelOuter = bvNone) {$IFDEF DELPHI9} and (BevelKind = bkNone){$ENDIF}
|
|
else if Control is TcxCustomGroupBox then
|
|
Result := TcxGroupBoxAccess(Control).Style.BorderStyle = ebsNone
|
|
else
|
|
Result := True;
|
|
end;
|
|
|
|
function TdxAutoLayoutItem.IsLabel: Boolean;
|
|
begin
|
|
Result := (Control is TCustomLabel) or (Control is TCustomStaticText) or
|
|
(Control is TcxCustomLabel);
|
|
end;
|
|
|
|
function TdxAutoLayoutItem.IsSeparator: Boolean;
|
|
begin
|
|
Result := Control is TBevel;
|
|
if Result then
|
|
Result := TBevel(Control).Shape in [bsTopLine, bsBottomLine, bsLeftLine, bsRightLine];
|
|
end;
|
|
|
|
function TdxAutoLayoutItem.IsSplitter: Boolean;
|
|
begin
|
|
Result := Control is TSplitter;
|
|
if not Result then
|
|
Result := Control is TcxSplitter;
|
|
end;
|
|
|
|
function TdxAutoLayoutItem.IsPageControl: Boolean;
|
|
begin
|
|
Result := (Control is TPageControl) or (Control is TcxPageControl);
|
|
end;
|
|
|
|
function TdxAutoLayoutItem.IsStandaloneLabel: Boolean;
|
|
begin
|
|
Result := IsLabel and not FLabeled;
|
|
end;
|
|
|
|
procedure TdxAutoLayoutItem.SetLabelInfo(ALabel: TControl);
|
|
|
|
function GetCaptionLayout(ALabel: TControl): TdxCaptionLayout;
|
|
begin
|
|
if ALabel.BoundsRect.Right <= Control.BoundsRect.Left then
|
|
Result := clLeft
|
|
else
|
|
if ALabel.BoundsRect.Left >= Control.BoundsRect.Right then
|
|
Result := clRight
|
|
else
|
|
if ALabel.BoundsRect.Bottom <= Control.BoundsRect.Top then
|
|
Result := clTop
|
|
else
|
|
Result := clBottom;
|
|
end;
|
|
|
|
procedure CorrectBounds;
|
|
begin
|
|
FLabeledBoundsRect.Left := Min(FBoundsRect.Left, ALabel.BoundsRect.Left);
|
|
FLabeledBoundsRect.Top := Min(FBoundsRect.Top, ALabel.BoundsRect.Top);
|
|
FLabeledBoundsRect.Right := Max(FBoundsRect.Right, ALabel.BoundsRect.Right);
|
|
FLabeledBoundsRect.Bottom := Max(FBoundsRect.Bottom, ALabel.BoundsRect.Bottom);
|
|
end;
|
|
|
|
begin
|
|
FCaption := TControlAccess(ALabel).Caption;
|
|
FCaptionLayout := GetCaptionLayout(ALabel);
|
|
FLabeled := True;
|
|
CorrectBounds;
|
|
end;
|
|
|
|
function TdxAutoLayoutItem.GetFocusControl: TWinControl;
|
|
begin
|
|
if Control is TcxCustomLabel then
|
|
Result := TcxLabelAccess(Control).FocusControl
|
|
else
|
|
if Control is TCustomLabel then
|
|
Result := TLabelAccess(Control).FocusControl
|
|
else
|
|
if Control is TCustomStaticText then
|
|
Result := TStaticTextAccess(Control).FocusControl
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
procedure TdxAutoLayoutItem.Apply(AProcessAnchors: Boolean = True);
|
|
begin
|
|
if Labeled and (LayoutControlItem is TdxLayoutItem) then
|
|
begin
|
|
TdxLayoutItem(LayoutControlItem).Caption := Caption;
|
|
TdxLayoutItem(LayoutControlItem).CaptionOptions.Layout := CaptionLayout;
|
|
end;
|
|
Control.Width := cxRectWidth(BoundsRect);
|
|
Control.Height := cxRectHeight(BoundsRect);
|
|
if AProcessAnchors then
|
|
ProcessAnchors;
|
|
end;
|
|
|
|
procedure TdxAutoLayoutItem.ProcessAnchors;
|
|
const
|
|
AlignHorzs: array[Boolean, Boolean] of TdxLayoutAlignHorz =
|
|
((ahLeft, ahRight), (ahLeft, ahClient));
|
|
AlignVerts: array[Boolean, Boolean] of TdxLayoutAlignVert =
|
|
((avTop, avBottom), (avTop, avClient));
|
|
begin
|
|
if LayoutControlItem.IsRoot then Exit;
|
|
with Control do
|
|
begin
|
|
LayoutControlItem.AlignHorz := AlignHorzs[akLeft in Anchors, akRight in Anchors];
|
|
LayoutControlItem.AlignVert := AlignVerts[akTop in Anchors, akBottom in Anchors];
|
|
end;
|
|
end;
|
|
|
|
{ TdxAutoLayoutItemList }
|
|
|
|
function TdxAutoLayoutItemList.CanConvert(ASide: TcxBorder): Boolean;
|
|
var
|
|
I, J: Integer;
|
|
begin
|
|
Result := Count > 0;
|
|
if Result then
|
|
begin
|
|
if ASide in [bTop, bBottom] then
|
|
begin
|
|
for I := 0 to Count - 1 do
|
|
for J := I + 1 to Count - 1 do
|
|
if (Items[I].BoundsRect.Top > Items[J].BoundsRect.Bottom) or
|
|
(Items[J].BoundsRect.Top > Items[I].BoundsRect.Bottom) then
|
|
begin
|
|
Result := False;
|
|
Break;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
for I := 0 to Count - 1 do
|
|
for J := I + 1 to Count - 1 do
|
|
if (Items[I].BoundsRect.Left > Items[J].BoundsRect.Right) or
|
|
(Items[J].BoundsRect.Left > Items[I].BoundsRect.Right) then
|
|
begin
|
|
Result := False;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TdxAutoLayoutItemList.First: TdxAutoLayoutItem;
|
|
begin
|
|
Result := Items[0];
|
|
end;
|
|
|
|
procedure TdxAutoLayoutItemList.FreeItems;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
for I := 0 to Count - 1 do
|
|
Items[I].Free;
|
|
Clear;
|
|
end;
|
|
|
|
function TdxAutoLayoutItemList.GetItem(Index: Integer): TdxAutoLayoutItem;
|
|
begin
|
|
Result := TdxAutoLayoutItem(inherited Items[Index]);
|
|
end;
|
|
|
|
function TdxAutoLayoutItemList.IsButtons: Boolean;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := Count > 0;
|
|
if Result then
|
|
for I := 0 to Count - 1 do
|
|
if not Items[I].IsButton then
|
|
begin
|
|
Result := False;
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
function TdxAutoLayoutItemList.Last: TdxAutoLayoutItem;
|
|
begin
|
|
Result := Items[Count - 1];
|
|
end;
|
|
|
|
{ TdxAutoLayoutOwnedItemList }
|
|
|
|
constructor TdxAutoLayoutOwnedItemList.Create(AAutoLayout: TdxAutoLayout);
|
|
begin
|
|
inherited Create;
|
|
FAutoLayout := AAutoLayout;
|
|
end;
|
|
|
|
function TdxAutoLayoutOwnedItemList.Add(AControl: TControl): TdxAutoLayoutItem;
|
|
begin
|
|
Result := TdxAutoLayoutItem.Create(FAutoLayout, AControl);
|
|
inherited Add(Result);
|
|
end;
|
|
|
|
procedure TdxAutoLayoutOwnedItemList.DeleteItems(AItems: TdxAutoLayoutItemList);
|
|
var
|
|
I, AIndex: Integer;
|
|
begin
|
|
for I := 0 to AItems.Count - 1 do
|
|
begin
|
|
AIndex := IndexOf(AItems[I]);
|
|
if AIndex >= 0 then
|
|
Delete(AIndex);
|
|
end;
|
|
end;
|
|
|
|
function TdxAutoLayoutOwnedItemList.Remove(AControl: TControl): Boolean;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := False;
|
|
for I := 0 to Count - 1 do
|
|
if AControl = Items[I].Control then
|
|
begin
|
|
Items[I].Free;
|
|
Delete(I);
|
|
Result := True;
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
procedure TdxAutoLayoutOwnedItemList.RemoveItems(AItems: TdxAutoLayoutItemList);
|
|
var
|
|
I, AIndex: Integer;
|
|
begin
|
|
for I := 0 to AItems.Count - 1 do
|
|
begin
|
|
AIndex := IndexOf(AItems[I]);
|
|
if AIndex >= 0 then
|
|
Extract(AIndex);
|
|
end;
|
|
end;
|
|
|
|
procedure TdxAutoLayoutOwnedItemList.Extract(AIndex: Integer);
|
|
begin
|
|
Items[AIndex].Free;
|
|
Delete(AIndex);
|
|
end;
|
|
|
|
function TdxAutoLayoutOwnedItemList.Find(AControl: TControl): TdxAutoLayoutItem;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := nil;
|
|
for I := 0 to Count - 1 do
|
|
if AControl = Items[I].Control then
|
|
begin
|
|
Result := Items[I];
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
function TdxAutoLayoutOwnedItemList.GetItem(Index: Integer): TdxAutoLayoutItem;
|
|
begin
|
|
Result := TdxAutoLayoutItem(inherited Items[Index]);
|
|
end;
|
|
|
|
{ TdxAutoLayoutHelper }
|
|
|
|
constructor TdxAutoLayoutHelper.Create(AOwner: TdxAutoLayout);
|
|
begin
|
|
inherited Create;
|
|
FOwner := AOwner;
|
|
FAlignedItems := TdxAutoLayoutOwnedItemList.Create(Owner);
|
|
FItems := TdxAutoLayoutOwnedItemList.Create(Owner);
|
|
end;
|
|
|
|
destructor TdxAutoLayoutHelper.Destroy;
|
|
begin
|
|
FreeAndNil(FItems);
|
|
FreeAndNil(FAlignedItems);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TdxAutoLayoutHelper.Execute(AContainer: TWinControl;
|
|
ARoot: TdxLayoutGroup);
|
|
begin
|
|
Initialize(AContainer, ARoot);
|
|
PopulateItems;
|
|
ProcessLabels;
|
|
DoExecute;
|
|
end;
|
|
|
|
procedure TdxAutoLayoutHelper.CheckRootDirection(ADirection: TdxLayoutDirection);
|
|
|
|
function GetRootDirection: TdxLayoutDirection;
|
|
begin
|
|
if ADirection = ldHorizontal then
|
|
Result := ldVertical
|
|
else
|
|
Result := ldHorizontal;
|
|
end;
|
|
|
|
begin
|
|
if Root.Count > 0 then
|
|
MakeNewRoot;
|
|
Root.LayoutDirection := GetRootDirection
|
|
end;
|
|
|
|
procedure TdxAutoLayoutHelper.ConvertGroup(AItems: TdxAutoLayoutItemList;
|
|
ASide: TcxBorder);
|
|
const
|
|
HAlign: array[TcxBorder] of TdxLayoutAlignHorz = (ahLeft, ahClient, ahRight, ahClient);
|
|
VAlign: array[TcxBorder] of TdxLayoutAlignVert = (avClient, avTop, avClient, avBottom);
|
|
|
|
function NewGroup: TdxLayoutGroup;
|
|
const
|
|
Directions: array[TcxBorder] of TdxLayoutDirection =
|
|
(ldVertical, ldHorizontal, ldVertical, ldHorizontal);
|
|
HAlign: array[TcxBorder] of TdxLayoutAlignHorz = (ahLeft, ahClient, ahRight, ahClient);
|
|
VAlign: array[TcxBorder] of TdxLayoutAlignVert = (avClient, avTop, avClient, avBottom);
|
|
begin
|
|
Result := Root.CreateGroup;
|
|
with Result do
|
|
begin
|
|
AlignHorz := HAlign[ASide];
|
|
AlignVert := VAlign[ASide];
|
|
Hidden := True;
|
|
LayoutDirection := Directions[ASide];
|
|
end;
|
|
end;
|
|
|
|
procedure AddItems(AGroup: TdxLayoutGroup);
|
|
var
|
|
I: Integer;
|
|
ACanProcessAnchors: Boolean;
|
|
begin
|
|
if (AItems.Count > 1) or not AItems[0].CanResize then
|
|
AGroup := NewGroup;
|
|
for I := 0 to AItems.Count - 1 do
|
|
AItems[I].CreateLayoutControlItem(AGroup);
|
|
ACanProcessAnchors := AItems.CanConvert(ASide);
|
|
for I := 0 to AItems.Count - 1 do
|
|
AItems[I].Apply(ACanProcessAnchors and not AItems[I].IsSeparator);
|
|
if (AItems.Count = 1) and (AItems[0].CanResize) then
|
|
begin
|
|
AItems[0].LayoutControlItem.AlignHorz := HAlign[ASide];
|
|
AItems[0].LayoutControlItem.AlignVert := VAlign[ASide];
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
if AItems.Count = 0 then Exit;
|
|
if ASide in [bTop, bBottom] then
|
|
AItems.Sort(CompareLeft)
|
|
else
|
|
AItems.Sort(CompareTop);
|
|
AddItems(Root);
|
|
Items.RemoveItems(AItems);
|
|
end;
|
|
|
|
procedure TdxAutoLayoutHelper.DoAlignedControls;
|
|
var
|
|
I: Integer;
|
|
AItem: TdxAutoLayoutItem;
|
|
begin
|
|
for I := ItemCount - 1 downto 0 do
|
|
begin
|
|
AItem := Items[I];
|
|
if AItem.IsAligned then
|
|
begin
|
|
FAlignedItems.Add(AItem.Control);
|
|
Items.Remove(AItem.Control);
|
|
end;
|
|
end;
|
|
GenerateAlignedLayout;
|
|
end;
|
|
|
|
procedure TdxAutoLayoutHelper.DoExecute;
|
|
begin
|
|
DoAlignedControls;
|
|
DoNonAlignedControls;
|
|
end;
|
|
|
|
procedure TdxAutoLayoutHelper.DoNonAlignedControls;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if ItemCount = 0 then Exit;
|
|
GenerateNonAlignedLayout;
|
|
for I := 0 to FAlignedItems.Count - 1 do
|
|
FAlignedItems[I].Apply;
|
|
end;
|
|
|
|
procedure TdxAutoLayoutHelper.Initialize(AContainer: TWinControl;
|
|
ARoot: TdxLayoutGroup);
|
|
begin
|
|
FRoot := ARoot;
|
|
FContainer := AContainer;
|
|
FAlignedItems.Clear;
|
|
FItems.Clear;
|
|
FAligns := [];
|
|
FNonAlignedBoundsRect := Container.ClientRect;
|
|
end;
|
|
|
|
procedure TdxAutoLayoutHelper.PopulateItems;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
for I := 0 to Container.ControlCount - 1 do
|
|
if CanExport(Container.Controls[I]) then
|
|
Items.Add(Container.Controls[I]);
|
|
end;
|
|
|
|
procedure TdxAutoLayoutHelper.PopulateItemsAtRect(AItems: TdxAutoLayoutItemList;
|
|
const R: TRect);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
AItems.Clear;
|
|
for I := 0 to ItemCount - 1 do
|
|
if cxRectIntersect(R, Items[I].BoundsRect) then
|
|
AItems.Add(Items[I]);
|
|
end;
|
|
|
|
procedure TdxAutoLayoutHelper.GenerateAligned(AAlign: TAlign);
|
|
var
|
|
L: TdxAutoLayoutItemList;
|
|
I: Integer;
|
|
ALastBounds: TRect;
|
|
begin
|
|
L := TdxAutoLayoutItemList.Create;
|
|
try
|
|
for I := 0 to FAlignedItems.Count - 1 do
|
|
if FAlignedItems[I].Align = AAlign then
|
|
L.Add(FAlignedItems[I]);
|
|
if L.Count > 1 then
|
|
case AAlign of
|
|
alTop: L.Sort(CompareTop);
|
|
alBottom: L.Sort(CompareBottom);
|
|
alLeft: L.Sort(CompareLeft);
|
|
alRight: L.Sort(CompareRight);
|
|
end;
|
|
if L.Count > 0 then
|
|
begin
|
|
Include(FAligns, AAlign);
|
|
ALastBounds := L.Last.BoundsRect;
|
|
case AAlign of
|
|
alTop: FNonAlignedBoundsRect.Top := ALastBounds.Bottom;
|
|
alBottom: FNonAlignedBoundsRect.Bottom := ALastBounds.Top;
|
|
alLeft: FNonAlignedBoundsRect.Left := ALastBounds.Right;
|
|
alRight: FNonAlignedBoundsRect.Right := ALastBounds.Bottom;
|
|
end;
|
|
end;
|
|
for I := 0 to L.Count - 1 do
|
|
L[I].CreateLayoutControlItem(Root);
|
|
finally
|
|
L.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TdxAutoLayoutHelper.GenerateAlignedLayout;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if FAlignedItems.Count = 0 then Exit;
|
|
GenerateAligned(alTop);
|
|
GenerateAligned(alBottom);
|
|
if FAligns * [alTop, alBottom] <> [] then
|
|
MakeNewRoot;
|
|
Root.LayoutDirection := ldHorizontal;
|
|
GenerateAligned(alLeft);
|
|
GenerateAligned(alRight);
|
|
if FAligns * [alLeft, alRight] <> [] then
|
|
MakeNewRoot;
|
|
Root.LayoutDirection := ldVertical;
|
|
GenerateAligned(alClient);
|
|
for I := 0 to FAlignedItems.Count - 1 do
|
|
FAlignedItems[I].Apply;
|
|
end;
|
|
|
|
procedure TdxAutoLayoutHelper.GenerateNonAlignedLayout;
|
|
var
|
|
ASides: array[TcxBorder] of TdxAutoLayoutItemList;
|
|
I, AMostSuitableSide: TcxBorder;
|
|
ATestSides: TcxBorders;
|
|
ASideOrder: TdxSideItemList;
|
|
|
|
function GetSideWeight(ASide: TcxBorder): Integer;
|
|
begin
|
|
Result := 0;
|
|
if not (ASide in ATestSides) then Exit;
|
|
Result := Result or (Ord(ASides[ASide].CanConvert(ASide)) shl 11);
|
|
Result := Result or ((ASides[ASide].Count and $FF) shl 3);
|
|
Result := Result or (Ord(ASides[ASide].IsButtons) shl 2);
|
|
Result := Result or (Ord(ASide) and $3);
|
|
end;
|
|
|
|
function GetMostSuitableSide: TcxBorder;
|
|
var
|
|
I: TcxBorder;
|
|
AWeight: Integer;
|
|
begin
|
|
Result := bLeft;
|
|
AWeight := GetSideWeight(Result);
|
|
for I := Succ(Low(TcxBorder)) to High(TcxBorder) do
|
|
if GetSideWeight(I) > AWeight then
|
|
begin
|
|
Result := I;
|
|
AWeight := GetSideWeight(Result);
|
|
end;
|
|
end;
|
|
|
|
procedure ProcessUngroupedItems(ASide: TcxBorder);
|
|
var
|
|
AUnroupedItems: TdxAutoLayoutItemList;
|
|
I: Integer;
|
|
begin
|
|
AUnroupedItems := TdxAutoLayoutItemList.Create;
|
|
try
|
|
for I := 0 to ItemCount - 1 do
|
|
AUnroupedItems.Add(Items[I]);
|
|
ConvertGroup(AUnroupedItems, ASide);
|
|
finally
|
|
AUnroupedItems.Free;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
AMostSuitableSide := bTop;
|
|
ASideOrder := TdxSideItemList.Create(Self);
|
|
try
|
|
for I := Low(TcxBorder) to High(TcxBorder) do
|
|
ASides[I] := TdxAutoLayoutItemList.Create;
|
|
try
|
|
while ItemCount > 0 do
|
|
begin
|
|
ATestSides := [];
|
|
for I := Low(TcxBorder) to High(TcxBorder) do
|
|
begin
|
|
GetSideItemGroup(ASides[I], I);
|
|
if ASides[I].CanConvert(I) then
|
|
Include(ATestSides, I);
|
|
end;
|
|
if ATestSides = [] then Break;
|
|
AMostSuitableSide := GetMostSuitableSide;
|
|
ASideOrder.Add(ASides[AMostSuitableSide], AMostSuitableSide);
|
|
if ItemCount = 1 then
|
|
begin
|
|
ASides[AMostSuitableSide].Clear;
|
|
ASides[AMostSuitableSide].Add(Items[0]);
|
|
ASideOrder.Add(ASides[AMostSuitableSide], AMostSuitableSide);
|
|
end;
|
|
end;
|
|
finally
|
|
for I := Low(TcxBorder) to High(TcxBorder) do
|
|
ASides[I].Free;
|
|
end;
|
|
ASideOrder.Build;
|
|
finally
|
|
ASideOrder.Free;
|
|
end;
|
|
ProcessUngroupedItems(AMostSuitableSide);
|
|
end;
|
|
|
|
procedure TdxAutoLayoutHelper.GetSideItemGroup(AItems: TdxAutoLayoutItemList;
|
|
ASide: TcxBorder);
|
|
|
|
procedure InitParams(out AIncX, AIncY, AEdge: Integer; out R: TRect);
|
|
const
|
|
DeltaX: array[TcxBorder] of Integer = (1, 0, -1, 0);
|
|
DeltaY: array[TcxBorder] of Integer = (0, 1, 0, -1);
|
|
var
|
|
APos: Integer;
|
|
begin
|
|
R := UpdateUtmostItems;
|
|
AIncX := DeltaX[ASide];
|
|
AIncY := DeltaY[ASide];
|
|
case ASide of
|
|
bTop:
|
|
begin
|
|
APos := FTopmostItem.BoundsRect.Bottom;
|
|
AEdge := R.Bottom;
|
|
R := cxRect(R.Left, APos, R.Right, APos + 1);
|
|
end;
|
|
bBottom:
|
|
begin
|
|
APos := FBottommostItem.BoundsRect.Top;
|
|
AEdge := R.Top;
|
|
R := cxRect(R.Left, APos, R.Right, APos + 1);
|
|
end;
|
|
bLeft:
|
|
begin
|
|
APos := FLeftmostItem.BoundsRect.Right;
|
|
AEdge := R.Right;
|
|
R := cxRect(APos, R.Top, APos + 1, R.Bottom);
|
|
end;
|
|
bRight:
|
|
begin
|
|
APos := FRightmostItem.BoundsRect.Left;
|
|
AEdge := R.Left;
|
|
R := cxRect(APos, R.Top, APos + 1, R.Bottom);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function GetItemsBounds(const ANonItemsBounds: TRect): TRect;
|
|
begin
|
|
with ANonItemsBounds do
|
|
begin
|
|
case ASide of
|
|
bTop:
|
|
Result := cxRect(Left, FTopmostItem.BoundsRect.Top, Right, Top);
|
|
bBottom:
|
|
Result := cxRect(Left, Top, Right, FBottommostItem.BoundsRect.Bottom);
|
|
bLeft:
|
|
Result := cxRect(FLeftmostItem.BoundsRect.Left, Top, Left, Bottom);
|
|
else //bRight:
|
|
Result := cxRect(Left + 1, Top, FRightmostItem.BoundsRect.Right, Bottom);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function Done(const R: TRect; AEdge: Integer): Boolean;
|
|
begin
|
|
case ASide of
|
|
bTop:
|
|
Result := R.Top > AEdge;
|
|
bBottom:
|
|
Result := R.Top < AEdge;
|
|
bLeft:
|
|
Result := R.Left > AEdge;
|
|
else //bRight:
|
|
Result := R.Left < AEdge;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
AIncX, AIncY, AEdge: Integer;
|
|
R: TRect;
|
|
begin
|
|
AItems.Clear;
|
|
if ItemCount = 0 then Exit;
|
|
InitParams(AIncX, AIncY, AEdge, R);
|
|
repeat
|
|
OffsetRect(R, AIncX, AIncY);
|
|
if not HasItemsAtRect(R) then
|
|
begin
|
|
PopulateItemsAtRect(AItems, GetItemsBounds(R));
|
|
if AItems.Count > 0 then
|
|
Exit;
|
|
end;
|
|
until Done(R, AEdge);
|
|
end;
|
|
|
|
function TdxAutoLayoutHelper.GetFocusControl(
|
|
AItem: TdxAutoLayoutItem): TWinControl;
|
|
var
|
|
I, X, Y, DX, DY: Integer;
|
|
AControlX, AControlY: TControl;
|
|
begin
|
|
Result := AItem.GetFocusControl;
|
|
if (Result = nil) and Owner.SmartFindFocusControl then
|
|
begin
|
|
AControlX := nil;
|
|
DX := MaxInt;
|
|
Y := (AItem.BoundsRect.Top + AItem.BoundsRect.Bottom) div 2;
|
|
for I := AItem.BoundsRect.Right + 1 to Container.ClientWidth do
|
|
begin
|
|
AControlX := GetControlAtPos(I, Y);
|
|
if AControlX <> nil then
|
|
begin
|
|
if not (AControlX is TWinControl) then
|
|
AControlX := nil
|
|
else
|
|
DX := AControlX.Left - AItem.BoundsRect.Right;
|
|
Break;
|
|
end;
|
|
end;
|
|
if (AControlX <> nil) and (DX < Owner.FocusControlDistanceX) then
|
|
begin
|
|
Result := TWinControl(AControlX);
|
|
Exit;
|
|
end;
|
|
AControlY := nil;
|
|
DY := MaxInt;
|
|
X := (AItem.BoundsRect.Right + AItem.BoundsRect.Left) div 2;
|
|
for I := AItem.BoundsRect.Bottom + 1 to Container.ClientHeight do
|
|
begin
|
|
AControlY := GetControlAtPos(X, I);
|
|
if AControlY <> nil then
|
|
begin
|
|
if not (AControlY is TWinControl) then
|
|
AControlY := nil
|
|
else
|
|
DY := AControlY.Top - AItem.BoundsRect.Bottom;
|
|
Break;
|
|
end;
|
|
end;
|
|
if (AControlY <> nil) and (DY < Owner.FocusControlDistanceY) then
|
|
Result := TWinControl(AControlY);
|
|
end;
|
|
end;
|
|
|
|
function TdxAutoLayoutHelper.CanExport(AControl: TControl): Boolean;
|
|
begin
|
|
Result := (AControl <> Owner.Control) and (AControl <> Container) and
|
|
not (csNoDesignVisible in AControl.ControlStyle) and
|
|
not (csSubComponent in AControl.ComponentStyle);
|
|
end;
|
|
|
|
function TdxAutoLayoutHelper.FindBottommostItem: TdxAutoLayoutItem;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := Items[0];
|
|
for I := 1 to ItemCount - 1 do
|
|
if Items[I].BoundsRect.Bottom > Result.BoundsRect.Bottom then
|
|
Result := Items[I];
|
|
end;
|
|
|
|
function TdxAutoLayoutHelper.FindLeftmostItem: TdxAutoLayoutItem;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := Items[0];
|
|
for I := 1 to ItemCount - 1 do
|
|
if Items[I].BoundsRect.Left < Result.BoundsRect.Left then
|
|
Result := Items[I];
|
|
end;
|
|
|
|
function TdxAutoLayoutHelper.FindRightmostItem: TdxAutoLayoutItem;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := Items[0];
|
|
for I := 1 to ItemCount - 1 do
|
|
if Items[I].BoundsRect.Right > Result.BoundsRect.Right then
|
|
Result := Items[I];
|
|
end;
|
|
|
|
function TdxAutoLayoutHelper.FindTopmostItem: TdxAutoLayoutItem;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := Items[0];
|
|
for I := 1 to ItemCount - 1 do
|
|
if Items[I].BoundsRect.Top < Result.BoundsRect.Top then
|
|
Result := Items[I];
|
|
end;
|
|
|
|
function TdxAutoLayoutHelper.GetControlAtPos(X, Y: Integer): TControl;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := nil;
|
|
for I := 0 to Container.ControlCount - 1 do
|
|
if (Owner.Control <> Container.Controls[I]) and
|
|
cxRectPtIn(Container.Controls[I].BoundsRect, X, Y) then
|
|
begin
|
|
Result := Container.Controls[I];
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
function TdxAutoLayoutHelper.GetItemCount: Integer;
|
|
begin
|
|
Result := Items.Count;
|
|
end;
|
|
|
|
function TdxAutoLayoutHelper.HasItemsAtRect(const R: TRect): Boolean;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := False;
|
|
for I := 0 to ItemCount - 1 do
|
|
if cxRectIntersect(R, Items[I].BoundsRect) then
|
|
begin
|
|
Result := True;
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
procedure TdxAutoLayoutHelper.MakeNewRoot;
|
|
begin
|
|
FRoot := FRoot.CreateGroup;
|
|
FRoot.AlignHorz := ahClient;
|
|
FRoot.AlignVert := avClient;
|
|
FRoot.Hidden := True;
|
|
end;
|
|
|
|
procedure TdxAutoLayoutHelper.ProcessLabels;
|
|
var
|
|
I: Integer;
|
|
AControl: TWinControl;
|
|
AItem: TdxAutoLayoutItem;
|
|
begin
|
|
for I := ItemCount - 1 downto 0 do
|
|
if Items[I].IsLabel then
|
|
begin
|
|
AControl := GetFocusControl(Items[I]);
|
|
if AControl <> nil then
|
|
begin
|
|
AItem := Items.Find(AControl);
|
|
if AItem <> nil then
|
|
begin
|
|
AItem.SetLabelInfo(Items[I].Control);
|
|
Items.Extract(I);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TdxAutoLayoutHelper.UpdateUtmostItems: TRect;
|
|
begin
|
|
FTopmostItem := FindTopmostItem;
|
|
FBottommostItem := FindBottommostItem;
|
|
FLeftmostItem := FindLeftmostItem;
|
|
FRightmostItem := FindRightmostItem;
|
|
Result := cxRect(FLeftmostItem.BoundsRect.Left, FTopmostItem.BoundsRect.Top,
|
|
FRightmostItem.BoundsRect.Right, FBottommostItem.BoundsRect.Bottom);
|
|
end;
|
|
|
|
{ TdxAutoLayout }
|
|
|
|
constructor TdxAutoLayout.Create(AControl: TdxCustomLayoutControl);
|
|
begin
|
|
inherited Create;
|
|
FControl := AControl;
|
|
FConvertPageControls := True;
|
|
FSmartFindFocusControl := True;
|
|
FFocusControlDistanceX := 48;
|
|
FFocusControlDistanceY := 16;
|
|
FUseLabeledItems := True;
|
|
end;
|
|
|
|
procedure TdxAutoLayout.DoExecute(AContainer: TWinControl; ARecursive: Boolean = True);
|
|
begin
|
|
FRecursive := ARecursive;
|
|
with TdxAutoLayoutHelper.Create(Self) do
|
|
try
|
|
Control.BeginUpdate;
|
|
try
|
|
Execute(AContainer, Control.Items);
|
|
CorrectAlign(Control.Items);
|
|
finally
|
|
Control.EndUpdate;
|
|
end;
|
|
finally
|
|
Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TdxAutoLayout.Execute(AContainer: TWinControl; ARecursive: Boolean = True);
|
|
begin
|
|
if AContainer = nil then
|
|
Exit;
|
|
AContainer.DisableAlign;
|
|
try
|
|
DoExecute(AContainer, ARecursive);
|
|
finally
|
|
AContainer.EnableAlign;
|
|
end;
|
|
end;
|
|
|
|
procedure TdxAutoLayout.CorrectAlign(AGroup: TdxLayoutGroup);
|
|
|
|
procedure DoCorrectAlign(AItem: TdxCustomLayoutItem);
|
|
begin
|
|
if AItem.AlignHorz = TdxCustomLayoutItemAccess(AItem).GetParentManagedAlignHorz then
|
|
AItem.AlignHorz := ahParentManaged;
|
|
if AItem.AlignVert = TdxCustomLayoutItemAccess(AItem).GetParentManagedAlignVert then
|
|
AItem.AlignVert := avParentManaged;
|
|
end;
|
|
|
|
var
|
|
I: Integer;
|
|
begin
|
|
for I := 0 to AGroup.Count -1 do
|
|
begin
|
|
if AGroup[I] is TdxLayoutGroup then
|
|
CorrectAlign(TdxLayoutGroup(AGroup[I]));
|
|
DoCorrectAlign(AGroup[I]);
|
|
end;
|
|
end;
|
|
|
|
procedure ImportLayout(ALayoutControl: TdxLayoutControl);
|
|
|
|
function GetRoot: TWinControl;
|
|
begin
|
|
Result := ALayoutControl.Owner as TWinControl;
|
|
end;
|
|
|
|
function CanExport(AControl: TControl): Boolean;
|
|
begin
|
|
Result := (AControl <> ALayoutControl) and
|
|
not (csNoDesignVisible in AControl.ControlStyle) and not (csSubComponent in AControl.ComponentStyle);
|
|
end;
|
|
|
|
function AddItems(AControl: TWinControl; AStrings: TStrings;
|
|
AInsertionIndex: Integer): Boolean;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
with AControl do
|
|
begin
|
|
Result := CanExport(AControl) and (csAcceptsControls in ControlStyle);
|
|
if Result then
|
|
begin
|
|
AStrings.InsertObject(AInsertionIndex, Name, AControl);
|
|
AInsertionIndex := AStrings.Count;
|
|
for I := 0 to ControlCount - 1 do
|
|
if Controls[I] is TWinControl then
|
|
if AddItems(TWinControl(Controls[I]), AStrings, AInsertionIndex) then
|
|
Inc(AInsertionIndex);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
ASaveCursor: TCursor;
|
|
begin
|
|
if ALayoutControl = nil then Exit;
|
|
with TfmImport.Create(nil) do
|
|
try
|
|
AddItems(GetRoot, cbContainers.Properties.Items, 0);
|
|
if cbContainers.Properties.Items.Count <> 0 then
|
|
cbContainers.ItemIndex := 0
|
|
else
|
|
btnImport.Enabled := False;
|
|
seDeltaX.Value := 48;
|
|
seDeltaY.Value := 16;
|
|
if ShowModal = mrOk then
|
|
begin
|
|
with TdxAutoLayout.Create(ALayoutControl) do
|
|
try
|
|
ConvertPageControls := cbConvertPageControls.Checked;
|
|
SmartFindFocusControl := cbAssociate.Checked;
|
|
UseLabeledItems := cbUseLabeledItems.Checked;
|
|
FocusControlDistanceX := seDeltaX.Value;
|
|
FocusControlDistanceY := seDeltaY.Value;
|
|
ASaveCursor := Screen.Cursor;
|
|
Screen.Cursor := crHourGlass;
|
|
try
|
|
Execute(TWinControl(cbContainers.Properties.Items.Objects[cbContainers.ItemIndex]), True);
|
|
finally
|
|
Screen.Cursor := ASaveCursor;
|
|
end;
|
|
finally
|
|
Free;
|
|
end;
|
|
ALayoutControl.AutoSize := True
|
|
end;
|
|
finally
|
|
Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TfmImport.cbAssociateClick(Sender: TObject);
|
|
begin
|
|
seDeltaX.Enabled := cbAssociate.Checked;
|
|
seDeltaY.Enabled := cbAssociate.Checked;
|
|
end;
|
|
|
|
end.
|