/* This file is part of the KDE project
   Copyright (C) 2001 Andrea Rizzi <rizzi@kde.org>
	              Ulrich Kuettler <ulrich.kuettler@mailbox.tu-dresden.de>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#include <tqpainter.h>

#include <kdebug.h>

#include "elementvisitor.h"
#include "formulacursor.h"
#include "formulaelement.h"
#include "kformulacommand.h"
#include "sequenceelement.h"
#include "symbolelement.h"

KFORMULA_NAMESPACE_BEGIN


class SymbolSequenceElement : public SequenceElement {
    typedef SequenceElement inherited;
public:

    SymbolSequenceElement( BasicElement* parent = 0 ) : SequenceElement( parent ) {}

    /**
     * This is called by the container to get a command depending on
     * the current cursor position (this is how the element gets chosen)
     * and the request.
     *
     * @returns the command that performs the requested action with
     * the containers active cursor.
     */
    virtual KCommand* buildCommand( Container*, Request* );
};


KCommand* SymbolSequenceElement::buildCommand( Container* container, Request* request )
{
    FormulaCursor* cursor = container->activeCursor();
    if ( cursor->isReadOnly() ) {
        return 0;
    }

    switch ( *request ) {
    case req_addIndex: {
        FormulaCursor* cursor = container->activeCursor();
        if ( cursor->isSelection() ||
             ( cursor->getPos() > 0 && cursor->getPos() < countChildren() ) ) {
            break;
        }
        IndexRequest* ir = static_cast<IndexRequest*>( request );
        if ( ( ir->index() == upperMiddlePos ) || ( ir->index() == lowerMiddlePos ) ) {
            SymbolElement* element = static_cast<SymbolElement*>( getParent() );
            ElementIndexPtr index = element->getIndex( ir->index() );
            if ( !index->hasIndex() ) {
                KFCAddGenericIndex* command = new KFCAddGenericIndex( container, index );
                return command;
            }
            else {
                index->moveToIndex( cursor, afterCursor );
                cursor->setSelection( false );
                formula()->cursorHasMoved( cursor );
                return 0;
            }
        }
    }
    default:
        break;
    }
    return inherited::buildCommand( container, request );
}


SymbolElement::SymbolElement(SymbolType type, BasicElement* parent)
    : BasicElement(parent), symbol( 0 ), symbolType( type )
{
    content = new SymbolSequenceElement( this );
    upper = 0;
    lower = 0;
}

SymbolElement::~SymbolElement()
{
    delete lower;
    delete upper;
    delete content;
    delete symbol;
}


SymbolElement::SymbolElement( const SymbolElement& other )
    : BasicElement( other ), symbol( 0 ), symbolType( other.symbolType )
{
    content = new SymbolSequenceElement( *dynamic_cast<SymbolSequenceElement*>( other.content ) );
    content->setParent( this );

    if ( other.upper ) {
        upper = new SequenceElement( *( other.upper ) );
        upper->setParent( this );
    }
    else {
        upper = 0;
    }
    if ( other.lower ) {
        lower = new SequenceElement( *( other.lower ) );
        lower->setParent( this );
    }
    else {
        lower = 0;
    }
}


bool SymbolElement::accept( ElementVisitor* visitor )
{
    return visitor->visit( this );
}


BasicElement* SymbolElement::goToPos( FormulaCursor* cursor, bool& handled,
                                      const LuPixelPoint& point, const LuPixelPoint& parentOrigin )
{
    BasicElement* e = BasicElement::goToPos(cursor, handled, point, parentOrigin);
    if (e != 0) {
        LuPixelPoint myPos(parentOrigin.x() + getX(),
                           parentOrigin.y() + getY());

        e = content->goToPos(cursor, handled, point, myPos);
        if (e != 0) {
            return e;
        }
        if (hasLower()) {
            e = lower->goToPos(cursor, handled, point, myPos);
            if (e != 0) {
                return e;
            }
        }
        if (hasUpper()) {
            e = upper->goToPos(cursor, handled, point, myPos);
            if (e != 0) {
                return e;
            }
        }

        // the positions after the indexes.
        luPixel dx = point.x() - myPos.x();
        luPixel dy = point.y() - myPos.y();
        if (dy < symbol->getY()) {
            if (hasUpper() && (dx > upper->getX())) {
                upper->moveLeft(cursor, this);
                handled = true;
                return upper;
            }
        }
        else if (dy > symbol->getY()+symbol->getHeight()) {
            if (hasLower() && (dx > lower->getX())) {
                lower->moveLeft(cursor, this);
                handled = true;
                return lower;
            }
        }

        // Have the cursor jump behind the integral.
        if ( ( dx < symbol->getX()+symbol->getWidth() ) &&
             ( dx > symbol->getX()+symbol->getWidth()/2 ) ) {
            content->moveRight( cursor, this );
            handled = true;
            return content;
        }

        return this;
    }
    return 0;
}


