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 55 kB image not shown  

Quelle  cursor.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 <cursor.hxx>
#include <visitors.hxx>
#include <document.hxx>
#include <view.hxx>
#include <comphelper/string.hxx>
#include <comphelper/lok.hxx>
#include <editeng/editeng.hxx>
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
#include <osl/diagnose.h>
#include <sfx2/lokhelper.hxx>
#include <vcl/transfer.hxx>
#include <vcl/unohelp2.hxx>

void SmCursor::Move(OutputDevice* pDev, SmMovementDirection direction, bool bMoveAnchor){
    SmCaretPosGraphEntry* NewPos = nullptr;
    switch(direction)
    {
        case MoveLeft:
            if (mpPosition)
                NewPos = mpPosition->Left;
            OSL_ENSURE(NewPos, "NewPos shouldn't be NULL here!");
            break;
        case MoveRight:
            if (mpPosition)
                NewPos = mpPosition->Right;
            OSL_ENSURE(NewPos, "NewPos shouldn't be NULL here!");
            break;
        case MoveUp:
            //Implementation is practically identical to MoveDown, except for a single if statement
            //so I've implemented them together and added a direction == MoveDown to the if statements.
        case MoveDown:
            if (mpPosition)
            {
                SmCaretLine from_line = SmCaretPos2LineVisitor(pDev, mpPosition->CaretPos).GetResult(),
                            best_line,  //Best approximated line found so far
                            curr_line;  //Current line
                tools::Long dbp_sq = 0;        //Distance squared to best line
                for(const auto &pEntry : *mpGraph)
                {
                    //Reject it if it's the current position
                    if(pEntry->CaretPos == mpPosition->CaretPos) continue;
                    //Compute caret line
                    curr_line = SmCaretPos2LineVisitor(pDev, pEntry->CaretPos).GetResult();
                    //Reject anything above if we're moving down
                    if(curr_line.GetTop() <= from_line.GetTop() && direction == MoveDown) continue;
                    //Reject anything below if we're moving up
                    if(curr_line.GetTop() + curr_line.GetHeight() >= from_line.GetTop() + from_line.GetHeight()
                            && direction == MoveUp) continue;
                    //Compare if it to what we have, if we have anything yet
                    if(NewPos){
                        //Compute distance to current line squared, multiplied with a horizontal factor
                        tools::Long dp_sq = curr_line.SquaredDistanceX(from_line) * HORIZONTICAL_DISTANCE_FACTOR +
                                     curr_line.SquaredDistanceY(from_line);
                        //Discard current line if best line is closer
                        if(dbp_sq <= dp_sq) continue;
                    }
                    //Take current line as the best
                    best_line = curr_line;
                    NewPos = pEntry.get();
                    //Update distance to best line
                    dbp_sq = best_line.SquaredDistanceX(from_line) * HORIZONTICAL_DISTANCE_FACTOR +
                             best_line.SquaredDistanceY(from_line);
                }
            }
            break;
        default:
            assert(false);
    }
    if(NewPos){
        mpPosition = NewPos;
        if(bMoveAnchor)
            mpAnchor = NewPos;
        RequestRepaint();
    }
}

void SmCursor::MoveTo(OutputDevice* pDev, const Point& pos, bool bMoveAnchor)
{
    SmCaretPosGraphEntry* NewPos = nullptr;
    tools::Long dp_sq = 0,     //Distance to current line squared
         dbp_sq = 1;    //Distance to best line squared
    for(const auto &pEntry : *mpGraph)
    {
        OSL_ENSURE(pEntry->CaretPos.IsValid(), "The caret position graph may not have invalid positions!");
        //Compute current line
        SmCaretLine curr_line = SmCaretPos2LineVisitor(pDev, pEntry->CaretPos).GetResult();
        //Compute squared distance to current line
        dp_sq = curr_line.SquaredDistanceX(pos) + curr_line.SquaredDistanceY(pos);
        //If we have a position compare to it
        if(NewPos){
            //If best line is closer, reject current line
            if(dbp_sq <= dp_sq) continue;
        }
        //Accept current position as the best
        NewPos = pEntry.get();
        //Update distance to best line
        dbp_sq = dp_sq;
    }
    if(NewPos){
        mpPosition = NewPos;
        if(bMoveAnchor)
            mpAnchor = NewPos;
        RequestRepaint();
    }
}

void SmCursor::BuildGraph(){
    //Save the current anchor and position
    SmCaretPos _anchor, _position;
    //Release mpGraph if allocated
    if(mpGraph){
        if(mpAnchor)
            _anchor = mpAnchor->CaretPos;
        if(mpPosition)
            _position = mpPosition->CaretPos;
        mpGraph.reset();
        //Reset anchor and position as they point into an old graph
        mpAnchor = nullptr;
        mpPosition = nullptr;
    }

    //Build the new graph
    mpGraph.reset(SmCaretPosGraphBuildingVisitor(mpTree).takeGraph());

    //Restore anchor and position pointers
    if(_anchor.IsValid() || _position.IsValid()){
        for(const auto &pEntry : *mpGraph)
        {
            if(_anchor == pEntry->CaretPos)
                mpAnchor = pEntry.get();
            if(_position == pEntry->CaretPos)
                mpPosition = pEntry.get();
        }
    }
    //Set position and anchor to first caret position
    auto it = mpGraph->begin();
    assert(it != mpGraph->end());
    if(!mpPosition)
        mpPosition = it->get();
    if(!mpAnchor)
        mpAnchor = mpPosition;

    assert(mpPosition);
    assert(mpAnchor);
    OSL_ENSURE(mpPosition->CaretPos.IsValid(), "Position must be valid");
    OSL_ENSURE(mpAnchor->CaretPos.IsValid(), "Anchor must be valid");
}

bool SmCursor::SetCaretPosition(SmCaretPos pos){
    for(const auto &pEntry : *mpGraph)
    {
        if(pEntry->CaretPos == pos)
        {
            mpPosition = pEntry.get();
            mpAnchor = pEntry.get();
            return true;
        }
    }
    return false;
}

void SmCursor::AnnotateSelection() const {
    //TODO: Manage a state, reset it upon modification and optimize this call
    SmSetSelectionVisitor(mpAnchor->CaretPos, mpPosition->CaretPos, mpTree);
}

void SmCursor::Draw(OutputDevice& pDev, Point Offset, bool isCaretVisible){
    SmCaretDrawingVisitor(pDev, GetPosition(), Offset, isCaretVisible);
}

tools::Rectangle SmCursor::GetCaretRectangle(OutputDevice& rOutDev) const
{
    return SmCaretRectanglesVisitor(rOutDev, GetPosition()).getCaret();
}

tools::Rectangle SmCursor::GetSelectionRectangle(OutputDevice& rOutDev) const
{
    AnnotateSelection();
    return SmSelectionRectanglesVisitor(rOutDev, mpTree).GetSelection();
}

