#include #include #include #include #include #include #include namespace Poincare { template<> TreeNode * LayoutRef::FailedAllocationStaticNode() { static AllocationFailedLayoutNode FailureNode; if (FailureNode.identifier() >= -1) { int newIdentifier = TreePool::sharedPool()->registerStaticNode(&FailureNode); FailureNode.rename(newIdentifier); } return &FailureNode; } // Cursor template LayoutCursor LayoutReference::cursor() const { return LayoutCursor(this->typedNode()); } template<> LayoutCursor LayoutRef::equivalentCursor(LayoutCursor * cursor) { return this->typedNode()->equivalentCursor(cursor); } // Tree modification template<> void LayoutRef::replaceChild(LayoutRef oldChild, LayoutRef newChild, LayoutCursor * cursor) { if (!typedNode()->willReplaceChild(oldChild.typedNode(), newChild.typedNode(), cursor)) { return; } replaceTreeChild(oldChild, newChild); if (cursor != nullptr) { if (isAllocationFailure()) { cursor->setLayoutReference(*this); } else { cursor->setLayoutReference(newChild); } } } template<> void LayoutRef::replaceWithJuxtapositionOf(LayoutRef leftChild, LayoutRef rightChild, LayoutCursor * cursor) { LayoutReference p = parent(); assert(p.isDefined()); assert(!p.isHorizontal()); /* One of the children to juxtapose might be "this", so we cannot just call * replaceWith. */ HorizontalLayoutRef horizontalLayoutR; p.replaceChild(*this, horizontalLayoutR, cursor); horizontalLayoutR.addOrMergeChildAtIndex(leftChild, 0, false); horizontalLayoutR.addOrMergeChildAtIndex(rightChild, 1, false); } template void LayoutReference::addChildAtIndex(LayoutRef l, int index, LayoutCursor * cursor) { int newIndex = index; if (!this->typedNode()->willAddChildAtIndex(l.typedNode(), &newIndex, cursor)) { return; } LayoutRef nextPointedLayout(nullptr); LayoutCursor::Position nextPosition = LayoutCursor::Position::Left; if (newIndex < this->numberOfChildren()) { nextPointedLayout = this->childAtIndex(newIndex); nextPosition = LayoutCursor::Position::Left; } else { nextPointedLayout = *this; nextPosition = LayoutCursor::Position::Right; } this->addChildTreeAtIndex(l, newIndex); if (cursor != nullptr) { if (this->isAllocationFailure()) { cursor->setLayoutReference(*this); } else { cursor->setLayoutReference(nextPointedLayout); cursor->setPosition(nextPosition); } } } template<> void LayoutRef::addSibling(LayoutCursor * cursor, LayoutReference sibling, bool moveCursor) { if (!typedNode()->willAddSibling(cursor, sibling.typedNode(), moveCursor)) { //TODO return; } /* The layout must have a parent, because HorizontalLayoutRef's * preprocessAddSibling returns false only an HorizontalLayout can be the * root layout. */ LayoutRef p = parent(); assert(p.isDefined()); if (p.isHorizontal()) { int indexInParent = p.indexOfChild(*this); int siblingIndex = cursor->position() == LayoutCursor::Position::Left ? indexInParent : indexInParent + 1; /* Special case: If the neighbour sibling is a VerticalOffsetLayout, let it * handle the insertion of the new sibling. Do not enter the special case if * "this" is a VerticalOffsetLayout, to avoid an infinite loop. */ if (!isVerticalOffset()) { LayoutRef neighbour(nullptr); if (cursor->position() == LayoutCursor::Position::Left && indexInParent > 0) { neighbour = p.childAtIndex(indexInParent - 1); } else if (cursor->position() ==LayoutCursor::Position::Right && indexInParent < p.numberOfChildren() - 1) { neighbour = p.childAtIndex(indexInParent + 1); } if (neighbour.isDefined() && neighbour.isVerticalOffset()) { if (moveCursor) { cursor->setLayoutReference(neighbour); cursor->setPosition(cursor->position() == LayoutCursor::Position::Left ? LayoutCursor::Position::Right : LayoutCursor::Position::Left); } neighbour.addSibling(cursor, sibling, moveCursor); return; } } // Else, let the parent add the sibling. HorizontalLayoutRef(p.typedNode()).addOrMergeChildAtIndex(sibling, siblingIndex, true, moveCursor ? cursor : nullptr); return; } if (cursor->position() == LayoutCursor::Position::Left) { replaceWithJuxtapositionOf(sibling, *this, cursor); } else { assert(cursor->position() == LayoutCursor::Position::Right); replaceWithJuxtapositionOf(*this, sibling, cursor); } } template void LayoutReference::removeChild(LayoutRef l, LayoutCursor * cursor, bool force) { if (!this->typedNode()->willRemoveChild(l.typedNode(), cursor)) { return; } assert(this->hasChild(l)); int index = this->indexOfChild(l); this->removeTreeChild(l); if (cursor) { if (index < this->numberOfChildren()) { LayoutRef newCursorRef = this->childAtIndex(index); cursor->setLayoutReference(newCursorRef); cursor->setPosition(LayoutCursor::Position::Left); } else { int newPointedLayoutIndex = index - 1; if (newPointedLayoutIndex >= 0 && newPointedLayoutIndex < this->numberOfChildren()) { cursor->setLayoutReference(this->childAtIndex(newPointedLayoutIndex)); cursor->setPosition(LayoutCursor::Position::Right); } else { cursor->setLayoutReference(*this); cursor->setPosition(LayoutCursor::Position::Right); } } } this->typedNode()->didRemoveChildAtIndex(index, cursor, force); } template <> void LayoutRef::collapseOnDirection(HorizontalDirection direction, int absorbingChildIndex) { LayoutRef p = parent(); if (!p.isDefined() || !p.isHorizontal()) { return; } int idxInParent = p.indexOfChild(*this); int numberOfSiblings = p.numberOfChildren(); int numberOfOpenParenthesis = 0; bool canCollapse = true; LayoutRef absorbingChild = childAtIndex(absorbingChildIndex); if (!absorbingChild.isDefined() || !absorbingChild.isHorizontal()) { return; } HorizontalLayoutRef horizontalAbsorbingChild = HorizontalLayoutRef(absorbingChild.node()); if (direction == HorizontalDirection::Right && idxInParent < numberOfSiblings - 1) { canCollapse = !(p.childAtIndex(idxInParent+1).mustHaveLeftSibling()); } LayoutRef sibling(nullptr); bool forceCollapse = false; while (canCollapse) { if (direction == HorizontalDirection::Right && idxInParent == numberOfSiblings - 1) { break; } if (direction == HorizontalDirection::Left && idxInParent == 0) { break; } int siblingIndex = direction == HorizontalDirection::Right ? idxInParent+1 : idxInParent-1; sibling = p.childAtIndex(siblingIndex); /* Even if forceCollapse is true, isCollapsable should be called to update * the number of open parentheses. */ bool shouldCollapse = sibling.isCollapsable(&numberOfOpenParenthesis, direction == HorizontalDirection::Left); if (shouldCollapse || forceCollapse) { /* If the collapse direction is Left and the next sibling to be collapsed * must have a left sibling, force the collapsing of this needed left * sibling. */ forceCollapse = direction == HorizontalDirection::Left && sibling.mustHaveLeftSibling(); p.removeChildAtIndex(siblingIndex, nullptr); int newIndex = direction == HorizontalDirection::Right ? absorbingChild.numberOfChildren() : 0; horizontalAbsorbingChild.addOrMergeChildAtIndex(sibling, newIndex, true); numberOfSiblings--; if (direction == HorizontalDirection::Left) { idxInParent--; } } else { break; } } } template LayoutCursor LayoutReference::cursor() const; template LayoutCursor LayoutReference::cursor() const; template void LayoutReference::removeChild(LayoutRef l, LayoutCursor * cursor, bool force); template void LayoutReference::removeChild(LayoutRef l, LayoutCursor * cursor, bool force); template void LayoutReference::addChildAtIndex(LayoutRef l, int index, LayoutCursor * cursor); template void LayoutReference::addChildAtIndex(LayoutRef l, int index, LayoutCursor * cursor); }