Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/starmath/source/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 69 kB image not shown  

Quelle  visitors.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */


#include <rtl/math.hxx>
#include <sal/log.hxx>
#include <tools/gen.hxx>
#include <vcl/lineinfo.hxx>
#include <visitors.hxx>
#include "tmpdevice.hxx"
#include <cursor.hxx>

#include <starmathdatabase.hxx>

// SmDefaultingVisitor

void SmDefaultingVisitor::Visit( SmTableNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmBraceNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmBracebodyNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmOperNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmAlignNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmAttributeNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmFontNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmUnHorNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmBinHorNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmBinVerNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmBinDiagonalNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmSubSupNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmMatrixNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmPlaceNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmTextNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmSpecialNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmGlyphSpecialNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmMathSymbolNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmBlankNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmErrorNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmLineNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmExpressionNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmPolyLineNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmRootNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmRootSymbolNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmRectangleNode* pNode )
{
    DefaultVisit( pNode );
}

void SmDefaultingVisitor::Visit( SmVerticalBraceNode* pNode )
{
    DefaultVisit( pNode );
}

// SmCaretLinesVisitor

SmCaretLinesVisitor::SmCaretLinesVisitor(OutputDevice& rDevice, SmCaretPos position, Point offset)
    : mrDev(rDevice)
    , maPos(position)
    , maOffset(offset)
{
}

void SmCaretLinesVisitor::DoIt()
{
    SAL_WARN_IF(!maPos.IsValid(), "starmath""Cannot draw invalid position!");
    if (!maPos.IsValid())
        return;

    //Save device state
    mrDev.Push( vcl::PushFlags::FONT | vcl::PushFlags::MAPMODE | vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR | vcl::PushFlags::TEXTCOLOR );

    maPos.pSelectedNode->Accept( this );
    //Restore device state
    mrDev.Pop( );
}

void SmCaretLinesVisitor::Visit( SmTextNode* pNode )
{
    tools::Long i = maPos.nIndex;

    mrDev.SetFont( pNode->GetFont( ) );

    //Find the line
    SmNode* pLine = SmCursor::FindTopMostNodeInLine( pNode );

    //Find coordinates
    tools::Long left = pNode->GetLeft( ) + mrDev.GetTextWidth( pNode->GetText( ), 0, i ) + maOffset.X( );
    tools::Long top = pLine->GetTop( ) + maOffset.Y( );
    tools::Long height = pLine->GetHeight( );
    tools::Long left_line = pLine->GetLeft( ) + maOffset.X( );
    tools::Long right_line = pLine->GetRight( ) + maOffset.X( );

    // Vertical line
    ProcessCaretLine({ left, top }, { left, top + height });

    // Underline
    ProcessUnderline({ left_line, top + height }, { right_line, top + height });
}

void SmCaretLinesVisitor::DefaultVisit( SmNode* pNode )
{
    //Find the line
    SmNode* pLine = SmCursor::FindTopMostNodeInLine( pNode );

    //Find coordinates
    tools::Long left = pNode->GetLeft( ) + maOffset.X( ) + ( maPos.nIndex == 1 ? pNode->GetWidth( ) : 0 );
    tools::Long top = pLine->GetTop( ) + maOffset.Y( );
    tools::Long height = pLine->GetHeight( );
    tools::Long left_line = pLine->GetLeft( ) + maOffset.X( );
    tools::Long right_line = pLine->GetRight( ) + maOffset.X( );

    // Vertical line
    ProcessCaretLine({ left, top }, { left, top + height });

    // Underline
    ProcessUnderline({ left_line, top + height }, { right_line, top + height });
}

// SmCaretRectanglesVisitor

SmCaretRectanglesVisitor::SmCaretRectanglesVisitor(OutputDevice& rDevice, SmCaretPos position)
    : SmCaretLinesVisitor(rDevice, position, {})
{
    DoIt();
}

void SmCaretRectanglesVisitor::ProcessCaretLine(Point from, Point to) { maCaret = { from, to }; }
void SmCaretRectanglesVisitor::ProcessUnderline(Point /*from*/, Point /*to*/) {} // No underline

// SmCaretDrawingVisitor

SmCaretDrawingVisitor::SmCaretDrawingVisitor( OutputDevice& rDevice,
                                             SmCaretPos position,
                                             Point offset,
                                             bool caretVisible )
    : SmCaretLinesVisitor(rDevice, position, offset)
    , mbCaretVisible( caretVisible )
{
    DoIt();
}

void SmCaretDrawingVisitor::ProcessCaretLine(Point from, Point to)
{
    if ( mbCaretVisible ) {
        //Set color
        getDev().SetLineColor(COL_BLACK);
        //Draw vertical line
        getDev().DrawLine(from, to);
    }
}

void SmCaretDrawingVisitor::ProcessUnderline(Point from, Point to)
{
    //Set color
    getDev().SetLineColor(COL_BLACK);
    //Underline the line
    getDev().DrawLine(from, to);
}

// SmCaretPos2LineVisitor

void SmCaretPos2LineVisitor::Visit( SmTextNode* pNode )
{
    //Save device state
    mpDev->Push( vcl::PushFlags::FONT | vcl::PushFlags::TEXTCOLOR );

    tools::Long i = maPos.nIndex;

    mpDev->SetFont( pNode->GetFont( ) );

    //Find coordinates
    tools::Long left = pNode->GetLeft( ) + mpDev->GetTextWidth( pNode->GetText( ), 0, i );
    tools::Long top = pNode->GetTop( );
    tools::Long height = pNode->GetHeight( );

    maLine = SmCaretLine( left, top, height );

    //Restore device state
    mpDev->Pop( );
}

void SmCaretPos2LineVisitor::DefaultVisit( SmNode* pNode )
{
    //Vertical line ( code from SmCaretDrawingVisitor )
    Point p1 = pNode->GetTopLeft( );
    if( maPos.nIndex == 1 )
        p1.Move( pNode->GetWidth( ), 0 );

    maLine = SmCaretLine( p1.X( ), p1.Y( ), pNode->GetHeight( ) );
}


// SmDrawingVisitor

void SmDrawingVisitor::Visit( SmTableNode* pNode )
{
    DrawChildren( pNode );
}

void SmDrawingVisitor::Visit( SmBraceNode* pNode )
{
    DrawChildren( pNode );
}

void SmDrawingVisitor::Visit( SmBracebodyNode* pNode )
{
    DrawChildren( pNode );
}

void SmDrawingVisitor::Visit( SmOperNode* pNode )
{
    DrawChildren( pNode );
}

void SmDrawingVisitor::Visit( SmAlignNode* pNode )
{
    DrawChildren( pNode );
}

void SmDrawingVisitor::Visit( SmAttributeNode* pNode )
{
    DrawChildren( pNode );
}

void SmDrawingVisitor::Visit( SmFontNode* pNode )
{
    DrawChildren( pNode );
}

void SmDrawingVisitor::Visit( SmUnHorNode* pNode )
{
    DrawChildren( pNode );
}

void SmDrawingVisitor::Visit( SmBinHorNode* pNode )
{
    DrawChildren( pNode );
}

void SmDrawingVisitor::Visit( SmBinVerNode* pNode )
{
    DrawChildren( pNode );
}

void SmDrawingVisitor::Visit( SmBinDiagonalNode* pNode )
{
    DrawChildren( pNode );
}

void SmDrawingVisitor::Visit( SmSubSupNode* pNode )
{
    DrawChildren( pNode );
}

void SmDrawingVisitor::Visit( SmMatrixNode* pNode )
{
    DrawChildren( pNode );
}

void SmDrawingVisitor::Visit( SmPlaceNode* pNode )
{
    DrawSpecialNode( pNode );
}

void SmDrawingVisitor::Visit( SmTextNode* pNode )
{
    DrawTextNode( pNode );
}

void SmDrawingVisitor::Visit( SmSpecialNode* pNode )
{
    DrawSpecialNode( pNode );
}

void SmDrawingVisitor::Visit( SmGlyphSpecialNode* pNode )
{
    DrawSpecialNode( pNode );
}

void SmDrawingVisitor::Visit( SmMathSymbolNode* pNode )
{
    DrawSpecialNode( pNode );
}

void SmDrawingVisitor::Visit( SmBlankNode* )
{
}

void SmDrawingVisitor::Visit( SmErrorNode* pNode )
{
    DrawSpecialNode( pNode );
}

void SmDrawingVisitor::Visit( SmLineNode* pNode )
{
    DrawChildren( pNode );
}

void SmDrawingVisitor::Visit( SmExpressionNode* pNode )
{
    DrawChildren( pNode );
}

void SmDrawingVisitor::Visit( SmRootNode* pNode )
{
    DrawChildren( pNode );
}

void SmDrawingVisitor::Visit( SmVerticalBraceNode* pNode )
{
    DrawChildren( pNode );
}

