BSOne.SFC/Tocsg.Lib/VCL/Tocsg.VTUtil.pas

767 lines
18 KiB
Plaintext

{*******************************************************}
{ }
{ Tocsg.VTUtil }
{ }
{ Copyright (C) 2022 kku }
{ }
{*******************************************************}
unit Tocsg.VTUtil;
interface
uses
WinAPi.Windows, VirtualTrees, VirtualTrees.Types, System.Classes, System.SysUtils;
type
TTgVirtualStringTreeHelper = class helper for TBaseVirtualTree
public
procedure SortEx(pBeginNode: PVirtualNode; nColumn: TColumnIndex; Direction: TSortDirection; nSortCount: Integer = -1);
end;
function VT_CountTotalNode(vt: TVirtualStringTree): DWORD;
function VT_CountVisibleNode(vt: TVirtualStringTree): DWORD;
function VT_CountVisibleChildNode(pNode: PVirtualNode): DWORD;
function VT_CountVisibleCheckedNode(vt: TVirtualStringTree): DWORD;
procedure VT_ReverseSelected(vt: TVirtualStringTree);
procedure VT_CheckAll(var vt: TVirtualStringTree);
procedure VT_UnCheckAll(var vt: TVirtualStringTree);
procedure VT_ForceCheckNode(pNode: PVirtualNode);
procedure VT_ForceUnCheckNode(pNode: PVirtualNode);
procedure VT_ReverseCheckAll(vt: TVirtualStringTree);
procedure VT_SetEnalbedNode(vt: TVirtualStringTree; pNode: PVirtualNode; bVal: Boolean);
function VT_HasDisableNode(vt: TVirtualStringTree; pNode: PVirtualNode): Boolean;
procedure VT_ExpandAll(vt: TVirtualStringTree; bFlag: Boolean);
procedure VT_ExpandNodeAll(vt: TVirtualStringTree; pNode: PVirtualNode; bFlag: Boolean);
function VT_CopyToClipboardSelectedInfo(vt: TVirtualStringTree; nStartColumnIdx: Integer = 1): Integer;
function VT_AddChild(vt: TVirtualStringTree; pParent: PVirtualNode = nil): PVirtualNode; inline;
function VT_AddChildDataN(vt: TVirtualStringTree; out pAddNode: PVirtualNode; pParent: PVirtualNode = nil): Pointer; overload; inline;
function VT_AddChildData(vt: TVirtualStringTree; pParent: PVirtualNode = nil): Pointer; inline; overload;
function VT_HasChildNode(vt: TVirtualStringTree; pParent, pFindNode: PVirtualNode): Boolean;
function VT_Get1SelNodeData(vt: TVirtualStringTree): Pointer;
procedure VT_SortAll(vt: TVirtualStringTree; nColumn: Integer; aDirection: TSortDirection);
procedure VT_SortNodeChilds(vt: TVirtualStringTree; pNode: PVirtualNode; nColumn: Integer; aDirection: TSortDirection);
function VT_FindNodeFromPath(vt: TVirtualStringTree; sPath: String; sDelimiter: String = '\'; nColumn: Integer = 0): PVirtualNode;
procedure VT_SetCheckTypeAllNode(vt: TVirtualStringTree; aCheckType: TCheckType);
procedure VT_SetFocuceNode(vt: TVirtualStringTree; pNode: PVirtualNode);
procedure VT_Clear(aVT: TVirtualStringTree);
implementation
uses
Vcl.Clipbrd, Tocsg.Safe, Tocsg.Strings;
// pBeginNode 부터 nSortCount 수만큼 정렬을 시도한다.
// 기본 골격은 TBaseVirtualTree >> procedure Sort(Node: PVirtualNode; Column: TColumnIndex; Direction: TSortDirection; DoInit: Boolean = True); virtual;
procedure TTgVirtualStringTreeHelper.SortEx(pBeginNode: PVirtualNode; nColumn: TColumnIndex; Direction: TSortDirection; nSortCount: Integer = -1);
function MergeAscending(A, B: PVirtualNode): PVirtualNode;
var
Dummy: TVirtualNode;
CompareResult: Integer;
begin
// This avoids checking for Result = nil in the loops.
Result := @Dummy;
while Assigned(A) and Assigned(B) do
begin
if OperationCanceled then
CompareResult := 0
else
CompareResult := DoCompare(A, B, nColumn);
if CompareResult <= 0 then
begin
Result.NextSibling := A;
Result := A;
A := A.NextSibling;
end
else
begin
Result.NextSibling := B;
Result := B;
B := B.NextSibling;
end;
end;
// Just append the list which is not nil (or set end of result list to nil if both lists are nil).
if Assigned(A) then
Result.NextSibling := A
else
Result.NextSibling := B;
// return start of the new merged list
Result := Dummy.NextSibling;
end;
//---------------------------------------------------------------------------
function MergeDescending(A, B: PVirtualNode): PVirtualNode;
var
Dummy: TVirtualNode;
CompareResult: Integer;
begin
// this avoids checking for Result = nil in the loops
Result := @Dummy;
while Assigned(A) and Assigned(B) do
begin
if OperationCanceled then
CompareResult := 0
else
CompareResult := DoCompare(A, B, nColumn);
if CompareResult >= 0 then
begin
Result.NextSibling := A;
Result := A;
A := A.NextSibling;
end
else
begin
Result.NextSibling := B;
Result := B;
B := B.NextSibling;
end;
end;
// Just append the list which is not nil (or set end of result list to nil if both lists are nil).
if Assigned(A) then
Result.NextSibling := A
else
Result.NextSibling := B;
// Return start of the newly merged list.
Result := Dummy.NextSibling;
end;
//---------------------------------------------------------------------------
function MergeSortAscending(var Node: PVirtualNode; N: Cardinal): PVirtualNode;
var
A, B: PVirtualNode;
begin
if N > 1 then
begin
A := MergeSortAscending(Node, N div 2);
B := MergeSortAscending(Node, (N + 1) div 2);
Result := MergeAscending(A, B);
end
else
begin
Result := Node;
Node := Node.NextSibling;
Result.NextSibling := nil;
end;
end;
//---------------------------------------------------------------------------
function MergeSortDescending(var Node: PVirtualNode; N: Cardinal): PVirtualNode;
// Sorts the list of nodes given by Node (which must not be nil).
var
A, B: PVirtualNode;
begin
if N > 1 then
begin
A := MergeSortDescending(Node, N div 2);
B := MergeSortDescending(Node, (N + 1) div 2);
Result := MergeDescending(A, B);
end
else
begin
Result := Node;
Node := Node.NextSibling;
Result.NextSibling := nil;
end;
end;
//--------------- end local functions ---------------------------------------
var
Run: PVirtualNode;
Index: Cardinal;
i: Integer;
bBeginFirstChild: Boolean;
pTempNode,
pSortCountNextNode,
pBeginPrevNode: PVirtualNode;
vtState: TVirtualTreeStates;
begin
with Self do
begin
InterruptValidation;
if tsEditPending in FStates then
begin
StopTimer(EditTimer);
DoStateChange([], [tsEditPending]);
end;
vtState := FStates;
end;
if not (tsEditing in vtState) or DoEndEdit then
begin
if pBeginNode = nil then
exit;
if nSortCount = -1 then
begin
nSortCount := 0;
pTempNode := pBeginNode;
while pTempNode <> nil do
begin
Inc(nSortCount);
pTempNode := pTempNode.NextSibling;
end;
pSortCountNextNode := nil;
end else begin
pTempNode := pBeginNode;
for i := 1 to nSortCount do
begin
pTempNode := pTempNode.NextSibling;
if pTempNode = nil then
begin
nSortCount := i;
break;
end;
end;
pSortCountNextNode := pTempNode;
end;
if nSortCount > 1 then
begin
bBeginFirstChild := false;
if pBeginNode.Parent <> nil then
begin
if pBeginNode.Parent.FirstChild = pBeginNode then
bBeginFirstChild := true;
end;
pBeginPrevNode := pBeginNode.PrevSibling;
Index := pBeginNode.Index;
StartOperation(okSortNode);
try
// Sort the linked list, check direction flag only once.
if Direction = sdAscending then
pTempNode := MergeSortAscending(pBeginNode, nSortCount)
else
pTempNode := MergeSortDescending(pBeginNode, nSortCount);
if pTempNode.Parent <> nil then
begin
if bBeginFirstChild then
pTempNode.Parent.FirstChild := pTempNode;
end;
finally
EndOperation(okSortNode);
end;
// Consolidate the child list finally.
Run := pTempNode;
if pBeginPrevNode <> nil then
pBeginPrevNode.NextSibling := Run;
Run.PrevSibling := pBeginPrevNode;
repeat
Run.Index := Index;
Inc(Index);
if Run.NextSibling = nil then
Break;
Run.NextSibling.PrevSibling := Run;
Run := Run.NextSibling;
until False;
if pSortCountNextNode <> nil then
begin
Run.NextSibling := pSortCountNextNode;
pSortCountNextNode.PrevSibling := Run;
end else
if (Run.Parent <> nil) and (Run.NextSibling = nil) then
Run.Parent.LastChild := Run;
InvalidateCache;
end;
with Self do
begin
if FUpdateCount = 0 then
begin
ValidateCache;
Invalidate;
end;
end;
end;
end;
{ Other }
function VT_CountTotalNode(vt: TVirtualStringTree): DWORD;
var
pNode: PVirtualNode;
begin
Result := 0;
pNode := vt.GetFirst;
while pNode <> nil do
begin
Inc(Result);
pNode := vt.GetNext(pNode);
end;
end;
function VT_CountVisibleNode(vt: TVirtualStringTree): DWORD;
var
pNode: PVirtualNode;
begin
Result := 0;
pNode := vt.GetFirst;
while pNode <> nil do
begin
if vt.IsVisible[pNode] then
Inc(Result);
pNode := vt.GetNext(pNode);
end;
end;
function VT_CountVisibleChildNode(pNode: PVirtualNode): DWORD;
begin
Result := 0;
pNode := pNode.FirstChild;
while pNode <> nil do
begin
if vsVisible in pNode.States then
Inc(Result);
pNode := pNode.NextSibling;
end;
end;
function VT_CountVisibleCheckedNode(vt: TVirtualStringTree): DWORD;
var
pNode: PVirtualNode;
begin
Result := 0;
pNode := vt.GetFirst;
while pNode <> nil do
begin
if vt.IsVisible[pNode] and (pNode.CheckState = csCheckedNormal) then
Inc(Result);
pNode := vt.GetNext(pNode);
end;
end;
procedure VT_ReverseSelected(vt: TVirtualStringTree);
var
pNode: PVirtualNode;
begin
vt.BeginUpdate;
try
pNode := vt.GetFirst;
while pNode <> nil do
begin
vt.Selected[pNode] := not vt.Selected[pNode];
pNode := vt.GetNext(pNode);
end;
finally
vt.EndUpdate;
end;
end;
procedure VT_CheckAll(var vt: TVirtualStringTree);
var
pNode: PVirtualNode;
begin
vt.BeginUpdate;
try
pNode := vt.GetFirst;
while pNode <> nil do
begin
if vt.IsVisible[pNode] and not vt.IsDisabled[pNode] then
pNode.CheckState := csCheckedNormal;
pNode := vt.GetNext(pNode, true);
end;
finally
vt.EndUpdate;
end;
end;
procedure VT_UnCheckAll(var vt: TVirtualStringTree);
var
pNode: PVirtualNode;
begin
vt.BeginUpdate;
try
pNode := vt.GetFirst;
while pNode <> nil do
begin
if vt.IsVisible[pNode] and not vt.IsDisabled[pNode] then
// vt.CheckState[pNode] := csUncheckedNormal;
pNode.CheckState := csUncheckedNormal;
pNode := vt.GetNext(pNode, true);
end;
finally
vt.EndUpdate;
end;
end;
procedure VT_ForceCheckNode(pNode: PVirtualNode);
var
pChildN: PVirtualNode;
begin
pNode.CheckState := csCheckedNormal;
pChildN := pNode.FirstChild;
while pChildN <> nil do
begin
pChildN.CheckState := csCheckedNormal;
if pChildN.ChildCount > 0 then
VT_ForceCheckNode(pChildN);
pChildN := pChildN.NextSibling;
end;
end;
procedure VT_ForceUnCheckNode(pNode: PVirtualNode);
var
pChildN: PVirtualNode;
begin
pNode.CheckState := csUncheckedNormal;
pChildN := pNode.FirstChild;
while pChildN <> nil do
begin
pChildN.CheckState := csUncheckedNormal;
if pChildN.ChildCount > 0 then
VT_ForceCheckNode(pChildN);
pChildN := pChildN.NextSibling;
end;
end;
procedure VT_ReverseCheckAll(vt: TVirtualStringTree);
var
pNode: PVirtualNode;
begin
vt.BeginUpdate;
try
pNode := vt.GetFirst;
while pNode <> nil do
begin
if vt.IsVisible[pNode] and not vt.IsDisabled[pNode] then
begin
if pNode.CheckState = csCheckedNormal then
pNode.CheckState := csUncheckedNormal
else
if pNode.CheckState = csUncheckedNormal then
pNode.CheckState := csCheckedNormal;
end;
pNode := vt.GetNext(pNode);
end;
finally
vt.EndUpdate;
end;
end;
procedure VT_SetEnalbedNode(vt: TVirtualStringTree; pNode: PVirtualNode; bVal: Boolean);
begin
vt.BeginUpdate;
try
vt.IsDisabled[pNode] := not bVal;
if pNode.ChildCount > 0 then
begin
pNode := pNode.FirstChild;
while pNode <> nil do
begin
VT_SetEnalbedNode(vt, pNode, bVal);
pNode := pNode.NextSibling;
end;
end;
finally
vt.EndUpdate;
end;
end;
function VT_HasDisableNode(vt: TVirtualStringTree; pNode: PVirtualNode): Boolean;
function CheckDisableNode(aNode: PVirtualNode): Boolean;
begin
Result := false;
if vt.IsDisabled[aNode] then
begin
Result := true;
exit;
end;
if aNode.ChildCount > 0 then
begin
aNode := aNode.FirstChild;
while aNode <> nil do
begin
Result := CheckDisableNode(aNode);
if Result then
exit;
aNode := aNode.NextSibling;
end;
end;
end;
begin
vt.BeginUpdate;
try
Result := CheckDisableNode(pNode);
finally
vt.EndUpdate;
end;
end;
procedure VT_ExpandAll(vt: TVirtualStringTree; bFlag: Boolean);
var
pNode: PVirtualNode;
begin
vt.BeginUpdate;
try
pNode := vt.GetFirst;
while pNode <> nil do
begin
vt.Expanded[pNode] := bFlag;
pNode := vt.GetNext(pNode);
end;
finally
vt.EndUpdate;
end;
end;
procedure VT_ExpandNodeAll(vt: TVirtualStringTree; pNode: PVirtualNode; bFlag: Boolean);
begin
vt.BeginUpdate;
try
while pNode <> nil do
begin
vt.Expanded[pNode] := bFlag;
pNode := vt.GetNext(pNode);
end;
finally
vt.EndUpdate;
end;
end;
function VT_CopyToClipboardSelectedInfo(vt: TVirtualStringTree; nStartColumnIdx: Integer = 1): Integer;
var
pNode: PVirtualNode;
sData: String;
i: Integer;
cbd: TClipboard;
begin
Result := 0;
pNode := vt.GetFirstSelected;
if pNode = nil then
begin
Result := 1;
exit;
end;
if vt.SelectedCount > 1000 then
begin
Result := 2;
exit;
end;
if nStartColumnIdx >= vt.Header.Columns.Count then
nStartColumnIdx := 0;
sData := '';
while pNode <> nil do
begin
for i := nStartColumnIdx to vt.Header.Columns.Count - 1 do
if coVisible in vt.Header.Columns[i].Options then
begin
sData := sData + vt.Text[pNode, i] + #9;
end;
sData := sData + #13#10;
pNode := vt.GetNextSelected(pNode);
end;
if sData <> '' then
begin
Guard(cbd, TClipboard.Create);
cbd.AsText := sData;
end else Result := 3;
end;
function VT_AddChild(vt: TVirtualStringTree; pParent: PVirtualNode = nil): PVirtualNode; inline;
begin
Result := vt.AddChild(pParent);
Include(Result.States, vsInitialized);
end;
function VT_AddChildDataN(vt: TVirtualStringTree; out pAddNode: PVirtualNode; pParent: PVirtualNode = nil): Pointer; inline;
begin
pAddNode := vt.AddChild(pParent);
Include(pAddNode.States, vsInitialized);
Result := vt.GetNodeData(pAddNode);
end;
function VT_AddChildData(vt: TVirtualStringTree; pParent: PVirtualNode = nil): Pointer; inline; overload;
var
pNode: PVirtualNode;
begin
Result := VT_AddChildDataN(vt, pNode, pParent);
end;
function VT_HasChildNode(vt: TVirtualStringTree; pParent, pFindNode: PVirtualNode): Boolean;
var
pNode: PVirtualNode;
begin
Result := false;
if pParent = nil then
exit;
if pParent = pFindNode then
begin
Result := true;
exit;
end;
pNode := pParent.FirstChild;
while pNode <> nil do
begin
Result := VT_HasChildNode(vt, pNode, pFindNode);
if Result then
exit;
pNode := pNode.NextSibling;
end;
end;
function VT_Get1SelNodeData(vt: TVirtualStringTree): Pointer;
var
pNode: PVirtualNode;
begin
pNode := vt.GetFirstSelected;
if pNode <> nil then
Result := vt.GetNodeData(pNode)
else
Result := nil;
end;
procedure VT_SortAll(vt: TVirtualStringTree; nColumn: Integer; aDirection: TSortDirection);
var
pNode: PVirtualNode;
begin
vt.Sort(nil, nColumn, aDirection);
pNode := vt.GetFirst;
while pNode <> nil do
begin
if pNode.ChildCount > 0 then
vt.Sort(pNode, nColumn, aDirection);
pNode := vt.GetNext(pNode);
end;
end;
procedure VT_SortNodeChilds(vt: TVirtualStringTree; pNode: PVirtualNode; nColumn: Integer; aDirection: TSortDirection);
begin
vt.Sort(pNode, nColumn, aDirection);
if pNode = nil then
pNode := vt.RootNode;
pNode := pNode.FirstChild;
while pNode <> nil do
begin
if pNode.ChildCount > 0 then
VT_SortNodeChilds(vt, pNode, nColumn, aDirection);
pNode := pNode.NextSibling;
end;
end;
// 잘 동작하는지 테스트 안됨 22_0119 21:34:10 kku
// 확인 되면 이거 지워 ㅇㅇ
function VT_FindNodeFromPath(vt: TVirtualStringTree; sPath: String; sDelimiter: String = '\'; nColumn: Integer = 0): PVirtualNode;
function GetMatchTextNode(pNode: PVirtualNode; sMatchText: String): PVirtualNode;
begin
Result := nil;
while pNode <> nil do
begin
if CompareText(vt.Text[pNode, nColumn], sMatchText) = 0 then
begin
Result := pNode;
exit;
end;
pNode := pNode.NextSibling;
end;
end;
var
PathList: TStringList;
pNode: PVirtualNode;
i: Integer;
begin
Result := nil;
Guard(PathList, TStringList.Create);
if SplitString2(sDelimiter, sPath, PathList) = 0 then
exit;
vt.BeginUpdate;
try
pNode := vt.RootNode;
if pNode = nil then
exit;
for i := 0 to PathList.Count - 1 do
begin
Result := GetMatchTextNode(pNode.FirstChild, PathList[i]);
if Result = nil then
break;
end;
finally
vt.EndUpdate;
end;
end;
procedure VT_SetCheckTypeAllNode(vt: TVirtualStringTree; aCheckType: TCheckType);
var
pNode: PVirtualNode;
begin
pNode := vt.GetFirst;
while pNode <> nil do
begin
pNode.CheckType := aCheckType;
pNode := vt.GetNext(pNode);
end;
end;
procedure VT_SetFocuceNode(vt: TVirtualStringTree; pNode: PVirtualNode);
begin
vt.FocusedNode := nil;
vt.ClearSelection;
vt.FocusedNode := pNode;
vt.Selected[pNode] := true;
vt.ScrollIntoView(pNode, true);
end;
procedure VT_Clear(aVT: TVirtualStringTree);
begin
with aVT.Header do
begin
if SortColumn <> -1 then
begin
Columns[SortColumn].Options := Columns[SortColumn].Options + [coParentColor];
SortColumn := -1;
end;
end;
aVT.Clear;
end;
end.