/* 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 <qptrlist.h>
#include <qpainter.h>
#include <qpen.h>
#include <qpointarray.h>

#include <kdebug.h>
#include <klocale.h>

#include "bracketelement.h"
#include "elementvisitor.h"
#include "fontstyle.h"
#include "formulacursor.h"
#include "formulaelement.h"
#include "sequenceelement.h"

KFORMULA_NAMESPACE_BEGIN

SingleContentElement::SingleContentElement(BasicElement* parent )
    : BasicElement( parent )
{
    content = new SequenceElement( this );
}


SingleContentElement::SingleContentElement( const SingleContentElement& other )
    : BasicElement( other )
{
    content = new SequenceElement( other.content );
    content->setParent( this );
}


SingleContentElement::~SingleContentElement()
{
    delete content;
}


QChar SingleContentElement::getCharacter() const
{
    // This is meant to make the SingleContentElement text only.
    // This "fixes" the parenthesis problem (parenthesis too large).
    // I'm not sure if we really want this. There should be better ways.
    if ( content->isTextOnly() ) {
        return '\\';
    }
    return content->getCharacter();
}

BasicElement* SingleContentElement::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;
        }
        return this;
    }
    return 0;
}

void SingleContentElement::dispatchFontCommand( FontCommand* cmd )
{
    content->dispatchFontCommand( cmd );
}

void SingleContentElement::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 {
            getParent()->moveLeft(cursor, this);
        }
    }
}

void SingleContentElement::moveRight(FormulaCursor* cursor, BasicElement* from)
{
    if (cursor->isSelectionMode()) {
        getParent()->moveRight(cursor, this);
    }
    else {
        //bool linear = cursor->getLinearMovement();
        if (from == getParent()) {
            content->moveRight(cursor, this);
        }
        else {
            getParent()->moveRight(cursor, this);
        }
    }
}

void SingleContentElement::moveUp(FormulaCursor* cursor, BasicElement* /*from*/)
{
    getParent()->moveUp(cursor, this);
}

void SingleContentElement::moveDown(FormulaCursor* cursor, BasicElement* /*from*/)
{
    getParent()->moveDown(cursor, this);
}

void SingleContentElement::remove( FormulaCursor* cursor,
                                   QPtrList<BasicElement>& removedChildren,
                                   Direction direction )
{
    switch (cursor->getPos()) {
    case contentPos:
        BasicElement* parent = getParent();
        parent->selectChild(cursor, this);
        parent->remove(cursor, removedChildren, direction);
    }
}

void SingleContentElement::normalize( FormulaCursor* cursor, Direction direction )
{
    if (direction == beforeCursor) {
        content->moveLeft(cursor, this);
    }
    else {
        content->moveRight(cursor, this);
    }
}

SequenceElement* SingleContentElement::getMainChild()
{
    return content;
}

void SingleContentElement::selectChild(FormulaCursor* cursor, BasicElement* child)
{
    if (child == content) {
        cursor->setTo(this, contentPos);
    }
}