void SmDrawingVisitor::Visit( SmRootSymbolNode* pNode )
{
    if ( pNode->IsPhantom( ) )
        return;

    // draw root-sign itself
    DrawSpecialNode( pNode );

    SmTmpDevice aTmpDev( mrDev, true );
    aTmpDev.SetFillColor( pNode->GetFont( ).GetColor( ) );
    mrDev.SetLineColor( );
    aTmpDev.SetFont( pNode->GetFont( ) );

    // since the width is always unscaled it corresponds to the _original_
    // _unscaled_ font height to be used, we use that to calculate the
    // bar height. Thus it is independent of the arguments height.
    // ( see display of sqrt QQQ versus sqrt stack{Q#Q#Q#Q} )
    tools::Long nBarHeight = pNode->GetWidth( ) * 7 / 100;
    tools::Long nBarWidth = pNode->GetBodyWidth( ) + pNode->GetBorderWidth( );
    Point aBarOffset( pNode->GetWidth( ), +pNode->GetBorderWidth( ) );
    Point aBarPos( maPosition + aBarOffset );

    tools::Rectangle  aBar( aBarPos, Size( nBarWidth, nBarHeight ) );

    if (mrFormat.IsRightToLeft() && mrDev.GetOutDevType() != OUTDEV_WINDOW)
        mrDev.ReMirror(aBar);

    mrDev.DrawRect( aBar );
}

void SmDrawingVisitor::Visit( SmPolyLineNode* pNode )
{
    if ( pNode->IsPhantom( ) )
        return;

    tools::Long nBorderwidth = pNode->GetFont( ).GetBorderWidth( );

    LineInfo  aInfo;
    aInfo.SetWidth( pNode->GetWidth( ) - 2 * nBorderwidth );

    Point aOffset ( Point( ) - pNode->GetPolygon( ).GetBoundRect( ).TopLeft( )
                   + Point( nBorderwidth, nBorderwidth ) ),
          aPos ( maPosition + aOffset );

    if (mrFormat.IsRightToLeft() && mrDev.GetOutDevType() != OUTDEV_WINDOW)
        mrDev.ReMirror(aPos);

    pNode->GetPolygon( ).Move( aPos.X( ), aPos.Y( ) );    //Works because Polygon wraps a pointer

    SmTmpDevice aTmpDev ( mrDev, false );
    aTmpDev.SetLineColor( pNode->GetFont( ).GetColor( ) );

    mrDev.DrawPolyLine( pNode->GetPolygon( ), aInfo );
}

void SmDrawingVisitor::Visit( SmRectangleNode* pNode )
{
    if ( pNode->IsPhantom( ) )
        return;

    SmTmpDevice aTmpDev ( mrDev, false );
    aTmpDev.SetFillColor( pNode->GetFont( ).GetColor( ) );
    mrDev.SetLineColor( );
    aTmpDev.SetFont( pNode->GetFont( ) );

    tools::Long  nTmpBorderWidth = pNode->GetFont( ).GetBorderWidth( );

    // get rectangle and remove borderspace
    tools::Rectangle  aTmp ( pNode->AsRectangle( ) + maPosition - pNode->GetTopLeft( ) );
    aTmp.AdjustLeft(nTmpBorderWidth );
    aTmp.AdjustRight( -sal_Int32(nTmpBorderWidth) );
    aTmp.AdjustTop(nTmpBorderWidth );
    aTmp.AdjustBottom( -sal_Int32(nTmpBorderWidth) );

    SAL_WARN_IF( aTmp.IsEmpty(), "starmath""Empty rectangle" );

    if (mrFormat.IsRightToLeft() && mrDev.GetOutDevType() != OUTDEV_WINDOW)
        mrDev.ReMirror(aTmp);

    mrDev.DrawRect( aTmp );
}

void SmDrawingVisitor::DrawTextNode( SmTextNode* pNode )
{
    if ( pNode->IsPhantom() || pNode->GetText().isEmpty() || pNode->GetText()[0] == '\0' )
        return;

    SmTmpDevice aTmpDev ( mrDev, false );
    aTmpDev.SetFont( pNode->GetFont( ) );

    Point  aPos ( maPosition );
    aPos.AdjustY(pNode->GetBaselineOffset( ) );

    if (mrFormat.IsRightToLeft() && mrDev.GetOutDevType() != OUTDEV_WINDOW)
        mrDev.ReMirror(aPos);

    mrDev.DrawStretchText( aPos, pNode->GetWidth( ), pNode->GetText( ) );
}

void SmDrawingVisitor::DrawSpecialNode( SmSpecialNode* pNode )
{
    //! since this chars might come from any font, that we may not have
    //! set to ALIGN_BASELINE yet, we do it now.
    pNode->GetFont( ).SetAlignment( ALIGN_BASELINE );

    DrawTextNode( pNode );
}

void SmDrawingVisitor::DrawChildren( SmStructureNode* pNode )
{
    if ( pNode->IsPhantom( ) )
        return;

    Point rPosition = maPosition;

    forauto pChild : *pNode )
    {
        if(!pChild)
            continue;
        Point  aOffset ( pChild->GetTopLeft( ) - pNode->GetTopLeft( ) );
        maPosition = rPosition + aOffset;
        pChild->Accept( this );
    }
}

// SmSetSelectionVisitor

SmSetSelectionVisitor::SmSetSelectionVisitor( SmCaretPos startPos, SmCaretPos endPos, SmNode* pTree)
    : maStartPos(startPos)
    , maEndPos(endPos)
    , mbSelecting(false)
{
    //Assume that pTree is a SmTableNode
    SAL_WARN_IF(pTree->GetType() != SmNodeType::Table, "starmath""pTree should be a SmTableNode!");
    //Visit root node, this is special as this node cannot be selected, but its children can!
    if(pTree->GetType() == SmNodeType::Table){
        //Change state if maStartPos is in front of this node
        if( maStartPos.pSelectedNode == pTree && maStartPos.nIndex == 0 )
            mbSelecting = !mbSelecting;
        //Change state if maEndPos is in front of this node
        if( maEndPos.pSelectedNode == pTree && maEndPos.nIndex == 0 )
            mbSelecting = !mbSelecting;
        SAL_WARN_IF(mbSelecting, "starmath""Caret positions needed to set mbSelecting about, shouldn't be possible!");

        //Visit lines
        forauto pChild : *static_cast<SmStructureNode*>(pTree) )
        {
            if(!pChild)
                continue;
            pChild->Accept( this );
            //If we started a selection in this line and it haven't ended, we do that now!
            if(mbSelecting) {
                mbSelecting = false;
                SetSelectedOnAll(pChild);
                //Set maStartPos and maEndPos to invalid positions, this ensures that an unused
                //start or end (because we forced end above), doesn't start a new selection.
                maStartPos = maEndPos = SmCaretPos();
            }
        }
        //Check if pTree isn't selected
        SAL_WARN_IF(pTree->IsSelected(), "starmath""pTree should never be selected!");
        //Discard the selection if there's a bug (it's better than crashing)
        if(pTree->IsSelected())
            SetSelectedOnAll(pTree, false);
    }else //This shouldn't happen, but I don't see any reason to die if it does
        pTree->Accept(this);
}

void SmSetSelectionVisitor::SetSelectedOnAll( SmNode* pSubTree, bool IsSelected ) {
    pSubTree->SetSelected( IsSelected );

    if(pSubTree->GetNumSubNodes() == 0)
        return;
    //Quick BFS to set all selections
    forauto pChild : *static_cast<SmStructureNode*>(pSubTree) )
    {
        if(!pChild)
            continue;
        SetSelectedOnAll( pChild, IsSelected );
    }
}

void SmSetSelectionVisitor::DefaultVisit( SmNode* pNode ) {
    //Change state if maStartPos is in front of this node
    if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 0 )
        mbSelecting = !mbSelecting;
    //Change state if maEndPos is in front of this node
    if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 0 )
        mbSelecting = !mbSelecting;

    //Cache current state
    bool WasSelecting = mbSelecting;
    bool ChangedState = false;

    //Set selected
    pNode->SetSelected( mbSelecting );

    //Visit children
    if(pNode->GetNumSubNodes() > 0)
    {
        forauto pChild : *static_cast<SmStructureNode*>(pNode) )
        {
            if(!pChild)
                continue;
            pChild->Accept( this );
            ChangedState = ( WasSelecting != mbSelecting ) || ChangedState;
        }
    }

    //If state changed
    if( ChangedState )
    {
        //Select this node and all of its children
        //(Make exception for SmBracebodyNode)
        if( pNode->GetType() != SmNodeType::Bracebody ||
            !pNode->GetParent() ||
            pNode->GetParent()->GetType() != SmNodeType::Brace )
            SetSelectedOnAll( pNode );
        else
            SetSelectedOnAll( pNode->GetParent() );
        /* If the equation is:      sqrt{2 + 4} + 5
         * And the selection is:    sqrt{2 + [4} +] 5
         *      Where [ denotes maStartPos and ] denotes maEndPos
         * Then the sqrt node should be selected, so that the
         * effective selection is:  [sqrt{2 + 4} +] 5
         * The same is the case if we swap maStartPos and maEndPos.
         */

    }

    //Change state if maStartPos is after this node
    if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 1 )
    {
        mbSelecting = !mbSelecting;
    }
    //Change state if maEndPos is after of this node
    if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 1 )
    {
        mbSelecting = !mbSelecting;
    }
}