void SmCursor::DeletePrev(OutputDevice* pDev){
    //Delete only a selection if there's a selection
    if(HasSelection()){
        Delete();
        return;
    }

    SmNode* pLine = FindTopMostNodeInLine(mpPosition->CaretPos.pSelectedNode);
    SmStructureNode* pLineParent = pLine->GetParent();
    int nLineOffsetIdx = pLineParent->IndexOfSubNode(pLine);
    assert(nLineOffsetIdx >= 0);

    //If we're in front of a node who's parent is a TABLE
    if (pLineParent->GetType() == SmNodeType::Table && mpPosition->CaretPos.nIndex == 0 && nLineOffsetIdx > 0)
    {
        size_t nLineOffset = nLineOffsetIdx;
        //Now we can merge with nLineOffset - 1
        BeginEdit();
        //Line to merge things into, so we can delete pLine
        SmNode* pMergeLine = pLineParent->GetSubNode(nLineOffset-1);
        OSL_ENSURE(pMergeLine, "pMergeLine cannot be NULL!");
        SmCaretPos PosAfterDelete;
        //Convert first line to list
        std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
        NodeToList(pMergeLine, *pLineList);
        if(!pLineList->empty()){
            //Find iterator to patch
            SmNodeList::iterator patchPoint = pLineList->end();
            --patchPoint;
            //Convert second line to list
            NodeToList(pLine, *pLineList);
            //Patch the line list
            ++patchPoint;
            PosAfterDelete = PatchLineList(pLineList.get(), patchPoint);
            //Parse the line
            pLine = SmNodeListParser().Parse(pLineList.get());
        }
        pLineList.reset();
        pLineParent->SetSubNode(nLineOffset-1, pLine);
        //Delete the removed line slot
        SmNodeArray lines(pLineParent->GetNumSubNodes()-1);
        for (size_t i = 0; i < pLineParent->GetNumSubNodes(); ++i)
        {
            if(i < nLineOffset)
                lines[i] = pLineParent->GetSubNode(i);
            else if(i > nLineOffset)
                lines[i-1] = pLineParent->GetSubNode(i);
        }
        pLineParent->SetSubNodes(std::move(lines));
        //Rebuild graph
        mpAnchor = nullptr;
        mpPosition = nullptr;
        BuildGraph();
        AnnotateSelection();
        //Set caret position
        if(!SetCaretPosition(PosAfterDelete))
            SetCaretPosition(SmCaretPos(pLine, 0));
        //Finish editing
        EndEdit();

    //TODO: If we're in an empty (sub/super/*) script
    /*}else if(pLineParent->GetType() == SmNodeType::SubSup &&
             nLineOffset != 0 &&
             pLine->GetType() == SmNodeType::Expression &&
             pLine->GetNumSubNodes() == 0){
        //There's a (sub/super) script we can delete
    //Consider selecting the entire script if GetNumSubNodes() != 0 or pLine->GetType() != SmNodeType::Expression
    //TODO: Handle case where we delete a limit
    */


    //Else move select, and delete if not complex
    }else{
        Move(pDev, MoveLeft, false);
        if(!HasComplexSelection())
            Delete();
    }
}

void SmCursor::Delete(){
    //Return if we don't have a selection to delete
    if(!HasSelection())
        return;

    //Enter edit section
    BeginEdit();

    //Set selected on nodes
    AnnotateSelection();

    //Find an arbitrary selected node
    SmNode* pSNode = FindSelectedNode(mpTree);
    assert(pSNode);

    //Find the topmost node of the line that holds the selection
    SmNode* pLine = FindTopMostNodeInLine(pSNode, true);
    OSL_ENSURE(pLine != mpTree, "Shouldn't be able to select the entire tree");

    //Get the parent of the line
    SmStructureNode* pLineParent = pLine->GetParent();
    //Find line offset in parent
    int nLineOffset = pLineParent->IndexOfSubNode(pLine);
    assert(nLineOffset >= 0);

    //Position after delete
    SmCaretPos PosAfterDelete;

    std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
    NodeToList(pLine, *pLineList);

    //Take the selected nodes and delete them...
    SmNodeList::iterator patchIt = TakeSelectedNodesFromList(pLineList.get());

    //Get the position to set after delete
    PosAfterDelete = PatchLineList(pLineList.get(), patchIt);

    //Finish editing
    FinishEdit(std::move(pLineList), pLineParent, nLineOffset, PosAfterDelete);
}

void SmCursor::InsertNodes(std::unique_ptr<SmNodeList> pNewNodes){
    if(pNewNodes->empty()){
        return;
    }

    //Begin edit section
    BeginEdit();

    //Get the current position
    const SmCaretPos pos = mpPosition->CaretPos;

    //Find top most of line that holds position
    SmNode* pLine = FindTopMostNodeInLine(pos.pSelectedNode);
    const bool bSelectedIsTopMost = pLine == pos.pSelectedNode;

    //Find line parent and line index in parent
    SmStructureNode* pLineParent = pLine->GetParent();
    int nParentIndex = pLineParent->IndexOfSubNode(pLine);
    assert(nParentIndex >= 0);

    //Convert line to list
    std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
    NodeToList(pLine, *pLineList); // deletes pLine, potentially deleting pos.pSelectedNode

    //Find iterator for place to insert nodes
    SmNodeList::iterator it = bSelectedIsTopMost ? pLineList->begin()
                                                 : FindPositionInLineList(pLineList.get(), pos);

    //Insert all new nodes
    SmNodeList::iterator newIt,
                         patchIt = it, // (pointless default value, fixes compiler warnings)
                         insIt;
    for(newIt = pNewNodes->begin(); newIt != pNewNodes->end(); ++newIt){
        insIt = pLineList->insert(it, *newIt);
        if(newIt == pNewNodes->begin())
            patchIt = insIt;
    }
    //Patch the places we've changed stuff
    PatchLineList(pLineList.get(), patchIt);
    SmCaretPos PosAfterInsert = PatchLineList(pLineList.get(), it);
    //Release list, we've taken the nodes
    pNewNodes.reset();

    //Finish editing
    FinishEdit(std::move(pLineList), pLineParent, nParentIndex, PosAfterInsert);
}

SmNodeList::iterator SmCursor::FindPositionInLineList(SmNodeList* pLineList,
                                                      const SmCaretPos& rCaretPos)
{
    //Find iterator for position
    SmNodeList::iterator it = std::find(pLineList->begin(), pLineList->end(), rCaretPos.pSelectedNode);
    if (it != pLineList->end())
    {
        if((*it)->GetType() == SmNodeType::Text)
        {
            //Split textnode if needed
            if(rCaretPos.nIndex > 0)
            {
                SmTextNode* pText = static_cast<SmTextNode*>(rCaretPos.pSelectedNode);
                if (rCaretPos.nIndex == pText->GetText().getLength())
                    return ++it;
                OUString str1 = pText->GetText().copy(0, rCaretPos.nIndex);
                OUString str2 = pText->GetText().copy(rCaretPos.nIndex);
                pText->ChangeText(str1);
                ++it;
                //Insert str2 as new text node
                assert(!str2.isEmpty());
                SmTextNode* pNewText = new SmTextNode(pText->GetToken(), pText->GetFontDesc());
                pNewText->ChangeText(str2);
                it = pLineList->insert(it, pNewText);
            }
        }else
            ++it;
        //it now pointer to the node following pos, so pLineList->insert(it, ...) will insert correctly
        return it;
    }
    //If we didn't find pSelectedNode, it must be because the caret is in front of the line
    return pLineList->begin();
}

