/* -*- 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.