void SingleContentElement::writeDom(QDomElement element)
{
    BasicElement::writeDom(element);

    QDomDocument doc = element.ownerDocument();

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

bool SingleContentElement::readContentFromDom(QDomNode& node)
{
    if (!BasicElement::readContentFromDom(node)) {
        return false;
    }

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

    return true;
}

int SingleContentElement::readContentFromMathMLDom( QDomNode& node )
{
    if ( BasicElement::readContentFromMathMLDom( node ) == -1 ) {
        return -1;
    }

    int nodeCounter = content->buildMathMLChild( node );
    if ( nodeCounter == -1 ) {
        kdWarning( DEBUGID) << "Empty content in SingleContentElement\n";
        return -1;
    }

    return nodeCounter;
}

void SingleContentElement::writeMathMLContent( QDomDocument& doc, QDomElement& element, bool oasisFormat ) const
{
    content->writeMathML( doc, element, oasisFormat );
}



BracketElement::BracketElement(SymbolType l, SymbolType r, BasicElement* parent)
    : SingleContentElement(parent),
      left( 0 ), right( 0 ),
      leftType( l ), rightType( r ),
      m_operator( false ), m_customLeft( false ), m_customRight( false )
{
}


BracketElement::~BracketElement()
{
    delete left;
    delete right;
}


BracketElement::BracketElement( const BracketElement& other )
    : SingleContentElement( other ),
      left( 0 ), right( 0 ),
      leftType( other.leftType ), rightType( other.rightType ),
      m_operator( other.m_operator ),
      m_customLeft( other.m_customLeft ), m_customRight( other.m_customRight )
{
}


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


void BracketElement::entered( SequenceElement* /*child*/ )
{
    formula()->tell( i18n( "Delimited list" ) );
}


BasicElement* BracketElement::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 = getContent()->goToPos(cursor, handled, point, myPos);
        if (e != 0) {
            return e;
        }

        // We are in one of those gaps.
        luPixel dx = point.x() - myPos.x();
        luPixel dy = point.y() - myPos.y();

        if ((dx > getContent()->getX()+getContent()->getWidth()) ||
            (dy > getContent()->getY()+getContent()->getHeight())) {
            getContent()->moveEnd(cursor);
            handled = true;
            return getContent();
        }
        return this;
    }
    return 0;
}


/**
 * Calculates our width and height and
 * our children's parentPosition.
 */
void BracketElement::calcSizes( const ContextStyle& context,
                                ContextStyle::TextStyle tstyle,
                                ContextStyle::IndexStyle istyle,
                                StyleAttributes& style )
{
    SequenceElement* content = getContent();
    content->calcSizes( context, tstyle, istyle, style );

    //if ( left == 0 ) {
    delete left;
    delete right;
    left = context.fontStyle().createArtwork( leftType );
    right = context.fontStyle().createArtwork( rightType );
    //}

    double factor = style.sizeFactor();
    if (content->isTextOnly()) {
        left->calcSizes(context, tstyle, factor);
        right->calcSizes(context, tstyle, factor);

        setBaseline(QMAX(content->getBaseline(),
                         QMAX(left->getBaseline(), right->getBaseline())));

        content->setY(getBaseline() - content->getBaseline());
        left   ->setY(getBaseline() - left   ->getBaseline());
        right  ->setY(getBaseline() - right  ->getBaseline());

        //setMidline(content->getY() + content->getMidline());
        setHeight(QMAX(content->getY() + content->getHeight(),
                       QMAX(left ->getY() + left ->getHeight(),
                            right->getY() + right->getHeight())));
    }
    else {
        //kdDebug( DEBUGID ) << "BracketElement::calcSizes " << content->axis( context, tstyle ) << " " << content->getHeight() << endl;
        luPixel contentHeight = 2 * QMAX( content->axis( context, tstyle, factor ),
                                          content->getHeight() - content->axis( context, tstyle, factor ) );
        left->calcSizes( context, tstyle, factor, contentHeight );
        right->calcSizes( context, tstyle, factor, contentHeight );

        // height
        setHeight(QMAX(contentHeight,
                       QMAX(left->getHeight(), right->getHeight())));
        //setMidline(getHeight() / 2);

        content->setY(getHeight() / 2 - content->axis( context, tstyle, factor ));
        setBaseline(content->getBaseline() + content->getY());

        if ( left->isNormalChar() ) {
            left->setY(getBaseline() - left->getBaseline());
        }
        else {
            left->setY((getHeight() - left->getHeight())/2);
        }
        if ( right->isNormalChar() ) {
            right->setY(getBaseline() - right->getBaseline());
        }
        else {
            right->setY((getHeight() - right->getHeight())/2);
        }

//         kdDebug() << "BracketElement::calcSizes" << endl
//                   << "getHeight(): " << getHeight() << endl
//                   << "left->getHeight():  " << left->getHeight() << endl
//                   << "right->getHeight(): " << right->getHeight() << endl
//                   << "left->getY():  " << left->getY() << endl
//                   << "right->getY(): " << right->getY() << endl
//                   << endl;
    }

    // width
    setWidth(left->getWidth() + content->getWidth() + right->getWidth());
    content->setX(left->getWidth());
    right  ->setX(left->getWidth()+content->getWidth());
}