SmCaretPos SmCursor::PatchLineList(SmNodeList* pLineList, SmNodeList::iterator aIter) {
    //The nodes we should consider merging
    SmNode *prev = nullptr,
           *next = nullptr;
    if(aIter != pLineList->end())
        next = *aIter;
    if(aIter != pLineList->begin()) {
        --aIter;
        prev = *aIter;
        ++aIter;
    }

    //Check if there's textnodes to merge
    if( prev &&
        next &&
        prev->GetType() == SmNodeType::Text &&
        next->GetType() == SmNodeType::Text &&
        ( prev->GetToken().eType != TNUMBER ||
          next->GetToken().eType == TNUMBER) ){
        SmTextNode *pText = static_cast<SmTextNode*>(prev),
                   *pOldN = static_cast<SmTextNode*>(next);
        SmCaretPos retval(pText, pText->GetText().getLength());
        OUString newText = pText->GetText() + pOldN->GetText();
        pText->ChangeText(newText);
        delete pOldN;
        pLineList->erase(aIter);
        return retval;
    }

    //Check if there's a SmPlaceNode to remove:
    if(prev && next && prev->GetType() == SmNodeType::Place && !SmNodeListParser::IsOperator(next->GetToken())){
        --aIter;
        aIter = pLineList->erase(aIter);
        delete prev;
        //Return caret pos in front of aIter
        if(aIter != pLineList->begin())
            --aIter; //Thus find node before aIter
        if(aIter == pLineList->begin())
            return SmCaretPos();
        return SmCaretPos::GetPosAfter(*aIter);
    }
    if(prev && next && next->GetType() == SmNodeType::Place && !SmNodeListParser::IsOperator(prev->GetToken())){
        aIter = pLineList->erase(aIter);
        delete next;
        return SmCaretPos::GetPosAfter(prev);
    }

    //If we didn't do anything return
    if(!prev) //return an invalid to indicate we're in front of line
        return SmCaretPos();
    return SmCaretPos::GetPosAfter(prev);
}

SmNodeList::iterator SmCursor::TakeSelectedNodesFromList(SmNodeList *pLineList,
                                                         SmNodeList *pSelectedNodes) {
    SmNodeList::iterator retval;
    SmNodeList::iterator it = pLineList->begin();
    while(it != pLineList->end()){
        if((*it)->IsSelected()){
            //Split text nodes
            if((*it)->GetType() == SmNodeType::Text) {
                SmTextNode* pText = static_cast<SmTextNode*>(*it);
                OUString aText = pText->GetText();
                //Start and lengths of the segments, 2 is the selected segment
                int start2 = pText->GetSelectionStart(),
                    start3 = pText->GetSelectionEnd(),
                    len1 = start2 - 0,
                    len2 = start3 - start2,
                    len3 = aText.getLength() - start3;
                SmToken aToken = pText->GetToken();
                sal_uInt16 eFontDesc = pText->GetFontDesc();
                //If we need make segment 1
                if(len1 > 0) {
                    OUString str = aText.copy(0, len1);
                    pText->ChangeText(str);
                    ++it;
                } else {//Remove it if not needed
                    it = pLineList->erase(it);
                    delete pText;
                }
                //Set retval to be right after the selection
                retval = it;
                //if we need make segment 3
                if(len3 > 0) {
                    OUString str = aText.copy(start3, len3);
                    SmTextNode* pSeg3 = new SmTextNode(aToken, eFontDesc);
                    pSeg3->ChangeText(str);
                    retval = pLineList->insert(it, pSeg3);
                }
                //If we need to save the selected text
                if(pSelectedNodes && len2 > 0) {
                    OUString str = aText.copy(start2, len2);
                    SmTextNode* pSeg2 = new SmTextNode(aToken, eFontDesc);
                    pSeg2->ChangeText(str);
                    pSelectedNodes->push_back(pSeg2);
                }
            } else { //if it's not textnode
                SmNode* pNode = *it;
                retval = it = pLineList->erase(it);
                if(pSelectedNodes)
                    pSelectedNodes->push_back(pNode);
                else
                    delete pNode;
            }
        } else
            ++it;
    }
    return retval;
}

void SmCursor::InsertSubSup(SmSubSup eSubSup) {
    AnnotateSelection();

    //Find line
    SmNode *pLine;
    if(HasSelection()) {
        SmNode *pSNode = FindSelectedNode(mpTree);
        assert(pSNode);
        pLine = FindTopMostNodeInLine(pSNode, true);
    } else
        pLine = FindTopMostNodeInLine(mpPosition->CaretPos.pSelectedNode);

    //Find Parent and offset in parent
    SmStructureNode *pLineParent = pLine->GetParent();
    int nParentIndex = pLineParent->IndexOfSubNode(pLine);
    assert(nParentIndex >= 0);

    //TODO: Consider handling special cases where parent is an SmOperNode,
    //      Maybe this method should be able to add limits to an SmOperNode...

    //We begin modifying the tree here
    BeginEdit();

    //Convert line to list
    std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
    NodeToList(pLine, *pLineList);

    //Take the selection, and/or find iterator for current position
    std::unique_ptr<SmNodeList> pSelectedNodesList(new SmNodeList);
    SmNodeList::iterator it;
    if(HasSelection())
        it = TakeSelectedNodesFromList(pLineList.get(), pSelectedNodesList.get());
    else
        it = FindPositionInLineList(pLineList.get(), mpPosition->CaretPos);

    //Find node that this should be applied to
    SmNode* pSubject;
    bool bPatchLine = !pSelectedNodesList->empty(); //If the line should be patched later
    if(it != pLineList->begin()) {
        --it;
        pSubject = *it;
        ++it;
    } else {
        //Create a new place node
        pSubject = new SmPlaceNode();
        pSubject->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
        it = pLineList->insert(it, pSubject);
        ++it;
        bPatchLine = true;  //We've modified the line it should be patched later.
    }

    //Wrap the subject in a SmSubSupNode
    SmSubSupNode* pSubSup;
    if(pSubject->GetType() != SmNodeType::SubSup){
        SmToken token;
        token.nGroup = TG::Power;
        pSubSup = new SmSubSupNode(token);
        pSubSup->SetBody(pSubject);
        *(--it) = pSubSup;
        ++it;
    }else
        pSubSup = static_cast<SmSubSupNode*>(pSubject);
    //pSubject shouldn't be referenced anymore, pSubSup is the SmSubSupNode in pLineList we wish to edit.
    //and it pointer to the element following pSubSup in pLineList.
    pSubject = nullptr;

    //Patch the line if we noted that was needed previously
    if(bPatchLine)
        PatchLineList(pLineList.get(), it);

    //Convert existing, if any, sub-/superscript line to list
    SmNode *pScriptLine = pSubSup->GetSubSup(eSubSup);
    std::unique_ptr<SmNodeList> pScriptLineList(new SmNodeList);
    NodeToList(pScriptLine, *pScriptLineList);

    //Add selection to pScriptLineList
    unsigned int nOldSize = pScriptLineList->size();
    pScriptLineList->insert(pScriptLineList->end(), pSelectedNodesList->begin(), pSelectedNodesList->end());
    pSelectedNodesList.reset();

    //Patch pScriptLineList if needed
    if(0 < nOldSize && nOldSize < pScriptLineList->size()) {
        SmNodeList::iterator iPatchPoint = pScriptLineList->begin();
        std::advance(iPatchPoint, nOldSize);
        PatchLineList(pScriptLineList.get(), iPatchPoint);
    }

    //Find caret pos, that should be used after sub-/superscription.
    SmCaretPos PosAfterScript; //Leave invalid for first position
    if (!pScriptLineList->empty())
        PosAfterScript = SmCaretPos::GetPosAfter(pScriptLineList->back());

    //Parse pScriptLineList
    pScriptLine = SmNodeListParser().Parse(pScriptLineList.get());
    pScriptLineList.reset();

    //Insert pScriptLine back into the tree
    pSubSup->SetSubSup(eSubSup, pScriptLine);

    //Finish editing
    FinishEdit(std::move(pLineList), pLineParent, nParentIndex, PosAfterScript, pScriptLine);
}

