MenuDesigner: BeginUpdate and EndUpdate methods to prevent AV. Related changes in menushadows.pp inside method TShadowMenu.DeleteChildlessShadowAndItem

(operation ownsIt.RemoveComponent(mi) was the starting point for many TMenuDesigner.OnDesignerSetSelection calls, especially visible with Sparta package.
During execution OnDesignerSetSelection, caller object TShadowMenu was destroyed after RemoveComponent operation, before end of DeleteChildlessShadowAndItem).
Issue #29328, from Maciej.

git-svn-id: trunk@51212 -
This commit is contained in:
juha 2016-01-06 15:16:20 +00:00
parent 8663e9c084
commit d5272def5e
2 changed files with 59 additions and 33 deletions

View File

@ -73,6 +73,7 @@ type
FTemplatesSaved: boolean; FTemplatesSaved: boolean;
FTotalMenuItemsCount: integer; FTotalMenuItemsCount: integer;
FVariableGlyphsInMenuBar: boolean; FVariableGlyphsInMenuBar: boolean;
FUpdateCount: integer;
function GetItemCounts(out aCaptionedItemCount, aShortcutItemCount, function GetItemCounts(out aCaptionedItemCount, aShortcutItemCount,
anIconCount, anAccelCount: integer): integer; anIconCount, anAccelCount: integer): integer;
function GetPopupAssignmentCount: integer; function GetPopupAssignmentCount: integer;
@ -94,6 +95,8 @@ type
procedure UpdateShortcutList(includeAccelerators: boolean=False); procedure UpdateShortcutList(includeAccelerators: boolean=False);
procedure UpdateStatistics; procedure UpdateStatistics;
procedure UpdateTemplatesCount; procedure UpdateTemplatesCount;
procedure BeginUpdate;
procedure EndUpdate;
property AcceleratorMenuItemsCount: integer read FAcceleratorMenuItemsCount; property AcceleratorMenuItemsCount: integer read FAcceleratorMenuItemsCount;
property EditedMenu: TMenu read FEditedMenu; property EditedMenu: TMenu read FEditedMenu;
property SavedTemplatesCount: integer read FSavedTemplatesCount; property SavedTemplatesCount: integer read FSavedTemplatesCount;
@ -203,6 +206,9 @@ var
isTMenu: boolean; isTMenu: boolean;
persist: TPersistent; persist: TPersistent;
begin begin
if FUpdateCount > 0 then
Exit; // This event will be executed after all updates, look at EndUpdate
persist:=GetSelectedMenuComponent(ASelection, isTMenu, selCount); persist:=GetSelectedMenuComponent(ASelection, isTMenu, selCount);
if (persist <> nil) then if (persist <> nil) then
begin begin
@ -571,6 +577,20 @@ begin
else FSavedTemplatesCount:=GetSavedTemplatesCount; else FSavedTemplatesCount:=GetSavedTemplatesCount;
end; end;
procedure TMenuDesigner.BeginUpdate;
begin
Inc(FUpdateCount);
end;
procedure TMenuDesigner.EndUpdate;
begin
if FUpdateCount<=0 then
RaiseGDBException('');
Dec(FUpdateCount);
if FUpdateCount = 0 then
OnDesignerSetSelection(FormEditingHook.GetCurrentObjectInspector.Selection);
end;
{ TMainMenuComponentEditor} { TMainMenuComponentEditor}
procedure TMainMenuComponentEditor.Edit; procedure TMainMenuComponentEditor.Edit;

View File

@ -5017,41 +5017,47 @@ var
box: TShadowBox; box: TShadowBox;
ownsIt: TComponent; ownsIt: TComponent;
begin begin
mi:=anExistingSI.RealItem; MenuDesigner.BeginUpdate;
if (mi.Count > 0) then try
DeleteShadowAndItemAndChildren(anExistingSI) mi:=anExistingSI.RealItem;
else begin if (mi.Count > 0) then
HideFakes; DeleteShadowAndItemAndChildren(anExistingSI)
if (mi = FSelectedMenuItem) then else begin
FSelectedMenuItem:=nil; HideFakes;
nearestMI:=GetNextNonSepItem(mi); if (mi = FSelectedMenuItem) then
if (nearestMI = nil) then FSelectedMenuItem:=nil;
nearestMI:=GetPreviousNonSepItem(mi); nearestMI:=GetNextNonSepItem(mi);
if (nearestMI = nil) then if (nearestMI = nil) then
nearestMI:=mi.Parent; nearestMI:=GetPreviousNonSepItem(mi);
box:=anExistingSI.ParentBox; if (nearestMI = nil) then
box.ParentMenuItem.Remove(mi); nearestMI:=mi.Parent;
ownsIt:=mi.Owner; box:=anExistingSI.ParentBox;
if (ownsIt <> nil) then box.ParentMenuItem.Remove(mi);
ownsIt.RemoveComponent(mi); ownsIt:=mi.Owner;
anExistingSI.RealItem:=nil; if (ownsIt <> nil) then
box.ShadowList.Remove(anExistingSI); ownsIt.RemoveComponent(mi);
anExistingSI.Parent:=nil; anExistingSI.RealItem:=nil;
box.RemoveComponent(anExistingSI); box.ShadowList.Remove(anExistingSI);
FreeAndNil(anExistingSI); anExistingSI.Parent:=nil;
FEditorDesigner.PropertyEditorHook.PersistentDeleting(TPersistent(mi)); box.RemoveComponent(anExistingSI);
FreeAndNil(mi); FreeAndNil(anExistingSI);
FEditorDesigner.Modified; FEditorDesigner.PropertyEditorHook.PersistentDeleting(TPersistent(mi));
FreeAndNil(mi);
FEditorDesigner.PropertyEditorHook.PersistentDeleted;
FEditorDesigner.Modified;
if (box.ShadowCount = 0) then begin if (box.ShadowCount = 0) then begin
FBoxList.Remove(box); FBoxList.Remove(box);
box.Parent:=nil; box.Parent:=nil;
RemoveComponent(box); RemoveComponent(box);
FreeAndNil(box); FreeAndNil(box);
end;
UpdateBoxLocationsAndSizes;
SetSelectedMenuItem(nearestMI, False, True);
MenuDesigner.UpdateStatistics;
end; end;
UpdateBoxLocationsAndSizes; finally
SetSelectedMenuItem(nearestMI, False, True); MenuDesigner.EndUpdate;
MenuDesigner.UpdateStatistics;
end; end;
end; end;