/**
 * 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 BracketElement::draw( QPainter& 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;

    SequenceElement* content = getContent();
    content->draw(painter, r, context, tstyle, istyle, style, myPos);

    if (content->isTextOnly()) {
        left->draw(painter, r, context, tstyle, style, myPos);
        right->draw(painter, r, context, tstyle, style, myPos);
    }
    else {
        double factor = style.sizeFactor();
        luPixel contentHeight = 2 * QMAX(content->axis( context, tstyle, factor ),
                                         content->getHeight() - content->axis( context, tstyle, factor ));
        left->draw(painter, r, context, tstyle, style, contentHeight, myPos);
        right->draw(painter, r, context, tstyle, style, contentHeight, myPos);
    }

    // Debug
#if 0
    painter.setBrush( Qt::NoBrush );
    painter.setPen( Qt::red );
    painter.drawRect( context.layoutUnitToPixelX( myPos.x()+left->getX() ),
                      context.layoutUnitToPixelY( myPos.y()+left->getY() ),
                      context.layoutUnitToPixelX( left->getWidth() ),
                      context.layoutUnitToPixelY( left->getHeight() ) );
    painter.drawRect( context.layoutUnitToPixelX( myPos.x()+right->getX() ),
                      context.layoutUnitToPixelY( myPos.y()+right->getY() ),
                      context.layoutUnitToPixelX( right->getWidth() ),
                      context.layoutUnitToPixelY( right->getHeight() ) );
#endif
}


/**
 * Appends our attributes to the dom element.
 */
void BracketElement::writeDom(QDomElement element)
{
    SingleContentElement::writeDom(element);
    element.setAttribute("LEFT", leftType);
    element.setAttribute("RIGHT", rightType);
}

/**
 * Reads our attributes from the element.
 * Returns false if it failed.
 */
bool BracketElement::readAttributesFromDom(QDomElement element)
{
    if (!BasicElement::readAttributesFromDom(element)) {
        return false;
    }
    QString leftStr = element.attribute("LEFT");
    if(!leftStr.isNull()) {
        leftType = static_cast<SymbolType>(leftStr.toInt());
    }
    QString rightStr = element.attribute("RIGHT");
    if(!rightStr.isNull()) {
        rightType = static_cast<SymbolType>(rightStr.toInt());
    }
    return true;
}

/**
 * Reads our attributes from the MathML element.
 * Returns false if it failed.
 */