void SmCursor::InsertBrackets(SmBracketType eBracketType) {
    BeginEdit();

    AnnotateSelection();

    //Find line
    SmNode *pLine;
    if(HasSelection()) {
        SmNode *pSNode = FindSelectedNode(mpTree);
        assert(pSNode);
        pLine = FindTopMostNodeInLine(pSNode, true);
    } else
        pLine = FindTopMostNodeInLine(mpPosition->CaretPos.pSelectedNode);

    //Find parent and offset in parent
    SmStructureNode *pLineParent = pLine->GetParent();
    int nParentIndex = pLineParent->IndexOfSubNode(pLine);
    assert(nParentIndex >= 0);

    //Convert line to list
    std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
    NodeToList(pLine, *pLineList);

    //Take the selection, and/or find iterator for current position
    std::unique_ptr<SmNodeList> pSelectedNodesList(new SmNodeList);
    SmNodeList::iterator it;
    if(HasSelection())
        it = TakeSelectedNodesFromList(pLineList.get(), pSelectedNodesList.get());
    else
        it = FindPositionInLineList(pLineList.get(), mpPosition->CaretPos);

    //If there's no selected nodes, create a place node
    std::unique_ptr<SmNode> pBodyNode;
    SmCaretPos PosAfterInsert;
    if(pSelectedNodesList->empty()) {
        pBodyNode.reset(new SmPlaceNode());
        PosAfterInsert = SmCaretPos(pBodyNode.get(), 1);
    } else
        pBodyNode.reset(SmNodeListParser().Parse(pSelectedNodesList.get()));

    pSelectedNodesList.reset();

    //Create SmBraceNode
    SmToken aTok(TLEFT, '\0', u"left"_ustr, TG::NONE, 5);
    SmBraceNode *pBrace = new SmBraceNode(aTok);
    pBrace->SetScaleMode(SmScaleMode::Height);
    std::unique_ptr<SmNode> pLeft( CreateBracket(eBracketType, true) ),
                            pRight( CreateBracket(eBracketType, false) );
    std::unique_ptr<SmBracebodyNode> pBody(new SmBracebodyNode(SmToken()));
    pBody->SetSubNodes(std::move(pBodyNode), nullptr);
    pBrace->SetSubNodes(std::move(pLeft), std::move(pBody), std::move(pRight));
    pBrace->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);

    //Insert into line
    pLineList->insert(it, pBrace);
    //Patch line (I think this is good enough)
    SmCaretPos aAfter = PatchLineList(pLineList.get(), it);
    if( !PosAfterInsert.IsValid() )
        PosAfterInsert = aAfter;

    //Finish editing
    FinishEdit(std::move(pLineList), pLineParent, nParentIndex, PosAfterInsert);
}

SmNode *SmCursor::CreateBracket(SmBracketType eBracketType, bool bIsLeft) {
    SmToken aTok;
    if(bIsLeft){
        switch(eBracketType){
            case SmBracketType::Round:
                aTok = SmToken(TLPARENT, MS_LPARENT, u"("_ustr, TG::LBrace, 5);
                break;
            case SmBracketType::Square:
                aTok = SmToken(TLBRACKET, MS_LBRACKET, u"["_ustr, TG::LBrace, 5);
                break;
            case SmBracketType::Curly:
                aTok = SmToken(TLBRACE, MS_LBRACE, u"lbrace"_ustr, TG::LBrace, 5);
                break;
        }
    } else {
        switch(eBracketType) {
            case SmBracketType::Round:
                aTok = SmToken(TRPARENT, MS_RPARENT, u")"_ustr, TG::RBrace, 5);
                break;
            case SmBracketType::Square:
                aTok = SmToken(TRBRACKET, MS_RBRACKET, u"]"_ustr, TG::RBrace, 5);
                break;
            case SmBracketType::Curly:
                aTok = SmToken(TRBRACE, MS_RBRACE, u"rbrace"_ustr, TG::RBrace, 5);
                break;
        }
    }
    SmNode* pRetVal = new SmMathSymbolNode(aTok);
    pRetVal->SetScaleMode(SmScaleMode::Height);
    return pRetVal;
}

bool SmCursor::InsertRow() {
    AnnotateSelection();

    //Find line
    SmNode *pLine;
    if(HasSelection()) {
        SmNode *pSNode = FindSelectedNode(mpTree);
        assert(pSNode);
        pLine = FindTopMostNodeInLine(pSNode, true);
    } else
        pLine = FindTopMostNodeInLine(mpPosition->CaretPos.pSelectedNode);

    //Find parent and offset in parent
    SmStructureNode *pLineParent = pLine->GetParent();
    int nParentIndex = pLineParent->IndexOfSubNode(pLine);
    assert(nParentIndex >= 0 && nParentIndex < INT_MAX);

    //Discover the context of this command
    SmTableNode  *pTable  = nullptr;
    SmMatrixNode *pMatrix = nullptr;
    int nTableIndex = nParentIndex;
    if(pLineParent->GetType() == SmNodeType::Table)
        pTable = static_cast<SmTableNode*>(pLineParent);
    //If it's wrapped in a SmLineNode, we can still insert a newline
    else if(pLineParent->GetType() == SmNodeType::Line &&
            pLineParent->GetParent() &&
            pLineParent->GetParent()->GetType() == SmNodeType::Table) {
        //NOTE: This hack might give problems if we stop ignoring SmAlignNode
        pTable = static_cast<SmTableNode*>(pLineParent->GetParent());
        nTableIndex = pTable->IndexOfSubNode(pLineParent);
        assert(nTableIndex >= 0 && nTableIndex < INT_MAX);
    }
    if(pLineParent->GetType() == SmNodeType::Matrix)
        pMatrix = static_cast<SmMatrixNode*>(pLineParent);

    //If we're not in a context that supports InsertRow, return sal_False
    if(!pTable && !pMatrix)
        return false;

    //Now we start editing
    BeginEdit();

    //Convert line to list
    std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
    NodeToList(pLine, *pLineList);

    //Find position in line
    SmNodeList::iterator it;
    if(HasSelection()) {
        //Take the selected nodes and delete them...
        it = TakeSelectedNodesFromList(pLineList.get());
    } else
        it = FindPositionInLineList(pLineList.get(), mpPosition->CaretPos);

    //New caret position after inserting the newline/row in whatever context
    SmCaretPos PosAfterInsert;

    //If we're in the context of a table
    if(pTable) {
        std::unique_ptr<SmNodeList> pNewLineList(new SmNodeList);
        //Move elements from pLineList to pNewLineList
        SmNodeList& rLineList = *pLineList;
        pNewLineList->splice(pNewLineList->begin(), rLineList, it, rLineList.end());
        //Make sure it is valid again
        it = pLineList->end();
        if(it != pLineList->begin())
            --it;
        if(pNewLineList->empty())
            pNewLineList->push_front(new SmPlaceNode());
        //Parse new line
        std::unique_ptr<SmNode> pNewLine(SmNodeListParser().Parse(pNewLineList.get()));
        pNewLineList.reset();
        //Wrap pNewLine in SmLineNode if needed
        if(pLineParent->GetType() == SmNodeType::Line) {
            std::unique_ptr<SmLineNode> pNewLineNode(new SmLineNode(SmToken(TNEWLINE, '\0', u"newline"_ustr)));
            pNewLineNode->SetSubNodes(std::move(pNewLine), nullptr);
            pNewLine = std::move(pNewLineNode);
        }
        //Get position
        PosAfterInsert = SmCaretPos(pNewLine.get(), 0);
        //Move other nodes if needed
        forint i = pTable->GetNumSubNodes(); i > nTableIndex + 1; i--)
            pTable->SetSubNode(i, pTable->GetSubNode(i-1));

        //Insert new line
        pTable->SetSubNode(nTableIndex + 1, pNewLine.release());

        //Check if we need to change token type:
        if(pTable->GetNumSubNodes() > 2 && pTable->GetToken().eType == TBINOM) {
            SmToken tok = pTable->GetToken();
            tok.eType = TSTACK;
            pTable->SetToken(tok);
        }
    }
    //If we're in the context of a matrix
    else {
        //Find position after insert and patch the list
        PosAfterInsert = PatchLineList(pLineList.get(), it);
        //Move other children
        sal_uInt16 rows = pMatrix->GetNumRows();
        sal_uInt16 cols = pMatrix->GetNumCols();
        int nRowStart = (nParentIndex - nParentIndex % cols) + cols;
        forint i = pMatrix->GetNumSubNodes() + cols - 1; i >= nRowStart + cols; i--)
            pMatrix->SetSubNode(i, pMatrix->GetSubNode(i - cols));
        forint i = nRowStart; i < nRowStart + cols; i++) {
            SmPlaceNode *pNewLine = new SmPlaceNode();
            if(i == nParentIndex + cols)
                PosAfterInsert = SmCaretPos(pNewLine, 0);
            pMatrix->SetSubNode(i, pNewLine);
        }
        pMatrix->SetRowCol(rows + 1, cols);
    }

    //Finish editing
    FinishEdit(std::move(pLineList), pLineParent, nParentIndex, PosAfterInsert);
    //FinishEdit is actually used to handle situations where parent is an instance of
    //SmSubSupNode. In this case parent should always be a table or matrix, however, for
    //code reuse we just use FinishEdit() here too.
    return true;
}