/**
 * Calculates our width and height and
 * our children's parentPosition.
 */
void SymbolElement::calcSizes( const ContextStyle& context,
                               ContextStyle::TextStyle tstyle,
                               ContextStyle::IndexStyle istyle,
                               StyleAttributes& style )
{
    double factor = style.sizeFactor();
    luPt mySize = context.getAdjustedSize( tstyle, factor );
    luPixel distX = context.ptToPixelX( context.getThinSpace( tstyle, factor ) );
    luPixel distY = context.ptToPixelY( context.getThinSpace( tstyle, factor ) );

    //if ( symbol == 0 ) {
    delete symbol;
    symbol = context.fontStyle().createArtwork( symbolType );
    //}

    symbol->calcSizes(context, tstyle, factor, mySize);
    content->calcSizes( context, tstyle, istyle, style );

    //symbol->scale(((double)parentSize)/symbol->getHeight()*2);

    luPixel upperWidth = 0;
    luPixel upperHeight = 0;
    if (hasUpper()) {
        upper->calcSizes( context, context.convertTextStyleIndex( tstyle ),
                          context.convertIndexStyleUpper( istyle ), style );
        upperWidth = upper->getWidth();
        upperHeight = upper->getHeight() + distY;
    }

    luPixel lowerWidth = 0;
    luPixel lowerHeight = 0;
    if (hasLower()) {
        lower->calcSizes( context, context.convertTextStyleIndex( tstyle ),
                          context.convertIndexStyleLower( istyle ), style );
        lowerWidth = lower->getWidth();
        lowerHeight = lower->getHeight() + distY;
    }

    // widths
    luPixel xOffset = TQMAX(symbol->getWidth(), TQMAX(upperWidth, lowerWidth));
    if (context.getCenterSymbol()) {
        symbol->setX((xOffset - symbol->getWidth()) / 2);
    }
    else {
        symbol->setX(xOffset - symbol->getWidth());
    }
    // ???
    content->setX(xOffset +
                  static_cast<luPixel>( symbol->slant()*symbol->getHeight()/2 ) +
                  distX/2);

    setWidth(TQMAX(content->getX() + content->getWidth(),
                  TQMAX(upperWidth, lowerWidth)));

    // heights
    //int toMidline = TQMAX(content->getHeight() / 2,
    luPixel toMidline = TQMAX(content->axis( context, tstyle, factor ),
                             upperHeight + symbol->getHeight()/2);
    //int fromMidline = TQMAX(content->getHeight() / 2,
    luPixel fromMidline = TQMAX(content->getHeight() - content->axis( context, tstyle, factor ),
                               lowerHeight + symbol->getHeight()/2);
    setHeight(toMidline + fromMidline);
    //setMidline(toMidline);

    symbol->setY(toMidline - symbol->getHeight()/2);
    //content->setY(toMidline - content->getHeight()/2);
    content->setY(toMidline - content->axis( context, tstyle, factor ));

    if (hasUpper()) {
        luPixel slant =
            static_cast<luPixel>( symbol->slant()*( symbol->getHeight()+distY ) );
        if (context.getCenterSymbol()) {
            upper->setX((xOffset - upperWidth) / 2 + slant );
        }
        else {
            if (upperWidth < symbol->getWidth()) {
                upper->setX(symbol->getX() +
                            (symbol->getWidth() - upperWidth) / 2 + slant );
            }
            else {
                upper->setX(xOffset - upperWidth);
            }
        }
        upper->setY(toMidline - upperHeight - symbol->getHeight()/2);
    }
    if (hasLower()) {
        luPixel slant = static_cast<luPixel>( -symbol->slant()*distY );
        if (context.getCenterSymbol()) {
            lower->setX((xOffset - lowerWidth) / 2 + slant);
        }
        else {
            if (lowerWidth < symbol->getWidth()) {
                lower->setX(symbol->getX() +
                            (symbol->getWidth() - lowerWidth) / 2 + slant );
            }
            else {
                lower->setX(xOffset - lowerWidth);
            }
        }
        lower->setY(toMidline + symbol->getHeight()/2 + distY);
    }
    setBaseline(content->getBaseline() + content->getY());
}

