mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-25 01:59:14 +02:00
LCL: fix crash when using freeing a component of a TRadioGroup or TCheckGroup. Issue #40261.
This commit is contained in:
parent
5c1be9de89
commit
5137735655
@ -730,6 +730,9 @@ type
|
|||||||
procedure SetItemIndex(Value: integer);
|
procedure SetItemIndex(Value: integer);
|
||||||
function GetItemIndex: integer;
|
function GetItemIndex: integer;
|
||||||
procedure CheckItemIndexChanged; virtual;
|
procedure CheckItemIndexChanged; virtual;
|
||||||
|
|
||||||
|
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
|
||||||
|
|
||||||
public
|
public
|
||||||
constructor Create(TheOwner: TComponent); override;
|
constructor Create(TheOwner: TComponent); override;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
@ -853,6 +856,9 @@ type
|
|||||||
procedure WriteData(Stream: TStream);
|
procedure WriteData(Stream: TStream);
|
||||||
procedure Loaded; override;
|
procedure Loaded; override;
|
||||||
procedure DoOnResize; override;
|
procedure DoOnResize; override;
|
||||||
|
|
||||||
|
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
|
||||||
|
|
||||||
public
|
public
|
||||||
constructor Create(TheOwner: TComponent); override;
|
constructor Create(TheOwner: TComponent); override;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
|
@ -53,13 +53,22 @@ var
|
|||||||
b: TByteDynArray;
|
b: TByteDynArray;
|
||||||
i: Integer;
|
i: Integer;
|
||||||
begin
|
begin
|
||||||
SaveCheckStates(b);
|
if (FCheckgroup.FButtonList.Count < FCheckgroup.Items.Count) then
|
||||||
|
//CheckBox has already been removed from FButtonList (via Components[x].Free)
|
||||||
inherited Delete(AIndex);
|
//All necessesary info for the checkboxes are already stored in FButtonList
|
||||||
|
//and FButtonList won't be altered in FCheckGroup.UpdateItems,
|
||||||
for i:= AIndex to High(b)-1 do b[i] := b[i+1];
|
//so no need for SaveCheckStates/RestoreCheckStates.
|
||||||
SetLength(b, Length(b)-1);
|
//(Also in this scenario Items and FButtonList are out of sysnc, so SaveCheckStates
|
||||||
RestoreCheckStates(b);
|
//will cause an EListError.)
|
||||||
|
//Issue #40261
|
||||||
|
inherited Delete(AIndex)
|
||||||
|
else begin
|
||||||
|
SaveCheckStates(b);
|
||||||
|
inherited Delete(AIndex);
|
||||||
|
for i:= AIndex to High(b)-1 do b[i] := b[i+1];
|
||||||
|
SetLength(b, Length(b)-1);
|
||||||
|
RestoreCheckStates(b);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TCheckGroupStringList.InsertItem(Index: Integer; const S: string; O: TObject);
|
procedure TCheckGroupStringList.InsertItem(Index: Integer; const S: string; O: TObject);
|
||||||
@ -95,6 +104,7 @@ procedure TCheckGroupStringList.SaveCheckStates(out AStates: TByteDynArray);
|
|||||||
var
|
var
|
||||||
i: Integer;
|
i: Integer;
|
||||||
begin
|
begin
|
||||||
|
Assert(FCheckgroup.FButtonList.Count = FCheckgroup.Items.Count); //see TCheckGroupStringList.Delete()
|
||||||
SetLength(AStates, FCheckgroup.Items.Count);
|
SetLength(AStates, FCheckgroup.Items.Count);
|
||||||
for i:=0 to FCheckgroup.Items.Count-1 do begin
|
for i:=0 to FCheckgroup.Items.Count-1 do begin
|
||||||
AStates[i] := 0;
|
AStates[i] := 0;
|
||||||
@ -218,7 +228,8 @@ begin
|
|||||||
while (FButtonList.Count<FItems.Count) do begin
|
while (FButtonList.Count<FItems.Count) do begin
|
||||||
CheckBox := TCheckBox.Create(Self);
|
CheckBox := TCheckBox.Create(Self);
|
||||||
with CheckBox do begin
|
with CheckBox do begin
|
||||||
Name:='CheckBox'+IntToStr(FButtonList.Count);
|
//Don't set name here, it may already exist if Components[x].Free was used
|
||||||
|
//Issue #40261
|
||||||
AutoSize := False;
|
AutoSize := False;
|
||||||
BorderSpacing.CellAlignHorizontal:=ccaLeftTop;
|
BorderSpacing.CellAlignHorizontal:=ccaLeftTop;
|
||||||
BorderSpacing.CellAlignVertical:=ccaCenter;
|
BorderSpacing.CellAlignVertical:=ccaCenter;
|
||||||
@ -233,10 +244,15 @@ begin
|
|||||||
end;
|
end;
|
||||||
FButtonList.Add(CheckBox);
|
FButtonList.Add(CheckBox);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
for i:=0 to FItems.Count-1 do begin
|
for i:=0 to FItems.Count-1 do begin
|
||||||
CheckBox:=TCheckBox(FButtonList[i]);
|
CheckBox:=TCheckBox(FButtonList[i]);
|
||||||
CheckBox.Caption:=FItems[i];
|
CheckBox.Caption:=FItems[i];
|
||||||
|
CheckBox.Name := '';
|
||||||
end;
|
end;
|
||||||
|
for i:=0 to FButtonList.Count-1 do
|
||||||
|
TCheckBox(FButtonList[i]).Name:='CheckBox'+IntToStr(i);
|
||||||
finally
|
finally
|
||||||
FUpdatingItems:=false;
|
FUpdatingItems:=false;
|
||||||
end;
|
end;
|
||||||
@ -395,6 +411,27 @@ begin
|
|||||||
inherited DoOnResize;
|
inherited DoOnResize;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TCustomCheckGroup.Notification(AComponent: TComponent;
|
||||||
|
Operation: TOperation);
|
||||||
|
var
|
||||||
|
Idx: Integer;
|
||||||
|
begin
|
||||||
|
inherited Notification(AComponent, Operation);
|
||||||
|
if (Operation = opRemove) and (Assigned(FButtonList)) then
|
||||||
|
begin
|
||||||
|
Idx := FButtonList.IndexOf(AComponent);
|
||||||
|
//if triggered by Items.Delete, then
|
||||||
|
// * it will always be the last CheckBox('s) that will be removed
|
||||||
|
// * Items.Count will already have been decremented, so Idx will be equal to Items.Count
|
||||||
|
if (Idx <> -1) and (Idx < Items.Count) then
|
||||||
|
begin
|
||||||
|
FButtonList.Delete(Idx);
|
||||||
|
AComponent.Name := ''; //otherwise we get duplicate name error in UpdateItems
|
||||||
|
Items.Delete(Idx);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
function TCustomCheckGroup.Rows: integer;
|
function TCustomCheckGroup.Rows: integer;
|
||||||
begin
|
begin
|
||||||
if FItems.Count>0 then
|
if FItems.Count>0 then
|
||||||
|
@ -180,7 +180,7 @@ begin
|
|||||||
ARadioButton := TRadioButton.Create(Self);
|
ARadioButton := TRadioButton.Create(Self);
|
||||||
with ARadioButton do
|
with ARadioButton do
|
||||||
begin
|
begin
|
||||||
Name := 'RadioButton'+IntToStr(FButtonList.Count);
|
//Don't set Name here, it may already exist if Components[x].Free was used. Issue #40261
|
||||||
OnClick := @Self.Clicked;
|
OnClick := @Self.Clicked;
|
||||||
OnChange := @Self.Changed;
|
OnChange := @Self.Changed;
|
||||||
OnEnter := @Self.ItemEnter;
|
OnEnter := @Self.ItemEnter;
|
||||||
@ -228,7 +228,11 @@ begin
|
|||||||
ARadioButton := TRadioButton(FButtonList[i]);
|
ARadioButton := TRadioButton(FButtonList[i]);
|
||||||
ARadioButton.Checked := (i = FItemIndex);
|
ARadioButton.Checked := (i = FItemIndex);
|
||||||
ARadioButton.Visible := true;
|
ARadioButton.Visible := true;
|
||||||
|
ARadioButton.Name := '';
|
||||||
end;
|
end;
|
||||||
|
for i:=0 to FButtonList.Count-1 do
|
||||||
|
TRadioButton(FButtonList[i]).Name:='RadioButton'+IntToStr(i);
|
||||||
|
|
||||||
//FHiddenButton must remain the last item in Controls[], so that Controls[] is in sync with Items[]
|
//FHiddenButton must remain the last item in Controls[], so that Controls[] is in sync with Items[]
|
||||||
Self.RemoveControl(FHiddenButton);
|
Self.RemoveControl(FHiddenButton);
|
||||||
Self.InsertControl(FHiddenButton);
|
Self.InsertControl(FHiddenButton);
|
||||||
@ -460,6 +464,26 @@ begin
|
|||||||
if Assigned (FOnSelectionChanged) then FOnSelectionChanged(Self);
|
if Assigned (FOnSelectionChanged) then FOnSelectionChanged(Self);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TCustomRadioGroup.Notification(AComponent: TComponent;
|
||||||
|
Operation: TOperation);
|
||||||
|
var
|
||||||
|
Idx: Integer;
|
||||||
|
begin
|
||||||
|
inherited Notification(AComponent, Operation);
|
||||||
|
if (Operation = opRemove) and (Assigned(FButtonList)) then
|
||||||
|
begin
|
||||||
|
Idx := FButtonList.IndexOf(AComponent);
|
||||||
|
//if triggered by Items.Delete, then
|
||||||
|
// * it will always be the last radiobutton(s) that will be removed
|
||||||
|
// * Items.Count will already have been decremented, so Idx will be equal to Items.Count
|
||||||
|
if (Idx <> -1) and (Idx < Items.Count) then
|
||||||
|
begin
|
||||||
|
FButtonList.Delete(Idx);
|
||||||
|
AComponent.Name := '';
|
||||||
|
Items.Delete(Idx);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
{------------------------------------------------------------------------------
|
{------------------------------------------------------------------------------
|
||||||
Method: TCustomRadioGroup.CanModify
|
Method: TCustomRadioGroup.CanModify
|
||||||
Params: none
|
Params: none
|
||||||
|
Loading…
Reference in New Issue
Block a user