void SmCursor::InsertFraction() {
    AnnotateSelection();

    //Find line
    SmNode *pLine;
    if(HasSelection()) {
        SmNode *pSNode = FindSelectedNode(mpTree);
        assert(pSNode);
        pLine = FindTopMostNodeInLine(pSNode, true);
    } else
        pLine = FindTopMostNodeInLine(mpPosition->CaretPos.pSelectedNode);

    //Find Parent and offset in parent
    SmStructureNode *pLineParent = pLine->GetParent();
    int nParentIndex = pLineParent->IndexOfSubNode(pLine);
    assert(nParentIndex >= 0);

    //We begin modifying the tree here
    BeginEdit();

    //Convert line to list
    std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
    NodeToList(pLine, *pLineList);

    //Take the selection, and/or find iterator for current position
    std::unique_ptr<SmNodeList> pSelectedNodesList(new SmNodeList);
    SmNodeList::iterator it;
    if(HasSelection())
        it = TakeSelectedNodesFromList(pLineList.get(), pSelectedNodesList.get());
    else
        it = FindPositionInLineList(pLineList.get(), mpPosition->CaretPos);

    //Create pNum, and pDenom
    bool bEmptyFraction = pSelectedNodesList->empty();
    std::unique_ptr<SmNode> pNum( bEmptyFraction
        ? new SmPlaceNode()
        : SmNodeListParser().Parse(pSelectedNodesList.get()) );
    std::unique_ptr<SmNode> pDenom(new SmPlaceNode());
    pSelectedNodesList.reset();

    //Create new fraction
    SmBinVerNode *pFrac = new SmBinVerNode(SmToken(TOVER, '\0', u"over"_ustr, TG::Product, 0));
    std::unique_ptr<SmNode> pRect(new SmRectangleNode(SmToken()));
    pFrac->SetSubNodes(std::move(pNum), std::move(pRect), std::move(pDenom));

    //Insert in pLineList
    SmNodeList::iterator patchIt = pLineList->insert(it, pFrac);
    PatchLineList(pLineList.get(), patchIt);
    PatchLineList(pLineList.get(), it);

    //Finish editing
    SmNode *pSelectedNode = bEmptyFraction ? pFrac->GetSubNode(0) : pFrac->GetSubNode(2);
    FinishEdit(std::move(pLineList), pLineParent, nParentIndex, SmCaretPos(pSelectedNode, 1));
}

void SmCursor::InsertText(const OUString& aString)
{
    BeginEdit();

    Delete();

    SmToken token;
    token.eType = TIDENT;
    token.cMathChar = u""_ustr;
    token.nGroup = TG::NONE;
    token.nLevel = 5;
    token.aText = aString;

    SmTextNode* pText = new SmTextNode(token, FNT_VARIABLE);
    pText->SetText(aString);
    pText->AdjustFontDesc();
    pText->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);

    std::unique_ptr<SmNodeList> pList(new SmNodeList);
    pList->push_front(pText);
    InsertNodes(std::move(pList));

    EndEdit();
}

void SmCursor::InsertElement(SmFormulaElement element){
    BeginEdit();

    Delete();

    //Create new node
    SmNode* pNewNode = nullptr;
    switch(element){
        case BlankElement:
        {
            SmToken token;
            token.eType = TBLANK;
            token.nGroup = TG::Blank;
            token.aText = "~";
            SmBlankNode* pBlankNode = new SmBlankNode(token);
            pBlankNode->IncreaseBy(token);
            pNewNode = pBlankNode;
        }break;
        case FactorialElement:
        {
            SmToken token(TFACT, MS_FACT, u"fact"_ustr, TG::UnOper, 5);
            pNewNode = new SmMathSymbolNode(token);
        }break;
        case PlusElement:
        {
            SmToken token;
            token.eType = TPLUS;
            token.setChar(MS_PLUS);
            token.nGroup = TG::UnOper | TG::Sum;
            token.nLevel = 5;
            token.aText = "+";
            pNewNode = new SmMathSymbolNode(token);
        }break;
        case MinusElement:
        {
            SmToken token;
            token.eType = TMINUS;
            token.setChar(MS_MINUS);
            token.nGroup = TG::UnOper | TG::Sum;
            token.nLevel = 5;
            token.aText = "-";
            pNewNode = new SmMathSymbolNode(token);
        }break;
        case CDotElement:
        {
            SmToken token;
            token.eType = TCDOT;
            token.setChar(MS_CDOT);
            token.nGroup = TG::Product;
            token.aText = "cdot";
            pNewNode = new SmMathSymbolNode(token);
        }break;
        case EqualElement:
        {
            SmToken token;
            token.eType = TASSIGN;
            token.setChar(MS_ASSIGN);
            token.nGroup = TG::Relation;
            token.aText = "=";
            pNewNode = new SmMathSymbolNode(token);
        }break;
        case LessThanElement:
        {
            SmToken token;
            token.eType = TLT;
            token.setChar(MS_LT);
            token.nGroup = TG::Relation;
            token.aText = "<";
            pNewNode = new SmMathSymbolNode(token);
        }break;
        case GreaterThanElement:
        {
            SmToken token;
            token.eType = TGT;
            token.setChar(MS_GT);
            token.nGroup = TG::Relation;
            token.aText = ">";
            pNewNode = new SmMathSymbolNode(token);
        }break;
        case PercentElement:
        {
            SmToken token;
            token.eType = TTEXT;
            token.setChar(MS_PERCENT);
            token.nGroup = TG::NONE;
            token.aText = "\"%\"";
            pNewNode = new SmMathSymbolNode(token);
        }break;
    }
    assert(pNewNode);

    //Prepare the new node
    pNewNode->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);

    //Insert new node
    std::unique_ptr<SmNodeList> pList(new SmNodeList);
    pList->push_front(pNewNode);
    InsertNodes(std::move(pList));

    EndEdit();
}

