{********************************************************************} { } { Developer Express Visual Component Library } { ExpressQuantumTreeList } { } { Copyright (c) 1998-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 EXPRESSQUANTUMTREELIST 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 cxTLData; {$I cxVer.inc} interface uses Classes, SysUtils, Windows, Messages, {$IFDEF DELPHI6} Variants, {$ENDIF} cxVariants, cxControls, cxClasses, cxData, cxCustomData, cxInplaceContainer, cxTL, cxTLIntf, cxTLStrs; type TcxCustomDataTreeList = class; TcxTreeListDataNode = class; { TcxTreeListCustomDataSource } TcxTreeListCustomDataSource = class(TcxCustomDataSource) protected procedure DeleteNode(AParentHandle: TcxDataRecordHandle); procedure DeleteChildItems(AParentHandle: TcxDataRecordHandle); virtual; function GetChildCount(AParentHandle: TcxDataRecordHandle): Integer; virtual; function GetChildRecordHandle(AParentHandle: TcxDataRecordHandle; AChildIndex: Integer): TcxDataRecordHandle; virtual; function GetHandleFromNode(ANode: TcxTreeListDataNode): TcxDataRecordHandle; function GetKeyValue(ARecordHandle: TcxDataRecordHandle): Variant; virtual; function GetParentKeyValue(ARecordHandle: TcxDataRecordHandle): Variant; virtual; function GetRootRecordHandle: TcxDataRecordHandle; virtual; procedure NodeMoveTo(ARecordHandle, AttachRecordHandle: TcxDataRecordHandle; AttachMode: TcxTreeListNodeAttachMode; IsCopy: Boolean); virtual; end; { TcxVirtualTreeListDataController } TcxVirtualTreeListDataController = class(TcxControlDataController) private function GetCustomDataSource: TcxTreeListCustomDataSource; function GetRecordHandle(var ARecordHandle: TcxDataRecordHandle): Boolean; function GetTreeList: TcxCustomDataTreeList; procedure SetCustomDataSource(Value: TcxTreeListCustomDataSource); protected function GetKeyValue(ARecordIndex: Integer): Variant; function GetParentKeyValue(ARecordIndex: Integer): Variant; function IsSmartLoad: Boolean; override; procedure NodesMoveTo(AttachNode: TcxTreeListDataNode; AttachMode: TcxTreeListNodeAttachMode; ANodes: TList; IsCopy: Boolean); public property TreeList: TcxCustomDataTreeList read GetTreeList; property CustomDataSource: TcxTreeListCustomDataSource read GetCustomDataSource write SetCustomDataSource; end; { TcxTreeListDataNode } TcxTreeListDataNode = class(TcxTreeListNode) private FKeyValue: Variant; FParentValue: Variant; protected function AddDataControllerRecord: Integer; override; property RecordIndex; public function AddChild: TcxTreeListNode; override; function AddChildFirst: TcxTreeListNode; override; function InsertChild(ABeforeNode: TcxTreeListNode): TcxTreeListNode; override; property KeyValue: Variant read FKeyValue write FKeyValue; property ParentValue: Variant read FParentValue write FParentValue; end; { TcxDataTreeListStructureLoader } TcxDataTreeListStructureLoader = class private function GetDataController: TcxCustomDataController; function GetLoadedNode(Index: Integer): TcxTreeListDataNode; function GetRoot: TcxTreeListNode; procedure SetLoadedNode(Index: Integer; Value: TcxTreeListDataNode); protected InSmartLoadMode: Boolean; IsLoading: Boolean; IsLoadLoadingData: Boolean; LoadingNode: TcxTreeListDataNode; LoadedList: TList; ExpandedKeysList: TList; TreeList: TcxCustomDataTreeList; IsRefresh: Boolean; // nodes handling procedures function Add: TcxTreeListDataNode; function AddChild(AParent: TcxTreeListNode): TcxTreeListDataNode; function AddNodeToList(ANode: TcxTreeListDataNode; ARecordIndex: Integer = -1): TcxTreeListDataNode; virtual; procedure AfterSmartLoad; virtual; procedure AssignNodeImages(ANode: TcxTreeListDataNode); procedure BeforeSmartLoad; virtual; function GetKeyValue(ARecordIndex: Integer): Variant; virtual; function GetNodeImageIndex(ARecordIndex: Integer): Integer; virtual; function GetNodeStateImage(ARecordIndex: Integer): Integer; virtual; function GetParentKeyValue(ARecordIndex: Integer): Variant; virtual; procedure InternalMove(ANode, ADestNode: TcxTreeListNode; AMode: TcxTreeListNodeAttachMode); function IsSmartLoad: Boolean; // loading procedures procedure CheckHasChildren(ANode: TcxTreeListDataNode); virtual; procedure DoneLoading; virtual; procedure InitLoading; virtual; procedure InitLoadedNode(ANode: TcxTreeListDataNode); virtual; function IsKeyExpanded(const AKeyValue: Variant; var ANode: TcxTreeListDataNode): Boolean; function IsNodeExpanded(const AKeyValue: Variant): Boolean; function FindParentNode(const AParentKeyValue: Variant; var Index: Integer): Boolean; procedure LoadChildItems(AParentNode: TcxTreeListDataNode); virtual; abstract; procedure LoadTreeList; virtual; procedure LoadTreeListInLoadAllRecordsMode; virtual; procedure LoadTreeListInSmartLoadMode; virtual; abstract; function LoadValues(const AParentValue, AKeyValue: Variant): TcxTreeListDataNode; overload; function LoadValues(AParentNode: TcxTreeListDataNode; const AParentValue, AKeyValue: Variant): TcxTreeListDataNode; overload; property DataController: TcxCustomDataController read GetDataController; property Loaded[Index: Integer]: TcxTreeListDataNode read GetLoadedNode write SetLoadedNode; property Root: TcxTreeListNode read GetRoot; public constructor Create(AOwner: TcxCustomDataTreeList); procedure DoLoadTreeList(ALoadingNode: TcxTreeListDataNode); end; TcxDataTreeListStructureLoaderClass = class of TcxDataTreeListStructureLoader; { TcxVirtualTreeListLoader } TcxVirtualTreeListLoader = class(TcxDataTreeListStructureLoader) private function GetDataController: TcxVirtualTreeListDataController; function GetDataSource: TcxTreeListCustomDataSource; protected procedure CheckHasChildren(ANode: TcxTreeListDataNode); override; function GetKeyValue(ARecordIndex: Integer): Variant; override; function GetParentKeyValue(ARecordIndex: Integer): Variant; override; procedure LoadChildItems(AParentNode: TcxTreeListDataNode); override; procedure LoadTreeListInSmartLoadMode; override; property DataController: TcxVirtualTreeListDataController read GetDataController; property DataSource: TcxTreeListCustomDataSource read GetDataSource; end; { TcxVirtualTreeListOptionsData } TcxVirtualTreeListOptionsData = class(TcxTreeListOptionsData) protected procedure SetSmartLoad(Index: Integer; Value: Boolean); virtual; published property SmartLoad: Boolean index 3 read GetState write SetSmartLoad default False; end; { TcxCustomDataTreeList } TcxCustomDataTreeList = class(TcxCustomTreeListControl) private FIsDataChanging: Boolean; function GetOptionsData: TcxVirtualTreeListOptionsData; procedure SetOptionsData(Value: TcxVirtualTreeListOptionsData); protected DragKeyValue: Variant; Loader: TcxDataTreeListStructureLoader; function CreateNode: TcxTreeListNode; override; procedure CreateSubClasses; override; procedure DataChanged; override; function DoCanNodeExpand(ANode: TcxTreeListNode): Boolean; override; procedure DoClear; override; procedure DoClearDataSource; virtual; procedure DoDataChanged; virtual; procedure DestroySubClasses; override; procedure DestroyNodes; virtual; function GetOptionsDataClass: TcxControlOptionsDataClass; override; function GetNodeByRecordIndex(AIndex: Integer): TcxTreeListDataNode; function IsDataChangedBusy: Boolean; virtual; function IsSmartLoad: Boolean; override; procedure RestoreSorting; override; function SkipFocusedRecordChanged: Boolean; override; function SkipCancelOperation: Boolean; override; // IcxTreeListDesignTimeOperations implementation function SupportsBandColumnEditor: Boolean; override; // nodes handling procedure function InsertNode(IsAppend: Boolean): Boolean; override; // loading structure procedures function GetLoaderClass: TcxDataTreeListStructureLoaderClass; virtual; function IsDataSettingsValid: Boolean; virtual; // for DB IsKeyFieldEmpty procedure LoadTreeList(ALoadingNode: TcxTreeListDataNode); virtual; procedure RecreateLoader; function UseRowIndexForNewRecord: Boolean; override; // dragdrop procedure SaveDragPos; override; procedure RestoreDragPos; override; property IsDataChanging: Boolean read FIsDataChanging write FIsDataChanging; property OptionsData: TcxVirtualTreeListOptionsData read GetOptionsData write SetOptionsData; public procedure FullRefresh; override; function FindNodeByKeyValue(const AKeyValue: Variant; AColumn: TcxTreeListColumn; AStartNode: TcxTreeListNode = nil; AExpandedOnly: Boolean = False; AForward: Boolean = True): TcxTreeListNode; end; { TcxCustomVirtualTreeList } TcxCustomVirtualTreeList = class(TcxCustomDataTreeList) private function GetDataController: TcxVirtualTreeListDataController; function GetDataSource: TcxTreeListCustomDataSource; procedure SetDataSource(Value: TcxTreeListCustomDataSource); protected procedure AddToDelitionList(ANode: TcxTreeListNode); override; procedure DoDeleteRecords; override; procedure DoMoveTo(AttachNode: TcxTreeListNode; AttachMode: TcxTreeListNodeAttachMode; ANodes: TList; IsCopy: Boolean); override; function GetDataControllerClass: TcxCustomDataControllerClass; override; function GetLoaderClass: TcxDataTreeListStructureLoaderClass; override; function IsCancelOperation: Boolean; override; function IsDataSettingsValid: Boolean; override; property DataController: TcxVirtualTreeListDataController read GetDataController; public function HandleFromNode(ANode: TcxTreeListNode): TcxDataRecordHandle; virtual; function NodeFromHandle(ARecordHandle: TcxDataRecordHandle): TcxTreeListNode; virtual; property CustomDataSource: TcxTreeListCustomDataSource read GetDataSource write SetDataSource; end; { TcxVirtualTreeList } TcxVirtualTreeList = class(TcxCustomVirtualTreeList) public property ColumnCount; property Columns; property Customizing; property DataController; property FocusedColumn; property FocusedNode; property HitTest; property IsEditing; property LevelIndentWidth; property Nodes; property SelectionCount; property Selections; property Sorted; property SortedColumnCount; property SortedColumns; property TopVisibleIndex; property TopVisibleNode; property VisibleBandCount; property VisibleBands; property VisibleColumnCount; property VisibleColumns; property VisibleCount; published property Align; property Anchors; property Bands; property BorderStyle; property BufferedPaint; property Constraints; property Cursor; property DefaultLayout; property DefaultRowHeight; property DragCursor; property DragKind; property DragMode; property Enabled; property Font; {$IFDEF DELPHI6} property HelpContext; property HelpKeyword; property HelpType; {$ENDIF} property Hint; property Images; property LookAndFeel; property OptionsBehavior; property OptionsCustomizing; property OptionsData; property OptionsSelection; property OptionsView; property ParentColor; property ParentFont; property PopupMenu; property Preview; property StateImages; property Styles; property StylesEvents; property TabOrder; property TabStop; property Visible; // TreeList events property OnAfterSorting; property OnAfterSummary; property OnBandHeaderClick; property OnBandSizeChanged; property OnCanResize; property OnCanSelectNode; property OnChange; property OnClick; property OnCollapsed; property OnCollapsing; property OnColumnHeaderClick; property OnColumnSizeChanged; property OnCompare; property OnConstrainedResize; property OnSorting; {$IFDEF DELPHI5} property OnContextPopup; {$ENDIF} property OnCustomDrawBackground; property OnCustomDrawBandHeader; property OnCustomDrawCell; property OnCustomDrawCellsGroup; property OnCustomDrawFooter; property OnCustomDrawFooterCell; property OnCustomDrawHeader; property OnCustomDrawHeaderCell; property OnCustomDrawIndentCell; property OnCustomDrawIndicatorCell; property OnCustomDrawPreview; property OnCustomizationFormVisibleChanged; property OnDataChanged; property OnDblClick; property OnDeletion; property OnDockDrop; property OnDockOver; property OnDragDrop; property OnDragOver; property OnEdited; property OnEditing; property OnEditValueChanged; property OnEndDock; property OnEndDrag; property OnEnter; property OnExit; property OnExpanded; property OnExpanding; property OnFocusedColumnChanged; property OnFocusedNodeChanged; property OnGetNodeImageIndex; property OnGetNodePreviewHeight; property OnGetSiteInfo; property OnHotTrackNode; property OnInitEdit; property OnIsGroupNode; property OnKeyDown; property OnKeyPress; property OnKeyUp; property OnLayoutChanged; property OnMouseDown; property OnMouseMove; property OnMouseUp; property OnMouseWheel; property OnMouseWheelDown; property OnMouseWheelUp; property OnMoveTo; property OnNodeChanged; property OnResize; property OnSelectionChanged; property OnStartDock; property OnStartDrag; property OnSummary; property OnTopRecordIndexChanged; property OnUnDock; end; const cxNullRecordHandle: TcxDataRecordHandle = TcxDataRecordHandle(-1); implementation type TcxEditContainerAccess = class(TcxCustomInplaceEditContainer); TcxNodeAccess = class(TcxTreeListNode); TcxNodesAccess = class(TcxTreeListNodes); TcxDataControllerAccess = class(TcxCustomDataController); function cxCompareDataNodesByKeyValue(Item1, Item2: Pointer): Integer; begin with TcxTreeListDataNode(Item1) do begin Result := VarCompare(KeyValue, TcxTreeListDataNode(Item2).KeyValue); if Result = 0 then Result := VarCompare(ParentValue, TcxTreeListDataNode(Item2).ParentValue); end; end; function cxKeyValues(Item1, Item2: Pointer): Integer; begin Result := VarCompare(PVariant(Item1)^, PVariant(Item2)^); end; { TcxTreeListCustomDataSource } procedure TcxTreeListCustomDataSource.DeleteNode( AParentHandle: TcxDataRecordHandle); begin DeleteChildItems(AParentHandle); DeleteRecord(AParentHandle); end; procedure TcxTreeListCustomDataSource.DeleteChildItems( AParentHandle: TcxDataRecordHandle); var I: Integer; AItemHandle: TcxDataRecordHandle; begin for I := GetChildCount(AParentHandle) - 1 downto 0 do begin AItemHandle := GetChildRecordHandle(AParentHandle, I); try DeleteChildItems(AItemHandle); finally DeleteRecord(AItemHandle); end; end; end; function TcxTreeListCustomDataSource.GetChildCount( AParentHandle: TcxDataRecordHandle): Integer; begin Result := 0; end; function TcxTreeListCustomDataSource.GetChildRecordHandle( AParentHandle: TcxDataRecordHandle; AChildIndex: Integer): TcxDataRecordHandle; begin Result := cxNullRecordHandle; end; function TcxTreeListCustomDataSource.GetHandleFromNode( ANode: TcxTreeListDataNode): TcxDataRecordHandle; begin if ANode = nil then Result := GetRootRecordHandle else Result := GetRecordHandleByIndex(TcxNodeAccess(ANode).RecordIndex) end; function TcxTreeListCustomDataSource.GetKeyValue(ARecordHandle: TcxDataRecordHandle): Variant; begin Result := Null; end; procedure TcxTreeListCustomDataSource.NodeMoveTo( ARecordHandle, AttachRecordHandle: TcxDataRecordHandle; AttachMode: TcxTreeListNodeAttachMode; IsCopy: Boolean); begin end; function TcxTreeListCustomDataSource.GetParentKeyValue( ARecordHandle: TcxDataRecordHandle): Variant; begin Result := Null; end; function TcxTreeListCustomDataSource.GetRootRecordHandle: TcxDataRecordHandle; begin Result := cxNullRecordHandle; end; { TcxVirtualTreeListDataController } function TcxVirtualTreeListDataController.GetKeyValue(ARecordIndex: Integer): Variant; begin if GetRecordHandle(TcxDataRecordHandle(ARecordIndex)) then Result := CustomDataSource.GetKeyValue(TcxDataRecordHandle(ARecordIndex)) else Result := Null; end; function TcxVirtualTreeListDataController.GetParentKeyValue(ARecordIndex: Integer): Variant; begin if GetRecordHandle(TcxDataRecordHandle(ARecordIndex)) then Result := CustomDataSource.GetParentKeyValue(TcxDataRecordHandle(ARecordIndex)) else Result := Null; end; function TcxVirtualTreeListDataController.IsSmartLoad: Boolean; begin Result := TreeList.OptionsData.SmartLoad and (CustomDataSource <> nil) and (CustomDataSource.GetRecordCount = 0); end; procedure TcxVirtualTreeListDataController.NodesMoveTo(AttachNode: TcxTreeListDataNode; AttachMode: TcxTreeListNodeAttachMode; ANodes: TList; IsCopy: Boolean); function GetRecordIndex(Index: Integer): Integer; begin Result := TcxTreeListDataNode(ANodes.List^[Index]).RecordIndex; end; var I: Integer; AMovedHandle, AttachHandle: TcxDataRecordHandle; begin if CustomDataSource <> nil then begin BeginUpdate; try CustomDataSource.CurrentProvider := Provider; AttachHandle := CustomDataSource.GetRecordHandleByIndex(AttachNode.RecordIndex); for I := 0 to ANodes.Count - 1 do begin AMovedHandle := CustomDataSource.GetRecordHandleByIndex(GetRecordIndex(I)); CustomDataSource.NodeMoveTo(AMovedHandle, AttachHandle, AttachMode, IsCopy); end; CustomDataSource.DataChanged; finally EndUpdate; end; end; end; function TcxVirtualTreeListDataController.GetCustomDataSource: TcxTreeListCustomDataSource; begin Result := TcxTreeListCustomDataSource(inherited CustomDataSource); end; function TcxVirtualTreeListDataController.GetRecordHandle( var ARecordHandle: TcxDataRecordHandle): Boolean; begin Result := CustomDataSource <> nil; if Result then begin CustomDataSource.CurrentProvider := Provider; ARecordHandle := CustomDataSource.GetRecordHandleByIndex(Integer(ARecordHandle)); end; end; function TcxVirtualTreeListDataController.GetTreeList: TcxCustomDataTreeList; begin Result := TcxCustomDataTreeList(GetOwner); end; procedure TcxVirtualTreeListDataController.SetCustomDataSource(Value: TcxTreeListCustomDataSource); begin inherited CustomDataSource := Value; end; { TcxTreeListDataNode } function TcxTreeListDataNode.AddChild: TcxTreeListNode; begin raise EcxTreeList.Create(scxOperationNotSupported); end; function TcxTreeListDataNode.AddChildFirst: TcxTreeListNode; begin raise EcxTreeList.Create(scxOperationNotSupported); end; function TcxTreeListDataNode.InsertChild( ABeforeNode: TcxTreeListNode): TcxTreeListNode; begin raise EcxTreeList.Create(scxOperationNotSupported); end; function TcxTreeListDataNode.AddDataControllerRecord: Integer; begin Result := inherited AddDataControllerRecord; end; { TcxDataTreeListStructureLoader } constructor TcxDataTreeListStructureLoader.Create(AOwner: TcxCustomDataTreeList); begin TreeList := AOwner; end; procedure TcxDataTreeListStructureLoader.DoLoadTreeList( ALoadingNode: TcxTreeListDataNode); begin if IsLoading or IsRefresh then begin IsRefresh := False; Exit; end; DataController.BeginUpdate; try if (ALoadingNode = nil) and IsSmartLoad and InSmartLoadMode then begin InSmartLoadMode := False; Exit; end; IsLoadLoadingData := not IsRefresh and IsSmartLoad and ((DataController.RecordCount <> 0) and (ALoadingNode = nil)); IsLoading := True; LoadingNode := ALoadingNode; InitLoading; TcxNodesAccess(TreeList.Nodes).IsInternalCreating := True; try LoadTreeList; finally TreeList.SafeDelete := True; DoneLoading; end; finally TreeList.SafeDelete := False; TcxNodesAccess(TreeList.Nodes).IsInternalCreating := False; InSmartLoadMode := IsSmartLoad and not IsLoadLoadingData; IsLoadLoadingData := False; DataController.EndUpdate; InSmartLoadMode := False; IsLoading := False; end; end; function TcxDataTreeListStructureLoader.Add: TcxTreeListDataNode; begin Result := TcxTreeListDataNode(TreeList.AddNode(nil, nil, nil, tlamAdd)); end; function TcxDataTreeListStructureLoader.AddChild( AParent: TcxTreeListNode): TcxTreeListDataNode; begin Result := TcxTreeListDataNode(TreeList.AddNode(nil, AParent, nil, tlamAddChild)); end; function TcxDataTreeListStructureLoader.AddNodeToList( ANode: TcxTreeListDataNode; ARecordIndex: Integer = -1): TcxTreeListDataNode; begin if ARecordIndex >= 0 then begin ANode.RecordIndex := ARecordIndex; AssignNodeImages(ANode); end; TreeList.NodesList.Add(ANode); Result := ANode; end; procedure TcxDataTreeListStructureLoader.InternalMove( ANode, ADestNode: TcxTreeListNode; AMode: TcxTreeListNodeAttachMode); begin TreeList.InternalMove(ANode, ADestNode, AMode); end; function TcxDataTreeListStructureLoader.IsSmartLoad: Boolean; begin Result := TcxDataControllerAccess(DataController).IsSmartLoad; end; procedure TcxDataTreeListStructureLoader.CheckHasChildren( ANode: TcxTreeListDataNode); begin end; procedure TcxDataTreeListStructureLoader.DoneLoading; procedure DoExpandExpandedNodes; var PrevCount, I: Integer; ANode: TcxTreeListDataNode; begin repeat PrevCount := ExpandedKeysList.Count; for I := 0 to ExpandedKeysList.Count - 1 do if IsKeyExpanded(PVariant(ExpandedKeysList.List[I])^, ANode) then begin if (ANode.Count = 0) then LoadChildItems(ANode); Dispose(PVariant(ExpandedKeysList.List^[I])); ExpandedKeysList.Delete(I); ANode.Expanded := True; Break; end; until PrevCount = ExpandedKeysList.Count; end; var I: Integer; begin try if (ExpandedKeysList <> nil) and (ExpandedKeysList.Count > 0) then begin LoadedList.Sort(cxCompareDataNodesByKeyValue); DoExpandExpandedNodes; end; finally if ExpandedKeysList <> nil then begin for I := 0 to ExpandedKeysList.Count - 1 do Dispose(PVariant(ExpandedKeysList.List^[I])); FreeAndNil(ExpandedKeysList); end; FreeAndNil(LoadedList); if IsSmartLoad then AfterSmartLoad; end; end; procedure TcxDataTreeListStructureLoader.InitLoading; var I: Integer; AKeyPtr: PVariant; begin if IsSmartLoad then BeforeSmartLoad; if LoadingNode = nil then try TreeList.ViewInfo.NodesViewInfo.Clear; ExpandedKeysList := TList.Create; LoadedList := TList.Create; if DataController.RecordCount > 0 then LoadedList.Capacity := DataController.RecordCount; if TreeList.NodesList.Count > 0 then begin ExpandedKeysList.Capacity := TreeList.NodesList.Count; for I := 0 to TreeList.NodesList.Count - 1 do with TcxTreeListDataNode(TreeList.NodesList.List^[I]) do begin if Expanded then begin New(AKeyPtr); AKeyPtr^ := KeyValue; ExpandedKeysList.Add(AKeyPtr); end; end; ExpandedKeysList.Sort(cxKeyValues); end; finally TreeList.DestroyNodes; end; end; procedure TcxDataTreeListStructureLoader.InitLoadedNode(ANode: TcxTreeListDataNode); begin end; function TcxDataTreeListStructureLoader.IsKeyExpanded( const AKeyValue: Variant; var ANode: TcxTreeListDataNode): Boolean; var L, H, I, C: Integer; begin Result := False; if (LoadedList = nil) or (LoadedList.Count = 0) then Exit; L := 0; H := LoadedList.Count - 1; while L <= H do begin I := (L + H) shr 1; ANode := TcxTreeListDataNode(LoadedList.List^[I]); C := VarCompare(ANode.KeyValue, AKeyValue); Result := C = 0; if not Result then begin if C < 0 then L := I + 1 else H := I - 1; end else Break; end; end; function TcxDataTreeListStructureLoader.IsNodeExpanded( const AKeyValue: Variant): Boolean; var L, H, I, C: Integer; begin Result := False; if (ExpandedKeysList = nil) or (ExpandedKeysList.Count = 0) then Exit; L := 0; H := ExpandedKeysList.Count - 1; while (L <= H) and not Result do begin I := (L + H) shr 1; C := VarCompare(PVariant(ExpandedKeysList.List^[I])^, AKeyValue); if C = 0 then Result := True else if C < 0 then L := I + 1 else H := I - 1; end; end; function TcxDataTreeListStructureLoader.FindParentNode( const AParentKeyValue: Variant; var Index: Integer): Boolean; var L, H, I, C: Integer; begin Result := False; L := 0; H := LoadedList.Count - 1; while L <= H do begin I := (L + H) shr 1; C := VarCompare(TcxTreeListDataNode(LoadedList.List^[I]).KeyValue, AParentKeyValue); if C < 0 then L := I + 1 else begin H := I - 1; if C = 0 then begin Result := True; L := I; end; end; end; Index := L; end; procedure TcxDataTreeListStructureLoader.AfterSmartLoad; begin if not IsLoadLoadingData then TcxDataControllerAccess(DataController).EndSmartLoad; end; procedure TcxDataTreeListStructureLoader.AssignNodeImages( ANode: TcxTreeListDataNode); begin with ANode do begin if RecordIndex >= 0 then begin FImageIndexes[tlitImageIndex] := GetNodeImageIndex(RecordIndex); FImageIndexes[tlitStateIndex] := GetNodeStateImage(RecordIndex); end; end; end; procedure TcxDataTreeListStructureLoader.BeforeSmartLoad; begin if not IsLoadLoadingData then TcxDataControllerAccess(DataController).BeginSmartLoad; end; function TcxDataTreeListStructureLoader.GetKeyValue( ARecordIndex: Integer): Variant; begin Result := Null; end; function TcxDataTreeListStructureLoader.GetNodeImageIndex( ARecordIndex: Integer): Integer; begin Result := -1; end; function TcxDataTreeListStructureLoader.GetNodeStateImage( ARecordIndex: Integer): Integer; begin Result := -1; end; function TcxDataTreeListStructureLoader.GetParentKeyValue( ARecordIndex: Integer): Variant; begin Result := -1; end; procedure TcxDataTreeListStructureLoader.LoadTreeListInLoadAllRecordsMode; var I, Index: Integer; ANode, APrevNode, ADestNode: TcxTreeListDataNode; begin with DataController do begin for I := 0 to RecordCount - 1 do begin ANode := LoadValues(GetParentKeyValue(I), GetKeyValue(I)); ANode.RecordIndex := I; AssignNodeImages(ANode); CheckHasChildren(ANode); end; ANode := TcxTreeListDataNode(TreeList.Nodes.Root.GetLastChild); while ANode <> nil do begin APrevNode := TcxTreeListDataNode(ANode.GetPrevSibling); if FindParentNode(ANode.FParentValue, Index) then begin ADestNode := Self.Loaded[Index]; if ANode.CanMove(ADestNode, tlamAddChild) then TreeList.InternalMove(ANode, ADestNode, tlamAddChild); end; ANode := APrevNode; end; end; end; procedure TcxDataTreeListStructureLoader.LoadTreeList; begin if IsSmartLoad and ((LoadingNode <> nil) or not IsLoadLoadingData) then LoadTreeListInSmartLoadMode else LoadTreeListInLoadAllRecordsMode; end; function TcxDataTreeListStructureLoader.LoadValues( const AParentValue, AKeyValue: Variant): TcxTreeListDataNode; function GetParent: TcxTreeListDataNode; var AIndex: Integer; begin Result := nil; if FindParentNode(AParentValue, AIndex) then Result := TcxTreeListDataNode(LoadedList.List^[AIndex]); end; begin Result := LoadValues(GetParent, AParentValue, AKeyValue); if Result.RecordIndex >= 0 then AssignNodeImages(Result); end; function TcxDataTreeListStructureLoader.LoadValues(AParentNode: TcxTreeListDataNode; const AParentValue, AKeyValue: Variant): TcxTreeListDataNode; var AIndex: Integer; begin Result := AddChild(AParentNode); AddNodeToList(Result); Result.FKeyValue := AKeyValue; Result.FParentValue := AParentValue; InitLoadedNode(Result); if (LoadedList <> nil) and not FindParentNode(AKeyValue, AIndex) then LoadedList.Insert(AIndex, Result); AssignNodeImages(Result); end; function TcxDataTreeListStructureLoader.GetDataController: TcxCustomDataController; begin Result := TreeList.DataController; end; function TcxDataTreeListStructureLoader.GetLoadedNode( Index: Integer): TcxTreeListDataNode; begin Result := TcxTreeListDataNode(LoadedList.List^[Index]); end; function TcxDataTreeListStructureLoader.GetRoot: TcxTreeListNode; begin Result := TreeList.Nodes.Root; end; procedure TcxDataTreeListStructureLoader.SetLoadedNode( Index: Integer; Value: TcxTreeListDataNode); begin LoadedList.List^[Index] := Value; end; { TcxVirtualTreeListLoader } procedure TcxVirtualTreeListLoader.CheckHasChildren(ANode: TcxTreeListDataNode); begin if IsSmartLoad and (ANode.Count = 0) then begin with DataSource, ANode do HasChildren := GetChildCount(GetRecordHandleByIndex(RecordIndex)) <> 0; end; end; function TcxVirtualTreeListLoader.GetKeyValue( ARecordIndex: Integer): Variant; begin Result := DataController.GetKeyValue(ARecordIndex); end; function TcxVirtualTreeListLoader.GetParentKeyValue( ARecordIndex: Integer): Variant; begin Result := DataController.GetParentKeyValue(ARecordIndex); end; procedure TcxVirtualTreeListLoader.LoadChildItems( AParentNode: TcxTreeListDataNode); var I: Integer; AParentValue: Variant; AParentHandle: TcxDataRecordHandle; procedure AddNewNode(AHandle: TcxDataRecordHandle); var ANode: TcxTreeListDataNode; begin ANode := AddNodeToList(AddChild(AParentNode), DataController.LoadRecord(AHandle)); ANode.ParentValue := AParentValue; ANode.KeyValue := DataSource.GetKeyValue(AHandle); ANode.HasChildren := DataSource.GetChildCount(AHandle) > 0; if IsNodeExpanded(ANode.KeyValue) then begin LoadChildItems(ANode); ANode.Expanded := True; end; end; begin with DataSource do begin AParentHandle := GetHandleFromNode(AParentNode); AParentValue := GetKeyValue(AParentHandle); for I := 0 to GetChildCount(AParentHandle) - 1 do AddNewNode(GetChildRecordHandle(AParentHandle, I)); end; end; procedure TcxVirtualTreeListLoader.LoadTreeListInSmartLoadMode; begin LoadChildItems(LoadingNode); end; function TcxVirtualTreeListLoader.GetDataController: TcxVirtualTreeListDataController; begin Result := TcxVirtualTreeListDataController(inherited DataController); end; function TcxVirtualTreeListLoader.GetDataSource: TcxTreeListCustomDataSource; begin Result := TcxCustomVirtualTreeList(TreeList).CustomDataSource; end; { TcxVirtualTreeListOptionsData } procedure TcxVirtualTreeListOptionsData.SetSmartLoad( Index: Integer; Value: Boolean); begin if SmartLoad <> Value then begin SetState(Index, Value); with TcxCustomDataTreeList(GetOwner) do begin if DataController <> nil then TcxDataControllerAccess(DataController).RestructData; end; end; end; { TcxCustomDataTreeList } function TcxCustomDataTreeList.CreateNode: TcxTreeListNode; begin Result := TcxTreeListDataNode.Create(Nodes); end; procedure TcxCustomDataTreeList.CreateSubClasses; begin RecreateLoader; inherited CreateSubClasses; end; procedure TcxCustomDataTreeList.DataChanged; function SkipDataChangedNotification: Boolean; begin Result := IsLoading or IsDestroying; with Loader do Result := Result or (IsLoading and InSmartLoadMode) end; begin FIsDataChanging := True; try if not SkipDataChangedNotification then DoDataChanged else Loader.InSmartLoadMode := False; finally FIsDataChanging := False; end; end; function TcxCustomDataTreeList.DoCanNodeExpand( ANode: TcxTreeListNode): Boolean; begin Result := inherited DoCanNodeExpand(ANode); if Result and IsSmartLoad and (ANode.Count = 0) and not Loader.IsLoading then begin LoadTreeList(TcxTreeListDataNode(ANode)); ANode.HasChildren := ANode.Count > 0; Result := ANode.HasChildren; end; end; procedure TcxCustomDataTreeList.DoClear; begin DoClearDataSource; inherited DoClear; end; procedure TcxCustomDataTreeList.DoClearDataSource; begin DataController.CustomDataSource := nil; end; procedure TcxCustomDataTreeList.DoDataChanged; begin FIsDataChanging := True; try if not IsDataSettingsValid then DestroyNodes else begin LoadTreeList(nil); inherited DataChanged; end; finally LayoutChanged; end; end; procedure TcxCustomDataTreeList.DestroySubClasses; begin inherited DestroySubClasses; FreeAndNil(Loader); end; procedure TcxCustomDataTreeList.DestroyNodes; var ATopIndex: Integer; begin SafeDelete := True; ATopIndex := TcxNodesAccess(Nodes).TopIndex; try Nodes.Clear; DeletedNodes.Clear; finally NodesList.Clear; TcxNodesAccess(Nodes).TopIndex := ATopIndex; SafeDelete := False; end; end; function TcxCustomDataTreeList.GetOptionsDataClass: TcxControlOptionsDataClass; begin Result := TcxVirtualTreeListOptionsData; end; function TcxCustomDataTreeList.GetNodeByRecordIndex( AIndex: Integer): TcxTreeListDataNode; begin if cxInRange(AIndex, 0, NodesList.Count - 1) then Result := TcxTreeListDataNode(NodesList.List[AIndex]) else Result := nil; end; function TcxCustomDataTreeList.SkipFocusedRecordChanged: Boolean; begin Result := IsDataChangedBusy; end; function TcxCustomDataTreeList.SkipCancelOperation: Boolean; begin Result := IsDataChangedBusy; end; function TcxCustomDataTreeList.IsDataChangedBusy: Boolean; begin Result := Loader.IsLoading or FIsDataChanging; end; procedure TcxCustomDataTreeList.RestoreSorting; var C, I: Integer; FIsExpanded: PByteArray; ATopIndex: Integer; const AExpandedState = tlnsCollapsed{ or tlnsHasChildren}; begin C := Nodes.Count; GetMem(FIsExpanded, C); ATopIndex := TcxNodesAccess(Nodes).TopIndex; try BeginUpdate; try for I := 0 to Nodes.Count - 1 do FIsExpanded^[I] := Byte(Nodes.Items[I].Expanded); DestroyNodes; finally try DataChanged; if C > Nodes.Count then C := Nodes.Count; // restore expanding for I := 0 to C - 1 do begin with TcxNodeAccess(NodesList.List^[I]) do begin if Boolean(FIsExpanded^[I]) then State := State and not AExpandedState else State := State or AExpandedState end; end; finally TcxNodesAccess(Nodes).TopIndex := ATopIndex; EndUpdate; if FocusedNode <> nil then FocusedNode.MakeVisible; end; end; finally FreeMem(FIsExpanded); end; end; function TcxCustomDataTreeList.SupportsBandColumnEditor: Boolean; begin Result := True; end; function TcxCustomDataTreeList.InsertNode(IsAppend: Boolean): Boolean; begin if IsAppend then DataController.Append else DataController.Insert; Result := True; end; function TcxCustomDataTreeList.GetLoaderClass: TcxDataTreeListStructureLoaderClass; begin Result := TcxDataTreeListStructureLoader; end; function TcxCustomDataTreeList.IsDataSettingsValid: Boolean; begin Result := (DataController.RecordCount > 0) or IsSmartLoad; end; function TcxCustomDataTreeList.IsSmartLoad: Boolean; begin Result := TcxDataControllerAccess(DataController).IsSmartLoad; end; procedure TcxCustomDataTreeList.LoadTreeList(ALoadingNode: TcxTreeListDataNode); begin SetGlassCursor; try Freeze; try NodesList.Pack; if not IsDataSettingsValid then DestroyNodes else Loader.DoLoadTreeList(ALoadingNode); finally if ALoadingNode = nil then RestoreCursor; UnFreeze; if not IsLocked and (FocusedNode <> nil) and (ALoadingNode = nil) then FocusedNode.MakeVisible; LayoutChanged; end; except RestoreCursor; raise; end; end; procedure TcxCustomDataTreeList.FullRefresh; begin BeginUpdate; try DestroyNodes; DataController.RecordCount := 0; LoadTreeList(nil); finally Loader.IsRefresh := True; EndUpdate; Loader.IsRefresh := False; DataController.UpdateItems(False); end; end; function FindKeyFilter(ANode: TcxTreeListDataNode; AKey: PVariant): Boolean; begin Result := VarCompare(ANode.FKeyValue, AKey^) = 0; end; function TcxCustomDataTreeList.FindNodeByKeyValue(const AKeyValue: Variant; AColumn: TcxTreeListColumn; AStartNode: TcxTreeListNode = nil; AExpandedOnly: Boolean = False; AForward: Boolean = True): TcxTreeListNode; begin Result := Find(@AKeyValue, AStartNode, AExpandedOnly, AForward, TcxTreeListFindFunc(@FindKeyFilter)); end; procedure TcxCustomDataTreeList.RecreateLoader; begin try FreeAndNil(Loader); finally Loader := GetLoaderClass.Create(Self); end; end; function TcxCustomDataTreeList.UseRowIndexForNewRecord: Boolean; begin Result := False; end; procedure TcxCustomDataTreeList.SaveDragPos; begin if DragNode <> nil then DragKeyValue := TcxTreeListDataNode(DragNode).KeyValue else DragKeyValue := Null; end; procedure TcxCustomDataTreeList.RestoreDragPos; var I: Integer; begin if not VarIsNull(DragKeyValue) then begin for I := 0 to NodesList.Count - 1 do with TcxTreeListDataNode(NodesList.List^[I]) do if VarEquals(DragKeyValue, KeyValue) then begin Focused := True; MakeVisible; Exit; end; end; end; function TcxCustomDataTreeList.GetOptionsData: TcxVirtualTreeListOptionsData; begin Result := TcxVirtualTreeListOptionsData(inherited OptionsData); end; procedure TcxCustomDataTreeList.SetOptionsData( Value: TcxVirtualTreeListOptionsData); begin OptionsData.Assign(Value); end; { TcxCustomVirtualTreeList } function TcxCustomVirtualTreeList.HandleFromNode( ANode: TcxTreeListNode): TcxDataRecordHandle; begin Result := CustomDataSource.GetHandleFromNode(TcxTreeListDataNode(ANode)); end; function TcxCustomVirtualTreeList.NodeFromHandle( ARecordHandle: TcxDataRecordHandle): TcxTreeListNode; begin with CustomDataSource do Result := GetNodeByRecordIndex(GetRecordIndexByHandle(ARecordHandle)); end; procedure TcxCustomVirtualTreeList.AddToDelitionList(ANode: TcxTreeListNode); begin if not IsSmartLoad or not ANode.Deleting then DeletedNodes.Add(Pointer(TcxNodeAccess(ANode).RecordIndex)); end; procedure TcxCustomVirtualTreeList.DoDeleteRecords; var I: Integer; begin if IsSmartLoad then begin DataController.BeginUpdate; with DeletedNodes do try for I := 0 to DeletedNodes.Count - 1 do List^[I] := CustomDataSource.GetRecordHandleByIndex(Integer(List^[I])); for I := 0 to DeletedNodes.Count - 1 do CustomDataSource.DeleteNode(List^[I]); finally Clear; DataController.EndUpdate; end; end else inherited DoDeleteRecords; end; procedure TcxCustomVirtualTreeList.DoMoveTo(AttachNode: TcxTreeListNode; AttachMode: TcxTreeListNodeAttachMode; ANodes: TList; IsCopy: Boolean); begin if IsSmartLoad and (AttachMode = tlamAddChild) and (AttachNode <> nil) then Loader.DoLoadTreeList(TcxTreeListDataNode(AttachNode)); DataController.NodesMoveTo( TcxTreeListDataNode(AttachNode), AttachMode, ANodes, IsCopy); end; function TcxCustomVirtualTreeList.GetDataControllerClass: TcxCustomDataControllerClass; begin Result := TcxVirtualTreeListDataController; end; function TcxCustomVirtualTreeList.GetLoaderClass: TcxDataTreeListStructureLoaderClass; begin Result := TcxVirtualTreeListLoader; end; function TcxCustomVirtualTreeList.IsCancelOperation: Boolean; begin if Loader.IsLoading then Result := False else Result := inherited IsCancelOperation; end; function TcxCustomVirtualTreeList.IsDataSettingsValid: Boolean; begin with DataController do Result := (CustomDataSource <> nil) and inherited IsDataSettingsValid; end; function TcxCustomVirtualTreeList.GetDataController: TcxVirtualTreeListDataController; begin Result := TcxVirtualTreeListDataController(inherited DataController); end; function TcxCustomVirtualTreeList.GetDataSource: TcxTreeListCustomDataSource; begin Result := DataController.CustomDataSource; if Result <> nil then Result.CurrentProvider := DataController.Provider; end; procedure TcxCustomVirtualTreeList.SetDataSource( Value: TcxTreeListCustomDataSource); begin DataController.CustomDataSource := Value; end; end.