bool BracketElement::readAttributesFromMathMLDom(const QDomElement& element)
{
    if ( !BasicElement::readAttributesFromMathMLDom( element ) ) {
        return false;
    }

    if ( element.tagName().lower() == "mo" ) {
        m_operator = true;
        // TODO: parse attributes in section 3.2.5.2
    }
    else { // mfenced, see attributes in section 3.3.8.2
        leftType = LeftRoundBracket;
        rightType = RightRoundBracket;
        QString openStr = element.attribute( "open" ).stripWhiteSpace();
        if ( !openStr.isNull() ) {
            m_customLeft = true;
            if ( openStr == "[" )
                leftType = LeftSquareBracket;
            else if ( openStr == "]" )
                leftType = RightSquareBracket;
            else if ( openStr == "{" )
                leftType = LeftCurlyBracket;
            else if ( openStr == "}" )
                leftType = RightCurlyBracket;
            else if ( openStr == "<" )
                leftType = LeftCornerBracket;
            else if ( openStr == ">" )
                leftType = RightCornerBracket;
            else if ( openStr == "(" )
                leftType = LeftRoundBracket;
            else if ( openStr == ")" )
                leftType = RightRoundBracket;
            else if ( openStr == "/" )
                leftType = SlashBracket;
            else if ( openStr == "\\" )
                leftType = BackSlashBracket;
            else // TODO: Check for entity references
                leftType = LeftRoundBracket;
        }
        QString closeStr = element.attribute( "close" ).stripWhiteSpace();
        if ( !closeStr.isNull() ) {
            m_customRight = true;
            if ( closeStr == "[" )
                rightType = LeftSquareBracket;
            else if ( closeStr == "]" )
                rightType = RightSquareBracket;
            else if ( closeStr == "{" )
                rightType = LeftCurlyBracket;
            else if ( closeStr == "}" )
                rightType = RightCurlyBracket;
            else if ( closeStr == "<" )
                rightType = LeftCornerBracket;
            else if ( closeStr == ">" )
                rightType = RightCornerBracket;
            else if ( closeStr == "(" )
                rightType = LeftRoundBracket;
            else if ( closeStr == ")" )
                rightType = RightRoundBracket;
            else if ( closeStr == "/" )
                rightType = SlashBracket;
            else if ( closeStr == "\\" )
                rightType = BackSlashBracket;
            else // TODO: Check for entity references
                rightType = LeftRoundBracket;
        }
        m_separators = element.attribute( "separators" ).simplifyWhiteSpace();
    }
    return true;
}

/**
 * Reads our content from the MathML node. Sets the node to the next node
 * that needs to be read.
 * Returns false if it failed.
 */
int BracketElement::readContentFromMathMLDom(QDomNode& node)
{
    bool empty = false;
    int nodeCounter = 0;
    if ( m_operator ) {
        node = node.parentNode();
        QDomNode open = node;
        QDomNode parent = node.parentNode();
        if ( ! operatorType( node, true ) )
            return -1;
        int nodeNum = searchOperator( node );
        if ( nodeNum == -1 ) // Closing bracket not found
            return -1;
        if ( nodeNum == 0 ) { // Empty content
            empty = true;
        }
        else if ( nodeNum == 1 ) {
            do {
                node = node.nextSibling();
                nodeCounter++;
            } while ( ! node.isElement() );
        }
        else { // More than two elements inside, infer a mrow
            nodeCounter += nodeNum;
            kdWarning() << "NodeNum: " << nodeNum << endl;
            QDomDocument doc = node.ownerDocument();
            QDomElement de = doc.createElement( "mrow" );
            int i = 0;
            do {
                QDomNode n = node.nextSibling();
                de.appendChild( node.toElement() );
                node = n;
            } while ( ++i < nodeNum );
            parent.insertAfter( de, open );
            node = de;
            kdWarning() << doc.toString() << endl;
        }
    }
    else {
        // if it's a mfence tag, we need to convert to equivalent expanded form.
        // See section 3.3.8
        while ( ! node.isNull() && ! node.isElement() )
            node = node.nextSibling();
        QDomNode next = node.nextSibling();
        while ( ! next.isNull() && ! next.isElement() )
            next = next.nextSibling();
        if ( ! next.isNull()) {
            QDomDocument doc = node.ownerDocument();
            QDomNode parent = node.parentNode();
            QString ns = parent.prefix();
            QDomElement de = doc.createElementNS( ns, "mrow" );
            uint pos = 0;
            while ( ! node.isNull() ) {
                QDomNode no = node.nextSibling();
                while ( ! no.isNull() && ! no.isElement() )
                    no = no.nextSibling();
                de.appendChild( node.toElement() );
                if ( ! no.isNull() && ( m_separators.isNull() || ! m_separators.isEmpty() ) ) {
                    QDomElement sep = doc.createElementNS( ns, "mo" );
                    de.appendChild( sep );
                    if ( m_separators.isNull() ) {
                        sep.appendChild( doc.createTextNode( "," ) );
                    }
                    else {
                        if ( m_separators.at( pos ).isSpace() ) {
                            pos++;
                        }
                        sep.appendChild( doc.createTextNode( QString ( m_separators.at( pos ) ) ) );
                    }
                    if ( pos < m_separators.length() - 1 ) {
                        pos++;
                    }
                }
                node = no;
            }
            parent.appendChild( de );
            node = parent.firstChild();
            while ( ! node.isElement() )
                node = node.nextSibling();
        }
    }
    if ( ! empty ) {
        int contentNumber = inherited::readContentFromMathMLDom( node );
        if ( contentNumber == -1 )
            return -1;
        nodeCounter += contentNumber;
        for (int i = 0; i < contentNumber; i++ ) {
            if ( node.isNull() ) {
                return -1;
            }
            node = node.nextSibling();
        }
    }
    if ( m_operator ) {
        int operatorNumber = operatorType( node, false );
        if ( operatorNumber == -1 ) {
            return -1;
        }
        nodeCounter += operatorNumber;
    }
    kdDebug( DEBUGID ) << "Number of bracket nodes: " << nodeCounter << endl;
    return nodeCounter;
}

