diff --git a/poincare/include/poincare/horizontal_layout_node.h b/poincare/include/poincare/horizontal_layout_node.h index b11907a54..4f4542874 100644 --- a/poincare/include/poincare/horizontal_layout_node.h +++ b/poincare/include/poincare/horizontal_layout_node.h @@ -44,6 +44,7 @@ public: m_numberOfChildren-= decrement; } void eraseNumberOfChildren() override { m_numberOfChildren = 0; } + void childAtIndexWillBeStolen(int index) override; #if POINCARE_TREE_LOG virtual void logNodeName(std::ostream & stream) const override { stream << "HorizontalLayout"; diff --git a/poincare/include/poincare/n_ary_expression_node.h b/poincare/include/poincare/n_ary_expression_node.h index 0cb8df62b..bb9f2ad3f 100644 --- a/poincare/include/poincare/n_ary_expression_node.h +++ b/poincare/include/poincare/n_ary_expression_node.h @@ -17,6 +17,7 @@ public: m_numberOfChildren-= decrement; } void eraseNumberOfChildren() override { m_numberOfChildren = 0; } + void childAtIndexWillBeStolen(int index) override; // Comparison typedef int (*ExpressionOrder)(const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted); diff --git a/poincare/include/poincare/tree_by_reference.h b/poincare/include/poincare/tree_by_reference.h index aeb21270d..a3c8822fd 100644 --- a/poincare/include/poincare/tree_by_reference.h +++ b/poincare/include/poincare/tree_by_reference.h @@ -83,6 +83,9 @@ public: int indexOfChild(TreeByReference t) const { return node()->indexOfChild(t.node()); } + void childAtIndexWillBeStolen(int index) { + node()->childAtIndexWillBeStolen(index); + } /* Hierarchy operations */ // Replace @@ -90,6 +93,10 @@ public: void replaceChildInPlace(TreeByReference oldChild, TreeByReference newChild); void replaceChildAtIndexInPlace(int oldChildIndex, TreeByReference newChild); void replaceWithAllocationFailureInPlace(int currentNumberOfChildren); + void replaceChildAtIndexWithGhostInPlace(int index) { + assert(index >= 0 && index < numberOfChildren()); + replaceChildWithGhostInPlace(childAtIndex(index)); + } void replaceChildWithGhostInPlace(TreeByReference t); // Merge void mergeChildrenAtIndexInPlace(TreeByReference t, int i); @@ -124,6 +131,7 @@ protected: int m_identifier; private: + void detachFromParent(); // Add ghost children on layout construction void buildGhostChildren(); }; diff --git a/poincare/include/poincare/tree_node.h b/poincare/include/poincare/tree_node.h index fb0c33ec3..202d453e1 100644 --- a/poincare/include/poincare/tree_node.h +++ b/poincare/include/poincare/tree_node.h @@ -69,6 +69,8 @@ public: bool hasChild(const TreeNode * child) const; bool hasAncestor(const TreeNode * node, bool includeSelf) const; bool hasSibling(const TreeNode * e) const; + // Prepare a child that will be stolen by another Expression + virtual void childAtIndexWillBeStolen(int index); // AddChild collateral effect virtual void didAddChildAtIndex(int newNumberOfChildren) {} diff --git a/poincare/src/horizontal_layout_node.cpp b/poincare/src/horizontal_layout_node.cpp index 56c91220d..3ee9373c2 100644 --- a/poincare/src/horizontal_layout_node.cpp +++ b/poincare/src/horizontal_layout_node.cpp @@ -193,6 +193,10 @@ bool HorizontalLayoutNode::hasText() const { return true; } +void HorizontalLayoutNode::childAtIndexWillBeStolen(int index) { + HorizontalLayoutRef(this).removeChildAtIndex(index, nullptr); +} + // Protected KDSize HorizontalLayoutNode::computeSize() { diff --git a/poincare/src/n_ary_expression_node.cpp b/poincare/src/n_ary_expression_node.cpp index 3f3889512..c788494ad 100644 --- a/poincare/src/n_ary_expression_node.cpp +++ b/poincare/src/n_ary_expression_node.cpp @@ -36,6 +36,10 @@ Expression NAryExpressionNode::squashUnaryHierarchy() { return copy; } +void NAryExpressionNode::childAtIndexWillBeStolen(int index) { + NAryExpression(this).removeChildAtIndexInPlace(index); +} + // Private int NAryExpressionNode::simplificationOrderSameType(const ExpressionNode * e, bool canBeInterrupted) const { diff --git a/poincare/src/tree_by_reference.cpp b/poincare/src/tree_by_reference.cpp index 09761d0a4..8232932db 100644 --- a/poincare/src/tree_by_reference.cpp +++ b/poincare/src/tree_by_reference.cpp @@ -68,6 +68,9 @@ void TreeByReference::replaceChildInPlace(TreeByReference oldChild, TreeByRefere return; } + // If the new child has a parent, detach from it + newChild.detachFromParent(); + // Move the new child assert(newChild.isGhost() || newChild.parent().isUninitialized()); TreePool::sharedPool()->move(oldChild.node(), newChild.node(), newChild.numberOfChildren()); @@ -144,6 +147,10 @@ void TreeByReference::replaceChildWithGhostInPlace(TreeByReference t) { } void TreeByReference::mergeChildrenAtIndexInPlace(TreeByReference t, int i) { + /* mergeChildrenAtIndexInPlace should only be called with a tree thant can + * have any number of children, so there is no need to replace the stolen + * children with ghosts. */ + // TODO assert this and t are "dynamic" trees assert(i >= 0 && i <= numberOfChildren()); // Steal operands int numberOfNewChildren = t.numberOfChildren(); @@ -196,8 +203,8 @@ void TreeByReference::addChildAtIndexInPlace(TreeByReference t, int index, int c replaceWithAllocationFailureInPlace(currentNumberOfChildren); return; } + assert(index >= 0 && index <= currentNumberOfChildren); - assert(t.parent().isUninitialized()); // If the new node is static, copy it in the pool and add the copy if (t.isStatic()) { @@ -210,6 +217,10 @@ void TreeByReference::addChildAtIndexInPlace(TreeByReference t, int index, int c return; } + // If t has a parent, detach t from it. + t.detachFromParent(); + assert(t.parent().isUninitialized()); + // Move t TreeNode * newChildPosition = node()->next(); for (int i = 0; i < index; i++) { @@ -251,6 +262,15 @@ void TreeByReference::removeChildrenInPlace(int currentNumberOfChildren) { /* Private */ +void TreeByReference::detachFromParent() { + TreeByReference myParent = parent(); + if (!myParent.isUninitialized()) { + int idxInParent = myParent.indexOfChild(*this); + myParent.childAtIndexWillBeStolen(idxInParent); + } + assert(parent().isUninitialized()); +} + void TreeByReference::setTo(const TreeByReference & tr) { /* We cannot use (*this)==tr because tr would need to be casted to * TreeByReference, which calls setTo and triggers an infinite loop */ diff --git a/poincare/src/tree_node.cpp b/poincare/src/tree_node.cpp index 16d0ec1b9..80a1922c4 100644 --- a/poincare/src/tree_node.cpp +++ b/poincare/src/tree_node.cpp @@ -192,6 +192,10 @@ TreeNode * TreeNode::nextSibling() const { return node; } +void TreeNode::childAtIndexWillBeStolen(int index) { + TreeByReference(this).replaceChildAtIndexWithGhostInPlace(index); +} + TreeNode * TreeNode::lastDescendant() const { TreeNode * node = const_cast(this); int remainingNodesToVisit = node->numberOfChildren();