{********************************************************************} { } { Developer Express Visual Component Library } { ExpressLayoutControl common routines } { } { 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 dxLayoutDragAndDrop; {$I cxVer.inc} interface uses Classes, Controls, Windows, Graphics, cxClasses, cxControls, cxGraphics, dxLayoutControl; type TdxLayoutDragSource = (dsControl, dsCustomizeFormAvailableItems, dsCustomizeFormTreeViewItems); { TdxLayoutDragAndDropObject } TdxLayoutCustomDragAndDropObject = class(TcxDragAndDropObject) private FSourceItem: TdxCustomLayoutItem; FStartDragPoint: TPoint; function GetSourceControl: TdxCustomLayoutControl; protected procedure Modified; property SourceControl: TdxCustomLayoutControl read GetSourceControl; public procedure Init(ASource: TdxLayoutDragSource; ASourceItem: TdxCustomLayoutItem; const P: TPoint); virtual; property SourceItem: TdxCustomLayoutItem read FSourceItem write FSourceItem; end; TdxLayoutDragAndDropObject = class(TdxLayoutCustomDragAndDropObject) private FAreaPart: TdxLayoutAreaPart; FDragImage: TcxDragImage; FDragImageOffset: TPoint; FDragImagePoint: TPoint; FDestinationControl: TdxCustomLayoutControl; FDestinationGroup: TdxLayoutGroup; FDestinationImage: TcxDragImage; FDestinationItem: TdxCustomLayoutItem; FHitTest: TdxCustomLayoutHitTest; FNeedRepaintDestinationImage: Boolean; FSource: TdxLayoutDragSource; FSourceItemBounds: TRect; procedure ResetDragAndDropObjects; procedure SetAreaPart(Value: TdxLayoutAreaPart); procedure SetDestinationControl(Value: TdxCustomLayoutControl); procedure SetDestinationGroup(Value: TdxLayoutGroup); procedure SetDestinationItem(Value: TdxCustomLayoutItem); protected procedure CreateDragImage; procedure CreateDestinationImage; procedure DirtyChanged; override; function GetDragAndDropCursor(Accepted: Boolean): TCursor; override; procedure PaintDestinationImage; procedure PaintDragImage; procedure RefreshDestinationImage; procedure ShowDragImage; procedure UpdateStates; property AreaPart: TdxLayoutAreaPart read FAreaPart write SetAreaPart; property DestinationControl: TdxCustomLayoutControl read FDestinationControl write SetDestinationControl; property DestinationGroup: TdxLayoutGroup read FDestinationGroup write SetDestinationGroup; property DestinationItem: TdxCustomLayoutItem read FDestinationItem write SetDestinationItem; property Source: TdxLayoutDragSource read FSource write FSource; property SourceItemBounds: TRect read FSourceItemBounds; public constructor Create(AControl: TcxControl); override; destructor Destroy; override; function CanDrop: Boolean; function CanRemove: Boolean; procedure BeginDragAndDrop; override; procedure DragAndDrop(const P: TPoint; var Accepted: Boolean); override; procedure EndDragAndDrop(Accepted: Boolean); override; procedure Init(ASource: TdxLayoutDragSource; ASourceItem: TdxCustomLayoutItem; const P: TPoint); override; end; { TdxLayoutDragAndDropObject } TdxLayoutCustomSizingDragAndDropObject = class(TdxLayoutCustomDragAndDropObject) protected procedure RestoreSize; virtual; public procedure BeginDragAndDrop; override; procedure EndDragAndDrop(Accepted: Boolean); override; end; TdxLayoutSizingDragAndDropObject = class(TdxLayoutCustomSizingDragAndDropObject) // cxinplaceContainer.TcxSizingDragAndDropObject private FOriginalSize: TSize; FOriginalBounds: TRect; FMarkerIndex: Integer; protected procedure RestoreSize; override; public constructor Create(AControl: TcxControl); override; destructor Destroy; override; procedure DragAndDrop(const P: TPoint; var Accepted: Boolean); override; procedure Init(ASource: TdxLayoutDragSource; ASourceItem: TdxCustomLayoutItem; const P: TPoint); override; end; TdxLayoutSizingStrategy = (lssLeft, lssRight, lssClient, lssNone); TdxLayoutSplitterDragAndDropObject = class(TdxLayoutCustomSizingDragAndDropObject) private FParentItem: TdxLayoutGroup; FLeftItem: TdxCustomLayoutItem; FRightItem: TdxCustomLayoutItem; FOriginalSizes: array of TPoint; FSizingStrategy: TdxLayoutSizingStrategy; FLeftItemSize: Integer; FRightItemSize: Integer; FPrevPoint: TPoint; protected procedure RestoreSize; override; function GetWorkAlign(AViewInfo: TdxCustomLayoutItemViewInfo): TdxLayoutAlignHorz; virtual; abstract; function GetItemSize(AViewInfo: TdxCustomLayoutItemViewInfo): Integer; virtual; abstract; function GetSignificantValue(const P: TPoint): Integer; virtual; abstract; procedure SetItemSize(AViewInfo: TdxCustomLayoutItemViewInfo; ASize: Integer); virtual; abstract; public procedure DragAndDrop(const P: TPoint; var Accepted: Boolean); override; procedure Init(ASource: TdxLayoutDragSource; ASourceItem: TdxCustomLayoutItem; const P: TPoint); override; end; TdxLayoutHSplitterDragAndDropObject = class(TdxLayoutSplitterDragAndDropObject) protected function GetWorkAlign(AViewInfo: TdxCustomLayoutItemViewInfo): TdxLayoutAlignHorz; override; function GetItemSize(AViewInfo: TdxCustomLayoutItemViewInfo): Integer; override; function GetSignificantValue(const P: TPoint): Integer; override; procedure SetItemSize(AViewInfo: TdxCustomLayoutItemViewInfo; ASize: Integer); override; end; TdxLayoutVSplitterDragAndDropObject = class(TdxLayoutSplitterDragAndDropObject) protected function GetWorkAlign(AViewInfo: TdxCustomLayoutItemViewInfo): TdxLayoutAlignHorz; override; function GetItemSize(AViewInfo: TdxCustomLayoutItemViewInfo): Integer; override; function GetSignificantValue(const P: TPoint): Integer; override; procedure SetItemSize(AViewInfo: TdxCustomLayoutItemViewInfo; ASize: Integer); override; end; { TdxLayoutDragAndDropHelper } TdxLayoutDragAndDropHelper = class(TdxLayoutControlPersistent) private FDragItem: TdxCustomLayoutItem; FMouseDownPos: TPoint; procedure BeginDragAndDrop(ASource: TdxLayoutDragSource); function CanBeginDragAndDrop(X, Y: Integer): Boolean; function GetDragAndDropObject: TdxLayoutDragAndDropObject; protected property DragAndDropObject: TdxLayoutDragAndDropObject read GetDragAndDropObject; public constructor Create(AControl: TdxCustomLayoutControl); override; procedure InitializeDragItem(AItem: TdxCustomLayoutItem; X, Y: Integer); procedure Reset; procedure TryBeginDragAndDrop(X, Y: Integer; ASource: TdxLayoutDragSource); property DragItem: TdxCustomLayoutItem read FDragItem; end; function dxLayoutDragAndDropObject: TdxLayoutDragAndDropObject; function dxLayoutSizingDragAndDropObject: TdxLayoutSizingDragAndDropObject; implementation uses Types, SysUtils, Forms, Math, cxLibraryConsts, cxGeometry; const dxLayoutSelectionBorderDefaultColor: TColor = $BD8753; dxLayoutDestinationColor = $D0D0EE; dxLayoutDestinationBorderColor = $000059; dxCustomizeFormHitTestCodes = [htCustomizeForm, htAvailableItems, htTreeViewItems]; var FLayoutDragAndDropObject: TdxLayoutDragAndDropObject; FLayoutSizingDragAndDropObject: TdxLayoutSizingDragAndDropObject; type TdxCustomLayoutControlAccess = class(TdxCustomLayoutControl); TdxCustomLayoutItemAccess = class(TdxCustomLayoutItem); TdxLayoutGroupAccess = class(TdxLayoutGroup); TdxCustomLayoutItemViewInfoAccess = class(TdxCustomLayoutItemViewInfo); TdxLayoutGroupViewInfoAccess = class(TdxLayoutGroupViewInfo); TdxLayoutControlViewInfoAccess = class(TdxLayoutControlViewInfo); TdxCustomLayoutItemPainterAccess = class(TdxCustomLayoutItemPainter); function dxLayoutDragAndDropObject: TdxLayoutDragAndDropObject; begin Result := FLayoutDragAndDropObject; end; function dxLayoutSizingDragAndDropObject: TdxLayoutSizingDragAndDropObject; begin Result := FLayoutSizingDragAndDropObject; end; { TdxLayoutCustomDragAndDropObject } procedure TdxLayoutCustomDragAndDropObject.Init(ASource: TdxLayoutDragSource; ASourceItem: TdxCustomLayoutItem; const P: TPoint); begin FSourceItem := ASourceItem; FStartDragPoint := P; end; procedure TdxLayoutCustomDragAndDropObject.Modified; begin TdxCustomLayoutControlAccess(SourceControl).Modified; end; function TdxLayoutCustomDragAndDropObject.GetSourceControl: TdxCustomLayoutControl; begin Result := inherited Control as TdxCustomLayoutControl; end; { TdxLayoutDragAndDropObject } constructor TdxLayoutDragAndDropObject.Create(AControl: TcxControl); begin inherited; FLayoutDragAndDropObject := Self; end; destructor TdxLayoutDragAndDropObject.Destroy; begin FLayoutDragAndDropObject := nil; inherited; end; function TdxLayoutDragAndDropObject.CanDrop: Boolean; begin Result := (DestinationGroup <> nil) and SourceItem.CanMoveTo(DestinationGroup) and not TdxLayoutGroupAccess(DestinationGroup).IsLocked; // (DestinationItem <> nil) or (FHitTest.HitTestCode in // [htClientArea, htGroup, htItem]); end; function TdxLayoutDragAndDropObject.CanRemove: Boolean; begin Result := (FHitTest.HitTestCode in [htAvailableItems, htNone]) and TdxCustomLayoutItemAccess(SourceItem).CanRemove and (SourceItem.Parent <> nil); end; procedure TdxLayoutDragAndDropObject.ResetDragAndDropObjects; begin SourceItem := nil; DestinationItem := nil; DestinationGroup := nil; FHitTest := nil; TdxCustomLayoutControlAccess(SourceControl).CustomizeFormPostUpdate([cfutDragAndDropState]); end; procedure TdxLayoutDragAndDropObject.SetAreaPart(Value: TdxLayoutAreaPart); begin if FAreaPart <> Value then begin FAreaPart := Value; Dirty := True; end; end; procedure TdxLayoutDragAndDropObject.SetDestinationControl(Value: TdxCustomLayoutControl); begin if FDestinationControl <> Value then begin FDestinationControl := Value; Dirty := True; end; end; procedure TdxLayoutDragAndDropObject.SetDestinationGroup(Value: TdxLayoutGroup); begin if FDestinationGroup <> Value then begin if FDestinationGroup <> nil then FreeAndNil(FDestinationImage); FDestinationGroup := Value; if FDestinationGroup <> nil then CreateDestinationImage; Dirty := True; end; end; procedure TdxLayoutDragAndDropObject.SetDestinationItem(Value: TdxCustomLayoutItem); begin if FDestinationItem <> Value then begin if FDestinationItem <> nil then begin if FHitTest.HitTestCode in dxCustomizeFormHitTestCodes then DestinationItem.Container.CustomizeForm.ToggleHotTrackState(DestinationItem); end; FDestinationItem := Value; if FDestinationItem <> nil then begin if FHitTest.HitTestCode in dxCustomizeFormHitTestCodes then DestinationItem.Container.CustomizeForm.ToggleHotTrackState(DestinationItem); end; Dirty := True; end; end; procedure TdxLayoutDragAndDropObject.CreateDragImage; begin FDragImage := TcxDragImage.Create; PaintDragImage; end; procedure TdxLayoutDragAndDropObject.CreateDestinationImage; var R: TRect; begin if (DestinationItem = nil) or TdxCustomLayoutItemAccess(DestinationItem).IsAvailable then Exit; FDestinationImage := TcxDragImage.Create; FDestinationImage.PopupParent := GetParentForm(DestinationControl); R := DestinationGroup.ViewInfo.SelectionArea; cxRectIntersect(R, R, DestinationControl.ClientRect); OffsetRect(R, -R.Left, -R.Top); FDestinationImage.SetBounds(R.Left, R.Top, R.Right, R.Bottom); end; procedure TdxLayoutDragAndDropObject.DirtyChanged; begin inherited DirtyChanged; FNeedRepaintDestinationImage := True; end; function TdxLayoutDragAndDropObject.GetDragAndDropCursor(Accepted: Boolean): TCursor; begin if CanDrop then Result := crDefault else if CanRemove then Result := crdxLayoutControlRemove else Result := crdxLayoutControlNoDrop; end; procedure TdxLayoutDragAndDropObject.PaintDestinationImage; const ABorderWidth = 1; var R: TRect; ATargetRect: TRect; AOffset: Integer; begin FDestinationImage.Canvas.SaveState; try R := FDestinationImage.ClientRect; FDestinationImage.Canvas.FrameRect(R, dxLayoutDestinationBorderColor); InflateRect(R, -ABorderWidth, -ABorderWidth); FDestinationImage.Canvas.FillRect(R, dxLayoutDestinationColor); ATargetRect := TdxLayoutGroupViewInfoAccess(DestinationGroup.ViewInfo).GetAreaPartBounds(AreaPart, DestinationItem.ViewInfo); cxRectIntersect(R, DestinationGroup.ViewInfo.SelectionArea, DestinationControl.ClientRect); with R do ATargetRect := cxRectOffset(ATargetRect, -Left, -Top); R := FDestinationImage.ClientRect; AOffset := ABorderWidth + 1; R := cxRectInflate(R, -AOffset, -AOffset); if cxRectWidth(ATargetRect) < cxRectHeight(ATargetRect) then begin if ATargetRect.Left < R.Left then ATargetRect := cxRectOffset(ATargetRect, R.Left - ATargetRect.Left, 0); if ATargetRect.Right > R.Right then ATargetRect := cxRectOffset(ATargetRect, R.Right - ATargetRect.Right, 0); end else begin if ATargetRect.Top < R.Top then ATargetRect := cxRectOffset(ATargetRect, 0, R.Top - ATargetRect.Top); if ATargetRect.Bottom > R.Bottom then ATargetRect := cxRectOffset(ATargetRect, 0, R.Bottom - ATargetRect.Bottom); end; FDestinationImage.Canvas.FrameRect(ATargetRect, dxLayoutDestinationBorderColor); finally FDestinationImage.Canvas.RestoreState; end; FNeedRepaintDestinationImage := False; end; procedure TdxLayoutDragAndDropObject.PaintDragImage; var AViewInfo: TdxCustomLayoutItemViewInfo; function GetItemDragBounds: TRect; begin Result := AViewInfo.SelectionBorderRect; end; procedure CalculateDragImageOffset; begin {#DG if Source = dsControl then FDragImageOffset := cxPointOffset(GetItemDragBounds.TopLeft, SourceControl.ScreenToClient(FStartDragPoint), False) else } FDragImageOffset := cxNullPoint; end; function GetViewInfoBounds(AHasBorderChanged: Boolean): TRect; begin if TdxCustomLayoutItemAccess(SourceItem).IsAvailable or not TdxCustomLayoutItemViewInfoAccess(AViewInfo).ActuallyVisible then Result := Rect(-TdxCustomLayoutItemViewInfoAccess(AViewInfo).CalculateWidth, -TdxCustomLayoutItemViewInfoAccess(AViewInfo).CalculateHeight, 0, 0) else begin Result := TdxCustomLayoutItemViewInfoAccess(AViewInfo).OriginalBounds; if AHasBorderChanged then Result := TdxLayoutGroupViewInfoAccess(AViewInfo).GetItemAreaBounds(Result); end; end; var ANeedDestroyViewInfo: Boolean; APrevHasBorder: Boolean; APrevBounds: TRect; begin if not TdxCustomLayoutItemAccess(SourceItem).ActuallyVisible then begin AViewInfo := TdxCustomLayoutItemAccess(SourceItem).GetViewInfoClass.Create(SourceControl.ViewInfo, nil, SourceItem); ANeedDestroyViewInfo := True; end else begin AViewInfo := SourceItem.ViewInfo; ANeedDestroyViewInfo := False; APrevBounds := AViewInfo.Bounds; end; try APrevHasBorder := TdxCustomLayoutItemViewInfoAccess(SourceItem.ViewInfo).HasBorder; TdxLayoutControlViewInfoAccess(SourceControl.ViewInfo).IsDragImagePainted := True; try AViewInfo.Calculate(GetViewInfoBounds(TdxCustomLayoutItemViewInfoAccess(SourceItem.ViewInfo).HasBorder <> APrevHasBorder)); FDragImage.SetBounds(0, 0, cxRectWidth(GetItemDragBounds), cxRectHeight(GetItemDragBounds)); FDragImage.Canvas.WindowOrg := GetItemDragBounds.TopLeft; try with TdxCustomLayoutItemViewInfoAccess(AViewInfo).GetPainterClass.Create(FDragImage.Canvas, AViewInfo) do try PaintDragImage; finally Free; end; finally FDragImage.Canvas.WindowOrg := cxNullPoint; end; CalculateDragImageOffset; finally TdxLayoutControlViewInfoAccess(SourceControl.ViewInfo).IsDragImagePainted := False; end; finally if ANeedDestroyViewInfo then AViewInfo.Destroy else AViewInfo.Calculate(APrevBounds); end; end; procedure TdxLayoutDragAndDropObject.RefreshDestinationImage; var P: TPoint; begin if (DestinationGroup = nil) or (DestinationItem = nil) or (AreaPart = apNone) or (FHitTest.HitTestCode in dxCustomizeFormHitTestCodes) then Exit; PaintDestinationImage; if FDestinationImage.Visible then FDestinationImage.Invalidate else begin P := DestinationGroup.ViewInfo.SelectionArea.TopLeft; P.X := Max(P.X, DestinationControl.ClientRect.Left); P.Y := Max(P.Y, DestinationControl.ClientRect.Top); P := DestinationControl.ClientToScreen(P); FDestinationImage.MoveTo(P); FDestinationImage.Show(SW_SHOWNOACTIVATE); end; end; procedure TdxLayoutDragAndDropObject.ShowDragImage; begin if FDragImage <> nil then begin FDragImage.MoveTo(FDragImagePoint); FDragImage.Show; end; end; procedure TdxLayoutDragAndDropObject.UpdateStates; function FindDestLayoutControl(const P: TPoint): TdxCustomLayoutControl; var AControl: TWinControl; AWnd: THandle; begin Result := nil; if SourceControl.IsDesigning then begin AWnd := cxWindowFromPoint(P); while (Result = nil) and (AWnd <> 0) do begin AControl := FindControl(AWnd); if (AControl is TdxCustomLayoutControl) and (AControl.Owner = SourceControl.Owner) and TdxCustomLayoutControlAccess(AControl).AllowDrop then Result := TdxLayoutControl(AControl); AWnd := GetAncestor(AWnd, GA_PARENT); end; end; if Result = nil then Result := SourceControl; end; procedure DetermineDestinationControl(AHitControl: TdxCustomLayoutControl); begin if DestinationItem = nil then DestinationControl := AHitControl else DestinationControl := DestinationItem.Container; end; procedure DetermineDestinationItem; var ADestinationItem: TdxCustomLayoutItem; begin // DetermineDestinationItems(ADestItems); // UpdatedestinationPlaces(AScreenPoint, ADestItems, ADestinationItem); ADestinationItem := FHitTest.GetDestinationItem; if (ADestinationItem = SourceItem) or ((ADestinationItem is TdxLayoutGroup) and not SourceItem.CanMoveTo(ADestinationItem)) then ADestinationItem := nil; DestinationItem := ADestinationItem; if (DestinationItem <> nil) and (DestinationItem.Container <> DestinationControl) then DestinationControl := DestinationItem.Container; end; procedure DetermineAreaPart(const P: TPoint); const CustomizeFormTreeViewItemAreaPart: array [TdxLayoutDirection] of TdxLayoutAreaPart = (apRight, apBottom, apBottom); var AAreaPart: TdxLayoutAreaPart; begin AAreaPart := apNone; if FHitTest.IsDeterminedAreaPart then AAreaPart := FHitTest.GetAreaPart else if (DestinationItem <> nil) then AAreaPart := TdxCustomLayoutItemViewInfoAccess(DestinationItem.ViewInfo).GetAreaPart(P); AreaPart := AAreaPart; end; procedure DetermineDestinationGroup; begin if DestinationItem = nil then DestinationGroup := nil else begin if DestinationItem.IsRoot or (AreaPart in [apCenter, apFirstChild, apLastChild]) then DestinationGroup := TdxLayoutGroup(DestinationItem) else DestinationGroup := DestinationItem.Parent; end; end; procedure DetermineHitTest(AHitControl: TdxCustomLayoutControl; const P: TPoint); begin FHitTest := AHitControl.GetHitTest(AHitControl.ScreenToClient(P)); end; var AScreenPoint, ADestPoint: TPoint; AHitControl: TdxCustomLayoutControl; begin AScreenPoint := GetMouseCursorPos; AHitControl := FindDestLayoutControl(AScreenPoint); FDragImagePoint := cxPointOffset(AScreenPoint, FDragImageOffset); DetermineHitTest(AHitControl, AScreenPoint); DetermineDestinationItem; DetermineDestinationControl(AHitControl); ADestPoint := DestinationControl.ScreenToClient(AScreenPoint); DetermineAreaPart(ADestPoint); DetermineDestinationGroup; end; procedure TdxLayoutDragAndDropObject.BeginDragAndDrop; begin inherited BeginDragAndDrop; //#DG SourceItem.SelectComponent([]); TdxCustomLayoutControlAccess(SourceControl).DragAndDropBegan; CreateDragImage; end; procedure TdxLayoutDragAndDropObject.DragAndDrop(const P: TPoint; var Accepted: Boolean); procedure UpdateDestinationImageState; begin if FNeedRepaintDestinationImage then RefreshDestinationImage; end; begin UpdateStates; ShowDragImage; Accepted := AreaPart <> apNone; UpdateDestinationImageState; inherited; end; procedure TdxLayoutDragAndDropObject.EndDragAndDrop(Accepted: Boolean); function GetDestPosition: Integer; begin case AreaPart of apRight, apBottom, apAfter: if DestinationItem = DestinationGroup then Result := DestinationGroup.Count else Result := DestinationItem.Index + 1; apLeft, apTop, apBefore: if DestinationItem = DestinationGroup then Result := 0 else Result := DestinationItem.Index; apLastChild: Result := DestinationGroup.Count; else Result := 0; end; if (SourceItem.Parent = DestinationGroup) and (SourceItem.Index < Result) then Dec(Result); end; function GetLayoutDirection: TdxLayoutDirection; begin Result := DestinationGroup.LayoutDirection; end; function GetOrthogonalDirection: TdxLayoutDirection; begin Result := TdxLayoutGroupAccess(DestinationItem.Parent).GetHelperClass.GetOrthogonalDirection; end; function IsHorizontalAreaPart: Boolean; begin Result := AreaPart in [apLeft, apRight, apAfter, apBefore]; end; function IsVerticalAreaPart: Boolean; begin Result := AreaPart in [apTop, apBottom, apAfter, apBefore]; end; procedure DoInsert; begin SourceItem.Move(DestinationGroup, GetDestPosition, True); end; procedure DoCreateGroup; const LayoutDirections: array[Boolean] of TdxLayoutDirection = (ldVertical, ldHorizontal); var AParentGroup: TdxLayoutGroup; begin if DestinationItem.IsRoot then begin DestinationGroup.PutChildrenIntoHiddenGroup; DestinationGroup.LayoutDirection := LayoutDirections[IsHorizontalAreaPart]; AParentGroup := DestinationGroup; end else AParentGroup := DestinationItem.PutIntoHiddenGroup(GetOrthogonalDirection); TdxLayoutGroupAccess(AParentGroup).BuildVisibleItemsList; SourceItem.Move(AParentGroup, GetDestPosition, True); end; procedure DoContentInsert; begin DestinationGroup.PutChildrenIntoHiddenGroup; DestinationGroup.LayoutDirection := GetOrthogonalDirection; SourceItem.Move(DestinationGroup, GetDestPosition, True); end; function CanProcessDrop: Boolean; begin Result := Accepted and (DestinationControl <> nil) and ((CanDrop and (DestinationItem <> nil)) or CanRemove); end; function GetActionType(AAreaPart: TdxLayoutAreaPart): TdxLayoutActionType; begin if AAreaPart in [apBefore, apAfter, apFirstChild, apLastChild] then Result := atInsert else Result := TdxLayoutGroupViewInfoAccess(DestinationGroup.ViewInfo).GetActionType(AAreaPart); end; var ALinkSelf: TcxObjectLink; begin FreeAndNil(FDragImage); UpdateStates; Dirty := True; if CanProcessDrop then begin TdxCustomLayoutControlAccess(DestinationControl).SaveToUndo; DestinationControl.BeginUpdate; TdxCustomLayoutControlAccess(SourceControl).SaveToUndo; SourceControl.BeginUpdate; try if CanDrop and (DestinationGroup <> nil) then case GetActionType(AreaPart) of atInsert: DoInsert; atCreateGroup: DoCreateGroup; atContentInsert: DoContentInsert; end else if CanRemove then SourceItem.Parent := nil; finally ALinkSelf := cxAddObjectLink(SourceItem); try ResetDragAndDropObjects; SourceControl.EndUpdate; DestinationControl.EndUpdate; if ALinkSelf.Ref <> nil then (ALinkSelf.Ref as TdxCustomLayoutItem).MakeVisible; finally cxRemoveObjectLink(ALinkSelf); end; end; SourceControl.Update; if DestinationControl <> SourceControl then DestinationControl.Update; Modified; end else ResetDragAndDropObjects; inherited; end; procedure TdxLayoutDragAndDropObject.Init(ASource: TdxLayoutDragSource; ASourceItem: TdxCustomLayoutItem; const P: TPoint); begin inherited; SourceControl.Update; Source := ASource; end; { TdxLayoutCustomSizingDragAndDropObject } procedure TdxLayoutCustomSizingDragAndDropObject.BeginDragAndDrop; begin inherited; TdxCustomLayoutControlAccess(Control).SaveToUndo; end; procedure TdxLayoutCustomSizingDragAndDropObject.EndDragAndDrop(Accepted: Boolean); begin if not Accepted then RestoreSize else Modified; end; procedure TdxLayoutCustomSizingDragAndDropObject.RestoreSize; begin // do nothing end; { TdxLayoutSizingDragAndDropObject } constructor TdxLayoutSizingDragAndDropObject.Create(AControl: TcxControl); begin inherited; FLayoutSizingDragAndDropObject := Self; end; destructor TdxLayoutSizingDragAndDropObject.Destroy; begin FLayoutSizingDragAndDropObject := nil; inherited; end; procedure TdxLayoutSizingDragAndDropObject.DragAndDrop(const P: TPoint; var Accepted: Boolean); var XC, YC: Integer; begin if SourceItem.AlignHorz in [ahCenter] then XC := 2 else XC := 1; if SourceItem.AlignVert in [avCenter] then YC := 2 else YC := 1; if FMarkerIndex in [0, 6, 7] then SourceItem.Width := cxRectWidth(FOriginalBounds) + XC * (FStartDragPoint.X - P.X); if FMarkerIndex in [2, 3, 4] then SourceItem.Width := cxRectWidth(FOriginalBounds) + XC * (P.X - FStartDragPoint.X); if FMarkerIndex in [0, 1, 2] then SourceItem.Height := cxRectHeight(FOriginalBounds) + YC * (FStartDragPoint.Y - P.Y); if FMarkerIndex in [4, 5, 6] then SourceItem.Height := cxRectHeight(FOriginalBounds) + YC * (P.Y - FStartDragPoint.Y); end; procedure TdxLayoutSizingDragAndDropObject.Init(ASource: TdxLayoutDragSource; ASourceItem: TdxCustomLayoutItem; const P: TPoint); begin inherited; FOriginalSize := cxSize(ASourceItem.Width, ASourceItem.Height); FOriginalBounds := ASourceItem.ViewInfo.Bounds; FMarkerIndex := TdxCustomLayoutItemViewInfoAccess(ASourceItem.ViewInfo).GetMarkerIndex(P); end; procedure TdxLayoutSizingDragAndDropObject.RestoreSize; begin SourceItem.Width := FOriginalSize.cx; SourceItem.Height := FOriginalSize.cy; end; { TdxLayoutSplitterDragAndDropObject } procedure TdxLayoutSplitterDragAndDropObject.DragAndDrop(const P: TPoint; var Accepted: Boolean); begin if not cxPointIsEqual(FPrevPoint, P) then begin FPrevPoint := P; case FSizingStrategy of lssLeft: SetItemSize(FLeftItem.ViewInfo, FLeftItemSize + (GetSignificantValue(P) - GetSignificantValue(FStartDragPoint))); lssRight: SetItemSize(FRightItem.ViewInfo, FRightItemSize + (GetSignificantValue(FStartDragPoint) - GetSignificantValue(P))); lssClient: begin SourceControl.BeginUpdate; try SetItemSize(FLeftItem.ViewInfo, FLeftItemSize + (GetSignificantValue(P) - GetSignificantValue(FStartDragPoint))); SetItemSize(FRightItem.ViewInfo, FRightItemSize + (GetSignificantValue(FStartDragPoint) - GetSignificantValue(P))); finally SourceControl.EndUpdate(False); end; end; end; end; end; procedure TdxLayoutSplitterDragAndDropObject.Init(ASource: TdxLayoutDragSource; ASourceItem: TdxCustomLayoutItem; const P: TPoint); function GetLeftItem: TdxCustomLayoutItem; var I: Integer; begin Result := nil; for I := ASourceItem.VisibleIndex - 1 downto 0 do if GetWorkAlign(FParentItem.VisibleItems[I].ViewInfo) in [GetWorkAlign(FSourceItem.ViewInfo), ahClient] then begin Result := FParentItem.VisibleItems[I]; Break; end; end; function GetRightItem: TdxCustomLayoutItem; var I: Integer; begin Result := nil; for I := ASourceItem.VisibleIndex + 1 to FParentItem.VisibleCount - 1 do if GetWorkAlign(FParentItem.VisibleItems[I].ViewInfo) in [GetWorkAlign(FSourceItem.ViewInfo), ahClient] then begin Result := FParentItem.VisibleItems[I]; Break; end; end; function GetSimpleStrategy(AAlign: TdxLayoutAlignHorz): TdxLayoutSizingStrategy; begin case AAlign of ahLeft: if (FLeftItem <> nil) and not TdxCustomLayoutItemAccess(FLeftItem).IsLocked then Result := lssLeft else Result := lssNone; ahRight: if (FRightItem <> nil) and not TdxCustomLayoutItemAccess(FRightItem).IsLocked then Result := lssRight else Result := lssNone; else Result := lssNone; end; if (Result in [lssRight, lssLeft]) and (FLeftItem <> nil) and (FRightItem <> nil) and (GetWorkAlign(FLeftItem.ViewInfo) = ahClient) and (GetWorkAlign(FRightItem.ViewInfo) = ahClient) then Result := lssClient; end; var I: Integer; begin inherited; FParentItem := ASourceItem.Parent; SetLength(FOriginalSizes, FParentItem.VisibleCount); for I := 0 to FParentItem.VisibleCount - 1 do FOriginalSizes[I] := Point(FParentItem.VisibleItems[I].Width, FParentItem.VisibleItems[I].Height); FLeftItem := GetLeftItem; FRightItem := GetRightItem; if FLeftItem <> nil then FLeftItemSize := GetItemSize(FLeftItem.ViewInfo); if FRightItem <> nil then FRightItemSize := GetItemSize(FRightItem.ViewInfo); case GetWorkAlign(FSourceItem.ViewInfo) of ahLeft: if (FLeftItem <> nil) and (GetWorkAlign(FLeftItem.ViewInfo) = ahClient) then FSizingStrategy := GetSimpleStrategy(ahRight) else FSizingStrategy := GetSimpleStrategy(ahLeft); ahRight: if (FRightItem <> nil) and (GetWorkAlign(FRightItem.ViewInfo) = ahClient) then FSizingStrategy := GetSimpleStrategy(ahLeft) else FSizingStrategy := GetSimpleStrategy(ahRight); else FSizingStrategy := lssNone; end; if FSizingStrategy = lssClient then begin SourceControl.BeginUpdate; try for I := 0 to FParentItem.VisibleCount - 1 do if GetWorkAlign(FParentItem.VisibleItems[I].ViewInfo) = ahClient then SetItemSize(FParentItem.VisibleItems[I].ViewInfo, GetItemSize(FParentItem.VisibleItems[I].ViewInfo)); finally SourceControl.CancelUpdate; end; end; FPrevPoint := P; end; procedure TdxLayoutSplitterDragAndDropObject.RestoreSize; var I: Integer; begin SourceControl.BeginUpdate; try for I := 0 to FParentItem.VisibleCount - 1 do SetItemSize(FParentItem.VisibleItems[I].ViewInfo, GetSignificantValue(FOriginalSizes[I])); finally SourceControl.EndUpdate(False); end; end; { TdxLayoutHSplitterDragAndDropObject } function TdxLayoutHSplitterDragAndDropObject.GetWorkAlign(AViewInfo: TdxCustomLayoutItemViewInfo): TdxLayoutAlignHorz; begin Result := AViewInfo.AlignHorz; end; function TdxLayoutHSplitterDragAndDropObject.GetItemSize(AViewInfo: TdxCustomLayoutItemViewInfo): Integer; begin Result := cxRectWidth(AViewInfo.Bounds); end; function TdxLayoutHSplitterDragAndDropObject.GetSignificantValue(const P: TPoint): Integer; begin Result := P.X; end; procedure TdxLayoutHSplitterDragAndDropObject.SetItemSize(AViewInfo: TdxCustomLayoutItemViewInfo; ASize: Integer); begin TdxCustomLayoutItemViewInfoAccess(AViewInfo).Item.Width := ASize; end; { TdxLayoutHSplitterDragAndDropObject } function TdxLayoutVSplitterDragAndDropObject.GetWorkAlign(AViewInfo: TdxCustomLayoutItemViewInfo): TdxLayoutAlignHorz; begin Result := TdxLayoutAlignHorz(AViewInfo.AlignVert); end; function TdxLayoutVSplitterDragAndDropObject.GetItemSize(AViewInfo: TdxCustomLayoutItemViewInfo): Integer; begin Result := cxRectHeight(AViewInfo.Bounds); end; function TdxLayoutVSplitterDragAndDropObject.GetSignificantValue(const P: TPoint): Integer; begin Result := P.Y; end; procedure TdxLayoutVSplitterDragAndDropObject.SetItemSize(AViewInfo: TdxCustomLayoutItemViewInfo; ASize: Integer); begin TdxCustomLayoutItemViewInfoAccess(AViewInfo).Item.Height := ASize; end; { TdxLayoutDragAndDropHelper } constructor TdxLayoutDragAndDropHelper.Create( AControl: TdxCustomLayoutControl); begin inherited Create(AControl); Reset; end; procedure TdxLayoutDragAndDropHelper.InitializeDragItem(AItem: TdxCustomLayoutItem; X, Y: Integer); begin FMouseDownPos := Point(X, Y); FDragItem := AItem; end; procedure TdxLayoutDragAndDropHelper.Reset; begin FDragItem := nil; FMouseDownPos := cxInvalidPoint; end; procedure TdxLayoutDragAndDropHelper.TryBeginDragAndDrop(X, Y: Integer; ASource: TdxLayoutDragSource); begin if CanBeginDragAndDrop(X, Y) then BeginDragAndDrop(ASource); end; function TdxLayoutDragAndDropHelper.CanBeginDragAndDrop(X, Y: Integer): Boolean; begin Result := TdxCustomLayoutControlAccess(Control).CanDragAndDrop and (Control.DragAndDropState = ddsNone) and not cxPointIsEqual(FMouseDownPos, cxInvalidPoint) and not IsPointInDragDetectArea(FMouseDownPos, X, Y) and (DragItem <> nil) and TdxCustomLayoutItemAccess(DragItem).CanDragAndDrop; end; procedure TdxLayoutDragAndDropHelper.BeginDragAndDrop(ASource: TdxLayoutDragSource); begin DragAndDropObject.Init(ASource, DragItem, GetMouseCursorPos); Control.BeginDragAndDrop; end; function TdxLayoutDragAndDropHelper.GetDragAndDropObject: TdxLayoutDragAndDropObject; begin Result := TdxLayoutDragAndDropObject(Control.DragAndDropObject); end; initialization Screen.Cursors[crdxLayoutControlNoDrop] := LoadCursor(HInstance, 'DXLAYOUTCONTROLNODROP'); Screen.Cursors[crdxLayoutControlRemove] := LoadCursor(HInstance, 'DXLAYOUTCONTROLREMOVE'); end.