/**
 * Draws the whole element including its children.
 * The `parentOrigin' is the point this element's parent starts.
 * We can use our parentPosition to get our own origin then.
 */
void SymbolElement::draw( TQPainter& painter, const LuPixelRect& r,
                          const ContextStyle& context,
                          ContextStyle::TextStyle tstyle,
                          ContextStyle::IndexStyle istyle,
                          StyleAttributes& style,
                          const LuPixelPoint& parentOrigin )
{
    LuPixelPoint myPos( parentOrigin.x()+getX(), parentOrigin.y()+getY() );
    //if ( !LuPixelRect( myPos.x(), myPos.y(), getWidth(), getHeight() ).intersects( r ) )
    //    return;

    luPt mySize = context.getAdjustedSize( tstyle, style.sizeFactor() );
    symbol->draw( painter, r, context, tstyle, style, mySize, myPos );
    content->draw( painter, r, context, tstyle, istyle, style, myPos );
    if ( hasUpper() ) {
        upper->draw( painter, r, context, context.convertTextStyleIndex( tstyle ),
                     context.convertIndexStyleUpper( istyle ), style, myPos );
    }
    if ( hasLower() ) {
        lower->draw( painter, r, context, context.convertTextStyleIndex( tstyle ),
                     context.convertIndexStyleLower( istyle ), style, myPos );
    }

    // Debug
#if 0
    painter.setBrush(TQt::NoBrush);
    painter.setPen(TQt::red);
//     painter.drawRect( context.layoutUnitToPixelX( myPos.x() ),
//                       context.layoutUnitToPixelY( myPos.y() ),
//                       context.layoutUnitToPixelX( getWidth() ),
//                       context.layoutUnitToPixelY( getHeight() ) );
    painter.drawRect( context.layoutUnitToPixelX( myPos.x()+symbol->getX() ),
                      context.layoutUnitToPixelY( myPos.y()+symbol->getY() ),
                      context.layoutUnitToPixelX( symbol->getWidth() ),
                      context.layoutUnitToPixelY( symbol->getHeight() ) );
    painter.setPen(TQt::green);
    painter.drawLine( context.layoutUnitToPixelX( myPos.x() ),
                      context.layoutUnitToPixelY( myPos.y()+axis(context, tstyle) ),
                      context.layoutUnitToPixelX( myPos.x()+getWidth() ),
                      context.layoutUnitToPixelY( myPos.y()+axis(context, tstyle) ) );
#endif
}


void SymbolElement::dispatchFontCommand( FontCommand* cmd )
{
    content->dispatchFontCommand( cmd );
    if ( hasUpper() ) {
        upper->dispatchFontCommand( cmd );
    }
    if ( hasLower() ) {
        lower->dispatchFontCommand( cmd );
    }
}

// navigation
//
// The elements are responsible to handle cursor movement themselves.
// To do this they need to know the direction the cursor moves and
// the element it comes from.
//
// The cursor might be in normal or in selection mode.

/**
 * Enters this element while moving to the left starting inside
 * the element `from'. Searches for a cursor position inside
 * this element or to the left of it.
 */
void SymbolElement::moveLeft(FormulaCursor* cursor, BasicElement* from)
{
    if (cursor->isSelectionMode()) {
        getParent()->moveLeft(cursor, this);
    }
    else {
        bool linear = cursor->getLinearMovement();
        if (from == getParent()) {
            content->moveLeft(cursor, this);
        }
        else if (from == content) {
            if (linear && hasLower()) {
                lower->moveLeft(cursor, this);
            }
            else if (linear && hasUpper()) {
                upper->moveLeft(cursor, this);
            }
            else {
                getParent()->moveLeft(cursor, this);
            }
        }
        else if (from == lower) {
            if (linear && hasUpper()) {
                upper->moveLeft(cursor, this);
            }
            else {
                getParent()->moveLeft(cursor, this);
            }
        }
        else if (from == upper) {
            getParent()->moveLeft(cursor, this);
        }
    }
}