void SmCursor::InsertSpecial(std::u16string_view _aString)
{
    BeginEdit();
    Delete();

    OUString aString( comphelper::string::strip(_aString, ' ') );

    //Create instance of special node
    SmToken token;
    token.eType = TSPECIAL;
    token.cMathChar = u""_ustr;
    token.nGroup = TG::NONE;
    token.nLevel = 5;
    token.aText = aString;
    SmSpecialNode* pSpecial = new SmSpecialNode(token);

    //Prepare the special node
    pSpecial->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);

    //Insert the node
    std::unique_ptr<SmNodeList> pList(new SmNodeList);
    pList->push_front(pSpecial);
    InsertNodes(std::move(pList));

    EndEdit();
}

void SmCursor::InsertCommandText(const OUString& aCommandText) {
    //Parse the sub expression
    auto xSubExpr = mpDocShell->GetParser()->ParseExpression(aCommandText);

    //Prepare the subtree
    xSubExpr->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);

    //Convert subtree to list
    SmNode* pSubExpr = xSubExpr.release();
    std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
    NodeToList(pSubExpr, *pLineList);

    BeginEdit();

    //Delete any selection
    Delete();

    //Insert it
    InsertNodes(std::move(pLineList));

    EndEdit();
}

void SmCursor::Copy(vcl::Window* pWindow)
{
    if(!HasSelection())
        return;

    AnnotateSelection();
    //Find selected node
    SmNode* pSNode = FindSelectedNode(mpTree);
    assert(pSNode);
    //Find visual line
    SmNode* pLine = FindTopMostNodeInLine(pSNode, true);
    assert(pLine);

    //Clone selected nodes
    // TODO: Simplify all this cloning since we only need a formula string now.
    SmClipboard aClipboard;
    if(IsLineCompositionNode(pLine))
        CloneLineToClipboard(static_cast<SmStructureNode*>(pLine), &aClipboard);
    else{
        //Special care to only clone selected text
        if(pLine->GetType() == SmNodeType::Text) {
            SmTextNode *pText = static_cast<SmTextNode*>(pLine);
            std::unique_ptr<SmTextNode> pClone(new SmTextNode( pText->GetToken(), pText->GetFontDesc() ));
            int start  = pText->GetSelectionStart(),
                length = pText->GetSelectionEnd() - pText->GetSelectionStart();
            pClone->ChangeText(pText->GetText().copy(start, length));
            pClone->SetScaleMode(pText->GetScaleMode());
            aClipboard.push_front(std::move(pClone));
        } else {
            SmCloningVisitor aCloneFactory;
            aClipboard.push_front(std::unique_ptr<SmNode>(aCloneFactory.Clone(pLine)));
        }
    }

    // Parse list of nodes to a tree
    SmNodeListParser parser;
    SmNode* pTree(parser.Parse(CloneList(aClipboard).get()));

    // Parse the tree to a string
    OUString aString;
    SmNodeToTextVisitor(pTree, aString);

    //Set clipboard
    auto xClipboard(pWindow ? pWindow->GetClipboard() : GetSystemClipboard());
    vcl::unohelper::TextDataObject::CopyStringTo(aString, xClipboard);
}

void SmCursor::Paste(vcl::Window* pWindow)
{
    BeginEdit();
    Delete();

    auto xClipboard(pWindow ? pWindow->GetClipboard() : GetSystemClipboard());
    auto aDataHelper(TransferableDataHelper::CreateFromClipboard(xClipboard));
    if (aDataHelper.GetTransferable().is())
    {
        // TODO: Support MATHML
        auto nId = SotClipboardFormatId::STRING;
        if (aDataHelper.HasFormat(nId))
        {
            OUString aString;
            if (aDataHelper.GetString(nId, aString))
                InsertCommandText(aString);
        }
    }

    EndEdit();
}

std::unique_ptr<SmNodeList> SmCursor::CloneList(SmClipboard &rClipboard){
    SmCloningVisitor aCloneFactory;
    std::unique_ptr<SmNodeList> pClones(new SmNodeList);

    for(auto &xNode : rClipboard){
        SmNode *pClone = aCloneFactory.Clone(xNode.get());
        pClones->push_back(pClone);
    }

    return pClones;
}

SmNode* SmCursor::FindTopMostNodeInLine(SmNode* pSNode, bool MoveUpIfSelected){
    assert(pSNode);
    //Move up parent until we find a node who's
    //parent is NULL or isn't selected and not a type of:
    //      SmExpressionNode
    //      SmLineNode
    //      SmBinHorNode
    //      SmUnHorNode
    //      SmAlignNode
    //      SmFontNode
    while(pSNode->GetParent() &&
          ((MoveUpIfSelected &&
            pSNode->GetParent()->IsSelected()) ||
           IsLineCompositionNode(pSNode->GetParent())))
        pSNode = pSNode->GetParent();
    //Now we have the selection line node
    return pSNode;
}

SmNode* SmCursor::FindSelectedNode(SmNode* pNode){
    if(pNode->GetNumSubNodes() == 0)
        return nullptr;
    for(auto pChild : *static_cast<SmStructureNode*>(pNode))
    {
        if(!pChild)
            continue;
        if(pChild->IsSelected())
            return pChild;
        SmNode* pRetVal = FindSelectedNode(pChild);
        if(pRetVal)
            return pRetVal;
    }
    return nullptr;
}

void SmCursor::LineToList(SmStructureNode* pLine, SmNodeList& list){
    for(auto pChild : *pLine)
    {
        if (!pChild)
            continue;
        switch(pChild->GetType()){
            case SmNodeType::Line:
            case SmNodeType::UnHor:
            case SmNodeType::Expression:
            case SmNodeType::BinHor:
            case SmNodeType::Align:
            case SmNodeType::Font:
                LineToList(static_cast<SmStructureNode*>(pChild), list);
                break;
            case SmNodeType::Error:
                delete pChild;
                break;
            default:
                list.push_back(pChild);
        }
    }
    pLine->ClearSubNodes();
    delete pLine;
}