void SmSetSelectionVisitor::VisitCompositionNode( SmStructureNode* pNode )
{
    //Change state if maStartPos is in front of this node
    if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 0 )
        mbSelecting = !mbSelecting;
    //Change state if maEndPos is in front of this node
    if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 0 )
        mbSelecting = !mbSelecting;

    //Cache current state
    bool WasSelecting = mbSelecting;

    //Visit children
    forauto pChild : *pNode )
    {
        if(!pChild)
            continue;
        pChild->Accept( this );
    }

    //Set selected, if everything was selected
    pNode->SetSelected( WasSelecting && mbSelecting );

    //Change state if maStartPos is after this node
    if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 1 )
        mbSelecting = !mbSelecting;
    //Change state if maEndPos is after of this node
    if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 1 )
        mbSelecting = !mbSelecting;
}

void SmSetSelectionVisitor::Visit( SmTextNode* pNode ) {
    tools::Long    i1 = -1,
            i2 = -1;
    if( maStartPos.pSelectedNode == pNode )
        i1 = maStartPos.nIndex;
    if( maEndPos.pSelectedNode == pNode )
        i2 = maEndPos.nIndex;

    tools::Long start, end;
    pNode->SetSelected(true);
    if( i1 != -1 && i2 != -1 ) {
        start = std::min(i1, i2);
        end   = std::max(i1, i2);
    } else if( mbSelecting && i1 != -1 ) {
        start = 0;
        end = i1;
        mbSelecting = false;
    } else if( mbSelecting && i2 != -1 ) {
        start = 0;
        end = i2;
        mbSelecting = false;
    } else if( !mbSelecting && i1 != -1 ) {
        start = i1;
        end = pNode->GetText().getLength();
        mbSelecting = true;
    } else if( !mbSelecting && i2 != -1 ) {
        start = i2;
        end = pNode->GetText().getLength();
        mbSelecting = true;
    } else if( mbSelecting ) {
        start = 0;
        end = pNode->GetText().getLength();
    } else {
        pNode->SetSelected( false );
        start = 0;
        end = 0;
    }
    pNode->SetSelected( start != end );
    pNode->SetSelectionStart( start );
    pNode->SetSelectionEnd( end );
}

void SmSetSelectionVisitor::Visit( SmExpressionNode* pNode ) {
    VisitCompositionNode( pNode );
}

void SmSetSelectionVisitor::Visit( SmLineNode* pNode ) {
    VisitCompositionNode( pNode );
}

void SmSetSelectionVisitor::Visit( SmAlignNode* pNode ) {
    VisitCompositionNode( pNode );
}

void SmSetSelectionVisitor::Visit( SmBinHorNode* pNode ) {
    VisitCompositionNode( pNode );
}

void SmSetSelectionVisitor::Visit( SmUnHorNode* pNode ) {
    VisitCompositionNode( pNode );
}

void SmSetSelectionVisitor::Visit( SmFontNode* pNode ) {
    VisitCompositionNode( pNode );
}

// SmCaretPosGraphBuildingVisitor

SmCaretPosGraphBuildingVisitor::SmCaretPosGraphBuildingVisitor( SmNode* pRootNode )
    : mpRightMost(nullptr)
    , mpGraph(new SmCaretPosGraph)
{
    //pRootNode should always be a table
    SAL_WARN_IF( pRootNode->GetType( ) != SmNodeType::Table, "starmath""pRootNode must be a table node");
    //Handle the special case where SmNodeType::Table is used a rootnode
    if( pRootNode->GetType( ) == SmNodeType::Table ){
        //Children are SmLineNodes
        //Or so I thought... Apparently, the children can be instances of SmExpression
        //especially if there's an error in the formula... So here we go, a simple work around.
        forauto pChild : *static_cast<SmStructureNode*>(pRootNode) )
        {
            if(!pChild)
                continue;
            mpRightMost = mpGraph->Add( SmCaretPos( pChild, 0 ) );
            pChild->Accept( this );
        }
    }else
        pRootNode->Accept(this);
}

SmCaretPosGraphBuildingVisitor::~SmCaretPosGraphBuildingVisitor()
{
}

void SmCaretPosGraphBuildingVisitor::Visit( SmLineNode* pNode ){
    forauto pChild : *pNode )
    {
        if(!pChild)
            continue;
        pChild->Accept( this );
    }
}

/** Build SmCaretPosGraph for SmTableNode
 * This method covers cases where SmTableNode is used in a binom or stack,
 * the special case where it is used as root node for the entire formula is
 * handled in the constructor.
 */

void SmCaretPosGraphBuildingVisitor::Visit( SmTableNode* pNode ){
    SmCaretPosGraphEntry *left  = mpRightMost,
                         *right = mpGraph->Add( SmCaretPos( pNode, 1) );
    bool bIsFirst = true;
    forauto pChild : *pNode )
    {
        if(!pChild)
            continue;
        mpRightMost = mpGraph->Add( SmCaretPos( pChild, 0 ), left);
        if(bIsFirst)
            left->SetRight(mpRightMost);
        pChild->Accept( this );
        mpRightMost->SetRight(right);
        if(bIsFirst)
            right->SetLeft(mpRightMost);
        bIsFirst = false;
    }
    mpRightMost = right;
}

/** Build SmCaretPosGraph for SmSubSupNode
 *
 * The child positions in a SubSupNode, where H is the body:
 * \code
 *      CSUP
 *
 * LSUP H  H RSUP
 *      H  H
 *      HHHH
 *      H  H
 * LSUB H  H RSUB
 *
 *      CSUB
 * \endcode
 *
 * Graph over these, where "left" is before the SmSubSupNode and "right" is after:
 * \dot
 *  digraph Graph{
 *      left -> H;
 *      H -> right;
 *      LSUP -> H;
 *      LSUB -> H;
 *      CSUP -> right;
 *      CSUB -> right;
 *      RSUP -> right;
 *      RSUB -> right;
 *  };
 * \enddot
 *
 */

void SmCaretPosGraphBuildingVisitor::Visit( SmSubSupNode* pNode )
{
    SmCaretPosGraphEntry *left,
                         *right,
                         *bodyLeft,
                         *bodyRight;

    assert(mpRightMost);
    left = mpRightMost;

    //Create bodyLeft
    SAL_WARN_IF( !pNode->GetBody(), "starmath""SmSubSupNode Doesn't have a body!" );
    bodyLeft = mpGraph->Add( SmCaretPos( pNode->GetBody( ), 0 ), left );
    left->SetRight( bodyLeft ); //TODO: Don't make this if LSUP or LSUB are NULL ( not sure??? )

    //Create right
    right = mpGraph->Add( SmCaretPos( pNode, 1 ) );

    //Visit the body, to get bodyRight
    mpRightMost = bodyLeft;
    pNode->GetBody( )->Accept( this );
    bodyRight = mpRightMost;
    bodyRight->SetRight( right );
    right->SetLeft( bodyRight );

    SmNode* pChild;
    for (SmSubSup const nodeType : { LSUP, LSUB, CSUP, CSUB, RSUP, RSUB })
    {
        pChild = pNode->GetSubSup(nodeType);
        if( pChild )
        {
            SmCaretPosGraphEntry *cLeft; //Child left
            cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), ((nodeType == RSUP) || (nodeType == RSUB))?bodyRight:left );

            mpRightMost = cLeft;
            pChild->Accept( this );

            mpRightMost->SetRight( ((nodeType == LSUP) || (nodeType == LSUB))?bodyLeft:right );
        }
    }

    //Set return parameters
    mpRightMost = right;
}

/** Build caret position for SmOperNode
 *
 * If first child is an SmSubSupNode we will ignore its
 * body, as this body is a SmMathSymbol, for SUM, INT or similar
 * that shouldn't be subject to modification.
 * If first child is not a SmSubSupNode, ignore it completely
 * as it is a SmMathSymbol.
 *
 * The child positions in a SmOperNode, where H is symbol, e.g. int, sum or similar:
 * \code
 *       TO
 *
 * LSUP H  H RSUP    BBB    BB  BBB  B   B
 *      H  H         B  B  B  B B  B  B B
 *      HHHH         BBB   B  B B  B   B
 *      H  H         B  B  B  B B  B   B
 * LSUB H  H RSUB    BBB    BB  BBB    B
 *
 *      FROM
 * \endcode
 * Notice, CSUP, etc. are actually grandchildren, but inorder to ignore H, these are visited
 * from here. If they are present, that is if pOper is an instance of SmSubSupNode.
 *
 * Graph over these, where "left" is before the SmOperNode and "right" is after:
 * \dot
 *  digraph Graph{
 *      left -> BODY;
 *      BODY -> right;
 *      LSUP -> BODY;
 *      LSUB -> BODY;
 *      TO   -> BODY;
 *      FROM -> BODY;
 *      RSUP -> BODY;
 *      RSUB -> BODY;
 *  };
 * \enddot
 */

