As said, the TGtroCustomCheckShellTreeView component needs supplementary capabilities:
- showing check boxes next to each node;
- detecting where the user clicks;
- updating the component once a user has clicked a check box; and
- producing the list of files.
Showing the check boxes
Showing the check boxes is fairly straightforward: simply override the CreateWnd() method of the ancestor as follows: procedure TGtroCustomCheckShellTreeView.CreateWnd;
var
Style: Integer;
begin
inherited CreateWnd;
Style:= GetWindowLong(Handle, GWL_STYLE) ;
Style:= Style or TVS_CHECKBOXES;
SetWindowLong(Handle, GWL_STYLE, Style) ;
end;
Detecting where the click occurred
Next, I had to detect the clicks on the check boxes and at the same time retain the basic capabilities of the ancestor when the user expands or collapses nodes. This is done using the WMLButtonDown() protected method which captures the WM_LBUTTONDOWN Windows message and probes where the click occurred on the screen. procedure TGtroCustomCheckShellTreeView.WMLButtonDown(var Msg: TWMLButtonDown) ;
var
HitTests: THitTests;
Node: TTreeNode;
OldState: Boolean;
begin
LockWindowUpdate(Handle) ; // prevents flicker
try
Node:= GetNodeAt(Msg.XPos, Msg.YPos) ; // reference of the node that was clicked
OldState:= IsNodeChecked(Node) ; // Check the status of the node
HitTests:= GetHitTestInfoAt(Msg.XPos, Msg.YPos) ; // where was the node clicked
if htOnStateIcon in HitTests then // Click on the checkbox
CheckIt(Node, not Node.Expanded, not OldState) ; // produce the list of files
if htOnLabel in HitTests then Select(Node) ; // Click on the label
if htOnButton in HitTests then // Click on the button
if Node.Expanded then Node.Collapse(False)
else
Node.Expand(False) ;
finally
LockWindowUpdate(0) ;
end; // try...finally
end;
Lets see what it does. The call to LockWindowUpdate() prevents flicker of the component during the process. Then,after the identification of the node on which the click was observed, the following questions are raised: was its check box previously checked or unchecked? Was the click on the check box, on the label, on the + or - button? The answers to these questions receives specific answer in the method, in particular when the check box is clicked.
Updating when a click occurs
Now, let's see what happens when a check box is clicked. As shown above in the highlighted statement in WMLButtonDown(), the private method CheckIt() is called. procedure TGtroCustomCheckShellTreeView.CheckIt(const Node: TTreeNode; IncludeSubs: Boolean; State: Boolean) ;
var
Pattern: string;
ANode: TTreeNode;
Level: Integer;
List: TStringList;
begin
Items.BeginUpdate;
try
SetNodeChecked(Node, State) ; // Check/uncheck the node
EnumFiles(Node, State, FList) ;
Pattern:= GetNodePath(Node) + '\'; // Get the path of the node (pattern)
if IncludeSubs then
begin
FPatterns.Add(Pattern + '/s') ; // Load the pattern in the FPatterns list
Level:= Node.Level;
Node.Expand(True) ; // absolutely necessary as GetNext sees only visible nodes
try
ANode:= Node.getFirstChild;
while (ANode <> nil) and (Level < ANode.Level) do
begin
if State <> IsNodeChecked(ANode) then
begin
SetNodeChecked(ANode, State) ;
EnumFiles(ANode, State, FList) ;
end;
ANode:= ANode.GetNext;
end;
finally
Node.Collapse(True) ;
end;
end
else FPatterns.Add(Pattern) ; // Load the pattern in the FPatterns list
finally
Items.EndUpdate;
end; // try...finally
if Assigned(FOnCheckChanged) then FOnCheckChanged(Self, Node, State, FList) ;
end;
Its first role is to update the user interface by showing check marks on the check box that was clicked as well as on the check boxes of all its sub-nodes if the node that was clicked was collapsed. The SetNodeCheck() method set the state of the node to checked or not checked depending on the value of State whereas EnumFiles() enumerates the files contained in the folder and add them to the list of files is State is true. Otherwise, the enumerated files are removed from the list of files.
If IncludeSubs is true (the node was collapsed), the node is expanded temporarily so that its sub-nodes are created and made available to the GetFirstChild() and GetNext() methods of the base component. All the sub-nodes are then scanned and those nodes which are in a state different from the value of State are handled once again by the SetNodeChecked() and the EnumFiles() methods. The scan is terminated when the list of nodes is exhausted or when the process meets a node at the same level as the starting node.