QString BracketElement::toLatex()
{
    QString ls,rs,cs;
    cs=getContent()->toLatex();
    ls="\\left"+latexString(leftType) + " ";
    rs=" \\right"+latexString(rightType);

    return ls+cs+rs;
}

QString BracketElement::latexString(char type)
{
    switch (type) {
	case ']':
	    return "]";
	case '[':
	    return "[";
	case '{':
	    return "\\{";
	case '}':
	    return "\\}";
	case '(':
	    return "(";
	case ')':
	    return ")";
	case '|':
	    return "|";
        case '<':
            return "\\langle";
        case '>':
            return "\\rangle";
        case '/':
            return "/";
        case '\\':
            return "\\backslash";
    }
    return ".";
}

QString BracketElement::formulaString()
{
    return "(" + getContent()->formulaString() + ")";
}

int BracketElement::operatorType( QDomNode& node, bool open )
{
    int counter = 1;
    SymbolType* type = open ? &leftType : &rightType;
    while ( ! node.isNull() && ! node.isElement() ) {
        node = node.nextSibling();
        counter++;
    }
    if ( node.isElement() ) {
        QDomElement e = node.toElement();
        QDomNode child = e.firstChild();
        if ( child.isEntityReference() ) {
            kdWarning() << "Entity Reference\n";
            QString name = node.nodeName();
            // TODO: To fully support these, SymbolType has to be extended,
            //       and better Unicode support is a must
            // CloseCurlyDoubleQuote 0x201D
            // CloseCurlyQoute       0x2019
            // LeftCeiling           0x2308
            // LeftDoubleBracket     0x301A
            // LeftFloor             0x230A
            // OpenCurlyDoubleQuote  0x201C
            // OpenCurlyQuote        0x2018
            // RightCeiling          0x2309
            // RightDoubleBracket    0x301B
            // RightFloor            0x230B
            if ( name == "LeftAngleBracket" ) {
                *type = LeftCornerBracket;
            }
            else if ( name == "RightAngleBracket" ) {
                *type = RightCornerBracket; 
            }
            else {
                if ( open ) {
                    *type = LeftRoundBracket;
                }
                else
                    *type = RightRoundBracket;
            }
        }
        else {
            QString s =  e.text();
            if ( s.isNull() )
                return -1;
            *type = static_cast<SymbolType>( QString::number( s.at( 0 ).latin1() ).toInt() );
        }
    }
    else {
        return -1;
    }
    return counter;
}