void SmCaretPosGraphBuildingVisitor::Visit( SmOperNode* pNode )
{
    SmNode *pOper = pNode->GetSubNode( 0 ),
           *pBody = pNode->GetSubNode( 1 );

    SmCaretPosGraphEntry *left = mpRightMost,
                         *bodyLeft,
                         *bodyRight,
                         *right;
    //Create body left
    bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
    left->SetRight( bodyLeft );

    //Visit body, get bodyRight
    mpRightMost = bodyLeft;
    pBody->Accept( this );
    bodyRight = mpRightMost;

    //Create right
    right = mpGraph->Add( SmCaretPos( pNode, 1 ), bodyRight );
    bodyRight->SetRight( right );

    //Get subsup pNode if any
    SmSubSupNode* pSubSup = pOper->GetType( ) == SmNodeType::SubSup ? static_cast<SmSubSupNode*>(pOper) : nullptr;

    if( pSubSup ) {
        SmNode* pChild;
        for (SmSubSup const nodeType : { LSUP, LSUB, CSUP, CSUB, RSUP, RSUB })
        {
            pChild = pSubSup->GetSubSup(nodeType);
            if( pChild )
            {
            //Create position in front of pChild
                SmCaretPosGraphEntry *childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
                //Visit pChild
                mpRightMost = childLeft;
                pChild->Accept( this );
                //Set right on mpRightMost from pChild
                mpRightMost->SetRight( bodyLeft );
            }
        }
    }

    //Return right
    mpRightMost = right;
}

void SmCaretPosGraphBuildingVisitor::Visit( SmMatrixNode* pNode )
{
    SmCaretPosGraphEntry *left  = mpRightMost,
                         *right = mpGraph->Add( SmCaretPos( pNode, 1 ) );

    for (size_t i = 0;  i < pNode->GetNumRows(); ++i)
    {
        SmCaretPosGraphEntry* r = left;
        for (size_t j = 0;  j < pNode->GetNumCols(); ++j)
        {
            SmNode* pSubNode = pNode->GetSubNode( i * pNode->GetNumCols( ) + j );

            mpRightMost = mpGraph->Add( SmCaretPos( pSubNode, 0 ), r );
            if( j != 0 || ( pNode->GetNumRows() - 1U ) / 2 == i )
                r->SetRight( mpRightMost );

            pSubNode->Accept( this );

            r = mpRightMost;
        }
        mpRightMost->SetRight( right );
        if( ( pNode->GetNumRows() - 1U ) / 2 == i )
            right->SetLeft( mpRightMost );
    }

    mpRightMost = right;
}

/** Build SmCaretPosGraph for SmTextNode
 *
 * Lines in an SmTextNode:
 * \code
 * A B C
 * \endcode
 * Where A B and C are characters in the text.
 *
 * Graph over these, where "left" is before the SmTextNode and "right" is after:
 * \dot
 *  digraph Graph{
 *      left -> A;
 *      A -> B
 *      B -> right;
 *  };
 * \enddot
 * Notice that C and right is the same position here.
 */

void SmCaretPosGraphBuildingVisitor::Visit( SmTextNode* pNode )
{
    SAL_WARN_IF( pNode->GetText().isEmpty(), "starmath""Empty SmTextNode is bad" );

    OUString& aText = pNode->GetText();
    sal_Int32 i = 0;
    while (i < aText.getLength())
    {
        aText.iterateCodePoints(&i);
        SmCaretPosGraphEntry* pRight = mpRightMost;
        mpRightMost = mpGraph->Add( SmCaretPos( pNode, i ), pRight );
        pRight->SetRight( mpRightMost );
    }
}

/** Build SmCaretPosGraph for SmBinVerNode
 *
 * Lines in an SmBinVerNode:
 * \code
 *    A
 *  -----
 *    B
 * \endcode
 *
 * Graph over these, where "left" is before the SmBinVerNode and "right" is after:
 * \dot
 *  digraph Graph{
 *      left -> A;
 *      A -> right;
 *      B -> right;
 *  };
 * \enddot
 */

void SmCaretPosGraphBuildingVisitor::Visit( SmBinVerNode* pNode )
{
    //None if these children can be NULL, see SmBinVerNode::Arrange
    SmNode  *pNum   = pNode->GetSubNode( 0 ),
            *pDenom = pNode->GetSubNode( 2 );

    SmCaretPosGraphEntry *left,
                         *right,
                         *numLeft,
                         *denomLeft;

    assert(mpRightMost);
    //Set left
    left = mpRightMost;

    //Create right
    right = mpGraph->Add( SmCaretPos( pNode, 1 ) );

    //Create numLeft
    numLeft = mpGraph->Add( SmCaretPos( pNum, 0 ), left );
    left->SetRight( numLeft );

    //Visit pNum
    mpRightMost = numLeft;
    pNum->Accept( this );
    mpRightMost->SetRight( right );
    right->SetLeft( mpRightMost );

    //Create denomLeft
    denomLeft = mpGraph->Add( SmCaretPos( pDenom, 0 ), left );

    //Visit pDenom
    mpRightMost = denomLeft;
    pDenom->Accept( this );
    mpRightMost->SetRight( right );

    //Set return parameter
    mpRightMost = right;
}

/** Build SmCaretPosGraph for SmVerticalBraceNode
 *
 * Lines in an SmVerticalBraceNode:
 * \code
 *   pScript
 *  ________
 * /        \
 *   pBody
 * \endcode
 *
 */

void SmCaretPosGraphBuildingVisitor::Visit( SmVerticalBraceNode* pNode )
{
    SmNode  *pBody   = pNode->Body(),
            *pScript = pNode->Script();
    //None of these children can be NULL

    SmCaretPosGraphEntry  *left,
                        *bodyLeft,
                        *scriptLeft,
                        *right;

    left = mpRightMost;

    //Create right
    right = mpGraph->Add( SmCaretPos( pNode, 1 ) );

    //Create bodyLeft
    bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
    left->SetRight( bodyLeft );
    mpRightMost = bodyLeft;
    pBody->Accept( this );
    mpRightMost->SetRight( right );
    right->SetLeft( mpRightMost );

    //Create script
    scriptLeft = mpGraph->Add( SmCaretPos( pScript, 0 ), left );
    mpRightMost = scriptLeft;
    pScript->Accept( this );
    mpRightMost->SetRight( right );

    //Set return value
    mpRightMost = right;
}

/** Build SmCaretPosGraph for SmBinDiagonalNode
 *
 * Lines in an SmBinDiagonalNode:
 * \code
 *  A /
 *   /
 *  / B
 * \endcode
 * Where A and B are lines.
 *
 * Used in formulas such as "A wideslash B"
 */

void SmCaretPosGraphBuildingVisitor::Visit( SmBinDiagonalNode* pNode )
{
    SmNode  *A = pNode->GetSubNode( 0 ),
            *B = pNode->GetSubNode( 1 );

    SmCaretPosGraphEntry  *left,
                        *leftA,
                        *rightA,
                        *leftB,
                        *right;
    left = mpRightMost;

    //Create right
    right = mpGraph->Add( SmCaretPos( pNode, 1 ) );

    //Create left A
    leftA = mpGraph->Add( SmCaretPos( A, 0 ), left );
    left->SetRight( leftA );

    //Visit A
    mpRightMost = leftA;
    A->Accept( this );
    rightA = mpRightMost;

    //Create left B
    leftB = mpGraph->Add( SmCaretPos( B, 0 ), rightA );
    rightA->SetRight( leftB );

    //Visit B
    mpRightMost = leftB;
    B->Accept( this );
    mpRightMost->SetRight( right );
    right->SetLeft( mpRightMost );

    //Set return value
    mpRightMost = right;
}

//Straight forward ( I think )
void SmCaretPosGraphBuildingVisitor::Visit( SmBinHorNode* pNode )
{
    forauto pChild : *pNode )
    {
        if(!pChild)
            continue;
        pChild->Accept( this );
    }
}
void SmCaretPosGraphBuildingVisitor::Visit( SmUnHorNode* pNode )
{
    // Unary operator node
    forauto pChild : *pNode )
    {
        if(!pChild)
            continue;
        pChild->Accept( this );
    }
}

void SmCaretPosGraphBuildingVisitor::Visit( SmExpressionNode* pNode )
{
    forauto pChild : *pNode )
    {
        if(!pChild)
            continue;
        pChild->Accept( this );
    }
}

void SmCaretPosGraphBuildingVisitor::Visit( SmFontNode* pNode )
{
    //Has only got one child, should act as an expression if possible
    forauto pChild : *pNode )
    {
        if(!pChild)
            continue;
        pChild->Accept( this );
    }
}