/**
 * Enters this element while moving to the right starting inside
 * the element `from'. Searches for a cursor position inside
 * this element or to the right of it.
 */
void SymbolElement::moveRight(FormulaCursor* cursor, BasicElement* from)
{
    if (cursor->isSelectionMode()) {
        getParent()->moveRight(cursor, this);
    }
    else {
        bool linear = cursor->getLinearMovement();
        if (from == getParent()) {
            if (linear && hasUpper()) {
                upper->moveRight(cursor, this);
            }
            else if (linear && hasLower()) {
                lower->moveRight(cursor, this);
            }
            else {
                content->moveRight(cursor, this);
            }
        }
        else if (from == upper) {
            if (linear && hasLower()) {
                lower->moveRight(cursor, this);
            }
            else {
                content->moveRight(cursor, this);
            }
        }
        else if (from == lower) {
            content->moveRight(cursor, this);
        }
        else if (from == content) {
            getParent()->moveRight(cursor, this);
        }
    }
}

/**
 * Enters this element while moving up starting inside
 * the element `from'. Searches for a cursor position inside
 * this element or above it.
 */
void SymbolElement::moveUp(FormulaCursor* cursor, BasicElement* from)
{
    if (cursor->isSelectionMode()) {
        getParent()->moveUp(cursor, this);
    }
    else {
        if (from == content) {
            if (hasUpper()) {
                upper->moveLeft(cursor, this);
            }
            else {
                getParent()->moveUp(cursor, this);
            }
        }
        else if (from == upper) {
            getParent()->moveUp(cursor, this);
        }
        else if ((from == getParent()) || (from == lower)) {
            content->moveRight(cursor, this);
        }
    }
}

/**
 * Enters this element while moving down starting inside
 * the element `from'. Searches for a cursor position inside
 * this element or below it.
 */
void SymbolElement::moveDown(FormulaCursor* cursor, BasicElement* from)
{
    if (cursor->isSelectionMode()) {
        getParent()->moveDown(cursor, this);
    }
    else {
        if (from == content) {
            if (hasLower()) {
                lower->moveLeft(cursor, this);
            }
            else {
                getParent()->moveDown(cursor, this);
            }
        }
        else if (from == lower) {
            getParent()->moveDown(cursor, this);
        }
        else if ((from == getParent()) || (from == upper)) {
            content->moveRight(cursor, this);
        }
    }
}

// children

// main child
//
// If an element has children one has to become the main one.

// void SymbolElement::setMainChild(SequenceElement* child)
// {
//     formula()->elementRemoval(content);
//     content = child;
//     content->setParent(this);
//     formula()->changed();
// }


/**
 * Inserts all new children at the cursor position. Places the
 * cursor according to the direction.
 *
 * You only can insert one index at a time. So the list must contain
 * exactly on SequenceElement. And the index you want to insert
 * must not exist already.
 *
 * The list will be emptied but stays the property of the caller.
 */
void SymbolElement::insert(FormulaCursor* cursor,
                           TQPtrList<BasicElement>& newChildren,
                           Direction direction)
{
    SequenceElement* index = static_cast<SequenceElement*>(newChildren.take(0));
    index->setParent(this);

    switch (cursor->getPos()) {
    case upperMiddlePos:
        upper = index;
        break;
    case lowerMiddlePos:
        lower = index;
        break;
    default:
        // this is an error!
        return;
    }

    if (direction == beforeCursor) {
        index->moveLeft(cursor, this);
    }
    else {
        index->moveRight(cursor, this);
    }
    cursor->setSelection(false);
    formula()->changed();
}

/**
 * Removes all selected children and returns them. Places the
 * cursor to where the children have been.
 *
 * The cursor has to be inside one of our indexes which is supposed
 * to be empty. The index will be removed and the cursor will
 * be placed to the removed index so it can be inserted again.
 * This methode is called by SequenceElement::remove only.
 *
 * The ownership of the list is passed to the caller.
 */