int BracketElement::searchOperator( const QDomNode& node )
{
    QDomNode n = node;
    for ( int i = -2; ! n.isNull(); n = n.nextSibling() ) {
        if ( n.isElement() ) {
            i++;
            QDomElement e = n.toElement();
            if ( e.tagName().lower() == "mo" ) {
                // Try to guess looking at attributes
                QString form = e.attribute( "form" );
                QString f;
                if ( ! form.isNull() ) {
                    f = form.stripWhiteSpace().lower();
                }
                QString fence = e.attribute( "fence" );
                if ( ! fence.isNull() ) {
                    if ( fence.stripWhiteSpace().lower() == "false" ) {
                        continue;
                    }
                    if ( ! f.isNull() ) {
                        if ( f == "postfix" ) {
                            return i;
                        }
                        else {
                            continue;
                        }
                    }
                }
                
                // Guess looking at contents
                QDomNode child = e.firstChild();
                QString name;
                if ( child.isText() )
                    name = child.toText().data().stripWhiteSpace();
                else if ( child.isEntityReference() )
                    name = child.nodeName();
                else 
                    continue;
                if ( name == ")"
                     || name == "]"
                     || name == "}"
                     || name == "CloseCurlyDoubleQuote"
                     || name == "CloseCurlyQuote"
                     || name == "RightAngleBracket"
                     || name == "RightCeiling"
                     || name == "RightDoubleBracket"
                     || name == "RightFloor" ) {
                    if ( f.isNull() || f == "postfix" )
                        return i;
                }
                if ( name == "("
                     || name == "["
                     || name == "{"
                     || name == "LeftAngleBracket"
                     || name == "LeftCeiling"
                     || name == "LeftDoubleBracket"
                     || name == "LeftFloor"
                     || name == "OpenCurlyQuote" ) {
                    if ( ! f.isNull() && f == "postfix" )
                        return i;
                }
            }
        }
    }
    return -1;
}


void BracketElement::writeMathMLAttributes( QDomElement& element ) const
{
    if ( left->getType() != LeftRoundBracket ||
         right->getType() != RightRoundBracket )
    {
        element.setAttribute( "open",  QString( QChar( leftType ) ) );
        element.setAttribute( "close", QString( QChar( rightType ) ) );
    }
    if ( ! m_separators.isNull() ) {
        element.setAttribute( "separators", m_separators );
    }
}

OverlineElement::OverlineElement( BasicElement* parent )
    : SingleContentElement( parent )
{
}

OverlineElement::~OverlineElement()
{
}

OverlineElement::OverlineElement( const OverlineElement& other )
    : SingleContentElement( other )
{
}


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


void OverlineElement::entered( SequenceElement* /*child*/ )
{
    formula()->tell( i18n( "Overline" ) );
}


void OverlineElement::calcSizes( const ContextStyle& context,
                                 ContextStyle::TextStyle tstyle,
                                 ContextStyle::IndexStyle istyle,
                                 StyleAttributes& style )
{
    SequenceElement* content = getContent();
    content->calcSizes(context, tstyle,
                       context.convertIndexStyleLower(istyle), style );

    //luPixel distX = context.ptToPixelX( context.getThinSpace( tstyle, style.sizeFactor() ) );
    luPixel distY = context.ptToPixelY( context.getThinSpace( tstyle, style.sizeFactor() ) );
    //luPixel unit = (content->getHeight() + distY)/ 3;

    setWidth( content->getWidth() );
    setHeight( content->getHeight() + distY );

    content->setX( 0 );
    content->setY( distY );
    setBaseline(content->getBaseline() + content->getY());
}

void OverlineElement::draw( QPainter& 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;

    SequenceElement* content = getContent();
    content->draw( painter, r, context, tstyle,
                   context.convertIndexStyleLower( istyle ), style, myPos );

    luPixel x = myPos.x();
    luPixel y = myPos.y();
    //int distX = context.getDistanceX(tstyle);
    double factor = style.sizeFactor();
    luPixel distY = context.ptToPixelY( context.getThinSpace( tstyle, factor ) );
    //luPixel unit = (content->getHeight() + distY)/ 3;

    painter.setPen( QPen( context.getDefaultColor(),
                          context.layoutUnitToPixelY( context.getLineWidth( factor ) ) ) );

    painter.drawLine( context.layoutUnitToPixelX( x ),
                      context.layoutUnitToPixelY( y+distY/3 ),
                      context.layoutUnitToPixelX( x+content->getWidth() ),
                      context.layoutUnitToPixelY( y+distY/3 ) );
}