/** Build SmCaretPosGraph for SmBracebodyNode
 * Acts as an SmExpressionNode
 *
 * Below is an example of a formula tree that has multiple children for SmBracebodyNode
 * \dot
 * digraph {
 * labelloc = "t";
 * label= "Equation: \"lbrace i mline i in setZ rbrace\"";
 * n0 [label="SmTableNode"];
 * n0 -> n1 [label="0"];
 * n1 [label="SmLineNode"];
 * n1 -> n2 [label="0"];
 * n2 [label="SmExpressionNode"];
 * n2 -> n3 [label="0"];
 * n3 [label="SmBraceNode"];
 * n3 -> n4 [label="0"];
 * n4 [label="SmMathSymbolNode: {"];
 * n3 -> n5 [label="1"];
 * n5 [label="SmBracebodyNode"];
 * n5 -> n6 [label="0"];
 * n6 [label="SmExpressionNode"];
 * n6 -> n7 [label="0"];
 * n7 [label="SmTextNode: i"];
 * n5 -> n8 [label="1"];
 * n8 [label="SmMathSymbolNode: |"]; // Unicode "VERTICAL LINE"
 * n5 -> n9 [label="2"];
 * n9 [label="SmExpressionNode"];
 * n9 -> n10 [label="0"];
 * n10 [label="SmBinHorNode"];
 * n10 -> n11 [label="0"];
 * n11 [label="SmTextNode: i"];
 * n10 -> n12 [label="1"];
 * n12 [label="SmMathSymbolNode: ∈"]; // Unicode "ELEMENT OF"
 * n10 -> n13 [label="2"];
 * n13 [label="SmMathSymbolNode: ℤ"]; // Unicode "DOUBLE-STRUCK CAPITAL Z"
 * n3 -> n14 [label="2"];
 * n14 [label="SmMathSymbolNode: }"];
 * }
 * \enddot
 */

void SmCaretPosGraphBuildingVisitor::Visit( SmBracebodyNode* pNode )
{
    forauto pChild : *pNode )
    {
        if(!pChild)
            continue;
        SmCaretPosGraphEntry* pStart = mpGraph->Add( SmCaretPos( pChild, 0), mpRightMost );
        mpRightMost->SetRight( pStart );
        mpRightMost = pStart;
        pChild->Accept( this );
    }
}

/** Build SmCaretPosGraph for SmAlignNode
 * Acts as an SmExpressionNode, as it only has one child this okay
 */

void SmCaretPosGraphBuildingVisitor::Visit( SmAlignNode* pNode )
{
    forauto pChild : *pNode )
    {
        if(!pChild)
            continue;
        pChild->Accept( this );
    }
}

/** Build SmCaretPosGraph for SmRootNode
 *
 * Lines in an SmRootNode:
 * \code
 *    _________
 *  A/
 * \/    B
 *
 * \endcode
 * A: pExtra ( optional, can be NULL ),
 * B: pBody
 *
 * Graph over these, where "left" is before the SmRootNode and "right" is after:
 * \dot
 *  digraph Graph{
 *      left -> B;
 *      B -> right;
 *      A -> B;
 *  }
 * \enddot
 */

void SmCaretPosGraphBuildingVisitor::Visit( SmRootNode* pNode )
{
    SmNode  *pExtra = pNode->GetSubNode( 0 ), //Argument, NULL for sqrt, and SmTextNode if cubicroot
            *pBody  = pNode->GetSubNode( 2 ); //Body of the root
    assert(pBody);

    SmCaretPosGraphEntry  *left,
                        *right,
                        *bodyLeft,
                        *bodyRight;

    //Get left and save it
    assert(mpRightMost);
    left = mpRightMost;

    //Create body left
    bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
    left->SetRight( bodyLeft );

    //Create right
    right = mpGraph->Add( SmCaretPos( pNode, 1 ) );

    //Visit body
    mpRightMost = bodyLeft;
    pBody->Accept( this );
    bodyRight = mpRightMost;
    bodyRight->SetRight( right );
    right->SetLeft( bodyRight );

    //Visit pExtra
    if( pExtra ){
        mpRightMost = mpGraph->Add( SmCaretPos( pExtra, 0 ), left );
        pExtra->Accept( this );
        mpRightMost->SetRight( bodyLeft );
    }

    mpRightMost = right;
}


/** Build SmCaretPosGraph for SmPlaceNode
 * Consider this a single character.
 */

void SmCaretPosGraphBuildingVisitor::Visit( SmPlaceNode* pNode )
{
    SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
    mpRightMost->SetRight( right );
    mpRightMost = right;
}

/** SmErrorNode is context dependent metadata, it can't be selected
 *
 * @remarks There's no point in deleting, copying and/or moving an instance
 * of SmErrorNode as it may not exist in another context! Thus there are no
 * positions to select an SmErrorNode.
 */

void SmCaretPosGraphBuildingVisitor::Visit( SmErrorNode* )
{
}

/** Build SmCaretPosGraph for SmBlankNode
 * Consider this a single character, as it is only a blank space
 */

void SmCaretPosGraphBuildingVisitor::Visit( SmBlankNode* pNode )
{
    SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
    mpRightMost->SetRight( right );
    mpRightMost = right;
}

/** Build SmCaretPosGraph for SmBraceNode
 *
 * Lines in an SmBraceNode:
 * \code
 * |     |
 * |  B  |
 * |     |
 * \endcode
 * B: Body
 *
 * Graph over these, where "left" is before the SmBraceNode and "right" is after:
 * \dot
 *  digraph Graph{
 *      left -> B;
 *      B -> right;
 *  }
 * \enddot
 */

void SmCaretPosGraphBuildingVisitor::Visit( SmBraceNode* pNode )
{
    SmNode* pBody = pNode->Body();

    SmCaretPosGraphEntry  *left = mpRightMost,
                        *right = mpGraph->Add( SmCaretPos( pNode, 1 ) );

    if( pBody->GetType() != SmNodeType::Bracebody ) {
        mpRightMost = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
        left->SetRight( mpRightMost );
    }else
        mpRightMost = left;

    pBody->Accept( this );
    mpRightMost->SetRight( right );
    right->SetLeft( mpRightMost );

    mpRightMost = right;
}

/** Build SmCaretPosGraph for SmAttributeNode
 *
 * Lines in an SmAttributeNode:
 * \code
 *   Attr
 *   Body
 * \endcode
 *
 * There's a body and an attribute, the construction is used for "widehat A", where "A" is the body
 * and "^" is the attribute ( note GetScaleMode( ) on SmAttributeNode tells how the attribute should be
 * scaled ).
 */

void SmCaretPosGraphBuildingVisitor::Visit( SmAttributeNode* pNode )
{
    SmNode  *pAttr = pNode->Attribute(),
            *pBody = pNode->Body();
    assert(pAttr);
    assert(pBody);

    SmCaretPosGraphEntry  *left = mpRightMost,
                        *attrLeft,
                        *bodyLeft,
                        *bodyRight,
                        *right;

    //Creating bodyleft
    bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
    left->SetRight( bodyLeft );

    //Creating right
    right = mpGraph->Add( SmCaretPos( pNode, 1 ) );

    //Visit the body
    mpRightMost = bodyLeft;
    pBody->Accept( this );
    bodyRight = mpRightMost;
    bodyRight->SetRight( right );
    right->SetLeft( bodyRight );

    //Create attrLeft
    attrLeft = mpGraph->Add( SmCaretPos( pAttr, 0 ), left );

    //Visit attribute
    mpRightMost = attrLeft;
    pAttr->Accept( this );
    mpRightMost->SetRight( right );

    //Set return value
    mpRightMost = right;
}

//Consider these single symbols
void SmCaretPosGraphBuildingVisitor::Visit( SmSpecialNode* pNode )
{
    SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
    mpRightMost->SetRight( right );
    mpRightMost = right;
}
void SmCaretPosGraphBuildingVisitor::Visit( SmGlyphSpecialNode* pNode )
{
    SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
    mpRightMost->SetRight( right );
    mpRightMost = right;
}
void SmCaretPosGraphBuildingVisitor::Visit( SmMathSymbolNode* pNode )
{
    SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
    mpRightMost->SetRight( right );
    mpRightMost = right;
}

void SmCaretPosGraphBuildingVisitor::Visit( SmRootSymbolNode* )
{
    //Do nothing
}

void SmCaretPosGraphBuildingVisitor::Visit( SmRectangleNode* )
{
    //Do nothing
}
void SmCaretPosGraphBuildingVisitor::Visit( SmPolyLineNode* )
{
    //Do nothing
}

// SmCloningVisitor

SmNode* SmCloningVisitor::Clone( SmNode* pNode )
{
    SmNode* pCurrResult = mpResult;
    pNode->Accept( this );
    SmNode* pClone = mpResult;
    mpResult = pCurrResult;
    return pClone;
}

void SmCloningVisitor::CloneNodeAttr( SmNode const * pSource, SmNode* pTarget )
{
    pTarget->SetScaleMode( pSource->GetScaleMode( ) );
    //Other attributes are set when prepare or arrange is executed
    //and may depend on stuff not being cloned here.
}

void SmCloningVisitor::CloneKids( SmStructureNode* pSource, SmStructureNode* pTarget )
{
    //Cache current result
    SmNode* pCurrResult = mpResult;

    //Create array for holding clones
    size_t nSize = pSource->GetNumSubNodes( );
    SmNodeArray aNodes( nSize );

    //Clone children
    for (size_t i = 0; i < nSize; ++i)
    {
        SmNode* pKid;
        if( nullptr != ( pKid = pSource->GetSubNode( i ) ) )
            pKid->Accept( this );
        else
            mpResult = nullptr;
        aNodes[i] = mpResult;
    }

    //Set subnodes of pTarget
    pTarget->SetSubNodes( std::move(aNodes) );

    //Restore result as where prior to call
    mpResult = pCurrResult;
}