void SmCursor::CloneLineToClipboard(SmStructureNode* pLine, SmClipboard* pClipboard){
    SmCloningVisitor aCloneFactory;
    for(auto pChild : *pLine)
    {
        if (!pChild)
            continue;
        if( IsLineCompositionNode( pChild ) )
            CloneLineToClipboard( static_cast<SmStructureNode*>(pChild), pClipboard );
        else if( pChild->IsSelected() && pChild->GetType() != SmNodeType::Error ) {
            //Only clone selected text from SmTextNode
            if(pChild->GetType() == SmNodeType::Text) {
                SmTextNode *pText = static_cast<SmTextNode*>(pChild);
                std::unique_ptr<SmTextNode> pClone(new SmTextNode( pChild->GetToken(), pText->GetFontDesc() ));
                int start = pText->GetSelectionStart(),
                    length = pText->GetSelectionEnd() - pText->GetSelectionStart();
                pClone->ChangeText(pText->GetText().copy(start, length));
                pClone->SetScaleMode(pText->GetScaleMode());
                pClipboard->push_back(std::move(pClone));
            } else
                pClipboard->push_back(std::unique_ptr<SmNode>(aCloneFactory.Clone(pChild)));
        }
    }
}

bool SmCursor::IsLineCompositionNode(SmNode const * pNode){
    switch(pNode->GetType()){
        case SmNodeType::Line:
        case SmNodeType::UnHor:
        case SmNodeType::Expression:
        case SmNodeType::BinHor:
        case SmNodeType::Align:
        case SmNodeType::Font:
            return true;
        default:
            return false;
    }
}

int SmCursor::CountSelectedNodes(SmNode* pNode){
    if(pNode->GetNumSubNodes() == 0)
        return 0;
    int nCount = 0;
    for(auto pChild : *static_cast<SmStructureNode*>(pNode))
    {
        if (!pChild)
            continue;
        if(pChild->IsSelected() && !IsLineCompositionNode(pChild))
            nCount++;
        nCount += CountSelectedNodes(pChild);
    }
    return nCount;
}

bool SmCursor::HasComplexSelection(){
    if(!HasSelection())
        return false;
    AnnotateSelection();

    return CountSelectedNodes(mpTree) > 1;
}

void SmCursor::FinishEdit(std::unique_ptr<SmNodeList> pLineList,
                          SmStructureNode* pParent,
                          int nParentIndex,
                          SmCaretPos PosAfterEdit,
                          SmNode* pStartLine) {
    //Store number of nodes in line for later
    int entries = pLineList->size();

    //Parse list of nodes to a tree
    SmNodeListParser parser;
    std::unique_ptr<SmNode> pLine(parser.Parse(pLineList.get()));
    pLineList.reset();

    //Check if we're making the body of a subsup node bigger than one
    if(pParent->GetType() == SmNodeType::SubSup &&
       nParentIndex == 0 &&
       entries > 1) {
        //Wrap pLine in scalable round brackets
        SmToken aTok(TLEFT, '\0', u"left"_ustr, TG::NONE, 5);
        std::unique_ptr<SmBraceNode> pBrace(new SmBraceNode(aTok));
        pBrace->SetScaleMode(SmScaleMode::Height);
        std::unique_ptr<SmNode> pLeft(  CreateBracket(SmBracketType::Round, true) ),
                                pRight( CreateBracket(SmBracketType::Round, false) );
        std::unique_ptr<SmBracebodyNode> pBody(new SmBracebodyNode(SmToken()));
        pBody->SetSubNodes(std::move(pLine), nullptr);
        pBrace->SetSubNodes(std::move(pLeft), std::move(pBody), std::move(pRight));
        pBrace->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
        pLine = std::move(pBrace);
        //TODO: Consider the following alternative behavior:
        //Consider the line: A + {B + C}^D lsub E
        //Here pLineList is B, + and C and pParent is a subsup node with
        //both RSUP and LSUB set. Imagine the user just inserted "B +" in
        //the body of the subsup node...
        //The most natural thing to do would be to make the line like this:
        //A + B lsub E + C ^ D
        //E.g. apply LSUB and LSUP to the first element in pLineList and RSUP
        //and RSUB to the last element in pLineList. But how should this act
        //for CSUP and CSUB ???
        //For this reason and because brackets was faster to implement, this solution
        //have been chosen. It might be worth working on the other solution later...
    }

    //Set pStartLine if NULL
    if(!pStartLine)
        pStartLine = pLine.get();

    //Insert it back into the parent
    pParent->SetSubNode(nParentIndex, pLine.release());

    //Rebuild graph of caret position
    mpAnchor = nullptr;
    mpPosition = nullptr;
    BuildGraph();
    AnnotateSelection(); //Update selection annotation!

    //Set caret position
    if(!SetCaretPosition(PosAfterEdit))
        SetCaretPosition(SmCaretPos(pStartLine, 0));

    //End edit section
    EndEdit();
}

void SmCursor::BeginEdit(){
    if(mnEditSections++ > 0) return;

    mbIsEnabledSetModifiedSmDocShell = mpDocShell->IsEnableSetModified();
    if( mbIsEnabledSetModifiedSmDocShell )
        mpDocShell->EnableSetModified( false );
}

