    /* -*- 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 <DocumentRedlineManager.hxx>
    #include <frmfmt.hxx>
    #include <doc.hxx>
    #include <IDocumentUndoRedo.hxx>
    #include <IDocumentState.hxx>
    #include <redline.hxx>
    #include <UndoRedline.hxx>
    #include <docary.hxx>
    #include <ndtxt.hxx>
    #include <unocrsr.hxx>
    #include <strings.hrc>
    #include <swmodule.hxx>
    #include <editsh.hxx>
    #include <vcl/svapp.hxx>
    #include <vcl/weld.hxx>
    #include <sal/log.hxx>

    using namespace com::sun::star;

    #ifdef DBG_UTIL

        #define ERROR_PREFIX "redline table corrupted: "

        namespace
        {
            // helper function for lcl_CheckRedline
            // 1. make sure that pPos->nContent points into pPos->nNode
            // 2. check that position is valid and doesn't point after text
            void lcl_CheckPosition( const SwPosition* pPos )
            {
                assert(dynamic_cast<SwIndexReg*>(&pPos->nNode.GetNode())
                        == pPos->nContent.GetIdxReg());

                SwTextNode* pTextNode = pPos->nNode.GetNode().GetTextNode();
                if( pTextNode == nullptr )
                {
                    assert(pPos->nContent == 0);
                }
                else
                {
                    assert(pPos->nContent >= 0 && pPos->nContent <= pTextNode->Len());
                }
            }

            void lcl_CheckPam( const SwPaM* pPam )
            {
                assert(pPam);
                lcl_CheckPosition( pPam->GetPoint() );
                lcl_CheckPosition( pPam->GetMark() );
            }

            // check validity of the redline table. Checks redline bounds, and make
            // sure the redlines are sorted and non-overlapping.
            void lcl_CheckRedline( IDocumentRedlineAccess& redlineAccess )
            {
                const SwRedlineTable& rTable = redlineAccess.GetRedlineTable();

                // verify valid redline positions
                for(SwRangeRedline* i : rTable)
                    lcl_CheckPam( i );

                for(SwRangeRedline* j : rTable)
                {
                    // check for empty redlines
                    OSL_ENSURE( ( *(j->GetPoint()) != *(j->GetMark()) ) ||
                                ( j->GetContentIdx() != nullptr ),
                                ERROR_PREFIX "empty redline" );
                 }

                // verify proper redline sorting
                for( size_t n = 1; n < rTable.size(); ++n )
                {
                    const SwRangeRedline* pPrev = rTable[ n-1 ];
                    const SwRangeRedline* pCurrent = rTable[ n ];

                    // check redline sorting
                    SAL_WARN_IF( *pPrev->Start() > *pCurrent->Start(), "sw",
                                 ERROR_PREFIX "not sorted correctly" );

                    // check for overlapping redlines
                    SAL_WARN_IF( *pPrev->End() > *pCurrent->Start(), "sw",
                                 ERROR_PREFIX "overlapping redlines" );
                }

                assert(std::is_sorted(rTable.begin(), rTable.end(), CompareSwRedlineTable()));
            }
        }

        #define CHECK_REDLINE( pDoc ) lcl_CheckRedline( pDoc );

    #else

        #define CHECK_REDLINE( pDoc )

    #endif

    namespace
    {
        inline bool IsPrevPos( const SwPosition & rPos1, const SwPosition & rPos2 )
        {
            const SwContentNode* pCNd;
            return 0 == rPos2.nContent.GetIndex() &&
                   rPos2.nNode.GetIndex() - 1 == rPos1.nNode.GetIndex() &&
                   nullptr != ( pCNd = rPos1.nNode.GetNode().GetContentNode() ) &&
                   rPos1.nContent.GetIndex() == pCNd->Len();
        }

        bool lcl_AcceptRedline( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
                                bool bCallDelete,
                                const SwPosition* pSttRng = nullptr,
                                const SwPosition* pEndRng = nullptr )
        {
            bool bRet = true;
            SwRangeRedline* pRedl = rArr[ rPos ];
            SwPosition *pRStt = nullptr, *pREnd = nullptr;
            SwComparePosition eCmp = SwComparePosition::Outside;
            if( pSttRng && pEndRng )
            {
                pRStt = pRedl->Start();
                pREnd = pRedl->End();
                eCmp = ComparePosition( *pSttRng, *pEndRng, *pRStt, *pREnd );
            }

            pRedl->InvalidateRange();

            switch( pRedl->GetType() )
            {
            case nsRedlineType_t::REDLINE_INSERT:
            case nsRedlineType_t::REDLINE_FORMAT:
                {
                    bool bCheck = false, bReplace = false;
                    switch( eCmp )
                    {
                    case SwComparePosition::Inside:
                        if( *pSttRng == *pRStt )
                            pRedl->SetStart( *pEndRng, pRStt );
                        else
                        {
                            if( *pEndRng != *pREnd )
                            {
                                // split up
                                SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
                                pNew->SetStart( *pEndRng );
                                rArr.Insert( pNew ); ++rPos;
                            }
                            pRedl->SetEnd( *pSttRng, pREnd );
                            bCheck = true;
                        }
                        break;

                    case SwComparePosition::OverlapBefore:
                        pRedl->SetStart( *pEndRng, pRStt );
                        bReplace = true;
                        break;

                    case SwComparePosition::OverlapBehind:
                        pRedl->SetEnd( *pSttRng, pREnd );
                        bCheck = true;
                        break;

                    case SwComparePosition::Outside:
                    case SwComparePosition::Equal:
                        rArr.DeleteAndDestroy( rPos-- );
                        break;

                    default:
                        bRet = false;
                    }

                    if( bReplace || ( bCheck && !pRedl->HasValidRange() ))
                    {
                        // re-insert
                        rArr.Remove( pRedl );
                        rArr.Insert( pRedl );
                    }
                }
                break;
            case nsRedlineType_t::REDLINE_DELETE:
                {
                    SwDoc& rDoc = *pRedl->GetDoc();
                    const SwPosition *pDelStt = nullptr, *pDelEnd = nullptr;
                    bool bDelRedl = false;
                    switch( eCmp )
                    {
                    case SwComparePosition::Inside:
                        if( bCallDelete )
                        {
                            pDelStt = pSttRng;
                            pDelEnd = pEndRng;
                        }
                        break;

                    case SwComparePosition::OverlapBefore:
                        if( bCallDelete )
                        {
                            pDelStt = pRStt;
                            pDelEnd = pEndRng;
                        }
                        break;
                    case SwComparePosition::OverlapBehind:
                        if( bCallDelete )
                        {
                            pDelStt = pREnd;
                            pDelEnd = pSttRng;
                        }
                        break;

                    case SwComparePosition::Outside:
                    case SwComparePosition::Equal:
                        {
                            rArr.Remove( rPos-- );
                            bDelRedl = true;
                            if( bCallDelete )
                            {
                                pDelStt = pRedl->Start();
                                pDelEnd = pRedl->End();
                            }
                        }
                        break;
                    default:
                        bRet = false;
                    }

                    if( pDelStt && pDelEnd )
                    {
                        SwPaM aPam( *pDelStt, *pDelEnd );
                        SwContentNode* pCSttNd = pDelStt->nNode.GetNode().GetContentNode();
                        SwContentNode* pCEndNd = pDelEnd->nNode.GetNode().GetContentNode();

                        if( bDelRedl )
                            delete pRedl;

                        RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
                        rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));

                        if( pCSttNd && pCEndNd )
                            rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
                        else
                        {
                            rDoc.getIDocumentContentOperations().DeleteRange( aPam );

                            if( pCSttNd && !pCEndNd )
                            {
                                aPam.GetBound().nContent.Assign( nullptr, 0 );
                                aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
                                aPam.DeleteMark();
                                rDoc.getIDocumentContentOperations().DelFullPara( aPam );
                            }
                        }
                        rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
                    }
                    else if( bDelRedl )
                        delete pRedl;
                }
                break;

            case nsRedlineType_t::REDLINE_FMTCOLL:
                rArr.DeleteAndDestroy( rPos-- );
                break;

            case nsRedlineType_t::REDLINE_PARAGRAPH_FORMAT:
                rArr.DeleteAndDestroy( rPos-- );
                break;

            default:
                bRet = false;
            }
            return bRet;
        }

        bool lcl_RejectRedline( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
                                bool bCallDelete,
                                const SwPosition* pSttRng = nullptr,
                                const SwPosition* pEndRng = nullptr )
        {
            bool bRet = true;
            SwRangeRedline* pRedl = rArr[ rPos ];
            SwPosition *pRStt = nullptr, *pREnd = nullptr;
            SwComparePosition eCmp = SwComparePosition::Outside;
            if( pSttRng && pEndRng )
            {
                pRStt = pRedl->Start();
                pREnd = pRedl->End();
                eCmp = ComparePosition( *pSttRng, *pEndRng, *pRStt, *pREnd );
            }

            pRedl->InvalidateRange();

            switch( pRedl->GetType() )
            {
            case nsRedlineType_t::REDLINE_INSERT:
                {
                    SwDoc& rDoc = *pRedl->GetDoc();
                    const SwPosition *pDelStt = nullptr, *pDelEnd = nullptr;
                    bool bDelRedl = false;
                    switch( eCmp )
                    {
                    case SwComparePosition::Inside:
                        if( bCallDelete )
                        {
                            pDelStt = pSttRng;
                            pDelEnd = pEndRng;
                        }
                        break;

                    case SwComparePosition::OverlapBefore:
                        if( bCallDelete )
                        {
                            pDelStt = pRStt;
                            pDelEnd = pEndRng;
                        }
                        break;
                    case SwComparePosition::OverlapBehind:
                        if( bCallDelete )
                        {
                            pDelStt = pREnd;
                            pDelEnd = pSttRng;
                        }
                        break;
                    case SwComparePosition::Outside:
                    case SwComparePosition::Equal:
                        {
                            // delete the range again
                            rArr.Remove( rPos-- );
                            bDelRedl = true;
                            if( bCallDelete )
                            {
                                pDelStt = pRedl->Start();
                                pDelEnd = pRedl->End();
                            }
                        }
                        break;

                    default:
                        bRet = false;
                    }
                    if( pDelStt && pDelEnd )
                    {
                        SwPaM aPam( *pDelStt, *pDelEnd );

                        SwContentNode* pCSttNd = pDelStt->nNode.GetNode().GetContentNode();
                        SwContentNode* pCEndNd = pDelEnd->nNode.GetNode().GetContentNode();

                        if( bDelRedl )
                            delete pRedl;

                        RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
                        rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));

                        if( pCSttNd && pCEndNd )
                            rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
                        else
                        {
                            rDoc.getIDocumentContentOperations().DeleteRange( aPam );

                            if( pCSttNd && !pCEndNd )
                            {
                                aPam.GetBound().nContent.Assign( nullptr, 0 );
                                aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
                                aPam.DeleteMark();
                                rDoc.getIDocumentContentOperations().DelFullPara( aPam );
                            }
                        }
                        rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
                    }
                    else if( bDelRedl )
                        delete pRedl;
                }
                break;
            case nsRedlineType_t::REDLINE_DELETE:
                {
                    SwRangeRedline* pNew = nullptr;
                    bool bCheck = false, bReplace = false;

                    switch( eCmp )
                    {
                    case SwComparePosition::Inside:
                        {
                            if( 1 < pRedl->GetStackCount() )
                            {
                                pNew = new SwRangeRedline( *pRedl );
                                pNew->PopData();
                            }
                            if( *pSttRng == *pRStt )
                            {
                                pRedl->SetStart( *pEndRng, pRStt );
                                bReplace = true;
                                if( pNew )
                                    pNew->SetEnd( *pEndRng );
                            }
                            else
                            {
                                if( *pEndRng != *pREnd )
                                {
                                    // split up
                                    SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
                                    pCpy->SetStart( *pEndRng );
                                    rArr.Insert( pCpy ); ++rPos;
                                    if( pNew )
                                        pNew->SetEnd( *pEndRng );
                                }

                                pRedl->SetEnd( *pSttRng, pREnd );
                                bCheck = true;
                                if( pNew )
                                    pNew->SetStart( *pSttRng );
                            }
                        }
                        break;

                    case SwComparePosition::OverlapBefore:
                        if( 1 < pRedl->GetStackCount() )
                        {
                            pNew = new SwRangeRedline( *pRedl );
                            pNew->PopData();
                        }
                        pRedl->SetStart( *pEndRng, pRStt );
                        bReplace = true;
                        if( pNew )
                            pNew->SetEnd( *pEndRng );
                        break;

                    case SwComparePosition::OverlapBehind:
                        if( 1 < pRedl->GetStackCount() )
                        {
                            pNew = new SwRangeRedline( *pRedl );
                            pNew->PopData();
                        }
                        pRedl->SetEnd( *pSttRng, pREnd );
                        bCheck = true;
                        if( pNew )
                            pNew->SetStart( *pSttRng );
                        break;

                    case SwComparePosition::Outside:
                    case SwComparePosition::Equal:
                        if( !pRedl->PopData() )
                            // deleting the RedlineObject is enough
                            rArr.DeleteAndDestroy( rPos-- );
                        break;

                    default:
                        bRet = false;
                    }

                    if( pNew )
                    {
                        rArr.Insert( pNew ); ++rPos;
                    }

                    if( bReplace || ( bCheck && !pRedl->HasValidRange() ))
                    {
                        // re-insert
                        rArr.Remove( pRedl );
                        rArr.Insert( pRedl );
                    }
                }
                break;

            case nsRedlineType_t::REDLINE_FORMAT:
            case nsRedlineType_t::REDLINE_FMTCOLL:
                {
                    if( pRedl->GetExtraData() )
                        pRedl->GetExtraData()->Reject( *pRedl );
                    rArr.DeleteAndDestroy( rPos-- );
                }
                break;

            default:
                bRet = false;
            }
            return bRet;
        }

        typedef bool (*Fn_AcceptReject)( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
                                bool bCallDelete,
                                const SwPosition* pSttRng,
                                const SwPosition* pEndRng);

        int lcl_AcceptRejectRedl( Fn_AcceptReject fn_AcceptReject,
                                    SwRedlineTable& rArr, bool bCallDelete,
                                    const SwPaM& rPam)
        {
            SwRedlineTable::size_type n = 0;
            int nCount = 0;

            const SwPosition* pStt = rPam.Start(),
                            * pEnd = pStt == rPam.GetPoint() ? rPam.GetMark()
                                                             : rPam.GetPoint();
            const SwRangeRedline* pFnd = rArr.FindAtPosition( *pStt, n );
            if( pFnd &&     // Is new a part of it?
                ( *pFnd->Start() != *pStt || *pFnd->End() > *pEnd ))
            {
                // Only revoke the partial selection
                if( (*fn_AcceptReject)( rArr, n, bCallDelete, pStt, pEnd ))
                    nCount++;
                ++n;
            }

            for( ; n < rArr.size(); ++n )
            {
                SwRangeRedline* pTmp = rArr[ n ];
                if( pTmp->HasMark() && pTmp->IsVisible() )
                {
                    if( *pTmp->End() <= *pEnd )
                    {
                        if( (*fn_AcceptReject)( rArr, n, bCallDelete, nullptr, nullptr ))
                            nCount++;
                    }
                    else
                    {
                        if( *pTmp->Start() < *pEnd )
                        {
                            // Only revoke the partial selection
                            if( (*fn_AcceptReject)( rArr, n, bCallDelete, pStt, pEnd ))
                                nCount++;
                        }
                        break;
                    }
                }
            }
            return nCount;
        }

        void lcl_AdjustRedlineRange( SwPaM& rPam )
        {
            // The Selection is only in the ContentSection. If there are Redlines
            // to Non-ContentNodes before or after that, then the Selections
            // expand to them.
            SwPosition* pStt = rPam.Start(),
                      * pEnd = pStt == rPam.GetPoint() ? rPam.GetMark()
                                                       : rPam.GetPoint();
            SwDoc* pDoc = rPam.GetDoc();
            if( !pStt->nContent.GetIndex() &&
                !pDoc->GetNodes()[ pStt->nNode.GetIndex() - 1 ]->IsContentNode() )
            {
                const SwRangeRedline* pRedl = pDoc->getIDocumentRedlineAccess().GetRedline( *pStt, nullptr );
                if( pRedl )
                {
                    const SwPosition* pRStt = pRedl->Start();
                    if( !pRStt->nContent.GetIndex() && pRStt->nNode.GetIndex() ==
                        pStt->nNode.GetIndex() - 1 )
                        *pStt = *pRStt;
                }
            }
            if( pEnd->nNode.GetNode().IsContentNode() &&
                !pDoc->GetNodes()[ pEnd->nNode.GetIndex() + 1 ]->IsContentNode() &&
                pEnd->nContent.GetIndex() == pEnd->nNode.GetNode().GetContentNode()->Len()    )
            {
                const SwRangeRedline* pRedl = pDoc->getIDocumentRedlineAccess().GetRedline( *pEnd, nullptr );
                if( pRedl )
                {
                    const SwPosition* pREnd = pRedl->End();
                    if( !pREnd->nContent.GetIndex() && pREnd->nNode.GetIndex() ==
                        pEnd->nNode.GetIndex() + 1 )
                        *pEnd = *pREnd;
                }
            }
        }

        /// in case some text is deleted, ensure that the not-yet-inserted
        /// SwRangeRedline has its positions corrected not to point to deleted node
        class TemporaryRedlineUpdater
        {
        private:
            SwRangeRedline & m_rRedline;
            std::shared_ptr<SwUnoCursor> m_pCursor;
        public:
            TemporaryRedlineUpdater(SwDoc & rDoc, SwRangeRedline & rRedline)
                : m_rRedline(rRedline)
                , m_pCursor(rDoc.CreateUnoCursor(*rRedline.GetPoint(), false))
            {
                if (m_rRedline.HasMark())
                {
                    m_pCursor->SetMark();
                    *m_pCursor->GetMark() = *m_rRedline.GetMark();
                    *m_rRedline.GetMark() = SwPosition(rDoc.GetNodes().GetEndOfContent());
                }
                *m_rRedline.GetPoint() = SwPosition(rDoc.GetNodes().GetEndOfContent());
            }
            ~TemporaryRedlineUpdater()
            {
                static_cast<SwPaM&>(m_rRedline) = *m_pCursor;
            }
        };
    }

    namespace sw
    {

    DocumentRedlineManager::DocumentRedlineManager(SwDoc& i_rSwdoc)
        : m_rDoc(i_rSwdoc)
        , meRedlineFlags(RedlineFlags::ShowInsert | RedlineFlags::ShowDelete)
        , mpRedlineTable(new SwRedlineTable)
        , mpExtraRedlineTable(new SwExtraRedlineTable)
        , mbIsRedlineMove(false)
        , mbReadlineChecked(false)
        , mnAutoFormatRedlnCommentNo(0)
    {
    }

    RedlineFlags DocumentRedlineManager::GetRedlineFlags() const
    {
        return meRedlineFlags;
    }

    void DocumentRedlineManager::SetRedlineFlags( RedlineFlags eMode )
    {
        if( meRedlineFlags != eMode )
        {
            if( (RedlineFlags::ShowMask & meRedlineFlags) != (RedlineFlags::ShowMask & eMode)
                || !(RedlineFlags::ShowMask & eMode) )
            {
                bool bSaveInXMLImportFlag = m_rDoc.IsInXMLImport();
                m_rDoc.SetInXMLImport( false );
                // and then hide/display everything
                void (SwRangeRedline::*pFnc)(sal_uInt16, size_t) = nullptr;

                RedlineFlags eShowMode = RedlineFlags::ShowMask & eMode;
                if (eShowMode == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
                    pFnc = &SwRangeRedline::Show;
                else if (eShowMode == RedlineFlags::ShowInsert)
                    pFnc = &SwRangeRedline::Hide;
                else if (eShowMode == RedlineFlags::ShowDelete)
                    pFnc = &SwRangeRedline::ShowOriginal;
                else
                {
                    pFnc = &SwRangeRedline::Hide;
                    eMode |= RedlineFlags::ShowInsert;
                }

                CheckAnchoredFlyConsistency(m_rDoc);
                CHECK_REDLINE( *this )

                if (pFnc)
                {
                    for (sal_uInt16 nLoop = 1; nLoop <= 2; ++nLoop)
                        for (size_t i = 0; i < mpRedlineTable->size(); ++i)
                            ((*mpRedlineTable)[i]->*pFnc)(nLoop, i);

                    //SwRangeRedline::MoveFromSection routinely changes
                    //the keys that mpRedlineTable is sorted by
                    mpRedlineTable->Resort();
                }

                CheckAnchoredFlyConsistency(m_rDoc);
                CHECK_REDLINE( *this )
                m_rDoc.SetInXMLImport( bSaveInXMLImportFlag );
            }
            meRedlineFlags = eMode;
            m_rDoc.getIDocumentState().SetModified();
        }

        // #TODO - add 'SwExtraRedlineTable' also ?
    }

    bool DocumentRedlineManager::IsRedlineOn() const
    {
        return IDocumentRedlineAccess::IsRedlineOn(meRedlineFlags);
    }

    bool DocumentRedlineManager::IsIgnoreRedline() const
    {
        return bool(RedlineFlags::Ignore & meRedlineFlags);
    }

    void DocumentRedlineManager::SetRedlineFlags_intern(RedlineFlags eMode)
    {
        meRedlineFlags = eMode;
    }

    const SwRedlineTable& DocumentRedlineManager::GetRedlineTable() const
    {
        return *mpRedlineTable;
    }

    SwRedlineTable& DocumentRedlineManager::GetRedlineTable()
    {
        return *mpRedlineTable;
    }

    const SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable() const
    {
        return *mpExtraRedlineTable;
    }

    SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable()
    {
        return *mpExtraRedlineTable;
    }

    bool DocumentRedlineManager::HasExtraRedlineTable() const
    {
        return mpExtraRedlineTable != nullptr;
    }

    bool DocumentRedlineManager::IsInRedlines(const SwNode & rNode) const
    {
        SwPosition aPos(rNode);
        SwNode & rEndOfRedlines = m_rDoc.GetNodes().GetEndOfRedlines();
        SwPaM aPam(SwPosition(*rEndOfRedlines.StartOfSectionNode()),
                   SwPosition(rEndOfRedlines));

        return aPam.ContainsPosition(aPos);
    }

    bool DocumentRedlineManager::IsRedlineMove() const
    {
        return mbIsRedlineMove;
    }

    void DocumentRedlineManager::SetRedlineMove(bool bFlag)
    {
        mbIsRedlineMove = bFlag;
    }

    /*
    Text means Text not "polluted" by Redlines.

    Behaviour of Insert-Redline:
        - in the Text                       - insert Redline Object
        - in InsertRedline (own)            - ignore, existing is extended
        - in InsertRedline (others)         - split up InsertRedline and
                                              insert Redline Object
        - in DeleteRedline                  - split up DeleteRedline or
                                              move at the end/beginning

    Behaviour of Delete-Redline:
        - in the Text                       - insert Redline Object
        - in DeleteRedline (own/others)     - ignore
        - in InsertRedline (own)            - ignore, but delete character
        - in InsertRedline (others)         - split up InsertRedline and
                                              insert Redline Object
        - Text and own Insert overlap       - delete Text in the own Insert,
                                              extend in the other Text
                                              (up to the Insert!)
        - Text and other Insert overlap     - insert Redline Object, the
                                              other Insert is overlapped by
                                              the Delete
    */
    IDocumentRedlineAccess::AppendResult
    DocumentRedlineManager::AppendRedline(SwRangeRedline* pNewRedl, bool const bCallDelete)
    {
        bool bMerged = false;
        CHECK_REDLINE( *this )

        if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
        {
            pNewRedl->InvalidateRange();

            if( m_rDoc.IsAutoFormatRedline() )
            {
                pNewRedl->SetAutoFormatFlag();
                if( mpAutoFormatRedlnComment && !mpAutoFormatRedlnComment->isEmpty() )
                {
                    pNewRedl->SetComment( *mpAutoFormatRedlnComment );
                    pNewRedl->SetSeqNo( mnAutoFormatRedlnCommentNo );
                }
            }

            SwPosition* pStt = pNewRedl->Start(),
                      * pEnd = pStt == pNewRedl->GetPoint() ? pNewRedl->GetMark()
                                                            : pNewRedl->GetPoint();
            {
                SwTextNode* pTextNode = pStt->nNode.GetNode().GetTextNode();
                if( pTextNode == nullptr )
                {
                    if( pStt->nContent > 0 )
                    {
                        OSL_ENSURE( false, "Redline start: non-text-node with content" );
                        pStt->nContent = 0;
                    }
                }
                else
                {
                    if( pStt->nContent > pTextNode->Len() )
                    {
                        OSL_ENSURE( false, "Redline start: index after text" );
                        pStt->nContent = pTextNode->Len();
                    }
                }
                pTextNode = pEnd->nNode.GetNode().GetTextNode();
                if( pTextNode == nullptr )
                {
                    if( pEnd->nContent > 0 )
                    {
                        OSL_ENSURE( false, "Redline end: non-text-node with content" );
                        pEnd->nContent = 0;
                    }
                }
                else
                {
                    if( pEnd->nContent > pTextNode->Len() )
                    {
                        OSL_ENSURE( false, "Redline end: index after text" );
                        pEnd->nContent = pTextNode->Len();
                    }
                }
            }
            if( ( *pStt == *pEnd ) &&
                ( pNewRedl->GetContentIdx() == nullptr ) )
            {   // Do not insert empty redlines
                delete pNewRedl;
                return AppendResult::IGNORED;
            }
            bool bCompress = false;
            SwRedlineTable::size_type n = 0;
            // look up the first Redline for the starting position
            if( !GetRedline( *pStt, &n ) && n )
                --n;
            bool bDec = false;

            for( ; pNewRedl && n < mpRedlineTable->size(); bDec ? n : ++n )
            {
                bDec = false;

                SwRangeRedline* pRedl = (*mpRedlineTable)[ n ];
                SwPosition* pRStt = pRedl->Start(),
                          * pREnd = pRStt == pRedl->GetPoint() ? pRedl->GetMark()
                                                               : pRedl->GetPoint();

                // #i8518# remove empty redlines while we're at it
                if( ( *pRStt == *pREnd ) &&
                    ( pRedl->GetContentIdx() == nullptr ) )
                {
                    mpRedlineTable->DeleteAndDestroy(n);
                    continue;
                }

                SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd );

                switch( pNewRedl->GetType() )
                {
                case nsRedlineType_t::REDLINE_INSERT:
                    switch( pRedl->GetType() )
                    {
                    case nsRedlineType_t::REDLINE_INSERT:
                        if( pRedl->IsOwnRedline( *pNewRedl ) )
                        {
                            bool bDelete = false;

                            // Merge if applicable?
                            if( (( SwComparePosition::Behind == eCmpPos &&
                                   IsPrevPos( *pREnd, *pStt ) ) ||
                                 ( SwComparePosition::CollideStart == eCmpPos ) ||
                                 ( SwComparePosition::OverlapBehind == eCmpPos ) ) &&
                                pRedl->CanCombine( *pNewRedl ) &&
                                ( n+1 >= mpRedlineTable->size() ||
                                 ( *(*mpRedlineTable)[ n+1 ]->Start() >= *pEnd &&
                                 *(*mpRedlineTable)[ n+1 ]->Start() != *pREnd ) ) )
                            {
                                pRedl->SetEnd( *pEnd, pREnd );
                                if( !pRedl->HasValidRange() )
                                {
                                    // re-insert
                                    mpRedlineTable->Remove( n );
                                    mpRedlineTable->Insert( pRedl );
                                }

                                bMerged = true;
                                bDelete = true;
                            }
                            else if( (( SwComparePosition::Before == eCmpPos &&
                                        IsPrevPos( *pEnd, *pRStt ) ) ||
                                       ( SwComparePosition::CollideEnd == eCmpPos ) ||
                                      ( SwComparePosition::OverlapBefore == eCmpPos ) ) &&
                                pRedl->CanCombine( *pNewRedl ) &&
                                ( !n ||
                                 *(*mpRedlineTable)[ n-1 ]->End() != *pRStt ))
                            {
                                pRedl->SetStart( *pStt, pRStt );
                                // re-insert
                                mpRedlineTable->Remove( n );
                                mpRedlineTable->Insert( pRedl );

                                bMerged = true;
                                bDelete = true;
                            }
                            else if ( SwComparePosition::Outside == eCmpPos )
                            {
                                // own insert-over-insert redlines:
                                // just scrap the inside ones
                                mpRedlineTable->DeleteAndDestroy( n );
                                bDec = true;
                            }
                            else if( SwComparePosition::OverlapBehind == eCmpPos )
                            {
                                *pStt = *pREnd;
                                if( ( *pStt == *pEnd ) &&
                                    ( pNewRedl->GetContentIdx() == nullptr ) )
                                    bDelete = true;
                            }
                            else if( SwComparePosition::OverlapBefore == eCmpPos )
                            {
                                *pEnd = *pRStt;
                                if( ( *pStt == *pEnd ) &&
                                    ( pNewRedl->GetContentIdx() == nullptr ) )
                                    bDelete = true;
                            }
                            else if( SwComparePosition::Inside == eCmpPos )
                            {
                                bDelete = true;
                                bMerged = true;
                            }
                            else if( SwComparePosition::Equal == eCmpPos )
                                bDelete = true;

                            if( bDelete )
                            {
                                delete pNewRedl;
                                pNewRedl = nullptr;
                                bCompress = true;
                            }
                        }
                        else if( SwComparePosition::Inside == eCmpPos )
                        {
                            // split up
                            if( *pEnd != *pREnd )
                            {
                                SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
                                pCpy->SetStart( *pEnd );
                                mpRedlineTable->Insert( pCpy );
                            }
                            pRedl->SetEnd( *pStt, pREnd );
                            if( ( *pStt == *pRStt ) &&
                                ( pRedl->GetContentIdx() == nullptr ) )
                            {
                                mpRedlineTable->DeleteAndDestroy( n );
                                bDec = true;
                            }
                            else if( !pRedl->HasValidRange() )
                            {
                                // re-insert
                                mpRedlineTable->Remove( n );
                                mpRedlineTable->Insert( pRedl );
                            }
                        }
                        else if ( SwComparePosition::Outside == eCmpPos )
                        {
                            // handle overlapping redlines in broken documents

                            // split up the new redline, since it covers the
                            // existing redline. Insert the first part, and
                            // progress with the remainder as usual
                            SwRangeRedline* pSplit = new SwRangeRedline( *pNewRedl );
                            pSplit->SetEnd( *pRStt );
                            pNewRedl->SetStart( *pREnd );
                            mpRedlineTable->Insert( pSplit );
                            if( *pStt == *pEnd && pNewRedl->GetContentIdx() == nullptr )
                            {
                                delete pNewRedl;
                                pNewRedl = nullptr;
                                bCompress = true;
                            }
                        }
                        else if ( SwComparePosition::OverlapBehind == eCmpPos )
                        {
                            // handle overlapping redlines in broken documents
                            pNewRedl->SetStart( *pREnd );
                        }
                        else if ( SwComparePosition::OverlapBefore == eCmpPos )
                        {
                            // handle overlapping redlines in broken documents
                            *pEnd = *pRStt;
                            if( ( *pStt == *pEnd ) &&
                                ( pNewRedl->GetContentIdx() == nullptr ) )
                            {
                                delete pNewRedl;
                                pNewRedl = nullptr;
                                bCompress = true;
                            }
                        }
                        break;
                    case nsRedlineType_t::REDLINE_DELETE:
                        if( SwComparePosition::Inside == eCmpPos )
                        {
                            // split up
                            if( *pEnd != *pREnd )
                            {
                                SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
                                pCpy->SetStart( *pEnd );
                                mpRedlineTable->Insert( pCpy );
                            }
                            pRedl->SetEnd( *pStt, pREnd );
                            if( ( *pStt == *pRStt ) &&
                                ( pRedl->GetContentIdx() == nullptr ) )
                            {
                                mpRedlineTable->DeleteAndDestroy( n );
                                bDec = true;
                            }
                            else if( !pRedl->HasValidRange() )
                            {
                                // re-insert
                                mpRedlineTable->Remove( n );
                                mpRedlineTable->Insert( pRedl, n );
                            }
                        }
                        else if ( SwComparePosition::Outside == eCmpPos )
                        {
                            // handle overlapping redlines in broken documents

                            // split up the new redline, since it covers the
                            // existing redline. Insert the first part, and
                            // progress with the remainder as usual
                            SwRangeRedline* pSplit = new SwRangeRedline( *pNewRedl );
                            pSplit->SetEnd( *pRStt );
                            pNewRedl->SetStart( *pREnd );
                            mpRedlineTable->Insert( pSplit );
                            if( *pStt == *pEnd && pNewRedl->GetContentIdx() == nullptr )
                            {
                                delete pNewRedl;
                                pNewRedl = nullptr;
                                bCompress = true;
                            }
                        }
                        else if ( SwComparePosition::Equal == eCmpPos )
                        {
                            // handle identical redlines in broken documents
                            // delete old (delete) redline
                            mpRedlineTable->DeleteAndDestroy( n );
                            bDec = true;
                        }
                        else if ( SwComparePosition::OverlapBehind == eCmpPos )
                        {   // Another workaround for broken redlines
                            pNewRedl->SetStart( *pREnd );
                        }
                        break;
                    case nsRedlineType_t::REDLINE_FORMAT:
                        switch( eCmpPos )
                        {
                        case SwComparePosition::OverlapBefore:
                            pRedl->SetStart( *pEnd, pRStt );
                            // re-insert
                            mpRedlineTable->Remove( n );
                            mpRedlineTable->Insert( pRedl, n );
                            bDec = true;
                            break;

                        case SwComparePosition::OverlapBehind:
                            pRedl->SetEnd( *pStt, pREnd );
                            if( *pStt == *pRStt && pRedl->GetContentIdx() == nullptr )
                            {
                                mpRedlineTable->DeleteAndDestroy( n );
                                bDec = true;
                            }
                            break;

                        case SwComparePosition::Equal:
                        case SwComparePosition::Outside:
                            // Overlaps the current one completely or has the
                            // same dimension, delete the old one
                            mpRedlineTable->DeleteAndDestroy( n );
                            bDec = true;
                            break;

                        case SwComparePosition::Inside:
                            // Overlaps the current one completely,
                            // split or shorten the new one
                            if( *pEnd != *pREnd )
                            {
                                if( *pEnd != *pRStt )
                                {
                                    SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
                                    pNew->SetStart( *pEnd );
                                    pRedl->SetEnd( *pStt, pREnd );
                                    if( *pStt == *pRStt && pRedl->GetContentIdx() == nullptr )
                                        mpRedlineTable->DeleteAndDestroy( n );
                                    AppendRedline( pNew, bCallDelete );
                                    n = 0;      // re-initialize
                                    bDec = true;
                                }
                            }
                            else
                                pRedl->SetEnd( *pStt, pREnd );
                            break;
                        default:
                            break;
                        }
                        break;
                    default:
                        break;
                    }
                    break;

                case nsRedlineType_t::REDLINE_DELETE:
                    switch( pRedl->GetType() )
                    {
                    case nsRedlineType_t::REDLINE_DELETE:
                        switch( eCmpPos )
                        {
                        case SwComparePosition::Outside:
                            {
                                // Overlaps the current one completely,
                                // split the new one
                                if( *pEnd != *pREnd )
                                {
                                    SwRangeRedline* pNew = new SwRangeRedline( *pNewRedl );
                                    pNew->SetStart( *pREnd );
                                    pNewRedl->SetEnd( *pRStt, pEnd );
                                    AppendRedline( pNew, bCallDelete );
                                    n = 0;      // re-initialize
                                    bDec = true;
                                }
                                else
                                    pNewRedl->SetEnd( *pRStt, pEnd );
                            }
                            break;

                        case SwComparePosition::Inside:
                        case SwComparePosition::Equal:
                            delete pNewRedl;
                            pNewRedl = nullptr;
                            bCompress = true;
                            break;

                        case SwComparePosition::OverlapBefore:
                        case SwComparePosition::OverlapBehind:
                            if( pRedl->IsOwnRedline( *pNewRedl ) &&
                                pRedl->CanCombine( *pNewRedl ))
                            {
                                // If that's the case we can merge it, meaning
                                // the new one covers this well
                                if( SwComparePosition::OverlapBehind == eCmpPos )
                                    pNewRedl->SetStart( *pRStt, pStt );
                                else
                                    pNewRedl->SetEnd( *pREnd, pEnd );
                                mpRedlineTable->DeleteAndDestroy( n );
                                bDec = true;
                            }
                            else if( SwComparePosition::OverlapBehind == eCmpPos )
                                pNewRedl->SetStart( *pREnd, pStt );
                            else
                                pNewRedl->SetEnd( *pRStt, pEnd );
                            break;

                        case SwComparePosition::CollideStart:
                        case SwComparePosition::CollideEnd:
                            if( pRedl->IsOwnRedline( *pNewRedl ) &&
                                pRedl->CanCombine( *pNewRedl ) )
                            {
                                if( IsHideChanges( meRedlineFlags ))
                                {
                                    // Before we can merge, we make it visible!
                                    // We insert temporarily so that pNew is
                                    // also dealt with when moving the indices.
                                    mpRedlineTable->Insert(pNewRedl);
                                    pRedl->Show(0, mpRedlineTable->GetPos(pRedl));
                                    mpRedlineTable->Remove( pNewRedl );
                                    pRStt = pRedl->Start();
                                    pREnd = pRedl->End();
                                }

                                // If that's the case we can merge it, meaning
                                // the new one covers this well
                                if( SwComparePosition::CollideStart == eCmpPos )
                                    pNewRedl->SetStart( *pRStt, pStt );
                                else
                                    pNewRedl->SetEnd( *pREnd, pEnd );

                                // delete current (below), and restart process with
                                // previous
                                SwRedlineTable::size_type nToBeDeleted = n;
                                bDec = true;

                                if( *(pNewRedl->Start()) <= *pREnd )
                                {
                                    // Whoooah, we just extended the new 'redline'
                                    // beyond previous redlines, so better start
                                    // again. Of course this is not supposed to
                                    // happen, and in an ideal world it doesn't,
                                    // but unfortunately this code is buggy and
                                    // totally rotten so it does happen and we
                                    // better fix it.
                                    n = 0;
                                    bDec = true;
                                }

                                mpRedlineTable->DeleteAndDestroy( nToBeDeleted );
                            }
                            break;
                        default:
                            break;
                        }
                        break;

                    case nsRedlineType_t::REDLINE_INSERT:
                    {
                        // b62341295: Do not throw away redlines
                        // even if they are not allowed to be combined
                        RedlineFlags eOld = meRedlineFlags;
                        if( !( eOld & RedlineFlags::DontCombineRedlines ) &&
                            pRedl->IsOwnRedline( *pNewRedl ) )
                        {

                            // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
                            // The ShowMode needs to be retained!
                            meRedlineFlags = eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore);
                            switch( eCmpPos )
                            {
                            case SwComparePosition::Equal:
                                bCompress = true;
                                mpRedlineTable->DeleteAndDestroy( n );
                                bDec = true;
                                SAL_FALLTHROUGH;

                            case SwComparePosition::Inside:
                                if( bCallDelete )
                                {
                                    // DeleteAndJoin does not yield the
                                    // desired result if there is no paragraph to
                                    // join with, i.e. at the end of the document.
                                    // For this case, we completely delete the
                                    // paragraphs (if, of course, we also start on
                                    // a paragraph boundary).
                                    if( (pStt->nContent == 0) &&
                                        pEnd->nNode.GetNode().IsEndNode() )
                                    {
                                        pEnd->nNode--;
                                        pEnd->nContent.Assign(
                                            pEnd->nNode.GetNode().GetTextNode(), 0);
                                        m_rDoc.getIDocumentContentOperations().DelFullPara( *pNewRedl );
                                    }
                                    else
                                        m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl );

                                    bCompress = true;
                                }
                                delete pNewRedl;
                                pNewRedl = nullptr;
                                break;

                            case SwComparePosition::Outside:
                                {
                                    mpRedlineTable->Remove( n );
                                    bDec = true;
                                    if( bCallDelete )
                                    {
                                        TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
                                        m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pRedl );
                                        n = 0;      // re-initialize
                                    }
                                    delete pRedl;
                                }
                                break;

                            case SwComparePosition::OverlapBefore:
                                {
                                    SwPaM aPam( *pRStt, *pEnd );

                                    if( *pEnd == *pREnd )
                                        mpRedlineTable->DeleteAndDestroy( n );
                                    else
                                    {
                                        pRedl->SetStart( *pEnd, pRStt );
                                        // re-insert
                                        mpRedlineTable->Remove( n );
                                        mpRedlineTable->Insert( pRedl, n );
                                    }

                                    if( bCallDelete )
                                    {
                                        TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
                                        m_rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
                                        n = 0;      // re-initialize
                                    }
                                    bDec = true;
                                }
                                break;

                            case SwComparePosition::OverlapBehind:
                                {
                                    SwPaM aPam( *pStt, *pREnd );

                                    if( *pStt == *pRStt )
                                    {
                                        mpRedlineTable->DeleteAndDestroy( n );
                                        bDec = true;
                                    }
                                    else
                                        pRedl->SetEnd( *pStt, pREnd );

                                    if( bCallDelete )
                                    {
                                        TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
                                        m_rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
                                        n = 0;      // re-initialize
                                        bDec = true;
                                    }
                                }
                                break;
                            default:
                                break;
                            }

                            meRedlineFlags = eOld;
                        }
                        else
                        {
                            // it may be necessary to split the existing redline in
                            // two. In this case, pRedl will be changed to cover
                            // only part of its former range, and pNew will cover
                            // the remainder.
                            SwRangeRedline* pNew = nullptr;

                            switch( eCmpPos )
                            {
                            case SwComparePosition::Equal:
                                {
                                    pRedl->PushData( *pNewRedl );
                                    delete pNewRedl;
                                    pNewRedl = nullptr;
                                    if( IsHideChanges( meRedlineFlags ))
                                    {
                                        pRedl->Hide(0, mpRedlineTable->GetPos(pRedl));
                                    }
                                    bCompress = true;
                                }
                                break;

                            case SwComparePosition::Inside:
                                {
                                    if( *pRStt == *pStt )
                                    {
                                        // #i97421#
                                        // redline w/out extent loops
                                        if (*pStt != *pEnd)
                                        {
                                            pNewRedl->PushData( *pRedl, false );
                                            pRedl->SetStart( *pEnd, pRStt );
                                            // re-insert
                                            mpRedlineTable->Remove( n );
                                            mpRedlineTable->Insert( pRedl, n );
                                            bDec = true;
                                        }
                                    }
                                    else
                                    {
                                        pNewRedl->PushData( *pRedl, false );
                                        if( *pREnd != *pEnd )
                                        {
                                            pNew = new SwRangeRedline( *pRedl );
                                            pNew->SetStart( *pEnd );
                                        }
                                        pRedl->SetEnd( *pStt, pREnd );
                                        if( !pRedl->HasValidRange() )
                                        {
                                            // re-insert
                                            mpRedlineTable->Remove( n );
                                            mpRedlineTable->Insert( pRedl, n );
                                        }
                                    }
                                }
                                break;

                            case SwComparePosition::Outside:
                                {
                                    pRedl->PushData( *pNewRedl );
                                    if( *pEnd == *pREnd )
                                        pNewRedl->SetEnd( *pRStt, pEnd );
                                    else
                                    {
                                        pNew = new SwRangeRedline( *pNewRedl );
                                        pNew->SetEnd( *pRStt );
                                        pNewRedl->SetStart( *pREnd, pStt );
                                    }
                                    bCompress = true;
                                }
                                break;

                            case SwComparePosition::OverlapBefore:
                                {
                                    if( *pEnd == *pREnd )
                                    {
                                        pRedl->PushData( *pNewRedl );
                                        pNewRedl->SetEnd( *pRStt, pEnd );
                                        if( IsHideChanges( meRedlineFlags ))
                                        {
                                            mpRedlineTable->Insert(pNewRedl);
                                            pRedl->Hide(0, mpRedlineTable->GetPos(pRedl));
                                            mpRedlineTable->Remove( pNewRedl );
                                        }
                                    }
                                    else
                                    {
                                        pNew = new SwRangeRedline( *pRedl );
                                        pNew->PushData( *pNewRedl );
                                        pNew->SetEnd( *pEnd );
                                        pNewRedl->SetEnd( *pRStt, pEnd );
                                        pRedl->SetStart( *pNew->End(), pRStt ) ;
                                        // re-insert
                                        mpRedlineTable->Remove( n );
                                        mpRedlineTable->Insert( pRedl );
                                        bDec = true;
                                    }
                                }
                                break;

                            case SwComparePosition::OverlapBehind:
                                {
                                    if( *pStt == *pRStt )
                                    {
                                        pRedl->PushData( *pNewRedl );
                                        pNewRedl->SetStart( *pREnd, pStt );
                                        if( IsHideChanges( meRedlineFlags ))
                                        {
                                            mpRedlineTable->Insert( pNewRedl );
                                            pRedl->Hide(0, mpRedlineTable->GetPos(pRedl));
                                            mpRedlineTable->Remove( pNewRedl );
                                        }
                                    }
                                    else
                                    {
                                        pNew = new SwRangeRedline( *pRedl );
                                        pNew->PushData( *pNewRedl );
                                        pNew->SetStart( *pStt );
                                        pNewRedl->SetStart( *pREnd, pStt );
                                        pRedl->SetEnd( *pNew->Start(), pREnd );
                                        if( !pRedl->HasValidRange() )
                                        {
                                            // re-insert
                                            mpRedlineTable->Remove( n );
                                            mpRedlineTable->Insert( pRedl );
                                        }
                                    }
                                }
                                break;
                            default:
                                break;
                            }

                            // insert the pNew part (if it exists)
                            if( pNew )
                            {
                                mpRedlineTable->Insert( pNew );

                                // pNew must be deleted if Insert() wasn't
                                // successful. But that can't happen, since pNew is
                                // part of the original pRedl redline.
                                // OSL_ENSURE( bRet, "Can't insert existing redline?" );

                                // restart (now with pRedl being split up)
                                n = 0;
                                bDec = true;
                            }
                        }
                    }
                    break;

                    case nsRedlineType_t::REDLINE_FORMAT:
                        switch( eCmpPos )
                        {
                        case SwComparePosition::OverlapBefore:
                            pRedl->SetStart( *pEnd, pRStt );
                            // re-insert
                            mpRedlineTable->Remove( n );
                            mpRedlineTable->Insert( pRedl, n );
                            bDec = true;
                            break;

                        case SwComparePosition::OverlapBehind:
                            pRedl->SetEnd( *pStt, pREnd );
                            break;

                        case SwComparePosition::Equal:
                        case SwComparePosition::Outside:
                            // Overlaps the current one completely or has the
                            // same dimension, delete the old one
                            mpRedlineTable->DeleteAndDestroy( n );
                            bDec = true;
                            break;

                        case SwComparePosition::Inside:
                            // Overlaps the current one completely,
                            // split or shorten the new one
                            if( *pEnd != *pREnd )
                            {
                                if( *pEnd != *pRStt )
                                {
                                    SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
                                    pNew->SetStart( *pEnd );
                                    pRedl->SetEnd( *pStt, pREnd );
                                    if( ( *pStt == *pRStt ) &&
                                        ( pRedl->GetContentIdx() == nullptr ) )
                                        mpRedlineTable->DeleteAndDestroy( n );
                                    AppendRedline( pNew, bCallDelete );
                                    n = 0;      // re-initialize
                                    bDec = true;
                                }
                            }
                            else
                                pRedl->SetEnd( *pStt, pREnd );
                            break;
                        default:
                            break;
                        }
                        break;
                    default:
                        break;
                    }
                    break;

                case nsRedlineType_t::REDLINE_FORMAT:
                    switch( pRedl->GetType() )
                    {
                    case nsRedlineType_t::REDLINE_INSERT:
                    case nsRedlineType_t::REDLINE_DELETE:
                        switch( eCmpPos )
                        {
                        case SwComparePosition::OverlapBefore:
                            pNewRedl->SetEnd( *pRStt, pEnd );
                            break;

                        case SwComparePosition::OverlapBehind:
                            pNewRedl->SetStart( *pREnd, pStt );
                            break;

                        case SwComparePosition::Equal:
                        case SwComparePosition::Inside:
                            delete pNewRedl;
                            pNewRedl = nullptr;
                            break;

                        case SwComparePosition::Outside:
                            // Overlaps the current one completely,
                            // split or shorten the new one
                            if( *pEnd != *pREnd )
                            {
                                if( *pEnd != *pRStt )
                                {
                                    SwRangeRedline* pNew = new SwRangeRedline( *pNewRedl );
                                    pNew->SetStart( *pREnd );
                                    pNewRedl->SetEnd( *pRStt, pEnd );
                                    AppendRedline( pNew, bCallDelete );
                                    n = 0;      // re-initialize
                                    bDec = true;
                                }
                            }
                            else
                                pNewRedl->SetEnd( *pRStt, pEnd );
                            break;
                        default:
                            break;
                        }
                        break;
                    case nsRedlineType_t::REDLINE_FORMAT:
                        switch( eCmpPos )
                        {
                        case SwComparePosition::Outside:
                        case SwComparePosition::Equal:
                            {
                                // Overlaps the current one completely or has the
                                // same dimension, delete the old one
                                mpRedlineTable->DeleteAndDestroy( n );
                                bDec = true;
                            }
                            break;

                        case SwComparePosition::Inside:
                            if( pRedl->IsOwnRedline( *pNewRedl ) &&
                                pRedl->CanCombine( *pNewRedl ))
                            {
                                // own one can be ignored completely
                                delete pNewRedl;
                                pNewRedl = nullptr;
                            }
                            else if( *pREnd == *pEnd )
                                // or else only shorten the current one
                                pRedl->SetEnd( *pStt, pREnd );
                            else if( *pRStt == *pStt )
                            {
                                // or else only shorten the current one
                                pRedl->SetStart( *pEnd, pRStt );
                                // re-insert
                                mpRedlineTable->Remove( n );
                                mpRedlineTable->Insert( pRedl, n );
                                bDec = true;
                            }
                            else
                            {
                                // If it lies completely within the current one
                                // we need to split it
                                SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
                                pNew->SetStart( *pEnd );
                                pRedl->SetEnd( *pStt, pREnd );
                                AppendRedline( pNew, bCallDelete );
                                n = 0;      // re-initialize
                                bDec = true;
                            }
                            break;

                        case SwComparePosition::OverlapBefore:
                        case SwComparePosition::OverlapBehind:
                            if( pRedl->IsOwnRedline( *pNewRedl ) &&
                                pRedl->CanCombine( *pNewRedl ))
                            {
                                // If that's the case we can merge it, meaning
                                // the new one covers this well
                                if( SwComparePosition::OverlapBehind == eCmpPos )
                                    pNewRedl->SetStart( *pRStt, pStt );
                                else
                                    pNewRedl->SetEnd( *pREnd, pEnd );
                                mpRedlineTable->DeleteAndDestroy( n );
                                bDec = false;
                            }
                            else if( SwComparePosition::OverlapBehind == eCmpPos )
                                pNewRedl->SetStart( *pREnd, pStt );
                            else
                                pNewRedl->SetEnd( *pRStt, pEnd );
                            break;

                        case SwComparePosition::CollideEnd:
                            if( pRedl->IsOwnRedline( *pNewRedl ) &&
                                pRedl->CanCombine( *pNewRedl ) && n &&
                                *(*mpRedlineTable)[ n-1 ]->End() < *pStt )
                            {
                                // If that's the case we can merge it, meaning
                                // the new one covers this well
                                pNewRedl->SetEnd( *pREnd, pEnd );
                                mpRedlineTable->DeleteAndDestroy( n );
                                bDec = true;
                            }
                            break;
                        case SwComparePosition::CollideStart:
                            if( pRedl->IsOwnRedline( *pNewRedl ) &&
                                pRedl->CanCombine( *pNewRedl ) &&
                                n+1 < mpRedlineTable->size() &&
                                *(*mpRedlineTable)[ n+1 ]->Start() < *pEnd )
                            {
                                // If that's the case we can merge it, meaning
                                // the new one covers this well
                                pNewRedl->SetStart( *pRStt, pStt );
                                mpRedlineTable->DeleteAndDestroy( n );
                                bDec = true;
                            }
                            break;
                        default:
                            break;
                        }
                        break;
                    default:
                        break;
                    }
                    break;

                case nsRedlineType_t::REDLINE_FMTCOLL:
                    // How should we behave here?
                    // insert as is
                    break;
                default:
                    break;
                }
            }

            if( pNewRedl )
            {
                if( ( *pStt == *pEnd ) &&
                    ( pNewRedl->GetContentIdx() == nullptr ) )
                {   // Do not insert empty redlines
                    delete pNewRedl;
                    pNewRedl = nullptr;
                }
                else
                {
                    if (pStt->nContent == 0)
                    {
                        // tdf#54819 to keep the style of the paragraph
                        // after the fully deleted paragraphs (normal behaviour
                        // of editing without change tracking), we copy its style
                        // to the first removed paragraph.
                        SwTextNode* pDelNode = pStt->nNode.GetNode().GetTextNode();
                        SwTextNode* pTextNode = pEnd->nNode.GetNode().GetTextNode();
                        if (pDelNode != nullptr && pTextNode != nullptr && pDelNode != pTextNode)
                            pTextNode->CopyCollFormat( *pDelNode );
                    }
                    else
                    {
                        // tdf#119571 update the style of the joined paragraph
                        // after a partially deleted paragraph to show its correct style
                        // in "Show changes" mode, too. All removed paragraphs
                        // get the style of the first (partially deleted) paragraph
                        // to avoid text insertion with bad style in the deleted
                        // area later.
                        SwContentNode* pDelNd = pStt->nNode.GetNode().GetContentNode();
                        SwContentNode* pTextNd = pEnd->nNode.GetNode().GetContentNode();
                        SwTextNode* pDelNode = pStt->nNode.GetNode().GetTextNode();
                        SwTextNode* pTextNode;
                        SwNodeIndex aIdx( pEnd->nNode.GetNode() );

                        while (pDelNode != nullptr && pTextNd != nullptr && pDelNd->GetIndex() < pTextNd->GetIndex())
                        {
                            pTextNode = pTextNd->GetTextNode();
                            if (pTextNode && pDelNode != pTextNode )
                                pDelNode->CopyCollFormat( *pTextNode );
                            pTextNd = SwNodes::GoPrevious( &aIdx );
                        }
                    }
                    mpRedlineTable->Insert( pNewRedl );
                }
            }

            if( bCompress )
                CompressRedlines();
        }
        else
        {
            if( bCallDelete && nsRedlineType_t::REDLINE_DELETE == pNewRedl->GetType() )
            {
                RedlineFlags eOld = meRedlineFlags;
                // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
                // The ShowMode needs to be retained!
                meRedlineFlags = eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore);
                m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl );
                meRedlineFlags = eOld;
            }
            delete pNewRedl;
            pNewRedl = nullptr;
        }
        CHECK_REDLINE( *this )

        return (nullptr != pNewRedl)
            ? AppendResult::APPENDED
            : (bMerged ? AppendResult::MERGED : AppendResult::IGNORED);
    }

    bool DocumentRedlineManager::AppendTableRowRedline( SwTableRowRedline* pNewRedl, bool )
    {
        // #TODO - equivalent for 'SwTableRowRedline'
        /*
        CHECK_REDLINE( this )
        */

        if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
        {
            // #TODO - equivalent for 'SwTableRowRedline'
            /*
            pNewRedl->InvalidateRange();
            */

            // Make equivalent of 'AppendRedline' checks inside here too

            mpExtraRedlineTable->Insert( pNewRedl );
        }
        else
        {
            // TO DO - equivalent for 'SwTableRowRedline'
            /*
            if( bCallDelete && nsRedlineType_t::REDLINE_DELETE == pNewRedl->GetType() )
            {
                RedlineFlags eOld = meRedlineFlags;
                // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
                // The ShowMode needs to be retained!
                meRedlineFlags = eOld & ~(RedlineFlags::On | RedlineFlags::Ignore);
                DeleteAndJoin( *pNewRedl );
                meRedlineFlags = eOld;
            }
            delete pNewRedl, pNewRedl = 0;
            */
        }
        // #TODO - equivalent for 'SwTableRowRedline'
        /*
        CHECK_REDLINE( this )
        */

        return nullptr != pNewRedl;
    }

    bool DocumentRedlineManager::AppendTableCellRedline( SwTableCellRedline* pNewRedl, bool )
    {
        // #TODO - equivalent for 'SwTableCellRedline'
        /*
        CHECK_REDLINE( this )
        */

        if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
        {
            // #TODO - equivalent for 'SwTableCellRedline'
            /*
            pNewRedl->InvalidateRange();
            */

            // Make equivalent of 'AppendRedline' checks inside here too

            mpExtraRedlineTable->Insert( pNewRedl );
        }
        else
        {
            // TO DO - equivalent for 'SwTableCellRedline'
            /*
            if( bCallDelete && nsRedlineType_t::REDLINE_DELETE == pNewRedl->GetType() )
            {
                RedlineFlags eOld = meRedlineFlags;
                // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
                // The ShowMode needs to be retained!
                meRedlineFlags = eOld & ~(RedlineFlags::On | RedlineFlags::Ignore);
                DeleteAndJoin( *pNewRedl );
                meRedlineFlags = eOld;
            }
            delete pNewRedl, pNewRedl = 0;
            */
        }
        // #TODO - equivalent for 'SwTableCellRedline'
        /*
        CHECK_REDLINE( this )
        */

        return nullptr != pNewRedl;
    }

    void DocumentRedlineManager::CompressRedlines()
    {
        CHECK_REDLINE( *this )

        void (SwRangeRedline::*pFnc)(sal_uInt16, size_t) = nullptr;
        RedlineFlags eShow = RedlineFlags::ShowMask & meRedlineFlags;
        if( eShow == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
            pFnc = &SwRangeRedline::Show;
        else if (eShow == RedlineFlags::ShowInsert)
            pFnc = &SwRangeRedline::Hide;

        // Try to merge identical ones
        for( SwRedlineTable::size_type n = 1; n < mpRedlineTable->size(); ++n )
        {
            SwRangeRedline* pPrev = (*mpRedlineTable)[ n-1 ],
                        * pCur = (*mpRedlineTable)[ n ];
            const SwPosition* pPrevStt = pPrev->Start(),
                            * pPrevEnd = pPrevStt == pPrev->GetPoint()
                                ? pPrev->GetMark() : pPrev->GetPoint();
            const SwPosition* pCurStt = pCur->Start(),
                            * pCurEnd = pCurStt == pCur->GetPoint()
                                ? pCur->GetMark() : pCur->GetPoint();
            if( *pPrevEnd == *pCurStt && pPrev->CanCombine( *pCur ) &&
                pPrevStt->nNode.GetNode().StartOfSectionNode() ==
                pCurEnd->nNode.GetNode().StartOfSectionNode() &&
                !pCurEnd->nNode.GetNode().StartOfSectionNode()->IsTableNode() )
            {
                // we then can merge them
                SwRedlineTable::size_type nPrevIndex = n-1;
                pPrev->Show(0, nPrevIndex);
                pCur->Show(0, n);

                pPrev->SetEnd( *pCur->End() );
                mpRedlineTable->DeleteAndDestroy( n );
                --n;
                if( pFnc )
                    (pPrev->*pFnc)(0, nPrevIndex);
            }
        }
        CHECK_REDLINE( *this )

        // #TODO - add 'SwExtraRedlineTable' also ?
    }

    bool DocumentRedlineManager::SplitRedline( const SwPaM& rRange )
    {
        bool bChg = false;
        SwRedlineTable::size_type n = 0;
        const SwPosition* pStt = rRange.Start();
        const SwPosition* pEnd = rRange.End();
        GetRedline( *pStt, &n );
        for ( ; n < mpRedlineTable->size(); ++n)
        {
            SwRangeRedline * pRedline = (*mpRedlineTable)[ n ];
            SwPosition *const pRedlineStart = pRedline->Start();
            SwPosition *const pRedlineEnd = pRedline->End();
            if (*pRedlineStart <= *pStt && *pStt <= *pRedlineEnd &&
                *pRedlineStart <= *pEnd && *pEnd <= *pRedlineEnd)
            {
                bChg = true;
                int nn = 0;
                if (*pStt == *pRedlineStart)
                    nn += 1;
                if (*pEnd == *pRedlineEnd)
                    nn += 2;

                SwRangeRedline* pNew = nullptr;
                switch( nn )
                {
                case 0:
                    pNew = new SwRangeRedline( *pRedline );
                    pRedline->SetEnd( *pStt, pRedlineEnd );
                    pNew->SetStart( *pEnd );
                    break;

                case 1:
                    *pRedlineStart = *pEnd;
                    break;

                case 2:
                    *pRedlineEnd = *pStt;
                    break;

                case 3:
                    pRedline->InvalidateRange();
                    mpRedlineTable->DeleteAndDestroy( n-- );
                    pRedline = nullptr;
                    break;
                }
                if (pRedline && !pRedline->HasValidRange())
                {
                    // re-insert
                    mpRedlineTable->Remove( n );
                    mpRedlineTable->Insert( pRedline, n );
                }
                if( pNew )
                    mpRedlineTable->Insert( pNew, n );
            }
            else if (*pEnd < *pRedlineStart)
                break;
        }
        return bChg;

        // #TODO - add 'SwExtraRedlineTable' also ?
    }

    bool DocumentRedlineManager::DeleteRedline( const SwPaM& rRange, bool bSaveInUndo,
                                sal_uInt16 nDelType )
    {
        if( RedlineFlags::IgnoreDeleteRedlines & meRedlineFlags ||
            !rRange.HasMark() || *rRange.GetMark() == *rRange.GetPoint() )
            return false;

        bool bChg = false;

        if (bSaveInUndo && m_rDoc.GetIDocumentUndoRedo().DoesUndo())
        {
            SwUndoRedline* pUndo = new SwUndoRedline( SwUndoId::REDLINE, rRange );
            if( pUndo->GetRedlSaveCount() )
            {
                m_rDoc.GetIDocumentUndoRedo().AppendUndo(pUndo);
            }
            else
                delete pUndo;
        }

        const SwPosition* pStt = rRange.Start(),
                        * pEnd = pStt == rRange.GetPoint() ? rRange.GetMark()
                                                           : rRange.GetPoint();
        SwRedlineTable::size_type n = 0;
        GetRedline( *pStt, &n );
        for( ; n < mpRedlineTable->size() ; ++n )
        {
            SwRangeRedline* pRedl = (*mpRedlineTable)[ n ];
            if( USHRT_MAX != nDelType && nDelType != pRedl->GetType() )
                continue;

            SwPosition* pRStt = pRedl->Start(),
                      * pREnd = pRStt == pRedl->GetPoint() ? pRedl->GetMark()
                                                           : pRedl->GetPoint();
            switch( ComparePosition( *pStt, *pEnd, *pRStt, *pREnd ) )
            {
            case SwComparePosition::Equal:
            case SwComparePosition::Outside:
                pRedl->InvalidateRange();
                mpRedlineTable->DeleteAndDestroy( n-- );
                bChg = true;
                break;

            case SwComparePosition::OverlapBefore:
                    pRedl->InvalidateRange();
                    pRedl->SetStart( *pEnd, pRStt );
                    // re-insert
                    mpRedlineTable->Remove( n );
                    mpRedlineTable->Insert( pRedl );
                    --n;
                break;

            case SwComparePosition::OverlapBehind:
                    pRedl->InvalidateRange();
                    pRedl->SetEnd( *pStt, pREnd );
                    if( !pRedl->HasValidRange() )
                    {
                        // re-insert
                        mpRedlineTable->Remove( n );
                        mpRedlineTable->Insert( pRedl );
                        --n;
                    }
                break;

            case SwComparePosition::Inside:
                {
                    // this one needs to be splitted
                    pRedl->InvalidateRange();
                    if( *pRStt == *pStt )
                    {
                        pRedl->SetStart( *pEnd, pRStt );
                        // re-insert
                        mpRedlineTable->Remove( n );
                        mpRedlineTable->Insert( pRedl );
                        --n;
                    }
                    else
                    {
                        SwRangeRedline* pCpy;
                        if( *pREnd != *pEnd )
                        {
                            pCpy = new SwRangeRedline( *pRedl );
                            pCpy->SetStart( *pEnd );
                        }
                        else
                            pCpy = nullptr;
                        pRedl->SetEnd( *pStt, pREnd );
                        if( !pRedl->HasValidRange() )
                        {
                            // re-insert
                            mpRedlineTable->Remove( n );
                            mpRedlineTable->Insert( pRedl );
                            --n;
                        }
                        if( pCpy )
                            mpRedlineTable->Insert( pCpy );
                    }
                }
                break;

            case SwComparePosition::CollideEnd:
            case SwComparePosition::Before:
                n = mpRedlineTable->size();
                break;
            default:
                break;
            }
        }

        if( bChg )
            m_rDoc.getIDocumentState().SetModified();

        return bChg;

        // #TODO - add 'SwExtraRedlineTable' also ?
    }

    bool DocumentRedlineManager::DeleteRedline( const SwStartNode& rNode, bool bSaveInUndo,
                                sal_uInt16 nDelType )
    {
        SwPaM aTemp(*rNode.EndOfSectionNode(), rNode);
        return DeleteRedline(aTemp, bSaveInUndo, nDelType);
    }

    SwRedlineTable::size_type DocumentRedlineManager::GetRedlinePos( const SwNode& rNd, sal_uInt16 nType ) const
    {
        const sal_uLong nNdIdx = rNd.GetIndex();
        for( SwRedlineTable::size_type n = 0; n < mpRedlineTable->size() ; ++n )
        {
            const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
            sal_uLong nPt = pTmp->GetPoint()->nNode.GetIndex(),
                  nMk = pTmp->GetMark()->nNode.GetIndex();
            if( nPt < nMk ) { long nTmp = nMk; nMk = nPt; nPt = nTmp; }

            if( ( USHRT_MAX == nType || nType == pTmp->GetType()) &&
                nMk <= nNdIdx && nNdIdx <= nPt )
                return n;

            if( nMk > nNdIdx )
                break;
        }
        return SwRedlineTable::npos;

        // #TODO - add 'SwExtraRedlineTable' also ?
    }

    const SwRangeRedline* DocumentRedlineManager::GetRedline( const SwPosition& rPos,
                                        SwRedlineTable::size_type* pFndPos ) const
    {
        SwRedlineTable::size_type nO = mpRedlineTable->size(), nM, nU = 0;
        if( nO > 0 )
        {
            nO--;
            while( nU <= nO )
            {
                nM = nU + ( nO - nU ) / 2;
                const SwRangeRedline* pRedl = (*mpRedlineTable)[ nM ];
                const SwPosition* pStt = pRedl->Start();
                const SwPosition* pEnd = pStt == pRedl->GetPoint()
                                            ? pRedl->GetMark()
                                            : pRedl->GetPoint();
                if( pEnd == pStt
                        ? *pStt == rPos
                        : ( *pStt <= rPos && rPos < *pEnd ) )
                {
                    while( nM && rPos == *(*mpRedlineTable)[ nM - 1 ]->End() &&
                        rPos == *(*mpRedlineTable)[ nM - 1 ]->Start() )
                    {
                        --nM;
                        pRedl = (*mpRedlineTable)[ nM ];
                    }
                    // if there are format and insert changes in the same position
                    // show insert change first.
                    // since the redlines are sorted by position, only check the redline
                    // before and after the current redline
                    if( nsRedlineType_t::REDLINE_FORMAT == pRedl->GetType() )
                    {
                        if( nM && rPos >= *(*mpRedlineTable)[ nM - 1 ]->Start() &&
                            rPos <= *(*mpRedlineTable)[ nM - 1 ]->End() &&
                            ( nsRedlineType_t::REDLINE_INSERT == (*mpRedlineTable)[ nM - 1 ]->GetType() ) )
                        {
                            --nM;
                            pRedl = (*mpRedlineTable)[ nM ];
                        }
                        else if( ( nM + 1 ) <= nO && rPos >= *(*mpRedlineTable)[ nM + 1 ]->Start() &&
                            rPos <= *(*mpRedlineTable)[ nM + 1 ]->End() &&
                            ( nsRedlineType_t::REDLINE_INSERT == (*mpRedlineTable)[ nM + 1 ]->GetType() ) )
                        {
                            ++nM;
                            pRedl = (*mpRedlineTable)[ nM ];
                        }
                    }

                    if( pFndPos )
                        *pFndPos = nM;
                    return pRedl;
                }
                else if( *pEnd <= rPos )
                    nU = nM + 1;
                else if( nM == 0 )
                {
                    if( pFndPos )
                        *pFndPos = nU;
                    return nullptr;
                }
                else
                    nO = nM - 1;
            }
        }
        if( pFndPos )
            *pFndPos = nU;
        return nullptr;

        // #TODO - add 'SwExtraRedlineTable' also ?
    }

    bool DocumentRedlineManager::AcceptRedline( SwRedlineTable::size_type nPos, bool bCallDelete )
    {
        bool bRet = false;

        // Switch to visible in any case
        if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
            (RedlineFlags::ShowMask & meRedlineFlags) )
          SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );

        SwRangeRedline* pTmp = (*mpRedlineTable)[ nPos ];
        if( pTmp->HasMark() && pTmp->IsVisible() )
        {
            if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
            {
                SwRewriter aRewriter;

                aRewriter.AddRule(UndoArg1, pTmp->GetDescr());
                m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::ACCEPT_REDLINE, &aRewriter);
            }

            int nLoopCnt = 2;
            sal_uInt16 nSeqNo = pTmp->GetSeqNo();

            do {

                if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
                {
                    SwUndo *const pUndo( new SwUndoAcceptRedline(*pTmp) );
                    m_rDoc.GetIDocumentUndoRedo().AppendUndo(pUndo);
                }

                bRet |= lcl_AcceptRedline( *mpRedlineTable, nPos, bCallDelete );

                if( nSeqNo )
                {
                    if( SwRedlineTable::npos == nPos )
                        nPos = 0;
                    SwRedlineTable::size_type nFndPos = 2 == nLoopCnt
                                        ? mpRedlineTable->FindNextSeqNo( nSeqNo, nPos )
                                        : mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos );
                    if( SwRedlineTable::npos != nFndPos || ( 0 != ( --nLoopCnt ) &&
                        SwRedlineTable::npos != ( nFndPos =
                            mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos ))) )
                        pTmp = (*mpRedlineTable)[ nPos = nFndPos ];
                    else
                        nLoopCnt = 0;
                }
                else
                    nLoopCnt = 0;

            } while( nLoopCnt );

            if( bRet )
            {
                CompressRedlines();
                m_rDoc.getIDocumentState().SetModified();
            }

            if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
            {
                m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
            }
        }
        return bRet;

        // #TODO - add 'SwExtraRedlineTable' also ?
    }

    bool DocumentRedlineManager::AcceptRedline( const SwPaM& rPam, bool bCallDelete )
    {
        // Switch to visible in any case
        if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
            (RedlineFlags::ShowMask & meRedlineFlags) )
          SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );

        // The Selection is only in the ContentSection. If there are Redlines
        // to Non-ContentNodes before or after that, then the Selections
        // expand to them.
        SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
        lcl_AdjustRedlineRange( aPam );

        if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
        {
            m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::ACCEPT_REDLINE, nullptr );
            m_rDoc.GetIDocumentUndoRedo().AppendUndo( new SwUndoAcceptRedline( aPam ));
        }

        int nRet = lcl_AcceptRejectRedl( lcl_AcceptRedline, *mpRedlineTable,
                                         bCallDelete, aPam );
        if( nRet > 0 )
        {
            CompressRedlines();
            m_rDoc.getIDocumentState().SetModified();
        }
        if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
        {
            OUString aTmpStr;

            {
                SwRewriter aRewriter;
                aRewriter.AddRule(UndoArg1, OUString::number(nRet));
                aTmpStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
            }

            SwRewriter aRewriter;
            aRewriter.AddRule(UndoArg1, aTmpStr);

            m_rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::ACCEPT_REDLINE, &aRewriter );
        }
        return nRet != 0;

        // #TODO - add 'SwExtraRedlineTable' also ?
    }

    void DocumentRedlineManager::AcceptRedlineParagraphFormatting( const SwPaM &rPam )
    {
        const SwPosition* pStt = rPam.Start(),
                        * pEnd = pStt == rPam.GetPoint() ? rPam.GetMark()
                                                         : rPam.GetPoint();

        const sal_uLong nSttIdx = pStt->nNode.GetIndex();
        const sal_uLong nEndIdx = pEnd->nNode.GetIndex();

        for( SwRedlineTable::size_type n = 0; n < mpRedlineTable->size() ; ++n )
        {
            const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
            sal_uLong nPt = pTmp->GetPoint()->nNode.GetIndex(),
                  nMk = pTmp->GetMark()->nNode.GetIndex();
            if( nPt < nMk ) { long nTmp = nMk; nMk = nPt; nPt = nTmp; }

            if( nsRedlineType_t::REDLINE_PARAGRAPH_FORMAT == pTmp->GetType() &&
                ( (nSttIdx <= nMk && nMk <= nEndIdx) || (nSttIdx <= nPt && nPt <= nEndIdx) ) )
                    AcceptRedline( n, false );

            if( nMk > nEndIdx )
                break;
        }
    }

    bool DocumentRedlineManager::RejectRedline( SwRedlineTable::size_type nPos, bool bCallDelete )
    {
        bool bRet = false;

        // Switch to visible in any case
        if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
            (RedlineFlags::ShowMask & meRedlineFlags) )
          SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );

        SwRangeRedline* pTmp = (*mpRedlineTable)[ nPos ];
        if( pTmp->HasMark() && pTmp->IsVisible() )
        {
            if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
            {
                SwRewriter aRewriter;

                aRewriter.AddRule(UndoArg1, pTmp->GetDescr());
                m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::REJECT_REDLINE, &aRewriter);
            }

            int nLoopCnt = 2;
            sal_uInt16 nSeqNo = pTmp->GetSeqNo();

            do {

                if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
                {
                    SwUndo *const pUndo( new SwUndoRejectRedline( *pTmp ) );
                    m_rDoc.GetIDocumentUndoRedo().AppendUndo(pUndo);
                }

                bRet |= lcl_RejectRedline( *mpRedlineTable, nPos, bCallDelete );

                if( nSeqNo )
                {
                    if( SwRedlineTable::npos == nPos )
                        nPos = 0;
                    SwRedlineTable::size_type nFndPos = 2 == nLoopCnt
                                        ? mpRedlineTable->FindNextSeqNo( nSeqNo, nPos )
                                        : mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos );
                    if( SwRedlineTable::npos != nFndPos || ( 0 != ( --nLoopCnt ) &&
                        SwRedlineTable::npos != ( nFndPos =
                                mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos ))) )
                        pTmp = (*mpRedlineTable)[ nPos = nFndPos ];
                    else
                        nLoopCnt = 0;
                }
                else
                    nLoopCnt = 0;

            } while( nLoopCnt );

            if( bRet )
            {
                CompressRedlines();
                m_rDoc.getIDocumentState().SetModified();
            }

            if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
            {
                m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
            }
        }
        return bRet;

        // #TODO - add 'SwExtraRedlineTable' also ?
    }

    bool DocumentRedlineManager::RejectRedline( const SwPaM& rPam, bool bCallDelete )
    {
        // Switch to visible in any case
        if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
            (RedlineFlags::ShowMask & meRedlineFlags) )
          SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );

        // The Selection is only in the ContentSection. If there are Redlines
        // to Non-ContentNodes before or after that, then the Selections
        // expand to them.
        SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
        lcl_AdjustRedlineRange( aPam );

        if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
        {
            m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::REJECT_REDLINE, nullptr );
            m_rDoc.GetIDocumentUndoRedo().AppendUndo( new SwUndoRejectRedline(aPam) );
        }

        int nRet = lcl_AcceptRejectRedl( lcl_RejectRedline, *mpRedlineTable,
                                            bCallDelete, aPam );
        if( nRet > 0 )
        {
            CompressRedlines();
            m_rDoc.getIDocumentState().SetModified();
        }
        if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
        {
            OUString aTmpStr;

            {
                SwRewriter aRewriter;
                aRewriter.AddRule(UndoArg1, OUString::number(nRet));
                aTmpStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
            }

            SwRewriter aRewriter;
            aRewriter.AddRule(UndoArg1, aTmpStr);

            m_rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::REJECT_REDLINE, &aRewriter );
        }

        return nRet != 0;

        // #TODO - add 'SwExtraRedlineTable' also ?
    }

    void DocumentRedlineManager::AcceptAllRedline(bool bAccept)
    {
        OUString sUndoStr;
        IDocumentUndoRedo& rUndoMgr = m_rDoc.GetIDocumentUndoRedo();

        if (mpRedlineTable->size() > 1)
        {
            {
                SwRewriter aRewriter;
                aRewriter.AddRule(UndoArg1, OUString::number(mpRedlineTable->size()));
                sUndoStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
            }

            SwRewriter aRewriter;
            aRewriter.AddRule(UndoArg1, sUndoStr);
            rUndoMgr.StartUndo(bAccept ? SwUndoId::ACCEPT_REDLINE : SwUndoId::REJECT_REDLINE, &aRewriter);
        }

        while (mpRedlineTable->size() > 0)
        {
            if (bAccept)
                AcceptRedline(mpRedlineTable->size() - 1, true);
            else
                RejectRedline(mpRedlineTable->size() - 1, true);
        }

        if (!sUndoStr.isEmpty())
        {
            rUndoMgr.EndUndo(SwUndoId::EMPTY, nullptr);
        }
    }

    const SwRangeRedline* DocumentRedlineManager::SelNextRedline( SwPaM& rPam ) const
    {
        rPam.DeleteMark();
        rPam.SetMark();

        SwPosition& rSttPos = *rPam.GetPoint();
        SwPosition aSavePos( rSttPos );
        bool bRestart;

        // If the starting position points to the last valid ContentNode,
        // we take the next Redline in any case.
        SwRedlineTable::size_type n = 0;
        const SwRangeRedline* pFnd = GetRedlineTable().FindAtPosition( rSttPos, n );
        if( pFnd )
        {
            const SwPosition* pEnd = pFnd->End();
            if( !pEnd->nNode.GetNode().IsContentNode() )
            {
                SwNodeIndex aTmp( pEnd->nNode );
                SwContentNode* pCNd = SwNodes::GoPrevSection( &aTmp );
                if( !pCNd || ( aTmp == rSttPos.nNode &&
                    pCNd->Len() == rSttPos.nContent.GetIndex() ))
                    pFnd = nullptr;
            }
            if( pFnd )
                rSttPos = *pFnd->End();
        }

        do {
            bRestart = false;

            for( ; !pFnd && n < mpRedlineTable->size(); ++n )
            {
                pFnd = (*mpRedlineTable)[ n ];
                if( pFnd->HasMark() && pFnd->IsVisible() )
                {
                    *rPam.GetMark() = *pFnd->Start();
                    rSttPos = *pFnd->End();
                    break;
                }
                else
                    pFnd = nullptr;
            }

            if( pFnd )
            {
                // Merge all of the same type and author that are
                // consecutive into one Selection.
                const SwPosition* pPrevEnd = pFnd->End();
                while( ++n < mpRedlineTable->size() )
                {
                    const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
                    if( pTmp->HasMark() && pTmp->IsVisible() )
                    {
                        const SwPosition *pRStt;
                        if( pFnd->GetType() == pTmp->GetType() &&
                            pFnd->GetAuthor() == pTmp->GetAuthor() &&
                            ( *pPrevEnd == *( pRStt = pTmp->Start() ) ||
                              IsPrevPos( *pPrevEnd, *pRStt )) )
                        {
                            pPrevEnd = pTmp->End();
                            rSttPos = *pPrevEnd;
                        }
                        else
                            break;
                    }
                }
            }

            if( pFnd )
            {
                const SwRangeRedline* pSaveFnd = pFnd;

                SwContentNode* pCNd;
                SwNodeIndex* pIdx = &rPam.GetMark()->nNode;
                if( !pIdx->GetNode().IsContentNode() &&
                    nullptr != ( pCNd = m_rDoc.GetNodes().GoNextSection( pIdx )) )
                {
                    if( *pIdx <= rPam.GetPoint()->nNode )
                        rPam.GetMark()->nContent.Assign( pCNd, 0 );
                    else
                        pFnd = nullptr;
                }

                if( pFnd )
                {
                    pIdx = &rPam.GetPoint()->nNode;
                    if( !pIdx->GetNode().IsContentNode() &&
                        nullptr != ( pCNd = SwNodes::GoPrevSection( pIdx )) )
                    {
                        if( *pIdx >= rPam.GetMark()->nNode )
                            rPam.GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
                        else
                            pFnd = nullptr;
                    }
                }

                if( !pFnd || *rPam.GetMark() == *rPam.GetPoint() )
                {
                    if( n < mpRedlineTable->size() )
                    {
                        bRestart = true;
                        *rPam.GetPoint() = *pSaveFnd->End();
                    }
                    else
                    {
                        rPam.DeleteMark();
                        *rPam.GetPoint() = aSavePos;
                    }
                    pFnd = nullptr;
                }
            }
        } while( bRestart );

        return pFnd;

        // #TODO - add 'SwExtraRedlineTable' also ?
    }

    const SwRangeRedline* DocumentRedlineManager::SelPrevRedline( SwPaM& rPam ) const
    {
        rPam.DeleteMark();
        rPam.SetMark();

        SwPosition& rSttPos = *rPam.GetPoint();
        SwPosition aSavePos( rSttPos );
        bool bRestart;

        // If the starting position points to the last valid ContentNode,
        // we take the previous Redline in any case.
        SwRedlineTable::size_type n = 0;
        const SwRangeRedline* pFnd = GetRedlineTable().FindAtPosition( rSttPos, n, false );
        if( pFnd )
        {
            const SwPosition* pStt = pFnd->Start();
            if( !pStt->nNode.GetNode().IsContentNode() )
            {
                SwNodeIndex aTmp( pStt->nNode );
                SwContentNode* pCNd = m_rDoc.GetNodes().GoNextSection( &aTmp );
                if( !pCNd || ( aTmp == rSttPos.nNode &&
                    !rSttPos.nContent.GetIndex() ))
                    pFnd = nullptr;
            }
            if( pFnd )
                rSttPos = *pFnd->Start();
        }

        do {
            bRestart = false;

            while( !pFnd && 0 < n )
            {
                pFnd = (*mpRedlineTable)[ --n ];
                if( pFnd->HasMark() && pFnd->IsVisible() )
                {
                    *rPam.GetMark() = *pFnd->End();
                    rSttPos = *pFnd->Start();
                }
                else
                    pFnd = nullptr;
            }

            if( pFnd )
            {
                // Merge all of the same type and author that are
                // consecutive into one Selection.
                const SwPosition* pNextStt = pFnd->Start();
                while( 0 < n )
                {
                    const SwRangeRedline* pTmp = (*mpRedlineTable)[ --n ];
                    if( pTmp->HasMark() && pTmp->IsVisible() )
                    {
                        const SwPosition *pREnd;
                        if( pFnd->GetType() == pTmp->GetType() &&
                            pFnd->GetAuthor() == pTmp->GetAuthor() &&
                            ( *pNextStt == *( pREnd = pTmp->End() ) ||
                              IsPrevPos( *pREnd, *pNextStt )) )
                        {
                            pNextStt = pTmp->Start();
                            rSttPos = *pNextStt;
                        }
                        else
                        {
                            ++n;
                            break;
                        }
                    }
                }
            }

            if( pFnd )
            {
                const SwRangeRedline* pSaveFnd = pFnd;

                SwContentNode* pCNd;
                SwNodeIndex* pIdx = &rPam.GetMark()->nNode;
                if( !pIdx->GetNode().IsContentNode() &&
                    nullptr != ( pCNd = SwNodes::GoPrevSection( pIdx )) )
                {
                    if( *pIdx >= rPam.GetPoint()->nNode )
                        rPam.GetMark()->nContent.Assign( pCNd, pCNd->Len() );
                    else
                        pFnd = nullptr;
                }

                if( pFnd )
                {
                    pIdx = &rPam.GetPoint()->nNode;
                    if( !pIdx->GetNode().IsContentNode() &&
                        nullptr != ( pCNd = m_rDoc.GetNodes().GoNextSection( pIdx )) )
                    {
                        if( *pIdx <= rPam.GetMark()->nNode )
                            rPam.GetPoint()->nContent.Assign( pCNd, 0 );
                        else
                            pFnd = nullptr;
                    }
                }

                if( !pFnd || *rPam.GetMark() == *rPam.GetPoint() )
                {
                    if( n )
                    {
                        bRestart = true;
                        *rPam.GetPoint() = *pSaveFnd->Start();
                    }
                    else
                    {
                        rPam.DeleteMark();
                        *rPam.GetPoint() = aSavePos;
                    }
                    pFnd = nullptr;
                }
            }
        } while( bRestart );

        return pFnd;

        // #TODO - add 'SwExtraRedlineTable' also ?
    }

    // Set comment at the Redline
    bool DocumentRedlineManager::SetRedlineComment( const SwPaM& rPaM, const OUString& rS )
    {
        bool bRet = false;
        const SwPosition* pStt = rPaM.Start(),
                        * pEnd = pStt == rPaM.GetPoint() ? rPaM.GetMark()
                                                         : rPaM.GetPoint();
        SwRedlineTable::size_type n = 0;
        if( GetRedlineTable().FindAtPosition( *pStt, n ) )
        {
            for( ; n < mpRedlineTable->size(); ++n )
            {
                bRet = true;
                SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
                if( pStt != pEnd && *pTmp->Start() > *pEnd )
                    break;

                pTmp->SetComment( rS );
                if( *pTmp->End() >= *pEnd )
                    break;
            }
        }
        if( bRet )
            m_rDoc.getIDocumentState().SetModified();

        return bRet;

        // #TODO - add 'SwExtraRedlineTable' also ?
    }

    // Create a new author if necessary
    std::size_t DocumentRedlineManager::GetRedlineAuthor()
    {
        return SW_MOD()->GetRedlineAuthor();
    }

    /// Insert new author into the Table for the Readers etc.
    std::size_t DocumentRedlineManager::InsertRedlineAuthor( const OUString& rNew )
    {
        return SW_MOD()->InsertRedlineAuthor(rNew);
    }

    void DocumentRedlineManager::UpdateRedlineAttr()
    {
        const SwRedlineTable& rTable = GetRedlineTable();
        for(SwRangeRedline* pRedl : rTable)
        {
            if( pRedl->IsVisible() )
                pRedl->InvalidateRange();
        }

        // #TODO - add 'SwExtraRedlineTable' also ?
    }

    const uno::Sequence <sal_Int8>& DocumentRedlineManager::GetRedlinePassword() const
    {
        return maRedlinePasswd;
    }

    void DocumentRedlineManager::SetRedlinePassword(
                /*[in]*/const uno::Sequence <sal_Int8>& rNewPassword)
    {
        maRedlinePasswd = rNewPassword;
        m_rDoc.getIDocumentState().SetModified();
    }

    /// Set comment text for the Redline, which is inserted later on via
    /// AppendRedline. Is used by Autoformat.
    /// A null pointer resets the mode. The pointer is not copied, so it
    /// needs to stay valid!
    void DocumentRedlineManager::SetAutoFormatRedlineComment( const OUString* pText, sal_uInt16 nSeqNo )
    {
        m_rDoc.SetAutoFormatRedline( nullptr != pText );
        if( pText )
        {
            mpAutoFormatRedlnComment.reset( new OUString( *pText ) );
        }
        else
        {
            mpAutoFormatRedlnComment.reset();
        }

        mnAutoFormatRedlnCommentNo = nSeqNo;
    }

    #define MAX_REDLINE_COUNT   250

    void DocumentRedlineManager::checkRedlining(RedlineFlags& _rReadlineMode)
    {
        const SwRedlineTable& rRedlineTable = GetRedlineTable();
        SwEditShell* pEditShell = m_rDoc.GetEditShell();
        vcl::Window* pParent = pEditShell ? pEditShell->GetWin() : nullptr;
        if ( pParent && !mbReadlineChecked && rRedlineTable.size() > MAX_REDLINE_COUNT
            && ((_rReadlineMode & RedlineFlags::ShowDelete) != RedlineFlags::ShowDelete) )
        {
            std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pParent->GetFrameWeld(), "modules/swriter/ui/queryshowchangesdialog.ui"));
            std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog("QueryShowChangesDialog"));
            sal_uInt16 nResult = xQuery->run();
            mbReadlineChecked = true;
            if ( nResult == RET_YES )
            {
                _rReadlineMode |= RedlineFlags::ShowInsert | RedlineFlags::ShowDelete;
            }
        }
    }

    DocumentRedlineManager::~DocumentRedlineManager()
    {
    }

    }

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

↑ V547 Expression 'pFnc' is always true.