void SmCloningVisitor::Visit( SmTableNode* pNode )
{
    SmTableNode* pClone = new SmTableNode( pNode->GetToken( ) );
    pClone->SetSelection( pNode->GetSelection() );
    CloneNodeAttr( pNode, pClone );
    CloneKids( pNode, pClone );
    mpResult = pClone;
}

void SmCloningVisitor::Visit( SmBraceNode* pNode )
{
    SmBraceNode* pClone = new SmBraceNode( pNode->GetToken( ) );
    pClone->SetSelection( pNode->GetSelection() );
    CloneNodeAttr( pNode, pClone );
    CloneKids( pNode, pClone );
    mpResult = pClone;
}

void SmCloningVisitor::Visit( SmBracebodyNode* pNode )
{
    SmBracebodyNode* pClone = new SmBracebodyNode( pNode->GetToken( ) );
    pClone->SetSelection( pNode->GetSelection() );
    CloneNodeAttr( pNode, pClone );
    CloneKids( pNode, pClone );
    mpResult = pClone;
}

void SmCloningVisitor::Visit( SmOperNode* pNode )
{
    SmOperNode* pClone = new SmOperNode( pNode->GetToken( ) );
    pClone->SetSelection( pNode->GetSelection() );
    CloneNodeAttr( pNode, pClone );
    CloneKids( pNode, pClone );
    mpResult = pClone;
}

void SmCloningVisitor::Visit( SmAlignNode* pNode )
{
    SmAlignNode* pClone = new SmAlignNode( pNode->GetToken( ) );
    pClone->SetSelection( pNode->GetSelection() );
    CloneNodeAttr( pNode, pClone );
    CloneKids( pNode, pClone );
    mpResult = pClone;
}

void SmCloningVisitor::Visit( SmAttributeNode* pNode )
{
    SmAttributeNode* pClone = new SmAttributeNode( pNode->GetToken( ) );
    pClone->SetSelection( pNode->GetSelection() );
    CloneNodeAttr( pNode, pClone );
    CloneKids( pNode, pClone );
    mpResult = pClone;
}

void SmCloningVisitor::Visit( SmFontNode* pNode )
{
    SmFontNode* pClone = new SmFontNode( pNode->GetToken( ) );
    pClone->SetSelection( pNode->GetSelection() );
    pClone->SetSizeParameter( pNode->GetSizeParameter( ), pNode->GetSizeType( ) );
    CloneNodeAttr( pNode, pClone );
    CloneKids( pNode, pClone );
    mpResult = pClone;
}

void SmCloningVisitor::Visit( SmUnHorNode* pNode )
{
    SmUnHorNode* pClone = new SmUnHorNode( pNode->GetToken( ) );
    pClone->SetSelection( pNode->GetSelection() );
    CloneNodeAttr( pNode, pClone );
    CloneKids( pNode, pClone );
    mpResult = pClone;
}

void SmCloningVisitor::Visit( SmBinHorNode* pNode )
{
    SmBinHorNode* pClone = new SmBinHorNode( pNode->GetToken( ) );
    pClone->SetSelection( pNode->GetSelection() );
    CloneNodeAttr( pNode, pClone );
    CloneKids( pNode, pClone );
    mpResult = pClone;
}

void SmCloningVisitor::Visit( SmBinVerNode* pNode )
{
    SmBinVerNode* pClone = new SmBinVerNode( pNode->GetToken( ) );
    pClone->SetSelection( pNode->GetSelection() );
    CloneNodeAttr( pNode, pClone );
    CloneKids( pNode, pClone );
    mpResult = pClone;
}

void SmCloningVisitor::Visit( SmBinDiagonalNode* pNode )
{
    SmBinDiagonalNode *pClone = new SmBinDiagonalNode( pNode->GetToken( ) );
    pClone->SetSelection( pNode->GetSelection() );
    pClone->SetAscending( pNode->IsAscending( ) );
    CloneNodeAttr( pNode, pClone );
    CloneKids( pNode, pClone );
    mpResult = pClone;
}

void SmCloningVisitor::Visit( SmSubSupNode* pNode )
{
    SmSubSupNode *pClone = new SmSubSupNode( pNode->GetToken( ) );
    pClone->SetSelection( pNode->GetSelection() );
    pClone->SetUseLimits( pNode->IsUseLimits( ) );
    CloneNodeAttr( pNode, pClone );
    CloneKids( pNode, pClone );
    mpResult = pClone;
}

void SmCloningVisitor::Visit( SmMatrixNode* pNode )
{
    SmMatrixNode *pClone = new SmMatrixNode( pNode->GetToken( ) );
    pClone->SetSelection( pNode->GetSelection() );
    pClone->SetRowCol( pNode->GetNumRows( ), pNode->GetNumCols( ) );
    CloneNodeAttr( pNode, pClone );
    CloneKids( pNode, pClone );
    mpResult = pClone;
}

void SmCloningVisitor::Visit( SmPlaceNode* pNode )
{
    mpResult = new SmPlaceNode( pNode->GetToken( ) );
    mpResult->SetSelection( pNode->GetSelection() );
    CloneNodeAttr( pNode, mpResult );
}

void SmCloningVisitor::Visit( SmTextNode* pNode )
{
    SmTextNode* pClone = new SmTextNode( pNode->GetToken( ), pNode->GetFontDesc( ) );
    pClone->SetSelection( pNode->GetSelection() );
    pClone->ChangeText( pNode->GetText( ) );
    CloneNodeAttr( pNode, pClone );
    mpResult = pClone;
}

void SmCloningVisitor::Visit( SmSpecialNode* pNode )
{
    mpResult = new SmSpecialNode( pNode->GetToken( ) );
    mpResult->SetSelection( pNode->GetSelection() );
    CloneNodeAttr( pNode, mpResult );
}

void SmCloningVisitor::Visit( SmGlyphSpecialNode* pNode )
{
    mpResult = new SmGlyphSpecialNode( pNode->GetToken( ) );
    mpResult->SetSelection( pNode->GetSelection() );
    CloneNodeAttr( pNode, mpResult );
}

void SmCloningVisitor::Visit( SmMathSymbolNode* pNode )
{
    mpResult = new SmMathSymbolNode( pNode->GetToken( ) );
    mpResult->SetSelection( pNode->GetSelection() );
    CloneNodeAttr( pNode, mpResult );
}

void SmCloningVisitor::Visit( SmBlankNode* pNode )
{
    SmBlankNode* pClone = new SmBlankNode( pNode->GetToken( ) );
    pClone->SetSelection( pNode->GetSelection() );
    pClone->SetBlankNum( pNode->GetBlankNum( ) );
    mpResult = pClone;
    CloneNodeAttr( pNode, mpResult );
}

void SmCloningVisitor::Visit( SmErrorNode* pNode )
{
    mpResult = new SmErrorNode( pNode->GetToken( ) );
    mpResult->SetSelection( pNode->GetSelection() );
    CloneNodeAttr( pNode, mpResult );
}

void SmCloningVisitor::Visit( SmLineNode* pNode )
{
    SmLineNode* pClone = new SmLineNode( pNode->GetToken( ) );
    pClone->SetSelection( pNode->GetSelection() );
    CloneNodeAttr( pNode, pClone );
    CloneKids( pNode, pClone );
    mpResult = pClone;
}

void SmCloningVisitor::Visit( SmExpressionNode* pNode )
{
    SmExpressionNode* pClone = new SmExpressionNode( pNode->GetToken( ) );
    pClone->SetSelection( pNode->GetSelection() );
    CloneNodeAttr( pNode, pClone );
    CloneKids( pNode, pClone );
    mpResult = pClone;
}

void SmCloningVisitor::Visit( SmPolyLineNode* pNode )
{
    mpResult = new SmPolyLineNode( pNode->GetToken( ) );
    mpResult->SetSelection( pNode->GetSelection() );
    CloneNodeAttr( pNode, mpResult );
}

void SmCloningVisitor::Visit( SmRootNode* pNode )
{
    SmRootNode* pClone = new SmRootNode( pNode->GetToken( ) );
    pClone->SetSelection( pNode->GetSelection() );
    CloneNodeAttr( pNode, pClone );
    CloneKids( pNode, pClone );
    mpResult = pClone;
}

void SmCloningVisitor::Visit( SmRootSymbolNode* pNode )
{
    mpResult = new SmRootSymbolNode( pNode->GetToken( ) );
    mpResult->SetSelection( pNode->GetSelection() );
    CloneNodeAttr( pNode, mpResult );
}

void SmCloningVisitor::Visit( SmRectangleNode* pNode )
{
    mpResult = new SmRectangleNode( pNode->GetToken( ) );
    mpResult->SetSelection( pNode->GetSelection() );
    CloneNodeAttr( pNode, mpResult );
}

