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