void SmCursor::EndEdit(){
    if(--mnEditSections > 0) return;

    mpDocShell->SetFormulaArranged(false);
    //Okay, I don't know what this does... :)
    //It's used in SmDocShell::SetText and with places where everything is modified.
    //I think it does some magic, with sfx, but everything is totally undocumented so
    //it's kinda hard to tell...
    if ( mbIsEnabledSetModifiedSmDocShell )
        mpDocShell->EnableSetModified( mbIsEnabledSetModifiedSmDocShell );
    //I think this notifies people around us that we've modified this document...
    mpDocShell->SetModified();
    //I think SmDocShell uses this value when it sends an update graphics event
    //Anyway comments elsewhere suggests it needs to be updated...
    mpDocShell->mnModifyCount++;

    //TODO: Consider copying the update accessibility code from SmDocShell::SetText in here...
    //This somehow updates the size of SmGraphicView if it is running in embedded mode
    if( mpDocShell->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
        mpDocShell->OnDocumentPrinterChanged(nullptr);

    //Request a repaint...
    RequestRepaint();

    //Update the edit engine and text of the document
    OUString formula;
    SmNodeToTextVisitor(mpTree, formula);
    mpDocShell->maText = formula;
    mpDocShell->GetEditEngine().QuickInsertText(formula, ESelection::All());
    mpDocShell->GetEditEngine().QuickFormatDoc();
}

void SmCursor::RequestRepaint()
{
    if (SmViewShell *pViewSh = SmGetActiveView())
    {
        if (comphelper::LibreOfficeKit::isActive())
        {
            pViewSh->SendCaretToLOK();
        }
        else if ( SfxObjectCreateMode::EMBEDDED == mpDocShell->GetCreateMode() )
            mpDocShell->Repaint();
        else
            pViewSh->GetGraphicWidget().Invalidate();
    }
}

bool SmCursor::IsAtTailOfBracket(SmBracketType eBracketType) const
{
    const SmCaretPos pos = GetPosition();
    if (!pos.IsValid()) {
        return false;
    }

    SmNode* pNode = pos.pSelectedNode;

    if (pNode->GetType() == SmNodeType::Text) {
        SmTextNode* pTextNode = static_cast<SmTextNode*>(pNode);
        if (pos.nIndex < pTextNode->GetText().getLength()) {
            // The cursor is on a text node and at the middle of it.
            return false;
        }
    } else {
        if (pos.nIndex < 1) {
            return false;
        }
    }

    while (true) {
        SmStructureNode* pParentNode = pNode->GetParent();
        if (!pParentNode) {
            // There's no brace body node in the ancestors.
            return false;
        }

        int index = pParentNode->IndexOfSubNode(pNode);
        assert(index >= 0);
        if (static_cast<size_t>(index + 1) != pParentNode->GetNumSubNodes()) {
            // The cursor is not at the tail at one of ancestor nodes.
            return false;
        }

        pNode = pParentNode;
        if (pNode->GetType() == SmNodeType::Bracebody) {
            // Found the brace body node.
            break;
        }
    }

    SmStructureNode* pBraceNodeTmp = pNode->GetParent();
    if (!pBraceNodeTmp || pBraceNodeTmp->GetType() != SmNodeType::Brace) {
        // Brace node is invalid.
        return false;
    }

    SmBraceNode* pBraceNode = static_cast<SmBraceNode*>(pBraceNodeTmp);
    SmMathSymbolNode* pClosingNode = pBraceNode->ClosingBrace();
    if (!pClosingNode) {
        // Couldn't get closing symbol node.
        return false;
    }

    // Check if the closing brace matches eBracketType.
    SmTokenType eClosingTokenType = pClosingNode->GetToken().eType;
    switch (eBracketType) {
    case SmBracketType::Round:        if (eClosingTokenType != TRPARENT)   { return false; } break;
    case SmBracketType::Square:       if (eClosingTokenType != TRBRACKET)  { return false; } break;
    case SmBracketType::Curly:        if (eClosingTokenType != TRBRACE)    { return false; } break;
    default:
        return false;
    }

    return true;
}

/////////////////////////////////////// SmNodeListParser

SmNode* SmNodeListParser::Parse(SmNodeList* list){
    pList = list;
    //Delete error nodes
    SmNodeList::iterator it = pList->begin();
    while(it != pList->end()) {
        if((*it)->GetType() == SmNodeType::Error){
            //Delete and erase
            delete *it;
            it = pList->erase(it);
        }else
            ++it;
    }
    SmNode* retval = Expression();
    pList = nullptr;
    return retval;
}

SmNode* SmNodeListParser::Expression(){
    SmNodeArray NodeArray;
    //Accept as many relations as there is
    while(Terminal())
        NodeArray.push_back(Relation());

    //Create SmExpressionNode, I hope SmToken() will do :)
    SmStructureNode* pExpr = new SmExpressionNode(SmToken());
    pExpr->SetSubNodes(std::move(NodeArray));
    return pExpr;
}

SmNode* SmNodeListParser::Relation(){
    //Read a sum
    std::unique_ptr<SmNode> pLeft(Sum());
    //While we have tokens and the next is a relation
    while(Terminal() && IsRelationOperator(Terminal()->GetToken())){
        //Take the operator
        std::unique_ptr<SmNode> pOper(Take());
        //Find the right side of the relation
        std::unique_ptr<SmNode> pRight(Sum());
        //Create new SmBinHorNode
        std::unique_ptr<SmStructureNode> pNewNode(new SmBinHorNode(SmToken()));
        pNewNode->SetSubNodes(std::move(pLeft), std::move(pOper), std::move(pRight));
        pLeft = std::move(pNewNode);
    }
    return pLeft.release();
}

SmNode* SmNodeListParser::Sum(){
    //Read a product
    std::unique_ptr<SmNode> pLeft(Product());
    //While we have tokens and the next is a sum
    while(Terminal() && IsSumOperator(Terminal()->GetToken())){
        //Take the operator
        std::unique_ptr<SmNode> pOper(Take());
        //Find the right side of the sum
        std::unique_ptr<SmNode> pRight(Product());
        //Create new SmBinHorNode
        std::unique_ptr<SmStructureNode> pNewNode(new SmBinHorNode(SmToken()));
        pNewNode->SetSubNodes(std::move(pLeft), std::move(pOper), std::move(pRight));
        pLeft = std::move(pNewNode);
    }
    return pLeft.release();
}

SmNode* SmNodeListParser::Product(){
    //Read a Factor
    std::unique_ptr<SmNode> pLeft(Factor());
    //While we have tokens and the next is a product
    while(Terminal() && IsProductOperator(Terminal()->GetToken())){
        //Take the operator
        std::unique_ptr<SmNode> pOper(Take());
        //Find the right side of the operation
        std::unique_ptr<SmNode> pRight(Factor());
        //Create new SmBinHorNode
        std::unique_ptr<SmStructureNode> pNewNode(new SmBinHorNode(SmToken()));
        pNewNode->SetSubNodes(std::move(pLeft), std::move(pOper), std::move(pRight));
        pLeft = std::move(pNewNode);
    }
    return pLeft.release();
}

SmNode* SmNodeListParser::Factor(){
    //Read unary operations
    if(!Terminal())
        return Error();
    //Take care of unary operators
    else if(IsUnaryOperator(Terminal()->GetToken()))
    {
        SmStructureNode *pUnary = new SmUnHorNode(SmToken());
        std::unique_ptr<SmNode> pOper(Terminal()),
                                pArg;

        if(Next())
            pArg.reset(Factor());
        else
            pArg.reset(Error());

        pUnary->SetSubNodes(std::move(pOper), std::move(pArg));
        return pUnary;
    }
    return Postfix();
}

SmNode* SmNodeListParser::Postfix(){
    if(!Terminal())
        return Error();
    std::unique_ptr<SmNode> pArg;
    if(IsPostfixOperator(Terminal()->GetToken()))
        pArg.reset(Error());
    else if(IsOperator(Terminal()->GetToken()))
        return Error();
    else
        pArg.reset(Take());
    while(Terminal() && IsPostfixOperator(Terminal()->GetToken())) {
        std::unique_ptr<SmStructureNode> pUnary(new SmUnHorNode(SmToken()) );
        std::unique_ptr<SmNode> pOper(Take());
        pUnary->SetSubNodes(std::move(pArg), std::move(pOper));
        pArg = std::move(pUnary);
    }
    return pArg.release();
}

SmNode* SmNodeListParser::Error(){
    return new SmErrorNode(SmToken());
}

bool SmNodeListParser::IsOperator(const SmToken &token) {
    return  IsRelationOperator(token) ||
            IsSumOperator(token) ||
            IsProductOperator(token) ||
            IsUnaryOperator(token) ||
            IsPostfixOperator(token);
}

bool SmNodeListParser::IsRelationOperator(const SmToken &token) {
    return bool(token.nGroup & TG::Relation);
}

bool SmNodeListParser::IsSumOperator(const SmToken &token) {
    return bool(token.nGroup & TG::Sum);
}

bool SmNodeListParser::IsProductOperator(const SmToken &token) {
    return token.nGroup & TG::Product &&
           token.eType != TWIDESLASH &&
           token.eType != TWIDEBACKSLASH &&
           token.eType != TUNDERBRACE &&
           token.eType != TOVERBRACE &&
           token.eType != TOVER;
}

bool SmNodeListParser::IsUnaryOperator(const SmToken &token) {
    return  token.nGroup & TG::UnOper &&
            (token.eType == TPLUS ||
             token.eType == TMINUS ||
             token.eType == TPLUSMINUS ||
             token.eType == TMINUSPLUS ||
             token.eType == TNEG ||
             token.eType == TUOPER);
}

bool SmNodeListParser::IsPostfixOperator(const SmToken &token) {
    return token.eType == TFACT;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=86 H=96 G=90

¤ Dauer der Verarbeitung: 0.15 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.