void SmCloningVisitor::Visit( SmVerticalBraceNode* pNode )
{
    SmVerticalBraceNode* pClone = new SmVerticalBraceNode( pNode->GetToken( ) );
    pClone->SetSelection( pNode->GetSelection() );
    CloneNodeAttr( pNode, pClone );
    CloneKids( pNode, pClone );
    mpResult = pClone;
}

// SmSelectionDrawingVisitor

SmSelectionDrawingVisitor::SmSelectionDrawingVisitor( OutputDevice& rDevice, SmNode* pTree, const Point& rOffset )
    : SmSelectionRectanglesVisitor( rDevice, pTree )
{
    //Draw selection if there's any
    if(GetSelection().IsEmpty())        return;

    tools::Rectangle aSelectionArea = GetSelection() + rOffset;

    //Save device state
    rDevice.Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
    //Change colors
    rDevice.SetLineColor( );
    rDevice.SetFillColor( COL_LIGHTGRAY );

    //Draw rectangle
    rDevice.DrawRect( aSelectionArea );

    //Restore device state
    rDevice.Pop( );
}

// SmSelectionRectanglesVisitor

SmSelectionRectanglesVisitor::SmSelectionRectanglesVisitor(OutputDevice& rDevice, SmNode* pTree)
    : mrDev(rDevice)
{
    // Visit everything
    SAL_WARN_IF(!pTree, "starmath""pTree can't be null!");
    if (pTree)
        pTree->Accept(this);
}

void SmSelectionRectanglesVisitor::DefaultVisit( SmNode* pNode )
{
    if( pNode->IsSelected( ) )
        ExtendSelectionArea( pNode->AsRectangle( ) );
    VisitChildren( pNode );
}

void SmSelectionRectanglesVisitor::VisitChildren( SmNode* pNode )
{
    if(pNode->GetNumSubNodes() == 0)
        return;
    forauto pChild : *static_cast<SmStructureNode*>(pNode) )
    {
        if(!pChild)
            continue;
        pChild->Accept( this );
    }
}

void SmSelectionRectanglesVisitor::Visit( SmTextNode* pNode )
{
    if( !pNode->IsSelected())
        return;

    mrDev.Push( vcl::PushFlags::TEXTCOLOR | vcl::PushFlags::FONT );

    mrDev.SetFont( pNode->GetFont( ) );
    Point Position = pNode->GetTopLeft( );
    tools::Long left   = Position.getX( ) + mrDev.GetTextWidth( pNode->GetText( ), 0, pNode->GetSelectionStart( ) );
    tools::Long right  = Position.getX( ) + mrDev.GetTextWidth( pNode->GetText( ), 0, pNode->GetSelectionEnd( ) );
    tools::Long top    = Position.getY( );
    tools::Long bottom = top + pNode->GetHeight( );
    tools::Rectangle rect( left, top, right, bottom );

    ExtendSelectionArea( rect );

    mrDev.Pop( );
}

// SmNodeToTextVisitor

SmNodeToTextVisitor::SmNodeToTextVisitor( SmNode* pNode, OUString &rText )
{
    pNode->Accept( this );
    maCmdText.stripEnd(' ');
    rText = maCmdText.makeStringAndClear();
}

void SmNodeToTextVisitor::Visit( SmTableNode* pNode )
{
    if( pNode->GetToken( ).eType == TBINOM ) {
        Append(u"{ binom");
        LineToText( pNode->GetSubNode( 0 ) );
        LineToText( pNode->GetSubNode( 1 ) );
        Append(u"} ");
    } else if( pNode->GetToken( ).eType == TSTACK ) {
        Append(u"stack{ ");
        bool bFirst = true;
        forauto pChild : *pNode )
        {
            if(!pChild)
                continue;
            if(bFirst)
                bFirst = false;
            else
            {
                Separate( );
                Append(u"# ");
            }
            LineToText( pChild );
        }
        Separate( );
        Append(u"}");
    } else { //Assume it's a toplevel table, containing lines
        bool bFirst = true;
        forauto pChild : *pNode )
        {
            if(!pChild)
                continue;
            if(bFirst)
                bFirst = false;
            else
            {
                Separate( );
                Append(u"newline");
            }
            Separate( );
            pChild->Accept( this );
        }
    }
}

void SmNodeToTextVisitor::Visit( SmBraceNode* pNode )
{
    if ( pNode->GetToken().eType == TEVALUATE )
    {
        SmNode *pBody = pNode->Body();
        Append(u"evaluate { ");
        pBody->Accept( this );
        Append(u"} ");
    }
    else{
        SmNode *pLeftBrace  = pNode->OpeningBrace(),
               *pBody       = pNode->Body(),
               *pRightBrace = pNode->ClosingBrace();
        //Handle special case where it's absolute function
        if( pNode->GetToken( ).eType == TABS ) {
            Append(u"abs");
            LineToText( pBody );
        } else {
            if( pNode->GetScaleMode( ) == SmScaleMode::Height )
                Append(u"left ");
            pLeftBrace->Accept( this );
            Separate( );
            pBody->Accept( this );
            Separate( );
            if( pNode->GetScaleMode( ) == SmScaleMode::Height )
                Append(u"right ");
            pRightBrace->Accept( this );
        }
    }
}

void SmNodeToTextVisitor::Visit( SmBracebodyNode* pNode )
{
    forauto pChild : *pNode )
    {
        if(!pChild)
            continue;
        Separate( );
        pChild->Accept( this );
    }
}

void SmNodeToTextVisitor::Visit( SmOperNode* pNode )
{
    Append( pNode->GetToken( ).aText );
    Separate( );
    if( pNode->GetToken( ).eType == TOPER ){
        //There's an SmGlyphSpecialNode if eType == TOPER
        if( pNode->GetSubNode( 0 )->GetType( ) == SmNodeType::SubSup )
            Append( pNode->GetSubNode( 0 )->GetSubNode( 0 )->GetToken( ).aText );
        else
            Append( pNode->GetSubNode( 0 )->GetToken( ).aText );
    }
    if( pNode->GetSubNode( 0 )->GetType( ) == SmNodeType::SubSup ) {
        SmSubSupNode *pSubSup = static_cast<SmSubSupNode*>( pNode->GetSubNode( 0 ) );
        SmNode* pChild = pSubSup->GetSubSup( LSUP );
        if( pChild ) {
            Separate( );
            Append(u"lsup { ");
            LineToText( pChild );
            Append(u"} ");
        }
        pChild = pSubSup->GetSubSup( LSUB );
        if( pChild ) {
            Separate( );
            Append(u"lsub { ");
            LineToText( pChild );
            Append(u"} ");
        }
        pChild = pSubSup->GetSubSup( RSUP );
        if( pChild ) {
            Separate( );
            Append(u"^ { ");
            LineToText( pChild );
            Append(u"} ");
        }
        pChild = pSubSup->GetSubSup( RSUB );
        if( pChild ) {
            Separate( );
            Append(u"_ { ");
            LineToText( pChild );
            Append(u"} ");
        }
        pChild = pSubSup->GetSubSup( CSUP );
        if( pChild ) {
            Separate( );
            if (pSubSup->IsUseLimits())
                Append(u"to { ");
            else
                Append(u"csup { ");
            LineToText( pChild );
            Append(u"} ");
        }
        pChild = pSubSup->GetSubSup( CSUB );
        if( pChild ) {
            Separate( );
            if (pSubSup->IsUseLimits())
                Append(u"from { ");
            else
                Append(u"csub { ");
            LineToText( pChild );
            Append(u"} ");
        }
    }
    LineToText( pNode->GetSubNode( 1 ) );
}

void SmNodeToTextVisitor::Visit( SmAlignNode* pNode )
{
    Append( pNode->GetToken( ).aText );
    LineToText( pNode->GetSubNode( 0 ) );
}

void SmNodeToTextVisitor::Visit( SmAttributeNode* pNode )
{
    Append( pNode->GetToken( ).aText );
    LineToText( pNode->Body() );
}

