Laz-VirtualTreeView: Protect calls to killtimer with a check for HandleAllocated. If the parent window is not visible (or no parent present) then the handle should not be requested. See notes on issue #41430

This commit is contained in:
Martin 2025-02-17 19:58:05 +01:00
parent 79bed04544
commit b96a679613

View File

@ -2868,6 +2868,7 @@ type
procedure SkipNode(Stream: TStream); virtual;
procedure StartOperation(OperationKind: TVTOperationKind);
procedure StartWheelPanning(const Position: TPoint); virtual;
procedure StopTimer(nIDEvent: UINT_PTR);
procedure StopWheelPanning; virtual;
procedure StructureChange(Node: PVirtualNode; Reason: TChangeReason); virtual;
function SuggestDropEffect(Source: TObject; Shift: TShiftState; const {%H-}Pt: TPoint; AllowedEffects: LongWord): LongWord; virtual;
@ -10903,10 +10904,10 @@ begin
if not (csDesigning in Treeview.ComponentState) then
begin
// make sure no auto scrolling is active...
KillTimer(Treeview.Handle, ScrollTimer);
Treeview.StopTimer(ScrollTimer);
Treeview.DoStateChange([], [tsScrollPending, tsScrolling]);
// ... pending editing is cancelled (actual editing remains active)
KillTimer(Treeview.Handle, EditTimer);
Treeview.StopTimer(EditTimer);
Treeview.DoStateChange([], [tsEditPending]);
end;
@ -11004,7 +11005,7 @@ begin
// Trigger header popup if there's one.
if Assigned(Menu) then
begin
KillTimer(Treeview.Handle, ScrollTimer);
Treeview.StopTimer(ScrollTimer);
FColumns.FHoverIndex := NoColumn;
Treeview.DoStateChange([], [tsScrollPending, tsScrolling]);
Menu.PopupComponent := Treeview;
@ -15420,7 +15421,7 @@ begin
FIncrementalSearch := Value;
if FIncrementalSearch = isNone then
begin
KillTimer(Handle, SearchTimer);
StopTimer(SearchTimer);
FSearchBuffer := '';
FLastSearchNode := nil;
end;
@ -16329,7 +16330,7 @@ begin
LeaveStates := [tsHint];
if [tsWheelPanning, tsWheelScrolling] * FStates = [] then
begin
KillTimer(Handle, ScrollTimer);
StopTimer(ScrollTimer);
LeaveStates := LeaveStates + [tsScrollPending, tsScrolling];
end;
DoStateChange([], LeaveStates);
@ -16582,11 +16583,11 @@ procedure TBaseVirtualTree.WMCancelMode(var Message: TLMessage);
begin
{$ifdef DEBUG_VTV}Logger.EnterMethod([lcMessages],'WMCancelMode');{$endif}
// Clear any transient state.
KillTimer(Handle, ExpandTimer);
KillTimer(Handle, EditTimer);
KillTimer(Handle, ScrollTimer);
KillTimer(Handle, SearchTimer);
KillTimer(Handle, ThemeChangedTimer);
StopTimer(ExpandTimer);
StopTimer(EditTimer);
StopTimer(ScrollTimer);
StopTimer(SearchTimer);
StopTimer(ThemeChangedTimer);
FSearchBuffer := '';
FLastSearchNode := nil;
@ -17512,10 +17513,10 @@ begin
StopWheelPanning;
// Don't let any timer continue if the tree is no longer the active control (except change timers).
KillTimer(Handle, ExpandTimer);
KillTimer(Handle, EditTimer);
KillTimer(Handle, ScrollTimer);
KillTimer(Handle, SearchTimer);
StopTimer(ExpandTimer);
StopTimer(EditTimer);
StopTimer(ScrollTimer);
StopTimer(SearchTimer);
FSearchBuffer := '';
FLastSearchNode := nil;
@ -18062,13 +18063,13 @@ begin
// When this event triggers then the user did not pressed any key for the specified timeout period.
// Hence incremental searching is stopped.
DoStateChange([], [tsIncrementalSearching]);
KillTimer(Handle, SearchTimer);
StopTimer(SearchTimer);
FSearchBuffer := '';
FLastSearchNode := nil;
end;
ThemeChangedTimer:
begin
KillTimer(Handle, ThemeChangedTimer);
StopTimer(ThemeChangedTimer);
RecreateWnd(Self);
end;
end;
@ -18373,7 +18374,7 @@ begin
else
begin
if tsChangePending in FStates then
KillTimer(Handle, ChangeTimer)
StopTimer(ChangeTimer)
else
DoStateChange([tsChangePending]);
@ -18886,8 +18887,8 @@ begin
// see if there will be issues calling here
InterruptValidation;
KillTimer(Handle, ChangeTimer);
KillTimer(Handle, StructureChangeTimer);
StopTimer(ChangeTimer);
StopTimer(StructureChangeTimer);
{$ifdef Windows}
if not (csDesigning in ComponentState) and (toAcceptOLEDrop in FOptions.FMiscOptions) then
@ -19533,7 +19534,7 @@ begin
begin
if ((FStates * [tsScrollPending, tsScrolling]) <> []) then
begin
KillTimer(Handle, ScrollTimer);
StopTimer(ScrollTimer);
DoStateChange([], [tsScrollPending, tsScrolling]);
end;
end
@ -19639,7 +19640,7 @@ function TBaseVirtualTree.DoCancelEdit: Boolean;
// Called when the current edit action or a pending edit must be cancelled.
begin
KillTimer(Handle, EditTimer);
StopTimer(EditTimer);
DoStateChange([], [tsEditPending]);
Result := (tsEditing in FStates) and FEditLink.CancelEdit;
if Result then
@ -19675,7 +19676,7 @@ end;
procedure TBaseVirtualTree.DoChange(Node: PVirtualNode);
begin
KillTimer(Handle, ChangeTimer);
StopTimer(ChangeTimer);
if Assigned(FOnChange) then
FOnChange(Self, Node);
@ -19981,7 +19982,7 @@ var
SourceTree: TBaseVirtualTree;
begin
KillTimer(Handle, ExpandTimer);
StopTimer(ExpandTimer);
if Assigned(FDropTargetNode) and (vsHasChildren in FDropTargetNode.States) and
not (vsExpanded in FDropTargetNode.States) then
begin
@ -20035,8 +20036,8 @@ procedure TBaseVirtualTree.DoEdit;
begin
Application.CancelHint;
KillTimer(Handle, ScrollTimer);
KillTimer(Handle, EditTimer);
StopTimer(ScrollTimer);
StopTimer(EditTimer);
DoStateChange([], [tsEditPending]);
if Assigned(FFocusedNode) and not (vsDisabled in FFocusedNode.States) and
not (toReadOnly in FOptions.FMiscOptions) and (FEditLink = nil) then
@ -20081,7 +20082,7 @@ end;
function TBaseVirtualTree.DoEndEdit: Boolean;
begin
KillTimer(Handle, EditTimer);
StopTimer(EditTimer);
Result := (tsEditing in FStates) and FEditLink.EndEdit;
if Result then
begin
@ -20766,7 +20767,7 @@ begin
if Assigned(Menu) then
begin
DoStateChange([tsPopupMenuShown]);
KillTimer(Handle, EditTimer);
StopTimer(EditTimer);
Menu.PopupComponent := Self;
with ClientToScreen(Position) do
Menu.Popup(X, Y);
@ -21047,7 +21048,7 @@ procedure TBaseVirtualTree.DoStructureChange(Node: PVirtualNode; Reason: TChange
begin
if HandleAllocated then
KillTimer(Handle, StructureChangeTimer);
StopTimer(StructureChangeTimer);
if Assigned(FOnStructureChange) then
FOnStructureChange(Self, Node, Reason);
@ -21181,7 +21182,7 @@ begin
if (FScrollDirections = []) and ([tsWheelPanning, tsWheelScrolling] * FStates = []) then
begin
KillTimer(Handle, ScrollTimer);
StopTimer(ScrollTimer);
DoStateChange([], [tsScrollPending, tsScrolling]);
end;
end;
@ -21346,8 +21347,8 @@ var
begin
{$ifdef DEBUG_VTV}Logger.EnterMethod([lcDrag],'DragDrop');{$endif}
KillTimer(Handle, ExpandTimer);
KillTimer(Handle, ScrollTimer);
StopTimer(ExpandTimer);
StopTimer(ScrollTimer);
DoStateChange([], [tsScrollPending, tsScrolling]);
Formats := nil;
@ -21499,7 +21500,7 @@ var
Effect: LongWord;
begin
KillTimer(Handle, ExpandTimer);
StopTimer(ExpandTimer);
if not VTVDragManager.DropTargetHelperSupported and Assigned(VTVDragManager.DragSource) then
TBaseVirtualTree(VTVDragManager.DragSource).FDragImage.HideDragImage;
@ -21619,7 +21620,7 @@ begin
FLastDropMode := NewDropMode;
if HitInfo.HitNode <> FDropTargetNode then
begin
KillTimer(Handle, ExpandTimer);
StopTimer(ExpandTimer);
// The last target node is needed for the rectangle determination but must already be set for
// the recapture call, hence it must be stored somewhere.
LastNode := FDropTargetNode;
@ -22522,7 +22523,7 @@ var
begin
//todo: handle correctly unicode char after WideString -> String conversion
KillTimer(Handle, SearchTimer);
StopTimer(SearchTimer);
if FIncrementalSearch <> isNone then
begin
@ -22647,7 +22648,7 @@ begin
MayEdit := not (tsEditing in FStates) and (toEditOnDblClick in FOptions.FMiscOptions);
if tsEditPending in FStates then
begin
KillTimer(Handle, EditTimer);
StopTimer(EditTimer);
DoStateChange([], [tsEditPending]);
end;
@ -22766,7 +22767,7 @@ begin
if tsEditPending in FStates then
begin
KillTimer(Handle, EditTimer);
StopTimer(EditTimer);
DoStateChange([], [tsEditPending]);
end;
@ -22998,7 +22999,7 @@ begin
end;
if DragKind = dkDock then
begin
KillTimer(Handle, ScrollTimer);
StopTimer(ScrollTimer);
DoStateChange([], [tsScrollPending, tsScrolling]);
end;
// Get the currently focused node to make multiple multi-selection blocks possible.
@ -23077,7 +23078,7 @@ begin
DoStateChange([], [tsOLEDragPending, tsOLEDragging, tsClearPending, tsDrawSelPending, tsToggleFocusedSelection,
tsScrollPending, tsScrolling]);
KillTimer(Handle, ScrollTimer);
StopTimer(ScrollTimer);
if tsMouseCheckPending in FStates then
begin
@ -23983,10 +23984,10 @@ begin
StopWheelPanning;
// Stop timers
KillTimer(Handle, ExpandTimer);
KillTimer(Handle, EditTimer);
KillTimer(Handle, ScrollTimer);
KillTimer(Handle, SearchTimer);
StopTimer(ExpandTimer);
StopTimer(EditTimer);
StopTimer(ScrollTimer);
StopTimer(SearchTimer);
FSearchBuffer := '';
FLastSearchNode := nil;
@ -25346,7 +25347,7 @@ begin
// Set both panning and scrolling flag. One will be removed shortly depending on whether the middle mouse button is
// released before the mouse is moved or vice versa. The first case is referred to as wheel scrolling while the
// latter is called wheel panning.
KillTimer(Handle, ScrollTimer);
StopTimer(ScrollTimer);
DoStateChange([tsWheelPanning, tsWheelScrolling]);
if FPanningWindow = nil then
@ -25389,6 +25390,14 @@ end;
//----------------------------------------------------------------------------------------------------------------------
procedure TBaseVirtualTree.StopTimer(nIDEvent: UINT_PTR);
begin
if HandleAllocated then
KillTimer(Handle, nIDEvent);
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TBaseVirtualTree.StopWheelPanning;
// Stops panning if currently active and destroys the helper window.
@ -25397,7 +25406,7 @@ begin
if [tsWheelPanning, tsWheelScrolling] * FStates <> [] then
begin
// Release the mouse capture and stop the panscroll timer.
KillTimer(Handle, ScrollTimer);
StopTimer(ScrollTimer);
ReleaseCapture;
DoStateChange([], [tsWheelPanning, tsWheelScrolling]);
@ -26359,12 +26368,12 @@ begin
DoUpdating(usBeginSynch);
// Stop all timers...
KillTimer(Handle, ChangeTimer);
KillTimer(Handle, StructureChangeTimer);
KillTimer(Handle, ExpandTimer);
KillTimer(Handle, EditTimer);
KillTimer(Handle, ScrollTimer);
KillTimer(Handle, SearchTimer);
StopTimer(ChangeTimer);
StopTimer(StructureChangeTimer);
StopTimer(ExpandTimer);
StopTimer(EditTimer);
StopTimer(ScrollTimer);
StopTimer(SearchTimer);
FSearchBuffer := '';
FLastSearchNode := nil;
DoStateChange([], [tsEditPending, tsScrollPending, tsScrolling, tsIncrementalSearching]);
@ -32212,7 +32221,7 @@ begin
InterruptValidation;
if tsEditPending in FStates then
begin
KillTimer(Handle, EditTimer);
StopTimer(EditTimer);
DoStateChange([], [tsEditPending]);
end;