QString OverlineElement::toLatex()
{
    return "\\overline{" + getContent()->toLatex() + "}";
}

QString OverlineElement::formulaString()
{
    return getContent()->formulaString();
}

void OverlineElement::writeMathML( QDomDocument& doc, QDomNode& parent, bool oasisFormat ) const
{
    QDomElement de = doc.createElement( oasisFormat ? "math:mover" : "mover" );
    SingleContentElement::writeMathML( doc, de, oasisFormat );
    QDomElement op = doc.createElement( oasisFormat ? "math:mo" : "mo" );
    // is this the right entity? Mozilla renders it correctly.
    op.appendChild( doc.createEntityReference( "OverBar" ) );
    de.appendChild( op );
    parent.appendChild( de );
}


UnderlineElement::UnderlineElement( BasicElement* parent )
    : SingleContentElement( parent )
{
}

UnderlineElement::~UnderlineElement()
{
}


UnderlineElement::UnderlineElement( const UnderlineElement& other )
    : SingleContentElement( other )
{
}


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


void UnderlineElement::entered( SequenceElement* /*child*/ )
{
    formula()->tell( i18n( "Underline" ) );
}


void UnderlineElement::calcSizes( const ContextStyle& context,
                                  ContextStyle::TextStyle tstyle,
                                  ContextStyle::IndexStyle istyle,
                                  StyleAttributes& style )
{
    SequenceElement* content = getContent();
    double factor = style.sizeFactor();
    content->calcSizes(context, tstyle,
                       context.convertIndexStyleLower(istyle), style );

    //luPixel distX = context.ptToPixelX( context.getThinSpace( tstyle ) );
    luPixel distY = context.ptToPixelY( context.getThinSpace( tstyle, factor ) );
    //luPixel unit = (content->getHeight() + distY)/ 3;

    setWidth( content->getWidth() );
    setHeight( content->getHeight() + distY );

    content->setX( 0 );
    content->setY( 0 );
    setBaseline(content->getBaseline() + content->getY());
}

void UnderlineElement::draw( QPainter& 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;

    SequenceElement* content = getContent();
    content->draw( painter, r, context, tstyle,
                   context.convertIndexStyleLower( istyle ), style, myPos );

    luPixel x = myPos.x();
    luPixel y = myPos.y();
    //int distX = context.getDistanceX(tstyle);
    //luPixel distY = context.ptToPixelY( context.getThinSpace( tstyle ) );
    //luPixel unit = (content->getHeight() + distY)/ 3;

    double factor = style.sizeFactor();
    painter.setPen( QPen( context.getDefaultColor(),
                          context.layoutUnitToPixelY( context.getLineWidth( factor ) ) ) );

    painter.drawLine( context.layoutUnitToPixelX( x ),
                      context.layoutUnitToPixelY( y+getHeight()-context.getLineWidth( factor ) ),
                      context.layoutUnitToPixelX( x+content->getWidth() ),
                      context.layoutUnitToPixelY( y+getHeight()-context.getLineWidth( factor ) ) );
}


QString UnderlineElement::toLatex()
{
    return "\\underline{" + getContent()->toLatex() + "}";
}

QString UnderlineElement::formulaString()
{
    return getContent()->formulaString();
}

void UnderlineElement::writeMathML( QDomDocument& doc, QDomNode& parent, bool oasisFormat ) const
{
    QDomElement de = doc.createElement( oasisFormat ? "math:munder" : "munder" );
    SingleContentElement::writeMathML( doc, de, oasisFormat );
    QDomElement op = doc.createElement( oasisFormat ? "math:mo" : "mo" );
    // is this the right entity? Mozilla renders it correctly.
    op.appendChild( doc.createEntityReference( "UnderBar" ) );
    de.appendChild( op );
    parent.appendChild( de );
}

KFORMULA_NAMESPACE_END