void SmNodeToTextVisitor::Visit( SmFontNode* pNode )
{
    sal_uInt32 nc;
    sal_uInt8  nr, ng, nb;
    switch ( pNode->GetToken( ).eType )
    {
        case TBOLD:
            Append(u"bold ");
            break;
        case TNBOLD:
            Append(u"nbold ");
            break;
        case TITALIC:
            Append(u"italic ");
            break;
        case TNITALIC:
            Append(u"nitalic ");
            break;
        case TPHANTOM:
            Append(u"phantom ");
            break;
        case TSIZE:
            {
                Append(u"size ");
                switch ( pNode->GetSizeType( ) )
                {
                    case FontSizeType::PLUS:
                        Append(u"+");
                        break;
                    case FontSizeType::MINUS:
                        Append(u"-");
                        break;
                    case FontSizeType::MULTIPLY:
                        Append(u"*");
                        break;
                    case FontSizeType::DIVIDE:
                        Append(u"/");
                        break;
                    case FontSizeType::ABSOLUT:
                    default:
                        break;
                }
                Append( ::rtl::math::doubleToUString(
                            static_cast<double>( pNode->GetSizeParameter( ) ),
                            rtl_math_StringFormat_Automatic,
                            rtl_math_DecimalPlaces_Max, '.'true ) );
                Separate( );
            }
            break;

        case TDVIPSNAMESCOL:
            Append(u"color dvip ");
            nc = pNode->GetToken().cMathChar.toUInt32(16);
            Append( starmathdatabase::Identify_Color_Parser( nc ).aIdent );
            break;
        case THTMLCOL:
        case TMATHMLCOL:
        case TICONICCOL:
            Append(u"color ");
            nc = pNode->GetToken().cMathChar.toUInt32(16);
            Append( starmathdatabase::Identify_Color_Parser( nc ).aIdent );
            break;
        case TRGB:
            nc = pNode->GetToken().cMathChar.toUInt32(16);
            Append(u"color rgb ");
            nb = nc % 256;
            nc /= 256;
            ng = nc % 256;
            nc /= 256;
            nr = nc % 256;
            Append(OUString::number(nr));
            Separate();
            Append(OUString::number(ng));
            Separate();
            Append(OUString::number(nb));
            Separate();
            break;
        case TRGBA:
            Append(u"color rgba ");
            nc = pNode->GetToken().cMathChar.toUInt32(16);
            nb = nc % 256;
            nc /= 256;
            ng = nc % 256;
            nc /= 256;
            nr = nc % 256;
            nc /= 256;
            Append(OUString::number(nr));
            Separate();
            Append(OUString::number(ng));
            Separate();
            Append(OUString::number(nb));
            Separate();
            Append(OUString::number(nc));
            Separate();
            break;
        case THEX:
            Append(u"color hex ");
            nc = pNode->GetToken().cMathChar.toUInt32(16);
            Append(OUString::number(nc,16));
            Separate();
            break;
        case TSANS:
            Append(u"font sans ");
            break;
        case TSERIF:
            Append(u"font serif ");
            break;
        case TFIXED:
            Append(u"font fixed ");
            break;
        default:
            break;
    }
    LineToText( pNode->GetSubNode( 1 ) );
}

void SmNodeToTextVisitor::Visit( SmUnHorNode* pNode )
{
    if(pNode->GetSubNode( 1 )->GetToken( ).eType == TFACT)
    {
        // visit children in the reverse order
        forauto it = pNode->rbegin(); it != pNode->rend(); ++it )
        {
            auto pChild = *it;
            if(!pChild)
                continue;
            Separate( );
            pChild->Accept( this );
        }
    }
    else
    {
        forauto pChild : *pNode )
        {
            if(!pChild)
                continue;
            Separate( );
            pChild->Accept( this );
        }
    }
}

void SmNodeToTextVisitor::Visit( SmBinHorNode* pNode )
{
    const SmNode *pParent = pNode->GetParent();
    bool bBraceNeeded = pParent;
    SmNode *pLeft  = pNode->LeftOperand(),
           *pOper  = pNode->Symbol(),
           *pRight = pNode->RightOperand();
    Separate( );
    if (bBraceNeeded)
        Append(u"{ ");
    pLeft->Accept( this );
    Separate( );
    pOper->Accept( this );
    Separate( );
    pRight->Accept( this );
    Separate( );
    if (bBraceNeeded)
        Append(u"} ");
}

void SmNodeToTextVisitor::Visit( SmBinVerNode* pNode )
{
    if( pNode->GetToken().eType == TOVER ){
        SmNode *pNum    = pNode->GetSubNode( 0 ),
               *pDenom  = pNode->GetSubNode( 2 );
        Append(u"{ ");
        LineToText( pNum );
        Append(u"over");
        LineToText( pDenom );
        Append(u"} ");
    } else{
        SmNode *pNum    = pNode->GetSubNode( 0 ),
               *pDenom  = pNode->GetSubNode( 2 );
        Append(u"{ frac {");
        LineToText( pNum );
        Append(u"} {");
        LineToText( pDenom );
        Append(u"} }");
    }
}

void SmNodeToTextVisitor::Visit( SmBinDiagonalNode* pNode )
{
    SmNode *pLeftOperand  = pNode->GetSubNode( 0 ),
           *pRightOperand = pNode->GetSubNode( 1 );
    Append(u"{ ");
    LineToText( pLeftOperand );
    Separate( );
    Append(u"wideslash ");
    LineToText( pRightOperand );
    Append(u"} ");
}

void SmNodeToTextVisitor::Visit( SmSubSupNode* pNode )
{
    if( pNode->GetToken().eType == TEVALUATE )
    {
        Append(u"evaluate { ");
        pNode->GetSubNode( 0 )->GetSubNode( 1 )->Accept(this);
        Append(u"} ");
        SmNode* pChild = pNode->GetSubSup( RSUP );
        if( pChild ) {
            Separate( );
            Append(u"to { ");
            LineToText( pChild );
            Append(u"} ");
        }
        pChild = pNode->GetSubSup( RSUB );
        if( pChild ) {
            Separate( );
            Append(u"from { ");
            LineToText( pChild );
            Append(u"} ");
        }
    }
    else
    {
        LineToText( pNode->GetBody( ) );
        SmNode *pChild = pNode->GetSubSup( LSUP );
        if( pChild ) {
            Separate( );
            Append(u"lsup ");
            LineToText( pChild );
        }
        pChild = pNode->GetSubSup( LSUB );
        if( pChild ) {
            Separate( );
            Append(u"lsub ");
            LineToText( pChild );
        }
        pChild = pNode->GetSubSup( RSUP );
        if( pChild ) {
            Separate( );
            Append(u"^ ");
            LineToText( pChild );
        }
        pChild = pNode->GetSubSup( RSUB );
        if( pChild ) {
            Separate( );
            Append(u"_ ");
            LineToText( pChild );
        }
        pChild = pNode->GetSubSup( CSUP );
        if( pChild ) {
            Separate( );
            if (pNode->IsUseLimits())
                Append(u"to ");
            else
                Append(u"csup ");
            LineToText( pChild );
        }
        pChild = pNode->GetSubSup( CSUB );
        if( pChild ) {
            Separate( );
            if (pNode->IsUseLimits())
                Append(u"from ");
            else
                Append(u"csub ");
            LineToText( pChild );
        }
    }
}

void SmNodeToTextVisitor::Visit( SmMatrixNode* pNode )
{
    Append(u"matrix{");
    for (size_t i = 0; i < pNode->GetNumRows(); ++i)
    {
        for (size_t j = 0; j < pNode->GetNumCols( ); ++j)
        {
            SmNode* pSubNode = pNode->GetSubNode( i * pNode->GetNumCols( ) + j );
            Separate( );
            if (pSubNode)
                pSubNode->Accept( this );
            Separate( );
            if (j != pNode->GetNumCols() - 1U)
                Append(u"#");
        }
        Separate( );
        if (i != pNode->GetNumRows() - 1U)
            Append(u"##");
    }
    Append(u"} ");
}

void SmNodeToTextVisitor::Visit( SmPlaceNode* )
{
    Append(u"");
}

void SmNodeToTextVisitor::Visit( SmTextNode* pNode )
{
    SmTokenType type = pNode->GetToken( ).eType;
    switch(type){
        case TTEXT:
            Append(u"\"");
            Append( pNode->GetToken().aText );
            Append(u"\"");
            break;
        case TNUMBER:
        case TIDENT:
            Append( pNode->GetToken().aText );
            break;
        case TFUNC:
            Append(u"func ");
            Append( pNode->GetToken().aText );
            break;
        case THEX:
            Append(u"hex ");
            Append( pNode->GetToken().aText );
            break;
        default:
            Append( pNode->GetToken().aText );
    }
    Separate( );
}

void SmNodeToTextVisitor::Visit( SmSpecialNode* pNode )
{
    SmTokenType type = pNode->GetToken().eType;
    switch(type){
        case TLIMSUP:
            Append(u"lim sup ");
            break;
        case TLIMINF:
            Append(u"lim inf ");
            break;
        default:
            Append( pNode->GetToken().aText );
            break;
    }
}

void SmNodeToTextVisitor::Visit( SmGlyphSpecialNode* pNode )
{
    if( pNode->GetToken( ).eType == TBOPER )
        Append(u"boper ");
    else
        Append(u"uoper ");
    Append( pNode->GetToken( ).aText );
}

//TODO to improve this it is required to improve mathmlimport.
void SmNodeToTextVisitor::Visit( SmMathSymbolNode* pNode )
{
    if (    ( pNode->GetToken().nGroup & TG::LBrace )
         || ( pNode->GetToken().nGroup & TG::RBrace )
         || ( pNode->GetToken().nGroup & TG::Sum )
         || ( pNode->GetToken().nGroup & TG::Product )
         || ( pNode->GetToken().nGroup & TG::Relation )
         || ( pNode->GetToken().nGroup & TG::UnOper )
         || ( pNode->GetToken().nGroup & TG::Oper )
    ) {
        Append( pNode->GetToken().aText );
        return;
    }
    sal_Unicode cChar = pNode->GetToken().cMathChar[0];
    Separate( );
    switch(cChar){
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=91 G=92

¤ Dauer der Verarbeitung: 0.19 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.