Skip to content
This repository was archived by the owner on Aug 31, 2021. It is now read-only.

Commit 804d341

Browse files
committed
[[ Bug 20462 ]] Fix crash when undo group deletion
This patch fixes a problem when a group is deleted, and then undo is performed on it. Deleting an object now causes it's object handle to be cleared and removed. This means that all descendent object's parent references are cleared. When undoing, the control which is temporarily owned by the undo list must be reinsterted into the object tree which requires setting the parent references correctly. This is done in MCControl::undo by using a visitor which visits a control to recreate its weak proxy, and then visits its children to set the parent link.
1 parent e81322e commit 804d341

File tree

4 files changed

+62
-1
lines changed

4 files changed

+62
-1
lines changed

docs/notes/bugfix-20642.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Fix crash when undoing a group deletion

engine/src/control.cpp

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,44 @@ void MCControl::paste(void)
561561
}
562562
}
563563

564+
/* The MCRereferenceChildrenVisitor visits each of a control's descendents
565+
* recursively and makes sure they have a weak-proxy and that the parent is
566+
* updated. */
567+
class MCRereferenceChildrenVisitor: public MCObjectVisitor
568+
{
569+
public:
570+
static void Visit(MCControl *p_control)
571+
{
572+
MCRereferenceChildrenVisitor t_visitor(p_control);
573+
574+
/* Make sure the control has a weak proxy (needed to set the parent of
575+
* its children!). */
576+
p_control->ensure_weak_proxy();
577+
578+
/* Now visit the controls children. */
579+
p_control->visit_children(0, 0, &t_visitor);
580+
}
581+
582+
private:
583+
MCRereferenceChildrenVisitor(MCObject *p_new_parent)
584+
: m_new_parent(p_new_parent)
585+
{
586+
}
587+
588+
bool OnControl(MCControl* p_control)
589+
{
590+
/* Update the control's parent */
591+
p_control->setparent(m_new_parent);
592+
593+
/* Update its children */
594+
MCRereferenceChildrenVisitor::Visit(p_control);
595+
596+
return true;
597+
}
598+
599+
MCObject *m_new_parent;
600+
};
601+
564602
void MCControl::undo(Ustruct *us)
565603
{
566604
MCRectangle newrect = rect;
@@ -583,7 +621,11 @@ void MCControl::undo(Ustruct *us)
583621
{
584622
MCCard *card = (MCCard *)parent->getcard();
585623
getstack()->appendcontrol(this);
586-
this->MCObject::m_weak_proxy = new MCObjectProxyBase(this);
624+
625+
/* Visit the control and its children, creating weak_proxys and
626+
* reparenting as we go. */
627+
MCRereferenceChildrenVisitor::Visit(this);
628+
587629
card->newcontrol(this, False);
588630
Boolean oldrlg = MCrelayergrouped;
589631
MCrelayergrouped = True;

engine/src/object.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,6 +1239,10 @@ class MCObject :
12391239
// in the parent chain.
12401240
bool isancestorof(MCObject *p_object);
12411241

1242+
// Reinstate the weak proxy object (used after an object is deleted, but is
1243+
// in the undo queue).
1244+
void ensure_weak_proxy(void) { if (m_weak_proxy == nullptr) m_weak_proxy = new MCObjectProxyBase(this); }
1245+
12421246
////////// PROPERTY SUPPORT METHODS
12431247

12441248
void Redraw(void);

tests/lcs/core/interface/group.livecodescript

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,17 @@ on TestGroupByScript
4949

5050
delete stack "Test"
5151
end TestGroupByScript
52+
53+
on TestGroupUndo_Bug20642
54+
create stack "Test"
55+
set the defaultStack to "Test"
56+
57+
create button "A"
58+
select button "A"
59+
group
60+
select the last group
61+
delete
62+
undo
63+
64+
TestAssert "Undo grouped button deletion does not crash", the short name of button "A" is "A"
65+
end TestGroupUndo_Bug20642

0 commit comments

Comments
 (0)