    /* -*- 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/.
     *
     * This file incorporates work covered by the following license notice:
     *
     *   Licensed to the Apache Software Foundation (ASF) under one or more
     *   contributor license agreements. See the NOTICE file distributed
     *   with this work for additional information regarding copyright
     *   ownership. The ASF licenses this file to you under the Apache
     *   License, Version 2.0 (the "License"); you may not use this file
     *   except in compliance with the License. You may obtain a copy of
     *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
     */

    #include <stdlib.h>

    #include <libxml/xmlwriter.h>

    #include <node.hxx>
    #include <doc.hxx>
    #include <IDocumentUndoRedo.hxx>
    #include <IDocumentFieldsAccess.hxx>
    #include <IDocumentLayoutAccess.hxx>
    #include <pam.hxx>
    #include <txtfld.hxx>
    #include <fmtfld.hxx>
    #include <hints.hxx>
    #include <numrule.hxx>
    #include <ndtxt.hxx>
    #include <ndnotxt.hxx>
    #include <swtable.hxx>
    #include <tblsel.hxx>
    #include <section.hxx>
    #include <ddefld.hxx>
    #include <swddetbl.hxx>
    #include <frame.hxx>
    #include <txtatr.hxx>
    #include <tox.hxx>
    #include <fmtrfmrk.hxx>
    #include <fmtftn.hxx>

    #include <docsh.hxx>

    typedef std::vector<SwStartNode*> SwSttNdPtrs;

    // function to determine the highest level in the given range
    sal_uInt16 HighestLevel( SwNodes & rNodes, const SwNodeRange & rRange );

    /** Constructor
     *
     * creates the base sections (PostIts, Inserts, AutoText, RedLines, Content)
     *
     * @param pDocument TODO: provide documentation
     */
    SwNodes::SwNodes( SwDoc* pDocument )
        : m_vIndices(nullptr), m_pMyDoc( pDocument )
    {
        m_bInNodesDel = m_bInDelUpdOutline = false;

        OSL_ENSURE( m_pMyDoc, "in which Doc am I?" );

        sal_uLong nPos = 0;
        SwStartNode* pSttNd = new SwStartNode( *this, nPos++ );
        m_pEndOfPostIts = new SwEndNode( *this, nPos++, *pSttNd );

        SwStartNode* pTmp = new SwStartNode( *this, nPos++ );
        m_pEndOfInserts = new SwEndNode( *this, nPos++, *pTmp );

        pTmp = new SwStartNode( *this, nPos++ );
        pTmp->m_pStartOfSection = pSttNd;
        m_pEndOfAutotext = new SwEndNode( *this, nPos++, *pTmp );

        pTmp = new SwStartNode( *this, nPos++ );
        pTmp->m_pStartOfSection = pSttNd;
        m_pEndOfRedlines = new SwEndNode( *this, nPos++, *pTmp );

        pTmp = new SwStartNode( *this, nPos++ );
        pTmp->m_pStartOfSection = pSttNd;
        m_pEndOfContent.reset(new SwEndNode( *this, nPos++, *pTmp ));

        m_pOutlineNodes.reset(new SwOutlineNodes);
    }

    /** Destructor
     *
     * Deletes all nodes whose pointer are in a dynamic array. This should be no
     * problem as nodes cannot be created outside this array and, thus, cannot be
     * part of multiple arrays.
     */
    SwNodes::~SwNodes()
    {
        m_pOutlineNodes.reset();

        {
            SwNodeIndex aNdIdx( *this );
            while( true )
            {
                SwNode *pNode = &aNdIdx.GetNode();
                if( pNode == m_pEndOfContent.get() )
                    break;

                ++aNdIdx;
                delete pNode;
            }
        }

        // here, all SwNodeIndices must be unregistered
        m_pEndOfContent.reset();
    }

    void SwNodes::ChgNode( SwNodeIndex const & rDelPos, sal_uLong nSz,
                            SwNodeIndex& rInsPos, bool bNewFrames )
    {
        // no need for frames in the UndoArea
        SwNodes& rNds = rInsPos.GetNodes();
        const SwNode* pPrevInsNd = rNds[ rInsPos.GetIndex() -1 ];

        // declare all fields as invalid, updating will happen
        // in the idle-handler of the doc
        if( GetDoc()->getIDocumentFieldsAccess().SetFieldsDirty( true, &rDelPos.GetNode(), nSz ) &&
            rNds.GetDoc() != GetDoc() )
            rNds.GetDoc()->getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 0 );

        // NEVER include nodes from the RedLineArea
        sal_uLong nNd = rInsPos.GetIndex();
        bool bInsOutlineIdx = !(
                rNds.GetEndOfRedlines().StartOfSectionNode()->GetIndex() < nNd &&
                nNd < rNds.GetEndOfRedlines().GetIndex() );

        if( &rNds == this ) // if in the same node array -> move
        {
            // Move order: from front to back, so that new entries are added at
            // first position, thus, deletion position stays the same
            const sal_uLong nDiff = rDelPos.GetIndex() < rInsPos.GetIndex() ? 0 : 1;

            for( sal_uLong n = rDelPos.GetIndex(); nSz; n += nDiff, --nSz )
            {
                SwNodeIndex aDelIdx( *this, n );
                SwNode& rNd = aDelIdx.GetNode();

                // #i57920# - correction of refactoring done by cws swnumtree:
                // - <SwTextNode::SetLevel( NO_NUMBERING ) is deprecated and
                //   set <IsCounted> state of the text node to <false>, which
                //   isn't correct here.
                if ( rNd.IsTextNode() )
                {
                    SwTextNode* pTextNode = rNd.GetTextNode();

                    pTextNode->RemoveFromList();

                    if (pTextNode->IsOutline())
                    {
                        const SwNodePtr pSrch = &rNd;
                        m_pOutlineNodes->erase( pSrch );
                    }
                }

                BigPtrArray::Move( aDelIdx.GetIndex(), rInsPos.GetIndex() );

                if( rNd.IsTextNode() )
                {
                    SwTextNode& rTextNd = static_cast<SwTextNode&>(rNd);

                    rTextNd.AddToList();

                    if (bInsOutlineIdx && rTextNd.IsOutline())
                    {
                        const SwNodePtr pSrch = &rNd;
                        m_pOutlineNodes->insert( pSrch );
                    }
                    rTextNd.InvalidateNumRule();

    //FEATURE::CONDCOLL
                    if( RES_CONDTXTFMTCOLL == rTextNd.GetTextColl()->Which() )
                        rTextNd.ChkCondColl();
    //FEATURE::CONDCOLL
                }
                else if( rNd.IsContentNode() )
                    static_cast<SwContentNode&>(rNd).InvalidateNumRule();
            }
        }
        else
        {
            bool bSavePersData(GetDoc()->GetIDocumentUndoRedo().IsUndoNodes(rNds));
            bool bRestPersData(GetDoc()->GetIDocumentUndoRedo().IsUndoNodes(*this));
            SwDoc* pDestDoc = rNds.GetDoc() != GetDoc() ? rNds.GetDoc() : nullptr;
            OSL_ENSURE(!pDestDoc, "SwNodes::ChgNode(): "
                "the code to handle text fields here looks broken\n"
                "if the target is in a different document.");
            if( !bRestPersData && !bSavePersData && pDestDoc )
                bSavePersData = bRestPersData = true;

            OUString sNumRule;
            for( sal_uLong n = 0; n < nSz; n++ )
            {
                SwNode* pNd = &rDelPos.GetNode();

                // NoTextNode keep their persistent data
                if( pNd->IsNoTextNode() )
                {
                    if( bSavePersData )
                        static_cast<SwNoTextNode*>(pNd)->SavePersistentData();
                }
                else if( pNd->IsTextNode() )
                {
                    SwTextNode* pTextNd = static_cast<SwTextNode*>(pNd);

                    // remove outline index from old nodes array
                    if (pTextNd->IsOutline())
                    {
                        m_pOutlineNodes->erase( pNd );
                    }

                    // copy rules if needed
                    if( pDestDoc )
                    {
                        const SwNumRule* pNumRule = pTextNd->GetNumRule();
                        if( pNumRule && sNumRule != pNumRule->GetName() )
                        {
                            sNumRule = pNumRule->GetName();
                            SwNumRule* pDestRule = pDestDoc->FindNumRulePtr( sNumRule );
                            if( pDestRule )
                                pDestRule->SetInvalidRule( true );
                            else
                                pDestDoc->MakeNumRule( sNumRule, pNumRule );
                        }
                    }
                    else
                        // if movement into the UndoNodes-array, update numbering
                        pTextNd->InvalidateNumRule();

                    pTextNd->RemoveFromList();
                }

                RemoveNode( rDelPos.GetIndex(), 1, false ); // move indices
                SwContentNode * pCNd = pNd->GetContentNode();
                rNds.InsertNode( pNd, rInsPos );

                if( pCNd )
                {
                    SwTextNode* pTextNd = pCNd->GetTextNode();
                    if( pTextNd )
                    {
                        SwpHints * const pHts = pTextNd->GetpSwpHints();
                        // OutlineNodes set the new nodes in the array
                        if (bInsOutlineIdx && pTextNd->IsOutline())
                        {
                            rNds.m_pOutlineNodes->insert( pTextNd );
                        }

                        pTextNd->AddToList();

                        // special treatment for fields
                        if( pHts && pHts->Count() )
                        {
                            bool const bToUndo = !pDestDoc &&
                                GetDoc()->GetIDocumentUndoRedo().IsUndoNodes(rNds);
                            for( size_t i = pHts->Count(); i; )
                            {
                                SwTextAttr * const pAttr = pHts->Get( --i );
                                switch ( pAttr->Which() )
                                {
                                case RES_TXTATR_FIELD:
                                case RES_TXTATR_ANNOTATION:
                                case RES_TXTATR_INPUTFIELD:
                                    {
                                        SwTextField* pTextField = static_txtattr_cast<SwTextField*>(pAttr);
                                        rNds.GetDoc()->getIDocumentFieldsAccess().InsDelFieldInFieldLst( !bToUndo, *pTextField );

                                        const SwFieldType* pTyp = pTextField->GetFormatField().GetField()->GetTyp();
                                        if ( SwFieldIds::Postit == pTyp->Which() )
                                        {
                                            rNds.GetDoc()->GetDocShell()->Broadcast(
                                                SwFormatFieldHint(
                                                    &pTextField->GetFormatField(),
                                                    ( pTextField->GetFormatField().IsFieldInDoc()
                                                      ? SwFormatFieldHintWhich::INSERTED
                                                      : SwFormatFieldHintWhich::REMOVED ) ) );
                                        }
                                        else if( SwFieldIds::Dde == pTyp->Which() )
                                        {
                                            if( bToUndo )
                                                const_cast<SwDDEFieldType*>(static_cast<const SwDDEFieldType*>(pTyp))->DecRefCnt();
                                            else
                                                const_cast<SwDDEFieldType*>(static_cast<const SwDDEFieldType*>(pTyp))->IncRefCnt();
                                        }
                                        static_cast<SwFormatField&>(pAttr->GetAttr())
                                            .InvalidateField();
                                    }
                                    break;

                                case RES_TXTATR_FTN:
                                    static_cast<SwFormatFootnote&>(pAttr->GetAttr())
                                        .InvalidateFootnote();
                                    break;

                                case RES_TXTATR_TOXMARK:
                                    static_cast<SwTOXMark&>(pAttr->GetAttr())
                                        .InvalidateTOXMark();
                                    break;

                                case RES_TXTATR_REFMARK:
                                    static_cast<SwFormatRefMark&>(pAttr->GetAttr())
                                        .InvalidateRefMark();
                                    break;

                                case RES_TXTATR_META:
                                case RES_TXTATR_METAFIELD:
                                    {
                                        SwTextMeta *const pTextMeta(
                                            static_txtattr_cast<SwTextMeta*>(pAttr));
                                        // force removal of UNO object
                                        pTextMeta->ChgTextNode(nullptr);
                                        pTextMeta->ChgTextNode(pTextNd);
                                    }
                                    break;

                                default:
                                    break;
                                }
                            }
                        }
                        //FEATURE::CONDCOLL
                        if( RES_CONDTXTFMTCOLL == pTextNd->GetTextColl()->Which() )
                            pTextNd->ChkCondColl();
                        //FEATURE::CONDCOLL
                    }
                    else
                    {
                        // Moved into different Docs? Persist data again!
                        if( pCNd->IsNoTextNode() && bRestPersData )
                            static_cast<SwNoTextNode*>(pCNd)->RestorePersistentData();
                    }
                }
            }
        }

        // declare all fields as invalid, updating will happen
        // in the idle-handler of the doc
        GetDoc()->getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 0 );
        if( rNds.GetDoc() != GetDoc() )
            rNds.GetDoc()->getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 0 );

        if( bNewFrames )
            bNewFrames = &GetDoc()->GetNodes() == &rNds &&
                        GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();

        if( bNewFrames )
        {
            // get the frames:
            SwNodeIndex aIdx( *pPrevInsNd, 1 );
            SwNodeIndex aFrameNdIdx( aIdx );
            SwNode* pFrameNd = rNds.FindPrvNxtFrameNode( aFrameNdIdx,
                                            rNds[ rInsPos.GetIndex() - 1 ] );

            if( !pFrameNd && aFrameNdIdx > rNds.GetEndOfExtras().GetIndex() )
            {
                OSL_ENSURE( false, "here, something wrong happened" );
                aFrameNdIdx = rNds.GetEndOfContent();
                pFrameNd = SwNodes::GoPrevSection( &aFrameNdIdx, true, false );
                if( pFrameNd && !static_cast<SwContentNode*>(pFrameNd)->HasWriterListeners() )
                    pFrameNd = nullptr;
                OSL_ENSURE( pFrameNd, "ChgNode() - no FrameNode found" );
            }
            if( pFrameNd )
                while( aIdx != rInsPos )
                {
                    SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
                    if( pCNd )
                    {
                        if( pFrameNd->IsTableNode() )
                            static_cast<SwTableNode*>(pFrameNd)->MakeFrames( aIdx );
                        else if( pFrameNd->IsSectionNode() )
                            static_cast<SwSectionNode*>(pFrameNd)->MakeFrames( aIdx );
                        else
                            static_cast<SwContentNode*>(pFrameNd)->MakeFrames( *pCNd );
                        pFrameNd = pCNd;
                    }
                    ++aIdx;
                }
        }
    }

    // TODO: provide documentation
    /** move the node pointer
     *
     * Move the node pointer from "(inclusive) start position to (exclusive) end
     * position" to target position.
     * If the target is in front of the first or in the area between first and
     * last element to move, nothing happens.
     * If the area to move is empty or the end position is before the start
     * position, nothing happens.
     *
     * @param aRange range to move (excluding end node)
     * @param rNodes
     * @param aIndex
     * @param bNewFrames
     * @return
     */
    bool SwNodes::MoveNodes( const SwNodeRange& aRange, SwNodes & rNodes,
                        const SwNodeIndex& aIndex, bool bNewFrames )
    {
        SwNode * pCurrentNode;
        if( aIndex == 0 ||
            ( (pCurrentNode = &aIndex.GetNode())->GetStartNode() &&
              !pCurrentNode->StartOfSectionIndex() ))
            return false;

        SwNodeRange aRg( aRange );

        // skip "simple" start or end nodes
        while( SwNodeType::Start == (pCurrentNode = &aRg.aStart.GetNode())->GetNodeType()
                || ( pCurrentNode->IsEndNode() &&
                    !pCurrentNode->m_pStartOfSection->IsSectionNode() ) )
            ++aRg.aStart;
        --aRg.aStart;

        // if aEnd-1 points to no ContentNode, search previous one
        --aRg.aEnd;
        while( ( (( pCurrentNode = &aRg.aEnd.GetNode())->GetStartNode() &&
                !pCurrentNode->IsSectionNode() ) ||
                ( pCurrentNode->IsEndNode() &&
                SwNodeType::Start == pCurrentNode->m_pStartOfSection->GetNodeType()) ) &&
                aRg.aEnd > aRg.aStart )
            --aRg.aEnd;

        // if in same array, check insertion position
        if( aRg.aStart >= aRg.aEnd )
            return false;

        if( this == &rNodes )
        {
            if( ( aIndex.GetIndex()-1 >= aRg.aStart.GetIndex() &&
                  aIndex.GetIndex()-1 < aRg.aEnd.GetIndex()) ||
                ( aIndex.GetIndex()-1 == aRg.aEnd.GetIndex() ) )
                return false;
        }

        sal_uLong nInsPos = 0; // counter for tmp array

        // array as a stack, storing all StartOfSelections
        SwSttNdPtrs aSttNdStack;
        SwSttNdPtrs::size_type nLevel = 0; // level counter

        // set start index
        SwNodeIndex  aIdx( aIndex );

        SwStartNode* pStartNode = aIdx.GetNode().m_pStartOfSection;
        aSttNdStack.insert( aSttNdStack.begin(), pStartNode );

        SwNodeRange aOrigInsPos( aIdx, -1, aIdx ); // original insertion position

        // call DelFrames/MakeFrames for the upmost SectionNode
        int nSectNdCnt = 0;
        bool bSaveNewFrames = bNewFrames;

        // continue until everything has been moved
        while( aRg.aStart < aRg.aEnd )
            switch( (pCurrentNode = &aRg.aEnd.GetNode())->GetNodeType() )
            {
            case SwNodeType::End:
                {
                    if( nInsPos ) // move everything until here
                    {
                        // delete and copy. CAUTION: all indices after
                        // "aRg.aEnd+1" will be moved as well!
                        SwNodeIndex aSwIndex( aRg.aEnd, 1 );
                        ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
                        aIdx -= nInsPos;
                        nInsPos = 0;
                    }

                    SwStartNode* pSttNd = pCurrentNode->m_pStartOfSection;
                    if( pSttNd->IsTableNode() )
                    {
                        SwTableNode* pTableNd = static_cast<SwTableNode*>(pSttNd);

                        // move the whole table/range
                        nInsPos = (aRg.aEnd.GetIndex() -
                                        pSttNd->GetIndex() )+1;
                        aRg.aEnd -= nInsPos;

                        // NEVER include nodes from the RedLineArea
                        sal_uLong nNd = aIdx.GetIndex();
                        bool bInsOutlineIdx = !( rNodes.GetEndOfRedlines().
                                StartOfSectionNode()->GetIndex() < nNd &&
                                nNd < rNodes.GetEndOfRedlines().GetIndex() );

                        if( bNewFrames )
                            // delete all frames
                            pTableNd->DelFrames();
                        if( &rNodes == this ) // move into self?
                        {
                            // move all Start/End/ContentNodes
                            // ContentNodes: delete also the frames!
                            pTableNd->m_pStartOfSection = aIdx.GetNode().m_pStartOfSection;
                            for( sal_uLong n = 0; n < nInsPos; ++n )
                            {
                                SwNodeIndex aMvIdx( aRg.aEnd, 1 );
                                SwContentNode* pCNd = nullptr;
                                SwNode* pTmpNd = &aMvIdx.GetNode();
                                if( pTmpNd->IsContentNode() )
                                {
                                    pCNd = static_cast<SwContentNode*>(pTmpNd);
                                    if( pTmpNd->IsTextNode() )
                                        static_cast<SwTextNode*>(pTmpNd)->RemoveFromList();

                                    // remove outline index from old nodes array
                                    if (pCNd->IsTextNode() && pCNd->GetTextNode()->IsOutline())
                                    {
                                        m_pOutlineNodes->erase( pCNd );
                                    }
                                    else
                                        pCNd = nullptr;
                                }

                                BigPtrArray::Move( aMvIdx.GetIndex(), aIdx.GetIndex() );

                                if( bInsOutlineIdx && pCNd )
                                    m_pOutlineNodes->insert( pCNd );
                                if( pTmpNd->IsTextNode() )
                                    static_cast<SwTextNode*>(pTmpNd)->AddToList();
                            }
                        }
                        else
                        {
                            // get StartNode
                            // Even aIdx points to a startnode, we need the startnode
                            // of the environment of aIdx (#i80941)
                            SwStartNode* pSttNode = aIdx.GetNode().m_pStartOfSection;

                            // get all boxes with content because their indices
                            // pointing to the StartNodes need to be reset
                            // (copying the array and deleting all found ones eases
                            // searching)
                            SwNodeIndex aMvIdx( aRg.aEnd, 1 );
                            for( sal_uLong n = 0; n < nInsPos; ++n )
                            {
                                SwNode* pNd = &aMvIdx.GetNode();

                                const bool bOutlNd = pNd->IsTextNode() && pNd->GetTextNode()->IsOutline();
                                // delete outline indices from old node array
                                if( bOutlNd )
                                    m_pOutlineNodes->erase( pNd );

                                RemoveNode( aMvIdx.GetIndex(), 1, false );
                                pNd->m_pStartOfSection = pSttNode;
                                rNodes.InsertNode( pNd, aIdx );

                                // set correct indices in Start/EndNodes
                                if( bInsOutlineIdx && bOutlNd )
                                    // and put them into the new node array
                                    rNodes.m_pOutlineNodes->insert( pNd );
                                else if( pNd->IsStartNode() )
                                    pSttNode = static_cast<SwStartNode*>(pNd);
                                else if( pNd->IsEndNode() )
                                {
                                    pSttNode->m_pEndOfSection = static_cast<SwEndNode*>(pNd);
                                    if( pSttNode->IsSectionNode() )
                                        static_cast<SwSectionNode*>(pSttNode)->NodesArrChgd();
                                    pSttNode = pSttNode->m_pStartOfSection;
                                }
                            }

                            if( dynamic_cast<const SwDDETable*>(&pTableNd->GetTable()) != nullptr )
                            {
                                SwDDEFieldType* pTyp = static_cast<SwDDETable&>(pTableNd->
                                                    GetTable()).GetDDEFieldType();
                                if( pTyp )
                                {
                                    if( rNodes.IsDocNodes() )
                                        pTyp->IncRefCnt();
                                    else
                                        pTyp->DecRefCnt();
                                }
                            }

                            if (GetDoc()->GetIDocumentUndoRedo().IsUndoNodes(
                                        rNodes))
                            {
                                SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
                                SwPtrMsgPoolItem aMsgHint( RES_REMOVE_UNO_OBJECT,
                                                            pTableFormat );
                                pTableFormat->ModifyNotification( &aMsgHint, &aMsgHint );
                            }
                        }
                        if( bNewFrames )
                        {
                            SwNodeIndex aTmp( aIdx );
                            pTableNd->MakeFrames( &aTmp );
                        }
                        aIdx -= nInsPos;
                        nInsPos = 0;
                    }
                    else if( pSttNd->GetIndex() < aRg.aStart.GetIndex() )
                    {
                        // SectionNode: not the whole section will be moved, thus,
                        //              move only the ContentNodes
                        // StartNode:   create a new section at the given position
                        do {        // middle check loop
                            if( !pSttNd->IsSectionNode() )
                            {
                                // create StartNode and EndNode at InsertPos
                                SwStartNode* pTmp = new SwStartNode( aIdx,
                                                        SwNodeType::Start,
    /*?? NodeType ??*/                                  SwNormalStartNode );

                                nLevel++; // put the index to StartNode on the stack
                                aSttNdStack.insert( aSttNdStack.begin() + nLevel, pTmp );

                                // create EndNode
                                new SwEndNode( aIdx, *pTmp );
                            }
                            else if (GetDoc()->GetIDocumentUndoRedo().IsUndoNodes(
                                        rNodes))
                            {
                                // use placeholder in UndoNodes array
                                new SwPlaceholderNode(aIdx);
                            }
                            else
                            {
                                // JP 18.5.2001 (Bug 70454) creating new section?
                                --aRg.aEnd;
                                break;

                            }

                            --aRg.aEnd;
                            --aIdx;
                        } while( false );
                    }
                    else
                    {
                        // move StartNode and EndNode in total

                        // if Start is exactly the Start of the area,
                        // then the Node needs to be re-visited
                        if( &aRg.aStart.GetNode() == pSttNd )
                            --aRg.aStart;

                        SwSectionNode* pSctNd = pSttNd->GetSectionNode();
                        if( bNewFrames && pSctNd )
                            pSctNd->DelFrames();

                        RemoveNode( aRg.aEnd.GetIndex(), 1, false ); // delete EndNode
                        sal_uLong nSttPos = pSttNd->GetIndex();

                        // this StartNode will be removed later
                        SwStartNode* pTmpSttNd = new SwStartNode( *this, nSttPos+1 );
                        pTmpSttNd->m_pStartOfSection = pSttNd->m_pStartOfSection;

                        RemoveNode( nSttPos, 1, false ); // delete SttNode

                        pSttNd->m_pStartOfSection = aIdx.GetNode().m_pStartOfSection;
                        rNodes.InsertNode( pSttNd, aIdx  );
                        rNodes.InsertNode( pCurrentNode, aIdx );
                        --aIdx;
                        pSttNd->m_pEndOfSection = static_cast<SwEndNode*>(pCurrentNode);

                        --aRg.aEnd;

                        nLevel++; // put the index pointing to the StartNode onto the stack
                        aSttNdStack.insert( aSttNdStack.begin() + nLevel, pSttNd );

                        // reset remaining indices if SectionNode
                        if( pSctNd )
                        {
                            pSctNd->NodesArrChgd();
                            ++nSectNdCnt;
                            bNewFrames = false;
                        }
                    }
                }
                break;

            case SwNodeType::Section:
                if( !nLevel &&
                    GetDoc()->GetIDocumentUndoRedo().IsUndoNodes(rNodes))
                {
                    // here, a SectionDummyNode needs to be inserted at the current position
                    if( nInsPos ) // move everything until here
                    {
                        // delete and copy. CAUTION: all indices after
                        // "aRg.aEnd+1" will be moved as well!
                        SwNodeIndex aSwIndex( aRg.aEnd, 1 );
                        ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
                        aIdx -= nInsPos;
                        nInsPos = 0;
                    }
                    new SwPlaceholderNode(aIdx);
                    --aRg.aEnd;
                    --aIdx;
                    break;
                }
                SAL_FALLTHROUGH;
            case SwNodeType::Table:
            case SwNodeType::Start:
                {
                    // empty section -> nothing to do
                    //  and only if it's a top level section
                    if( !nInsPos && !nLevel )
                    {
                        --aRg.aEnd;
                        break;
                    }

                    if( !nLevel ) // level is decreasing
                    {
                        // create decrease
                        SwNodeIndex aTmpSIdx( aOrigInsPos.aStart, 1 );
                        SwStartNode* pTmpStt = new SwStartNode( aTmpSIdx,
                                    SwNodeType::Start,
                                    static_cast<SwStartNode*>(pCurrentNode)->GetStartNodeType() );

                        --aTmpSIdx;

                        SwNodeIndex aTmpEIdx( aOrigInsPos.aEnd );
                        new SwEndNode( aTmpEIdx, *pTmpStt );
                        --aTmpEIdx;
                        ++aTmpSIdx;

                        // set correct StartOfSection
                        ++aRg.aEnd;
                        {
                            SwNodeIndex aCntIdx( aRg.aEnd );
                            for( sal_uLong n = 0; n < nInsPos; n++, ++aCntIdx)
                                aCntIdx.GetNode().m_pStartOfSection = pTmpStt;
                        }

                        // also set correct StartNode for all decreased nodes
                        while( aTmpSIdx < aTmpEIdx )
                            if( nullptr != (( pCurrentNode = &aTmpEIdx.GetNode())->GetEndNode()) )
                                aTmpEIdx = pCurrentNode->StartOfSectionIndex();
                            else
                            {
                                pCurrentNode->m_pStartOfSection = pTmpStt;
                                --aTmpEIdx;
                            }

                        --aIdx;     // after the inserted StartNode
                        --aRg.aEnd; // before StartNode
                        // copy array. CAUTION: all indices after
                        // "aRg.aEnd+1" will be moved as well!
                        SwNodeIndex aSwIndex( aRg.aEnd, 1 );
                        ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
                        aIdx -= nInsPos+1;
                        nInsPos = 0;
                    }
                    else // all nodes between StartNode and EndNode were moved
                    {
                        OSL_ENSURE( pCurrentNode == aSttNdStack[nLevel] ||
                                ( pCurrentNode->IsStartNode() &&
                                    aSttNdStack[nLevel]->IsSectionNode()),
                                 "wrong StartNode" );

                        SwNodeIndex aSwIndex( aRg.aEnd, 1 );
                        ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
                        aIdx -= nInsPos+1; // before inserted StartNode
                        nInsPos = 0;

                        // remove pointer from node array
                        RemoveNode( aRg.aEnd.GetIndex(), 1, true );
                        --aRg.aEnd;

                        SwSectionNode* pSectNd = aSttNdStack[ nLevel ]->GetSectionNode();
                        if( pSectNd && !--nSectNdCnt )
                        {
                            SwNodeIndex aTmp( *pSectNd );
                            pSectNd->MakeFrames( &aTmp );
                            bNewFrames = bSaveNewFrames;
                        }
                        aSttNdStack.erase( aSttNdStack.begin() + nLevel ); // remove from stack
                        nLevel--;
                    }

                    // delete all resulting empty start/end node pairs
                    SwNode* pTmpNode = (*this)[ aRg.aEnd.GetIndex()+1 ]->GetEndNode();
                    if( pTmpNode && SwNodeType::Start == (pCurrentNode = &aRg.aEnd.GetNode())
                        ->GetNodeType() && pCurrentNode->StartOfSectionIndex() &&
                        pTmpNode->StartOfSectionNode() == pCurrentNode )
                    {
                        DelNodes( aRg.aEnd, 2 );
                        --aRg.aEnd;
                    }
                }
                break;

            case SwNodeType::Text:
                //Add special function to text node.
                {
                    if( bNewFrames && pCurrentNode->GetContentNode() )
                        static_cast<SwContentNode*>(pCurrentNode)->DelFrames();
                    pCurrentNode->m_pStartOfSection = aSttNdStack[ nLevel ];
                    nInsPos++;
                    --aRg.aEnd;
                }
                break;
            case SwNodeType::Grf:
            case SwNodeType::Ole:
                {
                    if( bNewFrames && pCurrentNode->GetContentNode() )
                        static_cast<SwContentNode*>(pCurrentNode)->DelFrames();

                    pCurrentNode->m_pStartOfSection = aSttNdStack[ nLevel ];
                    nInsPos++;
                    --aRg.aEnd;
                }
                break;

            case SwNodeType::PlaceHolder:
                if (GetDoc()->GetIDocumentUndoRedo().IsUndoNodes(*this))
                {
                    if( &rNodes == this ) // inside UndoNodesArray
                    {
                        // move everything
                        pCurrentNode->m_pStartOfSection = aSttNdStack[ nLevel ];
                        nInsPos++;
                    }
                    else // move into "normal" node array
                    {
                        // than a SectionNode (start/end) is needed at the current
                        // InsPos; if so skip it, otherwise ignore current node
                        if( nInsPos ) // move everything until here
                        {
                            // delete and copy. CAUTION: all indices after
                            // "aRg.aEnd+1" will be moved as well!
                            SwNodeIndex aSwIndex( aRg.aEnd, 1 );
                            ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
                            aIdx -= nInsPos;
                            nInsPos = 0;
                        }
                        SwNode* pTmpNd = &aIdx.GetNode();
                        if( pTmpNd->IsSectionNode() ||
                            pTmpNd->StartOfSectionNode()->IsSectionNode() )
                            --aIdx; // skip
                    }
                }
                else {
                    OSL_FAIL( "How can this node be in the node array?" );
                }
                --aRg.aEnd;
                break;

            default:
                OSL_FAIL( "Unknown node type" );
                break;
            }

        if( nInsPos ) // copy remaining rest
        {
            // rest should be ok
            SwNodeIndex aSwIndex( aRg.aEnd, 1 );
            ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
        }
        ++aRg.aEnd; // again, exclusive end

        // delete all resulting empty start/end node pairs
        if( ( pCurrentNode = &aRg.aStart.GetNode())->GetStartNode() &&
            pCurrentNode->StartOfSectionIndex() &&
            aRg.aEnd.GetNode().GetEndNode() )
                DelNodes( aRg.aStart, 2 );

        // initialize numbering update
        ++aOrigInsPos.aStart;
        // Moved in same node array? Then call update top down!
        if( this == &rNodes &&
            aRg.aEnd.GetIndex() >= aOrigInsPos.aStart.GetIndex() )
        {
            UpdateOutlineIdx( aOrigInsPos.aStart.GetNode() );
            UpdateOutlineIdx( aRg.aEnd.GetNode() );
        }
        else
        {
            UpdateOutlineIdx( aRg.aEnd.GetNode() );
            rNodes.UpdateOutlineIdx( aOrigInsPos.aStart.GetNode() );
        }

        return true;
    }

    /** create a start/end section pair
     *
     * Other nodes might be in between.
     *
     * After this method call, the start node of pRange will be pointing to the
     * first node after the start section node and the end node will be the index
     * of the end section node. If this method is called multiple times with the
     * same input, multiple sections containing the previous ones will be created
     * (no content nodes between start or end node).
     *
     * @note Start and end node of the range must be on the same level but MUST
     *       NOT be on the top level.
     *
     * @param [IN,OUT] pRange the range (excl. end)
     * @param eSttNdTyp type of the start node
     */
    void SwNodes::SectionDown(SwNodeRange *pRange, SwStartNodeType eSttNdTyp )
    {
        if( pRange->aStart >= pRange->aEnd ||
            pRange->aEnd >= Count() ||
            !CheckNodesRange( pRange->aStart, pRange->aEnd ))
            return;

        // If the beginning of a range is before or at a start node position, so
        // delete it, otherwise empty S/E or E/S nodes would be created.
        // For other nodes, create a new start node.
        SwNode * pCurrentNode = &pRange->aStart.GetNode();
        SwNodeIndex aTmpIdx( *pCurrentNode->StartOfSectionNode() );

        if( pCurrentNode->GetEndNode() )
            DelNodes( pRange->aStart ); // prevent empty section
        else
        {
            // insert a new StartNode
            SwNode* pSttNd = new SwStartNode( pRange->aStart, SwNodeType::Start, eSttNdTyp );
            pRange->aStart = *pSttNd;
            aTmpIdx = pRange->aStart;
        }

        // If the end of a range is before or at a StartNode, so delete it,
        // otherwise empty S/E or E/S nodes would be created.
        // For other nodes, insert a new end node.
        --pRange->aEnd;
        if( pRange->aEnd.GetNode().GetStartNode() )
            DelNodes( pRange->aEnd );
        else
        {
            ++pRange->aEnd;
            // insert a new EndNode
            new SwEndNode( pRange->aEnd, *pRange->aStart.GetNode().GetStartNode() );
        }
        --pRange->aEnd;

        SectionUpDown( aTmpIdx, pRange->aEnd );
    }

    /** increase level of the given range
     *
     * The range contained in pRange will be lifted to the next higher level.
     * This is done by adding a end node at pRange.start and a start node at
     * pRange.end. Furthermore all indices for this range will be updated.
     *
     * After this method call, the start node of pRange will be pointing to the
     * first node inside the lifted range and the end node will be pointing to the
     * last position inside the lifted range.
     *
     * @param [IN,OUT] pRange the range of nodes where the level should be increased
     */
    void SwNodes::SectionUp(SwNodeRange *pRange)
    {
        if( pRange->aStart >= pRange->aEnd ||
            pRange->aEnd >= Count() ||
            !CheckNodesRange( pRange->aStart, pRange->aEnd ) ||
            ( HighestLevel( *this, *pRange ) <= 1 ))
            return;

        // If the beginning of a range is before or at a start node position, so
        // delete it, otherwise empty S/E or E/S nodes would be created.
        // For other nodes, create a new start node.
        SwNode * pCurrentNode = &pRange->aStart.GetNode();
        SwNodeIndex aIdx( *pCurrentNode->StartOfSectionNode() );
        if( pCurrentNode->IsStartNode() )       // is StartNode itself
        {
            SwEndNode* pEndNd = pRange->aEnd.GetNode().GetEndNode();
            if (pEndNd && pCurrentNode == pEndNd->m_pStartOfSection)
            {
                // there was a pairwise reset, adjust only those in the range
                SwStartNode* pTmpSttNd = pCurrentNode->m_pStartOfSection;
                RemoveNode( pRange->aStart.GetIndex(), 1, true );
                RemoveNode( pRange->aEnd.GetIndex(), 1, true );

                SwNodeIndex aTmpIdx( pRange->aStart );
                while( aTmpIdx < pRange->aEnd )
                {
                    pCurrentNode = &aTmpIdx.GetNode();
                    pCurrentNode->m_pStartOfSection = pTmpSttNd;
                    if( pCurrentNode->IsStartNode() )
                        aTmpIdx = pCurrentNode->EndOfSectionIndex() + 1;
                    else
                        ++aTmpIdx;
                }
                return ;
            }
            DelNodes( pRange->aStart );
        }
        else if( aIdx == pRange->aStart.GetIndex()-1 ) // before StartNode
            DelNodes( aIdx );
        else
            new SwEndNode( pRange->aStart, *aIdx.GetNode().GetStartNode() );

        // If the end of a range is before or at a StartNode, so delete it,
        // otherwise empty S/E or E/S nodes would be created.
        // For other nodes, insert a new end node.
        SwNodeIndex aTmpIdx( pRange->aEnd );
        if( pRange->aEnd.GetNode().IsEndNode() )
            DelNodes( pRange->aEnd );
        else
        {
            new SwStartNode( pRange->aEnd );
    /*?? which NodeType ??*/
            aTmpIdx = *pRange->aEnd.GetNode().EndOfSectionNode();
            --pRange->aEnd;
        }

        SectionUpDown( aIdx, aTmpIdx );
    }

    /** correct indices after movement
     *
     * Update all indices after movement so that the levels are consistent again.
     *
     * @param aStart index of the start node
     * @param aEnd index of the end point
     *
     * @see SwNodes::SectionUp
     * @see SwNodes::SectionDown
     */
    void SwNodes::SectionUpDown( const SwNodeIndex & aStart, const SwNodeIndex & aEnd )
    {
        SwNodeIndex aTmpIdx( aStart, +1 );
        // array forms a stack, holding all StartOfSelections
        SwSttNdPtrs aSttNdStack;
        SwStartNode* pTmp = aStart.GetNode().GetStartNode();
        aSttNdStack.push_back( pTmp );

        // loop until the first start node that needs to be change was found
        // (the indices are updated from the end node backwards to the start)
        for( ;; ++aTmpIdx )
        {
            SwNode * pCurrentNode = &aTmpIdx.GetNode();
            pCurrentNode->m_pStartOfSection = aSttNdStack[ aSttNdStack.size()-1 ];

            if( pCurrentNode->GetStartNode() )
            {
                pTmp = static_cast<SwStartNode*>(pCurrentNode);
                aSttNdStack.push_back( pTmp );
            }
            else if( pCurrentNode->GetEndNode() )
            {
                SwStartNode* pSttNd = aSttNdStack[ aSttNdStack.size() - 1 ];
                pSttNd->m_pEndOfSection = static_cast<SwEndNode*>(pCurrentNode);
                aSttNdStack.pop_back();
                if( !aSttNdStack.empty() )
                    continue; // still enough EndNodes on the stack

                else if( aTmpIdx < aEnd ) // too many StartNodes
                    // if the end is not reached, yet, get the start of the section above
                {
                    aSttNdStack.insert( aSttNdStack.begin(), pSttNd->m_pStartOfSection );
                }
                else // finished, as soon as out of the range
                    break;
            }
        }
    }

    /** delete nodes
     *
     * This is a specific implementation of a delete function for a variable array.
     * It is necessary as there might be inconsistencies after deleting start or
     * end nodes. This method can clean those up.
     *
     * @param rIndex position to delete at (unchanged afterwards)
     * @param nNodes number of nodes to delete (default: 1)
     */
    void SwNodes::Delete(const SwNodeIndex &rIndex, sal_uLong nNodes)
    {
        int nLevel = 0; // level counter
        SwNode * pCurrentNode;

        sal_uLong nCnt = Count() - rIndex.GetIndex() - 1;
        if( nCnt > nNodes ) nCnt = nNodes;

        if( nCnt == 0 ) // no count -> return
            return;

        SwNodeRange aRg( rIndex, 0, rIndex, nCnt-1 );
        // check if [rIndex..rIndex + nCnt] is larger than the range
        if( ( !aRg.aStart.GetNode().StartOfSectionIndex() &&
                !aRg.aStart.GetIndex() ) ||
                ! CheckNodesRange( aRg.aStart, aRg.aEnd ) )
            return;

        // if aEnd is not on a ContentNode, search the previous one
        while( ( pCurrentNode = &aRg.aEnd.GetNode())->GetStartNode() ||
                 ( pCurrentNode->GetEndNode() &&
                    !pCurrentNode->m_pStartOfSection->IsTableNode() ))
            --aRg.aEnd;

        nCnt = 0;
    //TODO: check/improve comment
        // increase start so that we are able to use "<" (using "<=" might cause
        // problems if aEnd == aStart and aEnd is deleted, so aEnd <= aStart)
        --aRg.aStart;

        bool bSaveInNodesDel = m_bInNodesDel;
        m_bInNodesDel = true;
        bool bUpdateOutline = false;

        // loop until everything is deleted
        while( aRg.aStart < aRg.aEnd )
        {
            pCurrentNode = &aRg.aEnd.GetNode();

            if( pCurrentNode->GetEndNode() )
            {
                // delete the whole section?
                if( pCurrentNode->StartOfSectionIndex() > aRg.aStart.GetIndex() )
                {
                    SwTableNode* pTableNd = pCurrentNode->m_pStartOfSection->GetTableNode();
                    if( pTableNd )
                        pTableNd->DelFrames();

                    SwNode *pNd, *pChkNd = pCurrentNode->m_pStartOfSection;
                    SwOutlineNodes::size_type nIdxPos;
                    do {
                        pNd = &aRg.aEnd.GetNode();

                        if( pNd->IsTextNode() )
                        {
                            SwTextNode *const pTextNode(pNd->GetTextNode());
                            if (pTextNode->IsOutline() &&
                                    m_pOutlineNodes->Seek_Entry( pNd, &nIdxPos ))
                            {
                                // remove outline indices
                                m_pOutlineNodes->erase(nIdxPos);
                                bUpdateOutline = true;
                            }
                            pTextNode->InvalidateNumRule();
                        }
                        else if( pNd->IsEndNode() &&
                                pNd->m_pStartOfSection->IsTableNode() )
                            static_cast<SwTableNode*>(pNd->m_pStartOfSection)->DelFrames();

                        --aRg.aEnd;
                        nCnt++;

                    } while( pNd != pChkNd );
                }
                else
                {
                    RemoveNode( aRg.aEnd.GetIndex()+1, nCnt, true ); // delete
                    nCnt = 0;
                    --aRg.aEnd; // before the EndNode
                    nLevel++;
                }
            }
            else if( pCurrentNode->GetStartNode() ) // found StartNode
            {
                if( nLevel == 0 ) // decrease one level
                {
                    if( nCnt )
                    {
                        // now delete array
                        ++aRg.aEnd;
                        RemoveNode( aRg.aEnd.GetIndex(), nCnt, true );
                        nCnt = 0;
                    }
                }
                else // remove all nodes between start and end node (incl. both)
                {
                    RemoveNode( aRg.aEnd.GetIndex(), nCnt + 2, true ); // delete array
                    nCnt = 0;
                    nLevel--;
                }

                // after deletion, aEnd might point to a EndNode...
                // delete all empty start/end node pairs
                SwNode* pTmpNode = aRg.aEnd.GetNode().GetEndNode();
                --aRg.aEnd;
                while(  pTmpNode &&
                        ( pCurrentNode = &aRg.aEnd.GetNode())->GetStartNode() &&
                        pCurrentNode->StartOfSectionIndex() )
                {
                    // remove end and start node
                    DelNodes( aRg.aEnd, 2 );
                    pTmpNode = aRg.aEnd.GetNode().GetEndNode();
                    --aRg.aEnd;
                }
            }
            else // "normal" node, so insert into TmpArray
            {
                SwTextNode* pTextNd = pCurrentNode->GetTextNode();
                if( pTextNd )
                {
                    if( pTextNd->IsOutline())
                    {
                        // delete outline indices
                        m_pOutlineNodes->erase( pTextNd );
                        bUpdateOutline = true;
                    }
                    pTextNd->InvalidateNumRule();
                }
                else if( pCurrentNode->IsContentNode() )
                    static_cast<SwContentNode*>(pCurrentNode)->InvalidateNumRule();

                --aRg.aEnd;
                nCnt++;
            }
        }

        ++aRg.aEnd;
        if( nCnt != 0 )
            RemoveNode( aRg.aEnd.GetIndex(), nCnt, true ); // delete the rest

        // delete all empty start/end node pairs
        while( aRg.aEnd.GetNode().GetEndNode() &&
                ( pCurrentNode = &aRg.aStart.GetNode())->GetStartNode() &&
                pCurrentNode->StartOfSectionIndex() )
        // but none of the holy 5. (???)
        {
            DelNodes( aRg.aStart, 2 );  // delete start and end node
            --aRg.aStart;
        }

        m_bInNodesDel = bSaveInNodesDel;

        if( !m_bInNodesDel )
        {
            // update numbering
            if( bUpdateOutline || m_bInDelUpdOutline )
            {
                UpdateOutlineIdx( aRg.aEnd.GetNode() );
                m_bInDelUpdOutline = false;
            }

        }
        else
        {
            if( bUpdateOutline )
                m_bInDelUpdOutline = true;
        }
    }

    /** get section level at the given position
     *
     * @note The first node in an array should always be a start node.
     *       Because of this, there is a special treatment here based on the
     *       assumption that this is true in this context as well.
     *
     * @param rIdx position of the node
     * @return section level at the given position
     */
    sal_uInt16 SwNodes::GetSectionLevel(const SwNodeIndex &rIdx)
    {
        // special treatment for 1st Node
        if(rIdx == 0) return 1;
        // no recursion! This calls a SwNode::GetSectionLevel (missing "s")
        return rIdx.GetNode().GetSectionLevel();
    }

    void SwNodes::GoStartOfSection(SwNodeIndex *pIdx)
    {
        // after the next start node
        SwNodeIndex aTmp( *pIdx->GetNode().StartOfSectionNode(), +1 );

        // If index points to no ContentNode, than go to one.
        // If there is no further available, do not change the index' position!
        while( !aTmp.GetNode().IsContentNode() )
        {   // go from this StartNode (can only be one) to its end
            if( *pIdx <= aTmp )
                return;     // ERROR: already after the section
            aTmp = aTmp.GetNode().EndOfSectionIndex()+1;
            if( *pIdx <= aTmp )
                return;     // ERROR: already after the section
        }
        (*pIdx) = aTmp;     // is on a ContentNode
    }

    void SwNodes::GoEndOfSection(SwNodeIndex *pIdx)
    {
        if( !pIdx->GetNode().IsEndNode() )
            (*pIdx) = *pIdx->GetNode().EndOfSectionNode();
    }

    SwContentNode* SwNodes::GoNext(SwNodeIndex *pIdx) const
    {
        if( pIdx->GetIndex() >= Count() - 1 )
            return nullptr;

        SwNodeIndex aTmp(*pIdx, +1);
        SwNode* pNd = nullptr;
        while( aTmp < Count()-1 && !( pNd = &aTmp.GetNode())->IsContentNode() )
            ++aTmp;

        if( aTmp == Count()-1 )
            pNd = nullptr;
        else
            (*pIdx) = aTmp;
        return static_cast<SwContentNode*>(pNd);
    }

    SwContentNode* SwNodes::GoPrevious(SwNodeIndex *pIdx)
    {
        if( !pIdx->GetIndex() )
            return nullptr;

        SwNodeIndex aTmp( *pIdx, -1 );
        SwNode* pNd = nullptr;
        while( aTmp.GetIndex() && !( pNd = &aTmp.GetNode())->IsContentNode() )
            --aTmp;

        if( !aTmp.GetIndex() )
            pNd = nullptr;
        else
            (*pIdx) = aTmp;
        return static_cast<SwContentNode*>(pNd);
    }

    inline bool TstIdx( sal_uLong nSttIdx, sal_uLong nEndIdx, sal_uLong nStt, sal_uLong nEnd )
    {
        return nStt < nSttIdx && nEnd >= nSttIdx &&
                nStt < nEndIdx && nEnd >= nEndIdx;
    }

    /** Check if the given range is inside the defined ranges
     *
     * The defined ranges are Content, AutoText, PostIts, Inserts, and Redlines.
     *
     * @param rStt start index of the range
     * @param rEnd end index of the range
     * @return <true> if valid range
     */
    bool SwNodes::CheckNodesRange( const SwNodeIndex& rStt, const SwNodeIndex& rEnd ) const
    {
        sal_uLong nStt = rStt.GetIndex(), nEnd = rEnd.GetIndex();
        if( TstIdx( nStt, nEnd, m_pEndOfContent->StartOfSectionIndex(),
                    m_pEndOfContent->GetIndex() )) return true;
        if( TstIdx( nStt, nEnd, m_pEndOfAutotext->StartOfSectionIndex(),
                    m_pEndOfAutotext->GetIndex() )) return true;
        if( TstIdx( nStt, nEnd, m_pEndOfPostIts->StartOfSectionIndex(),
                    m_pEndOfPostIts->GetIndex() )) return true;
        if( TstIdx( nStt, nEnd, m_pEndOfInserts->StartOfSectionIndex(),
                    m_pEndOfInserts->GetIndex() )) return true;
        if( TstIdx( nStt, nEnd, m_pEndOfRedlines->StartOfSectionIndex(),
                    m_pEndOfRedlines->GetIndex() )) return true;

        return false;       // is somewhere in the middle, ERROR
    }

    /** Delete a number of nodes
     *
     * @param rStart starting position in this nodes array
     * @param nCnt number of nodes to delete
     */
    void SwNodes::DelNodes( const SwNodeIndex & rStart, sal_uLong nCnt )
    {
        sal_uLong nSttIdx = rStart.GetIndex();

        if( !nSttIdx && nCnt == GetEndOfContent().GetIndex()+1 )
        {
            // The whole nodes array will be destroyed, you're in the Doc's DTOR!
            // The initial start/end nodes should be only destroyed in the SwNodes' DTOR!
            SwNode* aEndNdArr[] = { m_pEndOfContent.get(),
                                    m_pEndOfPostIts, m_pEndOfInserts,
                                    m_pEndOfAutotext, m_pEndOfRedlines,
                                    nullptr
                                  };

            SwNode** ppEndNdArr = aEndNdArr;
            while( *ppEndNdArr )
            {
                nSttIdx = (*ppEndNdArr)->StartOfSectionIndex() + 1;
                sal_uLong nEndIdx = (*ppEndNdArr)->GetIndex();

                if( nSttIdx != nEndIdx )
                    RemoveNode( nSttIdx, nEndIdx - nSttIdx, true );

                ++ppEndNdArr;
            }
        }
        else
        {
            int bUpdateNum = 0;
            for( sal_uLong n = nSttIdx, nEnd = nSttIdx + nCnt; n < nEnd; ++n )
            {
                SwNode* pNd = (*this)[ n ];

                if (pNd->IsTextNode() && pNd->GetTextNode()->IsOutline())
                {
                    // remove the outline indices
                    SwOutlineNodes::size_type nIdxPos;
                    if( m_pOutlineNodes->Seek_Entry( pNd, &nIdxPos ))
                    {
                        m_pOutlineNodes->erase(nIdxPos);
                        bUpdateNum = 1;
                    }
                }
                if( pNd->IsContentNode() )
                {
                    static_cast<SwContentNode*>(pNd)->InvalidateNumRule();
                    static_cast<SwContentNode*>(pNd)->DelFrames();
                }
            }
            RemoveNode( nSttIdx, nCnt, true );

            // update numbering
            if( bUpdateNum )
                UpdateOutlineIdx( rStart.GetNode() );
        }
    }

    struct HighLevel
    {
        sal_uInt16 nLevel, nTop;
        explicit HighLevel( sal_uInt16 nLv ) : nLevel( nLv ), nTop( nLv ) {}
    };

    static bool lcl_HighestLevel( const SwNodePtr& rpNode, void * pPara )
    {
        HighLevel * pHL = static_cast<HighLevel*>(pPara);
        if( rpNode->GetStartNode() )
            pHL->nLevel++;
        else if( rpNode->GetEndNode() )
            pHL->nLevel--;
        if( pHL->nTop > pHL->nLevel )
            pHL->nTop = pHL->nLevel;
        return true;

    }

    /** Calculate the highest level in a range
     *
     * @param rNodes the nodes array
     * @param rRange the range to inspect
     * @return the highest level
     */
    sal_uInt16 HighestLevel( SwNodes & rNodes, const SwNodeRange & rRange )
    {
        HighLevel aPara( SwNodes::GetSectionLevel( rRange.aStart ));
        rNodes.ForEach( rRange.aStart, rRange.aEnd, lcl_HighestLevel, &aPara );
        return aPara.nTop;

    }

    /** move a range
     *
     * @param rPam the range to move
     * @param rPos to destination position in the given nodes array
     * @param rNodes the node array to move the range into
     */
    void SwNodes::MoveRange( SwPaM & rPam, SwPosition & rPos, SwNodes& rNodes )
    {
        SwPosition * const pStt = rPam.Start();
        SwPosition * const pEnd = rPam.End();

        if( !rPam.HasMark() || *pStt >= *pEnd )
            return;

        if( this == &rNodes && *pStt <= rPos && rPos < *pEnd )
            return;

        SwNodeIndex aEndIdx( pEnd->nNode );
        SwNodeIndex aSttIdx( pStt->nNode );
        SwTextNode *const pSrcNd = aSttIdx.GetNode().GetTextNode();
        SwTextNode * pDestNd = rPos.nNode.GetNode().GetTextNode();
        bool bSplitDestNd = true;
        bool bCopyCollFormat = pDestNd && pDestNd->GetText().isEmpty();

        if( pSrcNd )
        {
            // if the first node is a TextNode, than there must
            // be also a TextNode in the NodesArray to store the content
            if( !pDestNd )
            {
                pDestNd = rNodes.MakeTextNode( rPos.nNode, pSrcNd->GetTextColl() );
                --rPos.nNode;
                rPos.nContent.Assign( pDestNd, 0 );
                bCopyCollFormat = true;
            }
            bSplitDestNd = pDestNd->Len() > rPos.nContent.GetIndex() ||
                            pEnd->nNode.GetNode().IsTextNode();

            // move the content into the new node
            bool bOneNd = pStt->nNode == pEnd->nNode;
            const sal_Int32 nLen =
                    ( bOneNd ? std::min(pEnd->nContent.GetIndex(), pSrcNd->Len()) : pSrcNd->Len() )
                    - pStt->nContent.GetIndex();

            if( !pEnd->nNode.GetNode().IsContentNode() )
            {
                bOneNd = true;
                sal_uLong nSttNdIdx = pStt->nNode.GetIndex() + 1;
                const sal_uLong nEndNdIdx = pEnd->nNode.GetIndex();
                for( ; nSttNdIdx < nEndNdIdx; ++nSttNdIdx )
                {
                    if( (*this)[ nSttNdIdx ]->IsContentNode() )
                    {
                        bOneNd = false;
                        break;
                    }
                }
            }

            // templates must be copied/set after a split
            if( !bOneNd && bSplitDestNd )
            {
                if( !rPos.nContent.GetIndex() )
                {
                    bCopyCollFormat = true;
                }
                if( rNodes.IsDocNodes() )
                {
                    SwDoc* const pInsDoc = pDestNd->GetDoc();
                    ::sw::UndoGuard const ug(pInsDoc->GetIDocumentUndoRedo());
                    pInsDoc->getIDocumentContentOperations().SplitNode( rPos, false );
                }
                else
                {
                    pDestNd->SplitContentNode( rPos );
                }

                if( rPos.nNode == aEndIdx )
                {
                    --aEndIdx;
                }
                bSplitDestNd = true;

                pDestNd = rNodes[ rPos.nNode.GetIndex() - 1 ]->GetTextNode();
                if( nLen )
                {
                    pSrcNd->CutText( pDestNd, SwIndex( pDestNd, pDestNd->Len()),
                                pStt->nContent, nLen );
                }
            }
            else if ( nLen )
            {
                pSrcNd->CutText( pDestNd, rPos.nContent, pStt->nContent, nLen );
            }

            if( bCopyCollFormat )
            {
                SwDoc* const pInsDoc = pDestNd->GetDoc();
                ::sw::UndoGuard const undoGuard(pInsDoc->GetIDocumentUndoRedo());
                pSrcNd->CopyCollFormat( *pDestNd );
                bCopyCollFormat = false;
            }

            if( bOneNd )
            {
                // Correct the PaM, because it might have happened that the move
                // went over the node borders (so the data might be in different nodes).
                // Also, a selection is invalidated.
                pEnd->nContent = pStt->nContent;
                rPam.DeleteMark();
                GetDoc()->GetDocShell()->Broadcast( SwFormatFieldHint( nullptr,
                    rNodes.IsDocNodes() ? SwFormatFieldHintWhich::INSERTED : SwFormatFieldHintWhich::REMOVED ) );
                return;
            }

            ++aSttIdx;
        }
        else if( pDestNd )
        {
            if( rPos.nContent.GetIndex() )
            {
                if( rPos.nContent.GetIndex() == pDestNd->Len() )
                {
                    ++rPos.nNode;
                }
                else if( rPos.nContent.GetIndex() )
                {
                    // if the EndNode is split than correct the EndIdx
                    const bool bCorrEnd = aEndIdx == rPos.nNode;

                    // if no text is attached to the TextNode, split it
                    if( rNodes.IsDocNodes() )
                    {
                        SwDoc* const pInsDoc = pDestNd->GetDoc();
                        ::sw::UndoGuard const ug(pInsDoc->GetIDocumentUndoRedo());
                        pInsDoc->getIDocumentContentOperations().SplitNode( rPos, false );
                    }
                    else
                    {
                        pDestNd->SplitContentNode( rPos );
                    }

                    if ( bCorrEnd )
                    {
                        --aEndIdx;
                    }
                }
            }
            // at the end only an empty TextNode is left over
            bSplitDestNd = true;
        }

        SwTextNode* const pEndSrcNd = aEndIdx.GetNode().GetTextNode();
        if ( pEndSrcNd )
        {
            // at the end of this range a new TextNode will be created
            if( !bSplitDestNd )
            {
                if( rPos.nNode < rNodes.GetEndOfContent().GetIndex() )
                {
                    ++rPos.nNode;
                }

                pDestNd =
                    rNodes.MakeTextNode( rPos.nNode, pEndSrcNd->GetTextColl() );
                --rPos.nNode;
                rPos.nContent.Assign( pDestNd, 0 );
            }
            else
            {
                pDestNd = rPos.nNode.GetNode().GetTextNode();
            }

            if (pDestNd && pEnd->nContent.GetIndex())
            {
                // move the content into the new node
                SwIndex aIdx( pEndSrcNd, 0 );
                pEndSrcNd->CutText( pDestNd, rPos.nContent, aIdx,
                                pEnd->nContent.GetIndex());
            }

            if (pDestNd && bCopyCollFormat)
            {
                SwDoc* const pInsDoc = pDestNd->GetDoc();
                ::sw::UndoGuard const ug(pInsDoc->GetIDocumentUndoRedo());
                pEndSrcNd->CopyCollFormat( *pDestNd );
            }
        }
        else
        {
            if ( pSrcNd && aEndIdx.GetNode().IsContentNode() )
            {
                ++aEndIdx;
            }
            if( !bSplitDestNd )
            {
                ++rPos.nNode;
                rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(), 0 );
            }
        }

        if( aEndIdx != aSttIdx )
        {
            // move the nodes into the NodesArary
            const sal_uLong nSttDiff = aSttIdx.GetIndex() - pStt->nNode.GetIndex();
            SwNodeRange aRg( aSttIdx, aEndIdx );
            MoveNodes( aRg, rNodes, rPos.nNode );

            // if in the same node array, all indices are now at new positions (so correct them)
            if( &rNodes == this )
            {
                pStt->nNode = aRg.aEnd.GetIndex() - nSttDiff;
            }
        }

        // if the StartNode was moved to whom the cursor pointed, so
        // the content must be registered in the current content!
        if ( &pStt->nNode.GetNode() == &GetEndOfContent() )
        {
            const bool bSuccess = GoPrevious( &pStt->nNode );
            OSL_ENSURE( bSuccess, "Move() - no ContentNode here" );
        }
        pStt->nContent.Assign( pStt->nNode.GetNode().GetContentNode(),
                                pStt->nContent.GetIndex() );
        // Correct the PaM, because it might have happened that the move
        // went over the node borders (so the data might be in different nodes).
        // Also, a selection is invalidated.
        *pEnd = *pStt;
        rPam.DeleteMark();
        GetDoc()->GetDocShell()->Broadcast( SwFormatFieldHint( nullptr,
                    rNodes.IsDocNodes() ? SwFormatFieldHintWhich::INSERTED : SwFormatFieldHintWhich::REMOVED ) );
    }

    ///@see SwNodes::MoveNodes (TODO: seems to be C&P programming here)
    void SwNodes::CopyNodes( const SwNodeRange& rRange,
                const SwNodeIndex& rIndex, bool bNewFrames, bool bTableInsDummyNode ) const
    {
        SwDoc* pDoc = rIndex.GetNode().GetDoc();

        SwNode * pCurrentNode;
        if( rIndex == 0 ||
            ( (pCurrentNode = &rIndex.GetNode())->GetStartNode() &&
              !pCurrentNode->StartOfSectionIndex() ))
            return;

        SwNodeRange aRg( rRange );

        // skip "simple" StartNodes or EndNodes
        while( SwNodeType::Start == (pCurrentNode = & aRg.aStart.GetNode())->GetNodeType()
                || ( pCurrentNode->IsEndNode() &&
                    !pCurrentNode->m_pStartOfSection->IsSectionNode() ) )
            ++aRg.aStart;

        const SwNode *aEndNode = &aRg.aEnd.GetNode();
        int nIsEndOfContent = (aEndNode == &aEndNode->GetNodes().GetEndOfContent()) ? 1 : 0;

        if (0 == nIsEndOfContent)
        {
            // if aEnd-1 points to no ContentNode, search previous one
            --aRg.aEnd;
            // #i107142#: if aEnd is start node of a special section, do nothing.
            // Otherwise this could lead to crash: going through all previous
            // special section nodes and then one before the first.
            if (aRg.aEnd.GetNode().StartOfSectionIndex() != 0)
            {
                while( ((pCurrentNode = & aRg.aEnd.GetNode())->GetStartNode() &&
                        !pCurrentNode->IsSectionNode() ) ||
                        ( pCurrentNode->IsEndNode() &&
                        SwNodeType::Start == pCurrentNode->m_pStartOfSection->GetNodeType()) )
                {
                    --aRg.aEnd;
                }
            }
            ++aRg.aEnd;
        }

        // is there anything left to copy?
        if( aRg.aStart >= aRg.aEnd )
            return;

        // when inserting into the source range, nothing need to be done
        OSL_ENSURE( &aRg.aStart.GetNodes() == this,
                    "aRg should use thisnodes array" );
        OSL_ENSURE( &aRg.aStart.GetNodes() == &aRg.aEnd.GetNodes(),
                   "Range across different nodes arrays? You deserve punishment!");
        if( &rIndex.GetNodes() == &aRg.aStart.GetNodes() &&
            rIndex.GetIndex() >= aRg.aStart.GetIndex() &&
            rIndex.GetIndex() < aRg.aEnd.GetIndex() )
                return;

        SwNodeIndex aInsPos( rIndex );
        SwNodeIndex aOrigInsPos( rIndex, -1 ); // original insertion position
        int nLevel = 0;                        // level counter

        for( long nNodeCnt = aRg.aEnd.GetIndex() - aRg.aStart.GetIndex();
                nNodeCnt > 0; --nNodeCnt )
        {
            pCurrentNode = &aRg.aStart.GetNode();
            switch( pCurrentNode->GetNodeType() )
            {
            case SwNodeType::Table:
                // Does it copy a table in(to) a footnote?
                if( aInsPos < pDoc->GetNodes().GetEndOfInserts().GetIndex() &&
                        pDoc->GetNodes().GetEndOfInserts().StartOfSectionIndex()
                        < aInsPos.GetIndex() )
                {
                    const long nDistance =
                        ( pCurrentNode->EndOfSectionIndex() -
                            aRg.aStart.GetIndex() );
                    if (nDistance < nNodeCnt)
                        nNodeCnt -= nDistance;
                    else
                        nNodeCnt = 1;

                    // insert a DummyNode for a TableNode
                    if( bTableInsDummyNode )
                        new SwPlaceholderNode(aInsPos);

                    // copy all of the table's nodes into the current cell
                    for( ++aRg.aStart; aRg.aStart.GetIndex() <
                        pCurrentNode->EndOfSectionIndex();
                        ++aRg.aStart )
                    {
                        // insert a DummyNode for the box-StartNode?
                        if( bTableInsDummyNode )
                            new SwPlaceholderNode(aInsPos);

                        SwStartNode* pSttNd = aRg.aStart.GetNode().GetStartNode();
                        CopyNodes( SwNodeRange( *pSttNd, + 1,
                                                *pSttNd->EndOfSectionNode() ),
                                    aInsPos, bNewFrames );

                        // insert a DummyNode for the box-EndNode?
                        if( bTableInsDummyNode )
                            new SwPlaceholderNode(aInsPos);
                        aRg.aStart = *pSttNd->EndOfSectionNode();
                    }
                    // insert a DummyNode for the table-EndNode
                    if( bTableInsDummyNode )
                        new SwPlaceholderNode(aInsPos);
                    aRg.aStart = *pCurrentNode->EndOfSectionNode();
                }
                else
                {
                    SwNodeIndex nStt( aInsPos, -1 );
                    SwTableNode* pTableNd = static_cast<SwTableNode*>(pCurrentNode)->
                                            MakeCopy( pDoc, aInsPos );
                    const long nDistance = aInsPos.GetIndex() - nStt.GetIndex() - 2;
                    if (nDistance < nNodeCnt)
                        nNodeCnt -= nDistance;
                    else
                        nNodeCnt = 1 - nIsEndOfContent;

                    aRg.aStart = pCurrentNode->EndOfSectionIndex();

                    if( bNewFrames && pTableNd )
                    {
                        nStt = aInsPos;
                        pTableNd->MakeFrames( &nStt );
                    }
                }
                break;

            case SwNodeType::Section:
                // If the end of the section is outside the copy range,
                // the section node will skipped, not copied!
                // If someone want to change this behaviour, he has to adjust the function
                // lcl_NonCopyCount(..) in ndcopy.cxx which relies on it.
                if( pCurrentNode->EndOfSectionIndex() < aRg.aEnd.GetIndex() )
                {
                    // copy of the whole section, so create a new SectionNode
                    SwNodeIndex nStt( aInsPos, -1 );
                    SwSectionNode* pSectNd = static_cast<SwSectionNode*>(pCurrentNode)->
                                        MakeCopy( pDoc, aInsPos );

                    const long nDistance = aInsPos.GetIndex() - nStt.GetIndex() - 2;
                    if (nDistance < nNodeCnt)
                        nNodeCnt -= nDistance;
                    else
                        nNodeCnt = 1 - nIsEndOfContent;
                    aRg.aStart = pCurrentNode->EndOfSectionIndex();

                    if( bNewFrames && pSectNd &&
                        !pSectNd->GetSection().IsHidden() )
                        pSectNd->MakeFrames( &nStt );
                }
                break;

            case SwNodeType::Start:
                {
                    SwStartNode* pTmp = new SwStartNode( aInsPos, SwNodeType::Start,
                                static_cast<SwStartNode*>(pCurrentNode)->GetStartNodeType() );
                    new SwEndNode( aInsPos, *pTmp );
                    --aInsPos;
                    nLevel++;
                }
                break;

            case SwNodeType::End:
                if( nLevel ) // complete section
                {
                    --nLevel;
                    ++aInsPos; // EndNode already exists
                }
                else if( 1 == nNodeCnt && 1 == nIsEndOfContent )
                    // we have reached the EndOfContent node - nothing to do!
                    continue;
                else if( !pCurrentNode->m_pStartOfSection->IsSectionNode() )
                {
                    // create a section at the original InsertPosition
                    SwNodeRange aTmpRg( aOrigInsPos, 1, aInsPos );
                    pDoc->GetNodes().SectionDown( &aTmpRg,
                            pCurrentNode->m_pStartOfSection->GetStartNodeType() );
                }
                break;

            case SwNodeType::Text:
            case SwNodeType::Grf:
            case SwNodeType::Ole:
                {
                    SwContentNode* pNew = static_cast<SwContentNode*>(pCurrentNode)->MakeCopy(
                                                pDoc, aInsPos );
                    // frames are always created as default, so delete if needed
                    if( !bNewFrames )
                        pNew->DelFrames();
                }
                break;

            case SwNodeType::PlaceHolder:
                if (GetDoc()->GetIDocumentUndoRedo().IsUndoNodes(*this))
                {
                    // than a SectionNode (start/end) is needed at the current
                    // InsPos; if so skip it, otherwise ignore current node
                    SwNode *const pTmpNd = & aInsPos.GetNode();
                    if( pTmpNd->IsSectionNode() ||
                        pTmpNd->StartOfSectionNode()->IsSectionNode() )
                        ++aInsPos;  // skip
                }
                else {
                    OSL_FAIL( "How can this node be in the node array?" );
                }
                break;

            default:
                OSL_FAIL( "Unknown node type" );
            }
            ++aRg.aStart;
        }
    }

    void SwNodes::DelDummyNodes( const SwNodeRange& rRg )
    {
        SwNodeIndex aIdx( rRg.aStart );
        while( aIdx.GetIndex() < rRg.aEnd.GetIndex() )
        {
            if (SwNodeType::PlaceHolder == aIdx.GetNode().GetNodeType())
                RemoveNode( aIdx.GetIndex(), 1, true );
            else
                ++aIdx;
        }
    }

    SwStartNode* SwNodes::MakeEmptySection( const SwNodeIndex& rIdx,
                                            SwStartNodeType eSttNdTyp )
    {
        SwStartNode* pSttNd = new SwStartNode( rIdx, SwNodeType::Start, eSttNdTyp );
        new SwEndNode( rIdx, *pSttNd );
        return pSttNd;
    }

    SwStartNode* SwNodes::MakeTextSection( const SwNodeIndex & rWhere,
                                            SwStartNodeType eSttNdTyp,
                                            SwTextFormatColl *pColl )
    {
        SwStartNode* pSttNd = new SwStartNode( rWhere, SwNodeType::Start, eSttNdTyp );
        new SwEndNode( rWhere, *pSttNd );
        MakeTextNode( SwNodeIndex( rWhere, - 1 ), pColl );
        return pSttNd;
    }

    //TODO: provide better documentation
    /** go to next section that is not protected nor hidden
     *
     * @note if !bSkipHidden and !bSkipProtect, use GoNext/GoPrevious
     *
     * @param pIdx
     * @param bSkipHidden
     * @param bSkipProtect
     * @return
     * @see SwNodes::GoNext
     * @see SwNodes::GoPrevious
     * @see SwNodes::GoNextSection (TODO: seems to be C&P programming here)
    */
    SwContentNode* SwNodes::GoNextSection( SwNodeIndex * pIdx,
                                bool bSkipHidden, bool bSkipProtect ) const
    {
        bool bFirst = true;
        SwNodeIndex aTmp( *pIdx );
        const SwNode* pNd;
        while( aTmp < Count() - 1 )
        {
            pNd = & aTmp.GetNode();
            if (SwNodeType::Section == pNd->GetNodeType())
            {
                const SwSection& rSect = static_cast<const SwSectionNode*>(pNd)->GetSection();
                if( (bSkipHidden && rSect.IsHiddenFlag()) ||
                    (bSkipProtect && rSect.IsProtectFlag()) )
                    // than skip the section
                    aTmp = *pNd->EndOfSectionNode();
            }
            else if( bFirst )
            {
                if( pNd->m_pStartOfSection->IsSectionNode() )
                {
                    const SwSection& rSect = static_cast<SwSectionNode*>(pNd->
                                    m_pStartOfSection)->GetSection();
                    if( (bSkipHidden && rSect.IsHiddenFlag()) ||
                        (bSkipProtect && rSect.IsProtectFlag()) )
                        // than skip the section
                        aTmp = *pNd->EndOfSectionNode();
                }
            }
            else if( SwNodeType::ContentMask & pNd->GetNodeType() )
            {
                const SwSectionNode* pSectNd;
                if( ( bSkipHidden || bSkipProtect ) &&
                    nullptr != (pSectNd = pNd->FindSectionNode() ) &&
                    ( ( bSkipHidden && pSectNd->GetSection().IsHiddenFlag() ) ||
                      ( bSkipProtect && pSectNd->GetSection().IsProtectFlag() )) )
                {
                    aTmp = *pSectNd->EndOfSectionNode();
                }
                else
                {
                    (*pIdx) = aTmp;
                    return const_cast<SwContentNode*>(static_cast<const SwContentNode*>(pNd));
                }
            }
            ++aTmp;
            bFirst = false;
        }
        return nullptr;
    }

    ///@see SwNodes::GoNextSection (TODO: seems to be C&P programming here)
    SwContentNode* SwNodes::GoPrevSection( SwNodeIndex * pIdx,
                                bool bSkipHidden, bool bSkipProtect )
    {
        bool bFirst = true;
        SwNodeIndex aTmp( *pIdx );
        const SwNode* pNd;
        while( aTmp > 0 )
        {
            pNd = & aTmp.GetNode();
            if (SwNodeType::End == pNd->GetNodeType())
            {
                if( pNd->m_pStartOfSection->IsSectionNode() )
                {
                    const SwSection& rSect = static_cast<SwSectionNode*>(pNd->
                                                m_pStartOfSection)->GetSection();
                    if( (bSkipHidden && rSect.IsHiddenFlag()) ||
                        (bSkipProtect && rSect.IsProtectFlag()) )
                        // than skip section
                        aTmp = *pNd->StartOfSectionNode();
                }
                bFirst = false;
            }
            else if( bFirst )
            {
                bFirst = false;
                if( pNd->m_pStartOfSection->IsSectionNode() )
                {
                    const SwSection& rSect = static_cast<SwSectionNode*>(pNd->
                                    m_pStartOfSection)->GetSection();
                    if( (bSkipHidden && rSect.IsHiddenFlag()) ||
                        (bSkipProtect && rSect.IsProtectFlag()) )
                        // than skip section
                        aTmp = *pNd->StartOfSectionNode();
                }
            }
            else if( SwNodeType::ContentMask & pNd->GetNodeType() )
            {
                const SwSectionNode* pSectNd;
                if( ( bSkipHidden || bSkipProtect ) &&
                    nullptr != (pSectNd = pNd->FindSectionNode() ) &&
                    ( ( bSkipHidden && pSectNd->GetSection().IsHiddenFlag() ) ||
                      ( bSkipProtect && pSectNd->GetSection().IsProtectFlag() )) )
                {
                    aTmp = *pSectNd;
                }
                else
                {
                    (*pIdx) = aTmp;
                    return const_cast<SwContentNode*>(static_cast<const SwContentNode*>(pNd));
                }
            }
            --aTmp;
        }
        return nullptr;
    }

    //TODO: improve documentation
    //TODO: The inventor of the "single responsibility principle" will be crying if you ever show this code to him!
    /** find the next/previous ContentNode or a table node with frames
     *
     * If no pEnd is given, search is started with FrameIndex; otherwise
     * search is started with the one before rFrameIdx and after pEnd.
     *
     * @param rFrameIdx node with frames to search in
     * @param pEnd ???
     * @return result node; 0 (!!!) if not found
     */
    SwNode* SwNodes::FindPrvNxtFrameNode( SwNodeIndex& rFrameIdx,
                                        const SwNode* pEnd ) const
    {
        SwNode* pFrameNd = nullptr;

        // no layout -> skip
        if( GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell() )
        {
            SwNode* pSttNd = &rFrameIdx.GetNode();

            // move of a hidden section?
            SwSectionNode* pSectNd = pSttNd->IsSectionNode()
                        ? pSttNd->StartOfSectionNode()->FindSectionNode()
                        : pSttNd->FindSectionNode();
            if( !( pSectNd && pSectNd->GetSection().CalcHiddenFlag() ) )
            {
                // in a table in table situation we have to assure that we don't leave the
                // outer table cell when the inner table is looking for a PrvNxt...
                SwTableNode* pTableNd = pSttNd->IsTableNode()
                        ? pSttNd->StartOfSectionNode()->FindTableNode()
                        : pSttNd->FindTableNode();
                SwNodeIndex aIdx( rFrameIdx );
                SwNode* pNd;
                if( pEnd )
                {
                    --aIdx;
                    pNd = &aIdx.GetNode();
                }
                else
                    pNd = pSttNd;

                if( ( pFrameNd = pNd )->IsContentNode() )
                    rFrameIdx = aIdx;

                // search forward or backward for a content node
                else if( nullptr != ( pFrameNd = GoPrevSection( &aIdx, true, false )) &&
                        ::CheckNodesRange( aIdx, rFrameIdx, true ) &&
                        // Never out of the table at the start
                        pFrameNd->FindTableNode() == pTableNd &&
                        // Bug 37652: Never out of the table at the end
                        (!pFrameNd->FindTableNode() || pFrameNd->FindTableBoxStartNode()
                            == pSttNd->FindTableBoxStartNode() ) &&
                         (!pSectNd || pSttNd->IsSectionNode() ||
                          pSectNd->GetIndex() < pFrameNd->GetIndex())
                        )
                {
                    rFrameIdx = aIdx;
                }
                else
                {
                    if( pEnd )
                        aIdx = pEnd->GetIndex() + 1;
                    else
                        aIdx = rFrameIdx;

                    // NEVER leave the section when doing this!
                    if( ( pEnd && ( pFrameNd = &aIdx.GetNode())->IsContentNode() ) ||
                        ( nullptr != ( pFrameNd = GoNextSection( &aIdx, true, false )) &&
                        ::CheckNodesRange( aIdx, rFrameIdx, true ) &&
                        ( pFrameNd->FindTableNode() == pTableNd &&
                            // NEVER go out of the table cell at the end
                            (!pFrameNd->FindTableNode() || pFrameNd->FindTableBoxStartNode()
                            == pSttNd->FindTableBoxStartNode() ) ) &&
                         (!pSectNd || pSttNd->IsSectionNode() ||
                          pSectNd->EndOfSectionIndex() > pFrameNd->GetIndex())
                        ))
                    {
                        // Undo when merging a table with one before, if there is also one after it.
                        // However, if the node is in a table, it needs to be returned if the
                        // SttNode is a section or a table!
                        SwTableNode* pTableNode;
                        if (pSttNd->IsTableNode() &&
                            nullptr != (pTableNode = pFrameNd->FindTableNode()) &&
                            // TABLE IN TABLE:
                            pTableNode != pSttNd->StartOfSectionNode()->FindTableNode())
                        {
                            pFrameNd = pTableNode;
                            rFrameIdx = *pFrameNd;
                        }
                        else
                            rFrameIdx = aIdx;
                    }
                    else if( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsTableNode() )
                    {
                        pFrameNd = pNd->StartOfSectionNode();
                        rFrameIdx = *pFrameNd;
                    }
                    else
                    {
                        if( pEnd )
                            aIdx = pEnd->GetIndex() + 1;
                        else
                            aIdx = rFrameIdx.GetIndex() + 1;

                        if( (pFrameNd = &aIdx.GetNode())->IsTableNode() )
                            rFrameIdx = aIdx;
                        else
                        {
                            pFrameNd = nullptr;

                            // is there some sectionnodes before a tablenode?
                            while( aIdx.GetNode().IsSectionNode() )
                            {
                                const SwSection& rSect = aIdx.GetNode().
                                    GetSectionNode()->GetSection();
                                if( rSect.IsHiddenFlag() )
                                    aIdx = aIdx.GetNode().EndOfSectionIndex()+1;
                                else
                                    ++aIdx;
                            }
                            if( aIdx.GetNode().IsTableNode() )
                            {
                                rFrameIdx = aIdx;
                                pFrameNd = &aIdx.GetNode();
                            }
                        }
                    }
                }
            }
        }
        return pFrameNd;
    }

    void SwNodes::ForEach( sal_uLong nStart, sal_uLong nEnd,
                           FnForEach_SwNodes fn, void* pArgs )
    {
        if( nEnd > m_nSize )
            nEnd = m_nSize;

        if( nStart < nEnd )
        {
            sal_uInt16 cur = Index2Block( nStart );
            BlockInfo** pp = m_ppInf.get() + cur;
            BlockInfo* p = *pp;
            sal_uInt16 nElem = sal_uInt16( nStart - p->nStart );
            auto pElem = p->mvData.begin() + nElem;
            nElem = p->nElem - nElem;
            for(;;)
            {
                if( !(*fn)( static_cast<SwNode *>(*pElem++), pArgs ) || ++nStart >= nEnd )
                    break;

                // next element
                if( !--nElem )
                {
                    // new block
                    p = *++pp;
                    pElem = p->mvData.begin();
                    nElem = p->nElem;
                }
            }
        }
    }

    void SwNodes::ForEach( const SwNodeIndex& rStart, const SwNodeIndex& rEnd,
                        FnForEach_SwNodes fnForEach, void* pArgs )
    {
        ForEach( rStart.GetIndex(), rEnd.GetIndex(), fnForEach, pArgs );
    }

    void SwNodes::RemoveNode( sal_uLong nDelPos, sal_uLong nSz, bool bDel )
    {
    #ifndef NDEBUG
        SwNode *const pFirst((*this)[nDelPos]);
    #endif
        for (sal_uLong nCnt = 0; nCnt < nSz; nCnt++)
        {
            SwNode* pNode = ((*this)[ nDelPos + nCnt ]);
            SwTextNode * pTextNd = pNode->GetTextNode();

            if (pTextNd)
            {
                pTextNd->RemoveFromList();
                // remove RndStdIds::FLY_AS_CHAR *before* adjusting SwNodeIndex
                // so their anchor still points to correct node when deleted!
                // NOTE: this will call RemoveNode() recursively!
                // so adjust our indexes to account for removed nodes
                sal_uLong const nPos = pTextNd->GetIndex();
                SwpHints *const pHints(pTextNd->GetpSwpHints());
                if (pHints)
                {
                    std::vector<SwTextAttr*> flys;
                    for (size_t i = 0; i < pHints->Count(); ++i)
                    {
                        SwTextAttr *const pHint(pHints->Get(i));
                        if (RES_TXTATR_FLYCNT == pHint->Which())
                        {
                            flys.push_back(pHint);
                        }
                    }
                    for (SwTextAttr * pHint : flys)
                    {
                        pTextNd->DeleteAttribute(pHint);
                    }   // pHints may be dead now
                    sal_uLong const nDiff = nPos - pTextNd->GetIndex();
                    if (nDiff)
                    {
                        nDelPos -= nDiff;
                    }
                    assert(pTextNd == (*this)[nDelPos + nCnt]);
                    assert(pFirst == (*this)[nDelPos]);
                }
            }
            SwTableNode* pTableNode = pNode->GetTableNode();
            if (pTableNode)
            {
                // The node that is deleted is a table node.
                // Need to make sure that all the redlines that are
                // related to this table are removed from the
                // 'Extra Redlines' array
                pTableNode->RemoveRedlines();
            }
        }

        sal_uLong nEnd = nDelPos + nSz;
        SwNode* pNew = (*this)[ nEnd ];

        for (SwNodeIndex& rIndex : m_vIndices->GetRingContainer())
        {
            sal_uLong const nIdx = rIndex.GetIndex();
            if (nDelPos <= nIdx && nIdx < nEnd)
                rIndex = *pNew;
        }

        std::vector<BigPtrEntry> aTempEntries;
        if( bDel )
        {
            sal_uLong nCnt = nSz;
            BigPtrEntry *pDel = (*this)[ nDelPos+nCnt-1 ], *pPrev = (*this)[ nDelPos+nCnt-2 ];

            // set temporary object
            // JP 24.08.98: this should actually be removed because one could
            // call Remove recursively, e.g. for character bound frames. However,
            // since there happens way too much here, this temporary object was
            // inserted that will be deleted in Remove again (see Bug 55406)
            aTempEntries.resize(nCnt);

            while( nCnt-- )
            {
                delete pDel;
                pDel = pPrev;
                sal_uLong nPrevNdIdx = pPrev->GetPos();
                BigPtrEntry* pTempEntry = &aTempEntries[nCnt];
                BigPtrArray::Replace( nPrevNdIdx+1, pTempEntry );
                if( nCnt )
                    pPrev = BigPtrArray::operator []( nPrevNdIdx  - 1 );
                        // the accessed element can be a naked BigPtrEntry from
                        // aTempEntries, so the downcast to SwNode* in
                        // SwNodes::operator[] would be illegal (and unnecessary)
            }
            nDelPos = pDel->GetPos() + 1;
        }

        BigPtrArray::Remove( nDelPos, nSz );
    }

    void SwNodes::InsertNode( const SwNodePtr pNode,
                              const SwNodeIndex& rPos )
    {
        BigPtrEntry* pIns = pNode;
        BigPtrArray::Insert( pIns, rPos.GetIndex() );
    }

    void SwNodes::InsertNode( const SwNodePtr pNode,
                              sal_uLong nPos )
    {
        BigPtrEntry* pIns = pNode;
        BigPtrArray::Insert( pIns, nPos );
    }

    // ->#112139#
    SwNode * SwNodes::DocumentSectionStartNode(SwNode * pNode) const
    {
        if (nullptr != pNode)
        {
            SwNodeIndex aIdx(*pNode);

            if (aIdx <= (*this)[0]->EndOfSectionIndex())
                pNode = (*this)[0];
            else
            {
                while ((*this)[0] != pNode->StartOfSectionNode())
                    pNode = pNode->StartOfSectionNode();
            }
        }

        return pNode;
    }

    SwNode * SwNodes::DocumentSectionEndNode(SwNode * pNode) const
    {
        return DocumentSectionStartNode(pNode)->EndOfSectionNode();
    }

    bool SwNodes::IsDocNodes() const
    {
        return this == &m_pMyDoc->GetNodes();
    }

    void SwNodes::dumpAsXml(xmlTextWriterPtr pWriter) const
    {
        xmlTextWriterStartElement(pWriter, BAD_CAST("SwNodes"));
        for (sal_uLong i = 0; i < Count(); ++i)
            (*this)[i]->dumpAsXml(pWriter);
        xmlTextWriterEndElement(pWriter);
    }

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

↑ V773 Visibility scope of the 'pTmpSttNd' pointer was exited without releasing the memory. A memory leak is possible.

↑ V773 Visibility scope of the 'pSttNd' pointer was exited without releasing the memory. A memory leak is possible.