void SymbolElement::remove(FormulaCursor* cursor,
                           TQPtrList<BasicElement>& removedChildren,
                           Direction direction)
{
    int pos = cursor->getPos();
    switch (pos) {
    case upperMiddlePos:
        removedChildren.append(upper);
        formula()->elementRemoval(upper);
        upper = 0;
        setToUpper(cursor);
        break;
    case lowerMiddlePos:
        removedChildren.append(lower);
        formula()->elementRemoval(lower);
        lower = 0;
        setToLower(cursor);
        break;
    case contentPos: {
        BasicElement* parent = getParent();
        parent->selectChild(cursor, this);
        parent->remove(cursor, removedChildren, direction);
        break;
    }
    }
    formula()->changed();
}

/**
 * Moves the cursor to a normal place where new elements
 * might be inserted.
 */
void SymbolElement::normalize(FormulaCursor* cursor, Direction direction)
{
    if (direction == beforeCursor) {
        content->moveLeft(cursor, this);
    }
    else {
        content->moveRight(cursor, this);
    }
}

/**
 * Returns the child at the cursor.
 */
BasicElement* SymbolElement::getChild(FormulaCursor* cursor, Direction)
{
    int pos = cursor->getPos();
    switch (pos) {
    case contentPos:
        return content;
    case upperMiddlePos:
        return upper;
    case lowerMiddlePos:
        return lower;
    }
    return 0;
}

/**
 * Sets the cursor to select the child. The mark is placed before,
 * the position behind it.
 */
void SymbolElement::selectChild(FormulaCursor* cursor, BasicElement* child)
{
    if (child == content) {
        setToContent(cursor);
    }
    else if (child == upper) {
        setToUpper(cursor);
    }
    else if (child == lower) {
        setToLower(cursor);
    }
}

void SymbolElement::setToUpper(FormulaCursor* cursor)
{
    cursor->setTo(this, upperMiddlePos);
}

void SymbolElement::setToLower(FormulaCursor* cursor)
{
    cursor->setTo(this, lowerMiddlePos);
}

/**
 * Sets the cursor to point to the place where the content is.
 * There always is a content so this is not a useful place.
 * No insertion or removal will succeed as long as the cursor is
 * there.
 */
void SymbolElement::setToContent(FormulaCursor* cursor)
{
    cursor->setTo(this, contentPos);
}


void SymbolElement::moveToUpper(FormulaCursor* cursor, Direction direction)
{
    if (hasUpper()) {
        if (direction == beforeCursor) {
            upper->moveLeft(cursor, this);
        }
        else {
            upper->moveRight(cursor, this);
        }
    }
}

void SymbolElement::moveToLower(FormulaCursor* cursor, Direction direction)
{
    if (hasLower()) {
        if (direction == beforeCursor) {
            lower->moveLeft(cursor, this);
        }
        else {
            lower->moveRight(cursor, this);
        }
    }
}


ElementIndexPtr SymbolElement::getIndex( int position )
{
    switch ( position ) {
	case lowerMiddlePos:
	    return getLowerIndex();
	case upperMiddlePos:
	    return getUpperIndex();
    }
    return getUpperIndex();
}


/**
 * Appends our attributes to the dom element.
 */
void SymbolElement::writeDom(TQDomElement element)
{
    BasicElement::writeDom(element);

    element.setAttribute("TYPE", symbolType);

    TQDomDocument doc = element.ownerDocument();

    TQDomElement con = doc.createElement("CONTENT");
    con.appendChild(content->getElementDom(doc));
    element.appendChild(con);

    if(hasLower()) {
        TQDomElement ind = doc.createElement("LOWER");
        ind.appendChild(lower->getElementDom(doc));
        element.appendChild(ind);
    }
    if(hasUpper()) {
        TQDomElement ind = doc.createElement("UPPER");
        ind.appendChild(upper->getElementDom(doc));
        element.appendChild(ind);
    }
}

/**
 * Reads our attributes from the element.
 * Returns false if it failed.
 */
