{********************************************************************} { } { Developer Express Visual Component Library } { ExpressLayoutControl customize form } { } { 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 dxLayoutCustomizeForm; {$I cxVer.inc} interface uses Windows, Messages, SysUtils, Classes, Graphics, Contnrs, Controls, Forms, Dialogs, ComCtrls, ActnList, Menus, ToolWin, StdCtrls, ImgList, cxClasses, dxLayoutLookAndFeels, cxControls, cxGraphics, dxLayoutControl; type TdxLayoutControlCustomizeFormClass = class of TdxLayoutControlCustomizeForm; TdxLayoutControlCustomizeForm = class(TForm) alMain: TActionList; acAddGroup: TAction; acAddItem: TAction; acClose: TAction; acTreeViewExpandAll: TAction; acTreeViewCollapseAll: TAction; acTreeViewItemsDelete: TAction; acAlignLeftSide: TAction; acAlignRightSide: TAction; acAlignTopSide: TAction; acAlignBottomSide: TAction; acAlignNone: TAction; ilActions: TcxImageList; ilItems: TcxImageList; tlbAvailableItems: TToolBar; tbAddGroup: TToolButton; tbAddItem: TToolButton; tbListBoxItemDelete: TToolButton; ToolButton1: TToolButton; tbAlignBy: TToolButton; tbTreeViewExpandAll: TToolButton; tbTreeViewCollapseAll: TToolButton; tbTreeViewItemDelete: TToolButton; ToolButton5: TToolButton; tvItems: TTreeView; pmTreeViewActions: TPopupMenu; miExpandAll: TMenuItem; miCallapseAll: TMenuItem; miSeparator1: TMenuItem; miTreeViewDelete: TMenuItem; miSeparator2: TMenuItem; miAlignBy: TMenuItem; pmAvailableItemsActions: TPopupMenu; AddGroup1: TMenuItem; AddItem1: TMenuItem; Delete1: TMenuItem; pmAlign: TPopupMenu; Left1: TMenuItem; Right1: TMenuItem; op1: TMenuItem; Bottom1: TMenuItem; miSeparator3: TMenuItem; miSeparator4: TMenuItem; None1: TMenuItem; btnClose: TButton; lcMainGroup_Root: TdxLayoutGroup; lcMain: TdxLayoutControl; lcMainItem3: TdxLayoutItem; lcMainItem4: TdxLayoutItem; lcgTreeView: TdxLayoutGroup; lcgAvailableItems: TdxLayoutGroup; lcMainItem5: TdxLayoutItem; lcMainItem6: TdxLayoutItem; acAvailableItemsDelete: TAction; tvAvailableItems: TTreeView; lcMainItem1: TdxLayoutItem; ToolButton2: TToolButton; tbAvailableItemsExpandAll: TToolButton; tbAvailableItemsCollapseAll: TToolButton; ToolButton7: TToolButton; tbAvailableItemsViewAsList: TToolButton; acAvailableItemsExpandAll: TAction; acAvailableItemsCollapseAll: TAction; acAvailableItemsViewAsList: TAction; tlbCustomize: TToolBar; lcMainItem2: TdxLayoutItem; ToolButton4: TToolButton; acTabbedView: TAction; acHighlightRoot: TAction; lcMainGroup1: TdxLayoutGroup; acShowDesignSelectors: TAction; ToolButton6: TToolButton; cbTabbedView: TCheckBox; lcMainItem7: TdxLayoutItem; lcMainGroup2: TdxLayoutGroup; lcMainGroup3: TdxLayoutGroup; ilToolBarActionsDisabled: TcxImageList; ilToolBarActions: TcxImageList; ToolButton3: TToolButton; ToolButton8: TToolButton; acStore: TAction; acRestore: TAction; tlbTreeView: TToolBar; procedure acCloseExecute(Sender: TObject); procedure acAddGroupExecute(Sender: TObject); procedure acAddItemExecute(Sender: TObject); procedure tvItemsEdited(Sender: TObject; Node: TTreeNode; var S: String); procedure tvItemsMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure ItemsMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); procedure ItemsMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure tvItemsChange(Sender: TObject; Node: TTreeNode); procedure tvItemsChanging(Sender: TObject; Node: TTreeNode; var AllowChange: Boolean); procedure AlignExecute(Sender: TObject); procedure acTreeViewItemsDeleteExecute(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure tvItemsContextPopup(Sender: TObject; MousePos: TPoint; var Handled: Boolean); procedure acAvailableItemsDeleteExecute(Sender: TObject); procedure tvItemsCustomDrawItem(Sender: TCustomTreeView; Node: TTreeNode; State: TCustomDrawState; var DefaultDraw: Boolean); procedure tvAvailableItemsContextPopup(Sender: TObject; MousePos: TPoint; var Handled: Boolean); procedure acAvailableItemsExpandAllExecute(Sender: TObject); procedure acAvailableItemsCollapseAllExecute(Sender: TObject); procedure acTreeViewExpandAllExecute(Sender: TObject); procedure acTreeViewCollapseAllExecute(Sender: TObject); procedure acAvailableItemsViewAsListExecute(Sender: TObject); procedure tvItemsEditing(Sender: TObject; Node: TTreeNode; var AllowEdit: Boolean); procedure FormShortCut(var Msg: TWMKey; var Handled: Boolean); procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure acTabbedViewExecute(Sender: TObject); procedure acHighlightRootExecute(Sender: TObject); procedure acShowDesignSelectorsExecute(Sender: TObject); procedure acStoreExecute(Sender: TObject); procedure acRestoreExecute(Sender: TObject); private FControl: TdxCustomLayoutControl; FDragHelper: TdxLayoutDragAndDropHelper; FLockCount: Integer; FAvailableItemsWndProc: TWndMethod; FItemsWndProc: TWndMethod; procedure AddItemNode(ANodes: TTreeNodes; ANode: TTreeNode; AItem: TdxCustomLayoutItem; const ACustomName: string = ''); procedure AvailableItemsWndProc(var Message: TMessage); procedure DoAfterInsertionItem(AItem: TdxCustomLayoutItem); function GetImageIndex(AItem: TdxCustomLayoutItem): Integer; function GetIsLocked: Boolean; function GetNextToSelectItem(ATreeView: TTreeView): TdxCustomLayoutItem; procedure InitializePopupMenu; procedure ItemsWndProc(var Message: TMessage); procedure RefreshLists(ARefreshSelection: Boolean = False); procedure RefreshButtonStates; procedure RefreshView; procedure SelectItem(AItem: TdxCustomLayoutItem); procedure SetControl(AValue: TdxCustomLayoutControl); procedure SetItemsSelections(AList: TList); procedure SetLayoutItemsSelections(ATreeView: TTreeView); function TreeViewWndProcHandler(ATreeView: TTreeView; var Message: TMessage): Boolean; protected procedure CreateParams(var Params: TCreateParams); override; procedure Notification(AComponent: TComponent; Operation: TOperation); override; procedure CalculateAddItemsActionStates; procedure CalculateDeleteItemsActionStates; function CanDeleteItems(ATreeView: TTreeView): Boolean; procedure DeleteItems(AList: TComponentList; ASelectedItem: TdxCustomLayoutItem); procedure InitializeControl; procedure Localize; virtual; function NeedRefreshItemsAfterDeleting(AComponent: TPersistent): Boolean; procedure RefreshAvailableItems; procedure RefreshEnableds; procedure RefreshTreeView; property DragHelper: TdxLayoutDragAndDropHelper read FDragHelper; property IsLocked: Boolean read GetIsLocked; property LockCount: Integer read FLockCount; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; function GetHitTest(const P: TPoint): TdxCustomLayoutHitTest; virtual; // Updates procedure BeginUpdate; procedure CancelUpdate; procedure EndUpdate; procedure UpdateCaption; procedure UpdateContent; procedure UpdateSelection; property Control: TdxCustomLayoutControl read FControl write SetControl; end; procedure cxTreeViewGetSelection(ATreeView: TTreeView; AList: TList); procedure cxTreeViewSyncSelection(ATreeView: TTreeView; AList: TList); procedure cxListViewGetSelection(AListView: TListView; AList: TList); procedure cxListViewSyncSelection(AListView: TListView; AList: TList); implementation {$R *.DFM} uses Types, Math, CommCtrl, dxLayoutCommon, dxLayoutEditForm, cxGeometry, dxLayoutStrs, dxCore, dxOffice11; type TdxLayoutControlAccess = class(TdxCustomLayoutControl); TdxCustomLayoutItemAccess = class(TdxCustomLayoutItem); function CompareItemsByClass(Item1, Item2: Pointer): Integer; var AItem1, AItem2: TdxCustomLayoutItem; begin AItem1 := TdxCustomLayoutItem(Item1); AItem2 := TdxCustomLayoutItem(Item2); Result := Integer(AItem1.ClassType) - Integer(AItem2.ClassType); end; function CompareItemsByName(Item1, Item2: Pointer): Integer; procedure SplitNameByTrailingDigits(const AName: string; out ATrailingDigits: Integer; out ANameWithoutTrailingDigitals: string); var ACurrentLength: Integer; begin ANameWithoutTrailingDigitals := AName; ACurrentLength := Length(ANameWithoutTrailingDigitals); while (ACurrentLength > 0) and dxCharIsNumeric(ANameWithoutTrailingDigitals[ACurrentLength]) do begin ATrailingDigits := ATrailingDigits * 10 + StrToInt(ANameWithoutTrailingDigitals[ACurrentLength]); ANameWithoutTrailingDigitals := Copy(ANameWithoutTrailingDigitals, 1, ACurrentLength - 1); ACurrentLength := Length(ANameWithoutTrailingDigitals); end; if ACurrentLength = Length(AName) then ATrailingDigits := -1 else ATrailingDigits := StrToInt(Copy(AName, ACurrentLength + 1, Length(AName) - ACurrentLength)); end; var AName1, AName2: string; ANameWithoutLastDigitals1, ANameWithoutLastDigitals2: string; ANumber1, ANumber2: Integer; begin Result := CompareItemsByClass(Item1, Item2); if Result = 0 then begin AName1 := TdxCustomLayoutItem(Item1).CaptionForCustomizeForm; SplitNameByTrailingDigits(AName1, ANumber1, ANameWithoutLastDigitals1); AName2 := TdxCustomLayoutItem(Item2).CaptionForCustomizeForm; SplitNameByTrailingDigits(AName2, ANumber2, ANameWithoutLastDigitals2); Result := CompareText(ANameWithoutLastDigitals1, ANameWithoutLastDigitals2); if Result = 0 then Result := ANumber1 - ANumber2; end; end; function CompareItems(Item1, Item2: Pointer): Integer; begin Result := CompareItemsByClass(Item1, Item2); if (Result = 0) and (TObject(Item1) is TdxLayoutGroup) then Result := Integer(TdxLayoutGroup(Item1).Count > 0) - Integer(TdxLayoutGroup(Item2).Count > 0); if Result = 0 then Result := CompareItemsByName(Item1, Item2); end; procedure cxTreeViewGetSelection(ATreeView: TTreeView; AList: TList); var I: Integer; begin for I := 0 to ATreeView.SelectionCount - 1 do AList.Add(ATreeView.Selections[I].Data); end; procedure cxTreeViewSyncSelection(ATreeView: TTreeView; AList: TList); procedure InternalSyncSelection(ANode: TTreeNode); var I: Integer; begin if ANode = nil then Exit; if ANode.Selected xor (ANode.Data <> nil) and (AList.IndexOf(ANode.Data) >= 0) then if ANode.Selected then ATreeView.Deselect(ANode) else ATreeView.Select(ANode, [ssCtrl]); ANode.Focused := ANode.Selected; for I := 0 to ANode.Count - 1 do InternalSyncSelection(ANode[I]); end; var I: Integer; begin with ATreeView do begin if AList.Count > 0 then for I := 0 to Items.Count - 1 do InternalSyncSelection(Items[I]) else ATreeView.ClearSelection(False); end; if ATreeView.SelectionCount = 0 then ATreeView.Selected := nil; end; procedure cxListViewGetSelection(AListView: TListView; AList: TList); var I: Integer; begin AList.Clear; with AListView do for I := 0 to Items.Count - 1 do if (Items[I].Data <> nil) and Items[I].Selected then AList.Add(Items[I].Data); end; procedure cxListViewSyncSelection(AListView: TListView; AList: TList); var I: Integer; begin with AListView do begin for I := 0 to AListView.Items.Count - 1 do with Items[I] do begin Selected := AList.IndexOf(Items[I].Data) >= 0; Focused := Focused or Selected; end; end; end; { TdxLayoutDesignForm } constructor TdxLayoutControlCustomizeForm.Create(AOwner: TComponent); begin inherited Create(AOwner); if not IsXPManifestEnabled then begin tlbCustomize.DisabledImages := ilToolBarActionsDisabled; tlbAvailableItems.DisabledImages := ilToolBarActionsDisabled; tlbTreeView.DisabledImages := ilToolBarActionsDisabled; ilToolBarActions.Clear; cxTransformImages(ilActions, ilToolBarActions, clBtnFace); ilToolBarActionsDisabled.Clear; cxTransformImages(ilActions, ilToolBarActionsDisabled, clBtnFace, False); cxTransformImages(ilItems, clWindow); end; Localize; InitializePopupMenu; FItemsWndProc := tvItems.WindowProc; tvItems.WindowProc := ItemsWndProc; FAvailableItemsWndProc := tvAvailableItems.WindowProc; tvAvailableItems.WindowProc := AvailableItemsWndProc; end; destructor TdxLayoutControlCustomizeForm.Destroy; begin Control.Customization := False; FreeAndNil(FDragHelper); tvItems.WindowProc := FItemsWndProc; tvAvailableItems.WindowProc := FAvailableItemsWndProc; inherited; end; function TdxLayoutControlCustomizeForm.GetHitTest(const P: TPoint): TdxCustomLayoutHitTest; begin if tvItems.Visible and PtInRect(tvItems.BoundsRect, P) then Result := TdxLayoutCustomizeFormTreeViewItemsHitTest.Instance else if tvAvailableItems.Visible and PtInRect(tvAvailableItems.BoundsRect, P) then Result := TdxLayoutCustomizeFormAvailableItemsHitTest.Instance else Result := TdxLayoutCustomizeFormHitTest.Instance end; procedure TdxLayoutControlCustomizeForm.BeginUpdate; begin Inc(FLockCount); end; procedure TdxLayoutControlCustomizeForm.CancelUpdate; begin Dec(FLockCount); end; procedure TdxLayoutControlCustomizeForm.EndUpdate; begin Dec(FLockCount); if LockCount = 0 then RefreshLists(True); end; procedure TdxLayoutControlCustomizeForm.UpdateCaption; begin if Control.IsDesigning then Caption := Format(cxGetResourceString(@sdxLayoutControlDesignerCaptionFormat), [cxGetFullComponentName(Control)]) else Caption := cxGetResourceString(@sdxLayoutControlCustomizeFormCaption); end; procedure TdxLayoutControlCustomizeForm.UpdateContent; begin RefreshLists; RefreshButtonStates; RefreshView; if (FDragHelper.DragItem <> nil) and (Control.DragAndDropState = ddsNone) then FDragHelper.Reset; end; procedure TdxLayoutControlCustomizeForm.UpdateSelection; var AList: TList; begin if IsLocked then Exit; AList := TList.Create; try (Control as IdxLayoutDesignerHelper).GetSelection(AList); SetItemsSelections(AList); finally AList.Free; end; end; procedure TdxLayoutControlCustomizeForm.CalculateAddItemsActionStates; begin acAddGroup.Enabled := True; acAddItem.Enabled := Control.IsDesigning; acAddItem.Visible := Control.IsDesigning; end; procedure TdxLayoutControlCustomizeForm.CalculateDeleteItemsActionStates; begin acTreeViewItemsDelete.Enabled := CanDeleteItems(tvItems); acAvailableItemsDelete.Enabled := CanDeleteItems(tvAvailableItems); end; function TdxLayoutControlCustomizeForm.CanDeleteItems(ATreeView: TTreeView): Boolean; var AList: TList; I: Integer; begin Result := not ATreeView.IsEditing; if Result then begin AList := TList.Create; try cxTreeViewGetSelection(ATreeView, AList); Result := AList.Count > 0; if Result then for I := 0 to AList.Count - 1 do begin Result := (Control as IdxLayoutDesignerHelper).CanDeleteComponent(TComponent(AList[I])); if not Result then Break; end; finally AList.Free; end; end; end; procedure TdxLayoutControlCustomizeForm.DeleteItems(AList: TComponentList; ASelectedItem: TdxCustomLayoutItem); begin if AList.Count = 0 then Exit; BeginUpdate; try SelectItem(ASelectedItem); Control.BeginUpdate; try while AList.Count > 0 do AList[0].Free; finally Control.EndUpdate; end; finally CancelUpdate; end; RefreshLists(True); end; procedure TdxLayoutControlCustomizeForm.CreateParams(var Params: TCreateParams); begin inherited; if Control <> nil then Params.WndParent := Control.Handle; end; procedure TdxLayoutControlCustomizeForm.Notification(AComponent: TComponent; Operation: TOperation); begin inherited; if (Operation = opRemove) and (AComponent = Control) then Free; end; procedure TdxLayoutControlCustomizeForm.InitializeControl; begin UpdateCaption; FDragHelper.Free; FDragHelper := TdxLayoutDragAndDropHelper.Create(Control); end; procedure TdxLayoutControlCustomizeForm.Localize; begin acAddGroup.Caption := cxGetResourceString(@sdxLayoutControlCustomizeFormAddGroup); acAddGroup.Hint := StripHotKey(acAddGroup.Caption); acAddItem.Caption := cxGetResourceString(@sdxLayoutControlCustomizeFormAddItem); acAddItem.Hint := StripHotKey(acAddItem.Caption); acAvailableItemsDelete.Caption := cxGetResourceString(@sdxLayoutControlCustomizeFormDelete); acAvailableItemsDelete.Hint := cxGetResourceString(@sdxLayoutControlCustomizeFormDeleteHint); acTreeViewItemsDelete.Caption := cxGetResourceString(@sdxLayoutControlCustomizeFormDelete); acTreeViewItemsDelete.Hint := cxGetResourceString(@sdxLayoutControlCustomizeFormDeleteHint); tbAlignBy.Caption := cxGetResourceString(@sdxLayoutControlCustomizeFormAlignBy); tbAlignBy.Hint := StripHotKey(tbAlignBy.Caption); acClose.Caption := cxGetResourceString(@sdxLayoutControlCustomizeFormClose); acClose.Hint := StripHotKey(acClose.Caption); acTreeViewExpandAll.Caption := cxGetResourceString(@sdxLayoutControlCustomizeFormExpandAll); acTreeViewExpandAll.Hint := StripHotKey(acTreeViewExpandAll.Caption); acAvailableItemsExpandAll.Caption := acTreeViewExpandAll.Caption; acAvailableItemsExpandAll.Hint := acTreeViewExpandAll.Hint; acTreeViewCollapseAll.Caption := cxGetResourceString(@sdxLayoutControlCustomizeFormCollapseAll); acTreeViewCollapseAll.Hint := StripHotKey(acTreeViewCollapseAll.Caption); acAvailableItemsCollapseAll.Caption := acTreeViewCollapseAll.Caption; acAvailableItemsCollapseAll.Hint := StripHotKey(acTreeViewCollapseAll.Hint); acAlignLeftSide.Caption := cxGetResourceString(@sdxLayoutControlCustomizeFormAlignLeftSide); acAlignRightSide.Caption := cxGetResourceString(@sdxLayoutControlCustomizeFormAlignRightSide); acAlignTopSide.Caption := cxGetResourceString(@sdxLayoutControlCustomizeFormAlignTopSide); acAlignBottomSide.Caption := cxGetResourceString(@sdxLayoutControlCustomizeFormAlignBottomSide); acAlignNone.Caption := cxGetResourceString(@sdxLayoutControlCustomizeFormAlignNone); lcgTreeView.Caption := cxGetResourceString(@sdxLayoutControlCustomizeFormTreeViewGroup); lcgAvailableItems.Caption := cxGetResourceString(@sdxLayoutControlCustomizeFormListViewGroup); acTabbedView.Caption := cxGetResourceString(@sdxLayoutControlCustomizeFormTabbedView); acTabbedView.Hint := StripHotKey(acTabbedView.Caption); acAvailableItemsViewAsList.Caption := cxGetResourceString(@sdxLayoutControlCustomizeFormTreeView); acAvailableItemsViewAsList.Hint := StripHotKey(acAvailableItemsViewAsList.Caption); acStore.Caption := cxGetResourceString(@sdxLayoutControlCustomizeFormStore); acStore.Hint := StripHotKey(acStore.Caption); acRestore.Caption := cxGetResourceString(@sdxLayoutControlCustomizeFormRestore); acRestore.Hint := StripHotKey(acRestore.Caption); end; function TdxLayoutControlCustomizeForm.NeedRefreshItemsAfterDeleting(AComponent: TPersistent): Boolean; begin Result := (AComponent is TdxCustomLayoutItem) and (TdxCustomLayoutItem(AComponent).Container = Control); end; procedure TdxLayoutControlCustomizeForm.RefreshAvailableItems; procedure PopulateItemChildren(AList: TList; AItem: TdxCustomLayoutItem); var I: Integer; begin if AItem is TdxLayoutGroup then begin for I := 0 to TdxLayoutGroup(AItem).Count - 1 do begin AList.Add(TdxLayoutGroup(AItem).Items[I]); PopulateItemChildren(AList, TdxLayoutGroup(AItem).Items[I]); end; end; end; procedure PopulateItems(AList: TList); var I: Integer; begin for I := 0 to Control.AvailableItemCount - 1 do begin AList.Add(Control.AvailableItems[I]); if Control.CustomizeAvailableItemsViewKind = oivkList then PopulateItemChildren(AList, Control.AvailableItems[I]); end; end; var AList: TList; I: Integer; begin with tvAvailableItems do begin Items.BeginUpdate; try Items.Clear; AList := TList.Create; try PopulateItems(AList); case Control.CustomizeAvailableItemsViewKind of oivkList: begin AList.Sort(CompareItemsByName); for I := 0 to AList.Count - 1 do with Items.AddObject(Items.GetFirstNode, TdxCustomLayoutItem(AList[I]).CaptionForCustomizeForm, AList[I]) do begin ImageIndex := GetImageIndex(AList[I]); SelectedIndex := ImageIndex; Selected := False; Focused := False; end; end; oivkTree: begin AList.Sort(CompareItems); for I := 0 to AList.Count - 1 do AddItemNode(Items, nil, TdxCustomLayoutItem(AList[I])); end; end; finally AList.Free; end; FullExpand; finally Items.EndUpdate; end; end; tvAvailableItems.ShowRoot := Control.CustomizeAvailableItemsViewKind = oivkTree; end; procedure TdxLayoutControlCustomizeForm.RefreshEnableds; begin if not IsLocked then begin tbAlignBy.Enabled := (tvItems.SelectionCount > 1); miAlignBy.Enabled := tbAlignBy.Enabled; acAvailableItemsExpandAll.Enabled := Control.CustomizeAvailableItemsViewKind = oivkTree; acAvailableItemsCollapseAll.Enabled := Control.CustomizeAvailableItemsViewKind = oivkTree; acRestore.Enabled := Control.CanRestore; CalculateAddItemsActionStates; CalculateDeleteItemsActionStates; end; end; procedure TdxLayoutControlCustomizeForm.RefreshTreeView; begin with tvItems do begin Items.BeginUpdate; try Items.Clear; AddItemNode(Items, Items.GetFirstNode, Control.Items, cxGetResourceString(@sdxLayoutControlRoot)); FullExpand; finally Items.EndUpdate; end; end; end; procedure TdxLayoutControlCustomizeForm.AddItemNode(ANodes: TTreeNodes; ANode: TTreeNode; AItem: TdxCustomLayoutItem; const ACustomName: string = ''); var AName: string; I: Integer; AThisNode: TTreeNode; AGroup: TdxLayoutGroup; begin if ACustomName = '' then AName := AItem.CaptionForCustomizeForm else AName := ACustomName; if ANode = nil then AThisNode := ANodes.AddObject(ANodes.GetFirstNode, AName, AItem) else AThisNode := ANodes.AddChildObject(ANode, AName, AItem); if AItem is TdxLayoutGroup then begin AGroup := TdxLayoutGroup(AItem); for I := 0 to AGroup.Count - 1 do AddItemNode(ANodes, AThisNode, AGroup[I]); end; AThisNode.ImageIndex := GetImageIndex(AItem); AThisNode.SelectedIndex := AThisNode.ImageIndex; AThisNode.Selected := False; AThisNode.Focused := False; end; procedure TdxLayoutControlCustomizeForm.AvailableItemsWndProc(var Message: TMessage); begin if not TreeViewWndProcHandler(tvAvailableItems, Message) then FAvailableItemsWndProc(Message); end; procedure TdxLayoutControlCustomizeForm.DoAfterInsertionItem(AItem: TdxCustomLayoutItem); begin (Control as IdxLayoutDesignerHelper).SelectComponent(AItem); end; function TdxLayoutControlCustomizeForm.GetImageIndex(AItem: TdxCustomLayoutItem): Integer; var AGroup: TdxLayoutGroup; begin if AItem is TdxLayoutGroup then begin Result := 1; AGroup := AItem as TdxLayoutGroup; if AGroup.Hidden and not AGroup.IsRoot then Inc(Result); end else Result := 0; end; function TdxLayoutControlCustomizeForm.GetIsLocked: Boolean; begin Result := (LockCount > 0) or TdxLayoutControlAccess(Control).IsUpdateLocked; end; function TdxLayoutControlCustomizeForm.GetNextToSelectItem(ATreeView: TTreeView): TdxCustomLayoutItem; var ANode: TTreeNode; begin Result := nil; ANode := ATreeView.FindNextToSelect; if ANode <> nil then Result := TdxCustomLayoutItem(ANode.Data) else begin ANode := ATreeView.Selected; while (ANode <> nil) and ANode.Selected do ANode := ANode.GetPrev; if ANode <> nil then Result := TdxCustomLayoutItem(ANode.Data); end; end; procedure TdxLayoutControlCustomizeForm.InitializePopupMenu; var I: Integer; AMenuItem: TMenuItem; begin for I := 0 to pmAlign.Items.Count - 1 do begin AMenuItem := TMenuItem.Create(miAlignBy); AMenuItem.Caption := pmAlign.Items[I].Caption; AMenuItem.Action := pmAlign.Items[I].Action; miAlignBy.Add(AMenuItem); end; end; procedure TdxLayoutControlCustomizeForm.ItemsWndProc(var Message: TMessage); begin if not TreeViewWndProcHandler(tvItems, Message) then FItemsWndProc(Message); end; procedure TdxLayoutControlCustomizeForm.RefreshLists(ARefreshSelection: Boolean = False); begin if IsLocked then Exit; BeginUpdate; try RefreshAvailableItems; RefreshTreeView; finally CancelUpdate; end; if ARefreshSelection then UpdateSelection; end; procedure TdxLayoutControlCustomizeForm.RefreshButtonStates; begin acAvailableItemsViewAsList.Checked := Control.CustomizeAvailableItemsViewKind = oivkTree; acTabbedView.Checked := Control.CustomizeFormTabbedView; acHighlightRoot.Checked := Control.HighlightRoot; acShowDesignSelectors.Checked := Control.ShowDesignSelectors; RefreshEnableds; end; procedure TdxLayoutControlCustomizeForm.RefreshView; begin if Control.CustomizeFormTabbedView then lcMainGroup1.LayoutDirection := ldTabbed else lcMainGroup1.LayoutDirection := ldHorizontal; acShowDesignSelectors.Visible := Control.IsDesigning; acHighlightRoot.Visible := Control.IsDesigning; acStore.Visible := not Control.IsDesigning; acRestore.Visible := not Control.IsDesigning; end; procedure TdxLayoutControlCustomizeForm.SelectItem(AItem: TdxCustomLayoutItem); begin if AItem <> nil then (Control as IdxLayoutDesignerHelper).SelectComponent(AItem) else (Control as IdxLayoutDesignerHelper).SelectComponent(Control); RefreshEnableds; end; procedure TdxLayoutControlCustomizeForm.SetControl(AValue: TdxCustomLayoutControl); procedure StoreMetrics; begin cxDialogsMetricsStore.StoreMetrics(Self); end; procedure RestoreMetrics; begin if Control <> nil then if EqualRect(Control.CustomizeFormBounds, cxNullRect) then BoundsRect := TdxLayoutControlAccess(Control).CalculateCustomizeFormBounds(BoundsRect) else BoundsRect := Control.CustomizeFormBounds; cxDialogsMetricsStore.InitDialog(Self); Position := poDesigned; end; begin if Control <> AValue then begin if Control <> nil then begin StoreMetrics; Control.RemoveFreeNotification(Self); end; FControl := AValue; if Control <> nil then begin Control.FreeNotification(Self); RestoreMetrics; end; RecreateWnd; end; InitializeControl; end; procedure TdxLayoutControlCustomizeForm.SetItemsSelections(AList: TList); procedure SetTreeViewAsActiveControl(ATreeView: TTreeView); begin if ATreeView.Visible then ActiveControl := ATreeView else ActiveControl := nil; end; var AItem: TdxCustomLayoutItemAccess; begin if IsLocked then Exit; BeginUpdate; try cxTreeViewSyncSelection(tvItems, AList); cxTreeViewSyncSelection(tvAvailableItems, AList); if (AList.Count > 0) and (TObject(AList.Last) is TdxCustomLayoutItem) then begin AItem := TdxCustomLayoutItemAccess(AList.Last); if AItem.IsAvailable then SetTreeViewAsActiveControl(tvAvailableItems) else SetTreeViewAsActiveControl(tvItems); end; finally CancelUpdate; end; RefreshEnableds; end; procedure TdxLayoutControlCustomizeForm.SetLayoutItemsSelections(ATreeView: TTreeView); var AList: TList; begin AList := TList.Create; try cxTreeViewGetSelection(ATreeView, AList); (Control as IdxLayoutDesignerHelper).SetSelection(AList); finally AList.Free; end; end; function TdxLayoutControlCustomizeForm.TreeViewWndProcHandler(ATreeView: TTreeView; var Message: TMessage): Boolean; var ANode: TTreeNode; AHitTest : THitTests; AShift: TShiftState; begin Result := False; case Message.Msg of CN_NOTIFY: case TWMNotify(Message).NMHdr^.code of NM_RCLICK: begin AHitTest := ATreeView.GetHitTestInfoAt(Message.LParamLo, Message.LParamHi); if (htOnItem in AHitTest) then tvItems.Perform(WM_CONTEXTMENU, ATreeView.Handle, Integer(PointToSmallPoint(GetMouseCursorPos))); Message.Result := 1; Result := True; end; end; WM_RBUTTONDOWN: begin ANode := ATreeView.GetNodeAt(Message.LParamLo, Message.LParamHi); if ANode <> nil then begin ANode.Focused := True; AShift := KeysToShiftState(Message.WParam); if not ANode.Selected then begin if [ssShift, ssCtrl] * AShift <> [] then AShift := []; BeginUpdate; try ATreeView.Select(ANode, AShift); finally CancelUpdate; end; SetLayoutItemsSelections(ATreeView); end; end; Message.Result := 1; Result := True; end; WM_LBUTTONDOWN: begin ANode := ATreeView.GetNodeAt(Message.LParamLo, Message.LParamHi); if (ANode <> nil) and (ATreeView.SelectionCount > 1) then begin ANode.Focused := True; AShift := KeysToShiftState(Message.WParam); if ANode.Selected and ([ssCtrl, ssShift] * AShift = []) then begin BeginUpdate; try ATreeView.ClearSelection; finally CancelUpdate; end; end; end; end; end; end; procedure TdxLayoutControlCustomizeForm.acCloseExecute(Sender: TObject); begin Close; end; procedure TdxLayoutControlCustomizeForm.acAddGroupExecute(Sender: TObject); var AGroup: TdxLayoutGroup; begin Control.BeginUpdate; try AGroup := Control.CreateGroup; AGroup.Caption := cxGetResourceString(@sdxLayoutControlNewGroupCaption); finally Control.EndUpdate; end; DoAfterInsertionItem(AGroup); end; procedure TdxLayoutControlCustomizeForm.acAddItemExecute(Sender: TObject); var AItem: TdxCustomLayoutItem; begin Control.BeginUpdate; try AItem := Control.CreateItem; AItem.Caption := cxGetResourceString(@sdxLayoutControlNewItemCaption); finally Control.EndUpdate; end; DoAfterInsertionItem(AItem); end; procedure TdxLayoutControlCustomizeForm.tvItemsEdited(Sender: TObject; Node: TTreeNode; var S: String); begin if TdxCustomLayoutItem(Node.Data).IsRoot then Exit; BeginUpdate; try if Control.IsDesigning then TdxCustomLayoutItem(Node.Data).Name := S else TdxCustomLayoutItem(Node.Data).Caption := S; Node.Text := TdxCustomLayoutItem(Node.Data).CaptionForCustomizeForm; finally CancelUpdate; end; end; procedure TdxLayoutControlCustomizeForm.tvItemsMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var ANode: TTreeNode; ATreeView: TTreeView; AHitTests: THitTests; begin ATreeView := TTreeView(Sender); AHitTests := ATreeView.GetHitTestInfoAt(X, Y); if htOnItem in AHitTests then begin ANode := ATreeView.GetNodeAt(X, Y); if ANode = nil then Exit; ANode.Selected := True; if (Button = mbLeft) and ANode.Selected and (ANode = ATreeView.Selected) then DragHelper.InitializeDragItem(ATreeView.Selected.Data, X, Y); if (Button = mbRight) or (ssCtrl in Shift) then SetLayoutItemsSelections(ATreeView); end; end; procedure TdxLayoutControlCustomizeForm.ItemsMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); var ASource: TdxLayoutDragSource; begin if Sender = tvItems then ASource := dsCustomizeFormTreeViewItems else ASource := dsCustomizeFormAvailableItems; if ssLeft in Shift then DragHelper.TryBeginDragAndDrop(X, Y, ASource); end; procedure TdxLayoutControlCustomizeForm.ItemsMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if Button = mbLeft then DragHelper.Reset; end; procedure TdxLayoutControlCustomizeForm.tvItemsChange(Sender: TObject; Node: TTreeNode); begin if not IsLocked then SetLayoutItemsSelections(TTreeView(Sender)); end; procedure TdxLayoutControlCustomizeForm.tvItemsChanging(Sender: TObject; Node: TTreeNode; var AllowChange: Boolean); begin AllowChange := (Node = nil) or (Node.Data <> nil); end; procedure TdxLayoutControlCustomizeForm.AlignExecute(Sender: TObject); var AList: TList; I: Integer; ATag: Integer; begin ATag := (Sender as TAction).Tag; AList := TList.Create; try (Control as IdxLayoutDesignerHelper).GetSelection(AList); BeginUpdate; try Control.BeginUpdate; try if ATag = -1 then for I := 0 to AList.Count - 1 do TdxCustomLayoutItem(AList[I]).AlignmentConstraint := nil else with Control.CreateAlignmentConstraint do begin Kind := TdxLayoutAlignmentConstraintKind(ATag); for I := 0 to AList.Count - 1 do AddItem(TdxCustomLayoutItem(AList[I])); end; finally Control.EndUpdate; end; finally CancelUpdate; end; finally AList.Free; end; end; procedure TdxLayoutControlCustomizeForm.acTreeViewItemsDeleteExecute(Sender: TObject); procedure CheckSelectedParent(ANode: TTreeNode); var AParentNode: TTreeNode; AItem: TdxCustomLayoutItem; ANotSelectedChildCount: Integer; I: Integer; begin AParentNode := ANode.Parent; if AParentNode = nil then Exit; AItem := TdxCustomLayoutItem(AParentNode.Data); if not AParentNode.Selected and not AItem.IsRoot and (AItem is TdxLayoutGroup) and TdxLayoutGroup(AItem).Hidden then begin ANotSelectedChildCount := AParentNode.Count; for I := 0 to AParentNode.Count - 1 do if AParentNode[I].Selected then Dec(ANotSelectedChildCount); if ANotSelectedChildCount < 2 then tvItems.Select(AParentNode, [ssCtrl]); end; end; procedure SelectChildren(ANode: TTreeNode); var I: Integer; begin if ANode.Count > 0 then for I := 0 to ANode.Count - 1 do begin tvItems.Select(ANode[I], [ssCtrl]); SelectChildren(ANode[I]); end end; procedure CheckNeighbors(ANode: TTreeNode); var I: Integer; AParentNode: TTreeNode; ANeighborsSelectedCount: Integer; begin if (ANode.Parent = nil) or (ANode.Parent.Count = 0) then Exit; AParentNode := ANode.Parent; ANeighborsSelectedCount := 0; for I := 0 to AParentNode.Count - 1 do if AParentNode[I].Selected then Inc(ANeighborsSelectedCount); if AParentNode.Count - ANeighborsSelectedCount = 1 then for I := 0 to AParentNode.Count - 1 do if not AParentNode[I].Selected and (TObject(AParentNode[I].Data) is TdxLayoutGroup) and TdxLayoutGroup(AParentNode[I].Data).Hidden then tvItems.Select(AParentNode[I], [ssCtrl]); end; procedure AdjustSelectionForDeletion; var I: Integer; AList: TList; ASaveSelectedNode: TTreeNode; begin BeginUpdate; try AList := TList.Create; try ASaveSelectedNode := tvItems.GetSelections(AList); for I := 0 to AList.Count - 1 do begin SelectChildren(AList[I]); CheckNeighbors(AList[I]); CheckSelectedParent(AList[I]); end; with ASaveSelectedNode do TreeView_SelectItem(Handle, ItemId); finally AList.Free; end; finally CancelUpdate; end; end; var AList: TComponentList; begin if tvItems.IsEditing then Exit; AList := TComponentList.Create; try cxTreeViewGetSelection(tvItems, AList); AdjustSelectionForDeletion; DeleteItems(AList, GetNextToSelectItem(tvItems)); finally AList.Free; end; end; procedure TdxLayoutControlCustomizeForm.FormClose(Sender: TObject; var Action: TCloseAction); begin cxDialogsMetricsStore.StoreMetrics(Self); Action := caFree; end; procedure TdxLayoutControlCustomizeForm.tvItemsContextPopup( Sender: TObject; MousePos: TPoint; var Handled: Boolean); begin CalculateDeleteItemsActionStates; acAvailableItemsDelete.Enabled := False; end; procedure TdxLayoutControlCustomizeForm.acAvailableItemsDeleteExecute( Sender: TObject); var AList: TComponentList; begin if tvAvailableItems.IsEditing then Exit; AList := TComponentList.Create; try cxTreeViewGetSelection(tvAvailableItems, AList); DeleteItems(AList, GetNextToSelectItem(tvAvailableItems)); finally AList.Free; end; end; procedure TdxLayoutControlCustomizeForm.tvItemsCustomDrawItem( Sender: TCustomTreeView; Node: TTreeNode; State: TCustomDrawState; var DefaultDraw: Boolean); begin if (TObject(Node.Data) is TdxLayoutGroup) and TdxLayoutGroup(Node.Data).Hidden and not TdxLayoutGroup(Node.Data).IsRoot then Sender.Canvas.Font.Style := [fsItalic]; end; procedure TdxLayoutControlCustomizeForm.tvAvailableItemsContextPopup( Sender: TObject; MousePos: TPoint; var Handled: Boolean); begin CalculateDeleteItemsActionStates; acTreeViewItemsDelete.Enabled := False; end; procedure TdxLayoutControlCustomizeForm.acAvailableItemsExpandAllExecute( Sender: TObject); begin tvAvailableItems.FullExpand; end; procedure TdxLayoutControlCustomizeForm.acAvailableItemsCollapseAllExecute( Sender: TObject); begin tvAvailableItems.FullCollapse; end; procedure TdxLayoutControlCustomizeForm.acTreeViewExpandAllExecute( Sender: TObject); begin tvItems.FullExpand; end; procedure TdxLayoutControlCustomizeForm.acTreeViewCollapseAllExecute( Sender: TObject); begin tvItems.FullCollapse; end; procedure TdxLayoutControlCustomizeForm.acAvailableItemsViewAsListExecute( Sender: TObject); begin if acAvailableItemsViewAsList.Checked then Control.CustomizeAvailableItemsViewKind := oivkTree else Control.CustomizeAvailableItemsViewKind := oivkList; end; procedure TdxLayoutControlCustomizeForm.tvItemsEditing(Sender: TObject; Node: TTreeNode; var AllowEdit: Boolean); function IsKeyDown(AShift: TShiftState): Boolean; begin Result := (AShift * KeyboardStateToShiftState) <> []; end; begin AllowEdit := not (TdxCustomLayoutItem(Node.Data).IsRoot or (DragHelper.DragItem <> nil) or IsKeyDown([ssCtrl, ssShift])); if not AllowEdit or Control.IsDesigning then Exit; Node.Text := TdxCustomLayoutItem(Node.Data).Caption; RefreshEnableds; end; procedure TdxLayoutControlCustomizeForm.FormShortCut(var Msg: TWMKey; var Handled: Boolean); begin case Msg.CharCode of VK_ESCAPE: begin Handled := tvItems.IsEditing or tvAvailableItems.IsEditing; if Handled then if tvItems.Selected <> nil then tvItems.Selected.EndEdit(True) else if tvAvailableItems.Selected <> nil then tvAvailableItems.Selected.EndEdit(True); end; end; end; procedure TdxLayoutControlCustomizeForm.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if Key = VK_DELETE then begin if tvItems.Focused and not tvItems.IsEditing then acTreeViewItemsDelete.Execute; if tvAvailableItems.Focused and not tvAvailableItems.IsEditing then acAvailableItemsDelete.Execute; end; end; procedure TdxLayoutControlCustomizeForm.acTabbedViewExecute( Sender: TObject); begin Control.CustomizeFormTabbedView := acTabbedView.Checked; end; procedure TdxLayoutControlCustomizeForm.acHighlightRootExecute( Sender: TObject); begin Control.HighlightRoot := acHighlightRoot.Checked; end; procedure TdxLayoutControlCustomizeForm.acShowDesignSelectorsExecute( Sender: TObject); begin Control.ShowDesignSelectors := acShowDesignSelectors.Checked; end; procedure TdxLayoutControlCustomizeForm.acStoreExecute(Sender: TObject); begin Control.Store; RefreshEnableds; end; procedure TdxLayoutControlCustomizeForm.acRestoreExecute(Sender: TObject); begin Control.Restore; end; end.