bool SymbolElement::readAttributesFromDom(TQDomElement element)
{
    if (!BasicElement::readAttributesFromDom(element)) {
        return false;
    }

    TQString typeStr = element.attribute("TYPE");
    if(!typeStr.isNull()) {
        symbolType = static_cast<SymbolType>(typeStr.toInt());
    }

    return true;
}

/**
 * Reads our content from the node. Sets the node to the next node
 * that needs to be read.
 * Returns false if it failed.
 */
bool SymbolElement::readContentFromDom(TQDomNode& node)
{
    if (!BasicElement::readContentFromDom(node)) {
        return false;
    }

    if ( !buildChild( content, node, "CONTENT" ) ) {
        kdWarning( DEBUGID ) << "Empty content in SymbolElement." << endl;
        return false;
    }
    node = node.nextSibling();

    bool lowerRead = false;
    bool upperRead = false;

    while (!node.isNull() && !(upperRead && lowerRead)) {

        if (!lowerRead && (node.nodeName().upper() == "LOWER")) {
            lowerRead = buildChild( lower=new SequenceElement( this ), node, "LOWER" );
            if ( !lowerRead ) return false;
        }

        if (!upperRead && (node.nodeName().upper() == "UPPER")) {
            upperRead = buildChild( upper=new SequenceElement( this ), node, "UPPER" );
            if ( !upperRead ) return false;
        }

        node = node.nextSibling();
    }
    return true;
}

TQString SymbolElement::toLatex()
{
    TQString sym;

    switch(symbolType) {

	case 1001:
	 sym="\\int";
	break;
	case 1002:
	 sym="\\sum";
	break;
	case 1003:
	 sym="\\prod";
	break;

	default:
	 sym=" ";

    }


    if(hasLower()) {
        sym+="_{";
	sym+=lower->toLatex();
	sym+="}";
    }

    if(hasUpper()) {
        sym+="^{";
	sym+=upper->toLatex();
	sym+="} ";
    }

    sym += " ";

    sym+=content->toLatex();


    return sym;
}

TQString SymbolElement::formulaString()
{
    TQString sym;
    switch ( symbolType ) {
    case 1001:
        sym="int(";
	break;
    case 1002:
        sym="sum(";
	break;
    case 1003:
        sym="prod(";
	break;
    default:
        sym="(";
    }
    sym += content->formulaString();
    if ( hasLower() ) {
        sym += ", " + lower->formulaString();
    }
    if ( hasUpper() ) {
        sym += ", " + upper->formulaString();
    }
    return sym + ")";
}

void SymbolElement::writeMathML( TQDomDocument& doc, TQDomNode& parent, bool oasisFormat ) const
{
    TQDomElement de = doc.createElement( oasisFormat ? "math:mrow" : "mrow" );
    TQDomElement mo = doc.createElement( oasisFormat ? "math:mo" : "mo" );

    TQString value;

    switch( symbolType )
    {
    case EmptyBracket: break;
    case LeftLineBracket: case RightLineBracket:
        mo.appendChild( doc.createTextNode( "|" ) ); break;
    case Integral:
        mo.appendChild( doc.createEntityReference( "int" ) ); break;
    case Sum:
        mo.appendChild( doc.createEntityReference( "sum" ) ); break;
    case Product:
        mo.appendChild( doc.createEntityReference( "prod" ) ); break;
    default:
        mo.appendChild( doc.createTextNode( TQChar( symbolType ) ) );
    }

    TQDomElement between;
    if ( hasUpper() && hasLower() )
    {
        between = doc.createElement( oasisFormat ? "math:msubsup" : "msubsup" );
        between.appendChild( mo );
        lower->writeMathML( doc, between, oasisFormat );
        upper->writeMathML( doc, between, oasisFormat );
    }
    else if ( hasUpper() )
    {
        between = doc.createElement( oasisFormat ? "math:msup" : "msup" );
        between.appendChild( mo );
        upper->writeMathML( doc, between, oasisFormat );
    }
    else if ( hasLower() )
    {
        between = doc.createElement( oasisFormat ? "math:msub" : "msub" );
        between.appendChild( mo );
        lower->writeMathML( doc, between, oasisFormat );
    }
    else
        between = mo;

    de.appendChild( between );
    content->writeMathML( doc, de, oasisFormat );

    parent.appendChild( de );
}

KFORMULA_NAMESPACE_END
