Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  DocumentRedlineManager.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * 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 <rootfrm.hxx>
#include <txtfrm.hxx>
#include <txtfld.hxx>
#include <doc.hxx>
#include <docsh.hxx>
#include <wrtsh.hxx>
#include <fmtfld.hxx>
#include <frmtool.hxx>
#include <IDocumentUndoRedo.hxx>
#include <IDocumentFieldsAccess.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <IDocumentState.hxx>
#include <redline.hxx>
#include <UndoRedline.hxx>
#include <docary.hxx>
#include <ndtxt.hxx>
#include <unocrsr.hxx>
#include <ftnidx.hxx>
#include <authfld.hxx>
#include <strings.hrc>
#include <swmodule.hxx>
#include <osl/diagnose.h>
#include <editeng/prntitem.hxx>
#include <comphelper/lok.hxx>
#include <svl/itemiter.hxx>
#include <istyleaccess.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<SwContentIndexReg*>(&pPos->GetNode())
                    == pPos->GetContentNode());

            SwTextNode* pTextNode = pPos->GetNode().GetTextNode();
            if( pTextNode == nullptr )
            {
                assert(pPos->GetContentIndex() == 0);
            }
            else
            {
                assert(pPos->GetContentIndex() >= 0 && pPos->GetContentIndex() <= 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( const 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
                // note: these can destroy sorting in SwTextNode::Update()
                // if there's another one without mark on the same pos.
                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 sw {

static void UpdateFieldsForRedline(IDocumentFieldsAccess & rIDFA)
{
    auto const pAuthType(static_cast<SwAuthorityFieldType*>(rIDFA.GetFieldType(
        SwFieldIds::TableOfAuthorities, OUString(), false)));
    if (pAuthType) // created on demand...
    {
        pAuthType->DelSequenceArray();
    }
    rIDFA.GetFieldType(SwFieldIds::RefPageGet, OUString(), false)->UpdateFields();
    rIDFA.GetSysFieldType(SwFieldIds::Chapter)->UpdateFields();
    rIDFA.UpdateExpFields(nullptr, false);
    rIDFA.UpdateRefFields();
}

void UpdateFramesForAddDeleteRedline(SwDoc & rDoc, SwPaM const& rPam)
{
    if (rDoc.IsClipBoard())
    {
        return;
    }
    // no need to call UpdateFootnoteNums for FTNNUM_PAGE:
    // the AppendFootnote/RemoveFootnote will do it by itself!
    rDoc.GetFootnoteIdxs().UpdateFootnote(rPam.Start()->GetNode());
    SwPosition currentStart(*rPam.Start());
    SwTextNode * pStartNode(rPam.Start()->GetNode().GetTextNode());
    while (!pStartNode)
    {
        // note: branch only taken for redlines, not fieldmarks
        SwStartNode *const pTableOrSectionNode(
            currentStart.GetNode().IsTableNode()
                ? static_cast<SwStartNode*>(currentStart.GetNode().GetTableNode())
                : static_cast<SwStartNode*>(currentStart.GetNode().GetSectionNode()));
        if ( !pTableOrSectionNode )
        {
            SAL_WARN("sw.core""UpdateFramesForAddDeleteRedline:: known pathology (or ChangesInRedline mode)");
            return;
        }
        for (SwNodeOffset j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j)
        {
            pTableOrSectionNode->GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::Hidden);
        }
        for (SwRootFrame const*const pLayout : rDoc.GetAllLayouts())
        {
            if (pLayout->HasMergedParas())
            {
                if (pTableOrSectionNode->IsTableNode())
                {
                    static_cast<SwTableNode*>(pTableOrSectionNode)->DelFrames(pLayout);
                }
                else
                {
                    static_cast<SwSectionNode*>(pTableOrSectionNode)->DelFrames(pLayout);
                }
            }
        }
        currentStart.Assign( pTableOrSectionNode->EndOfSectionIndex() + 1 );
        pStartNode = currentStart.GetNode().GetTextNode();
    }
    if (currentStart < *rPam.End())
    {
        SwTextNode * pNode(pStartNode);
        do
        {
            // deleted text node: remove it from "hidden" list
            // to update numbering in Show Changes mode
            SwPosition aPos( *pNode, pNode->Len() );
            if ( pNode->GetNumRule() && aPos < *rPam.End() )
                pNode->RemoveFromListRLHidden();

            std::vector<SwTextFrame*> frames;
            SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNode);
            for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
            {
                if (pFrame->getRootFrame()->HasMergedParas())
                {
                    frames.push_back(pFrame);
                }
                // set anchored objects as deleted
                pFrame->SetDrawObjsAsDeleted(true);
            }
            if (frames.empty())
            {
                auto const layouts(rDoc.GetAllLayouts());
                assert(std::none_of(layouts.begin(), layouts.end(),
                    [](SwRootFrame const*const pLayout) { return pLayout->IsHideRedlines(); }));
                (void) layouts;
                break;
            }
            auto eMode(sw::FrameMode::Existing);
            SwTextNode * pLast(pNode);
            for (SwTextFrame * pFrame : frames)
            {
                SwTextNode & rFirstNode(pFrame->GetMergedPara()
                    ? *pFrame->GetMergedPara()->pFirstNode
                    : *pNode);
                assert(pNode == pStartNode
                        ? rFirstNode.GetIndex() <= pNode->GetIndex()
                        : &rFirstNode == pNode);
                // clear old one first to avoid DelFrames confusing updates & asserts...
                pFrame->SetMergedPara(nullptr);
                pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
                    *pFrame, rFirstNode, eMode));
                eMode = sw::FrameMode::New// Existing is not idempotent!
                // the first node of the new redline is not necessarily the first
                // node of the merged frame, there could be another redline nearby
                sw::AddRemoveFlysAnchoredToFrameStartingAtNode(*pFrame, *pNode, nullptr);
                // if redline is split across table and table cell is empty, there's no redline in the cell and so no merged para
                if (pFrame->GetMergedPara())
                {
                    pLast = const_cast<SwTextNode*>(pFrame->GetMergedPara()->pLastNode);
                }
            }
            SwNodeIndex tmp(*pLast);
            // skip over hidden sections!
            pNode = static_cast<SwTextNode*>(SwNodes::GoNextSection(&tmp, /*bSkipHidden=*/true, /*bSkipProtect=*/false));
        }
        while (pNode && pNode->GetIndex() <= rPam.End()->GetNodeIndex());
    }
    // fields last - SwGetRefField::UpdateField requires up-to-date frames
    UpdateFieldsForRedline(rDoc.getIDocumentFieldsAccess()); // after footnotes

    // update SwPostItMgr / notes in the margin
    rDoc.GetDocShell()->Broadcast(
            SwFormatFieldHint(nullptr, SwFormatFieldHintWhich::REMOVED) );
}

void UpdateFramesForRemoveDeleteRedline(SwDoc & rDoc, SwPaM const& rPam)
{
    // tdf#147006 fieldmark command may be empty => do not call AppendAllObjs()
    if (rDoc.IsClipBoard() || *rPam.GetPoint() == *rPam.GetMark())
    {
        return;
    }
    bool isAppendObjsCalled(false);
    rDoc.GetFootnoteIdxs().UpdateFootnote(rPam.Start()->GetNode());
    SwPosition currentStart(*rPam.Start());
    SwTextNode * pStartNode(rPam.Start()->GetNode().GetTextNode());
    while (!pStartNode)
    {
        // note: branch only taken for redlines, not fieldmarks
        SwStartNode *const pTableOrSectionNode(
            currentStart.GetNode().IsTableNode()
                ? static_cast<SwStartNode*>(currentStart.GetNode().GetTableNode())
                : static_cast<SwStartNode*>(currentStart.GetNode().GetSectionNode()));
        assert(pTableOrSectionNode); // known pathology
        for (SwNodeOffset j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j)
        {
            pTableOrSectionNode->GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::None);
        }
        if (rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->HasMergedParas())
        {
            // note: this will also create frames for all currently hidden flys
            // because it calls AppendAllObjs
            ::MakeFrames(rDoc, currentStart.GetNode(), *pTableOrSectionNode->EndOfSectionNode());
            isAppendObjsCalled = true;
        }
        currentStart.Assign( pTableOrSectionNode->EndOfSectionIndex() + 1 );
        pStartNode = currentStart.GetNode().GetTextNode();
    }
    if (currentStart < *rPam.End())
    {
        SwTextNode * pNode(pStartNode);
        do
        {
            // undeleted text node: add it to the "hidden" list
            // to update numbering in Show Changes mode
            SwPosition aPos( *pNode, pNode->Len() );
            if ( pNode->GetNumRule() && aPos < *rPam.End() )
                pNode->AddToListRLHidden();

            std::vector<SwTextFrame*> frames;
            SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNode);
            for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
            {
                if (pFrame->getRootFrame()->HasMergedParas())
                {
                    frames.push_back(pFrame);
                }
                // set anchored objects as not deleted
                pFrame->SetDrawObjsAsDeleted(false);
            }
            if (frames.empty())
            {
                // in SwUndoSaveSection::SaveSection(), DelFrames() preceded this call
                if (!pNode->FindTableBoxStartNode() && !pNode->FindFlyStartNode())
                {
                    auto const layouts(rDoc.GetAllLayouts());
                    assert(std::none_of(layouts.begin(), layouts.end(),
                        [](SwRootFrame const*const pLayout) { return pLayout->IsHideRedlines(); }));
                    (void) layouts;
                }
                isAppendObjsCalled = true// skip that!
                break;
            }

            // no nodes can be unmerged by this - skip MakeFrames() etc.
            if (rPam.GetPoint()->GetNode() == rPam.GetMark()->GetNode())
            {
                break// continue with AppendAllObjs()
            }

            // first, call CheckParaRedlineMerge on the first paragraph,
            // to init flag on new merge range (if any) + 1st node post the merge
            auto eMode(sw::FrameMode::Existing);
            SwTextNode * pLast(pNode);
            for (SwTextFrame * pFrame : frames)
            {
                if (auto const pMergedPara = pFrame->GetMergedPara())
                {
                    pLast = const_cast<SwTextNode*>(pMergedPara->pLastNode);
                    assert(pNode == pStartNode
                        ? pMergedPara->pFirstNode->GetIndex() <= pNode->GetIndex()
                        : pMergedPara->pFirstNode == pNode);
                    // clear old one first to avoid DelFrames confusing updates & asserts...
                    SwTextNode & rFirstNode(*pMergedPara->pFirstNode);
                    pFrame->SetMergedPara(nullptr);
                    pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
                        *pFrame, rFirstNode, eMode));
                    eMode = sw::FrameMode::New// Existing is not idempotent!
                    // update pNode so MakeFrames starts on 2nd node
                    pNode = &rFirstNode;
                }
            }
            if (pLast != pNode)
            {
                // now start node until end of merge + 1 has proper flags; MakeFrames
                // should pick up from the next node in need of frames by checking flags
                SwNodeIndex const start(*pNode, +1);
                SwNodeIndex const end(*pLast, +1); // end is exclusive
                // note: this will also create frames for all currently hidden flys
                // both on first and non-first nodes because it calls AppendAllObjs
                ::MakeFrames(rDoc, start.GetNode(), end.GetNode());
                isAppendObjsCalled = true;
                // re-use this to move flys that are now on the wrong frame, with end
                // of redline as "second" node; the nodes between start and end should
                // be complete with MakeFrames already
                sw::MoveMergedFlysAndFootnotes(frames, *pNode, *pLast, false);
            }
            SwNodeIndex tmp(*pLast);
            // skip over hidden sections!
            pNode = static_cast<SwTextNode*>(SwNodes::GoNextSection(&tmp, /*bSkipHidden=*/true, /*bSkipProtect=*/false));
        }
        while (pNode && pNode->GetIndex() <= rPam.End()->GetNodeIndex());
    }

    if (!isAppendObjsCalled)
    {   // recreate flys in the one node the hard way...
        for (auto const& pLayout : rDoc.GetAllLayouts())
        {
            if (pLayout->HasMergedParas())
            {
                AppendAllObjs(rDoc.GetSpzFrameFormats(), pLayout);
                break;
            }
        }
    }
    // fields last - SwGetRefField::UpdateField requires up-to-date frames
    UpdateFieldsForRedline(rDoc.getIDocumentFieldsAccess()); // after footnotes

    const SwTextNode *pTextNode = rPam.GetPointNode().GetTextNode();
    SwTextAttr* pTextAttr = pTextNode ? pTextNode->GetFieldTextAttrAt(rPam.GetPoint()->GetContentIndex() - 1, ::sw::GetTextAttrMode::Default) : nullptr;
    SwTextField *const pTextField(static_txtattr_cast<SwTextField*>(pTextAttr));
    if (pTextField && comphelper::LibreOfficeKit::isActive() )
        rDoc.GetDocShell()->Broadcast(
            SwFormatFieldHint(&pTextField->GetFormatField(), SwFormatFieldHintWhich::INSERTED));
    else
        rDoc.GetDocShell()->Broadcast(
            SwFormatFieldHint(nullptr, SwFormatFieldHintWhich::INSERTED) );
}

// namespace sw

namespace
{
    bool IsPrevPos( const SwPosition & rPos1, const SwPosition & rPos2 )
    {
        const SwContentNode* pCNd;
        if( 0 != rPos2.GetContentIndex() )
            return false;
        if( rPos2.GetNodeIndex() - 1 != rPos1.GetNodeIndex() )
            return false;
        pCNd = rPos1.GetNode().GetContentNode();
        return pCNd && rPos1.GetContentIndex() == pCNd->Len();
    }

    // copy style or return with SwRedlineExtra_FormatColl with reject data of the upcoming copy
    std::unique_ptr<SwRedlineExtraData_FormatColl> lcl_CopyStyle( const SwPosition & rFrom, const SwPosition & rTo, bool bCopy = true )
    {
        SwTextNode* pToNode = rTo.GetNode().GetTextNode();
        SwTextNode* pFromNode = rFrom.GetNode().GetTextNode();
        if (pToNode != nullptr && pFromNode != nullptr && pToNode != pFromNode)
        {
            const SwPaM aPam(*pToNode);
            SwDoc& rDoc = aPam.GetDoc();
            // using Undo, copy paragraph style
            SwTextFormatColl* pFromColl = pFromNode->GetTextColl();
            SwTextFormatColl* pToColl = pToNode->GetTextColl();
            if (bCopy && pFromColl != pToColl)
                rDoc.SetTextFormatColl(aPam, pFromColl);

            // using Undo, remove direct paragraph formatting of the "To" paragraph,
            // and apply here direct paragraph formatting of the "From" paragraph
            SfxItemSet aTmp(SfxItemSet::makeFixedSfxItemSet<
                        RES_PARATR_BEGIN, RES_PARATR_END - 3, // skip RSID and GRABBAG
                        RES_PARATR_LIST_BEGIN, RES_UL_SPACE, // skip PAGEDESC and BREAK
                        RES_CNTNT, RES_FRMATR_END - 1>(rDoc.GetAttrPool()));
            SfxItemSet aTmp2(aTmp);

            pToNode->GetParaAttr(aTmp, 0, 0);
            pFromNode->GetParaAttr(aTmp2, 0, 0);

            bool bSameSet = aTmp == aTmp2;

            if (!bSameSet)
            {
                for (SfxItemIter aIter(aTmp); !aIter.IsAtEnd(); aIter.NextItem())
                {
                    const sal_uInt16 nWhich(aIter.GetCurWhich());
                    if( SfxItemState::SET == aTmp.GetItemState( nWhich, false ) &&
                        SfxItemState::SET != aTmp2.GetItemState( nWhich, false ) )
                            aTmp2.Put( aTmp.GetPool()->GetUserOrPoolDefaultItem(nWhich) );
                }
            }

            if (bCopy && !bSameSet)
                rDoc.getIDocumentContentOperations().InsertItemSet(aPam, aTmp2);
            else if (!bCopy && (!bSameSet || pFromColl != pToColl))
            {
                IStyleAccess& rStyleAccess = rDoc.GetIStyleAccess();
                std::shared_ptr<SfxItemSet> pAutoStyle = rStyleAccess.getAutomaticStyle(aTmp2, IStyleAccess::AUTO_STYLE_CHAR);
                return std::make_unique<SwRedlineExtraData_FormatColl>( pFromColl->GetName(), USHRT_MAX, pAutoStyle );
            }
        }
        return nullptr;
    }

    // delete the empty tracked table row (i.e. if it's last tracked deletion was accepted)
    void lcl_DeleteTrackedTableRow ( const SwPosition* pPos )
    {
        const SwTableBox* pBox = pPos->GetNode().GetTableBox();
        if ( !pBox )
            return;

        // tracked column deletion

        const SvxPrintItem *pHasBoxTextChangesOnlyProp =
                pBox->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
        // empty table cell with property "HasTextChangesOnly" = false
        if ( pHasBoxTextChangesOnlyProp && !pHasBoxTextChangesOnlyProp->GetValue() )
        {
            SwCursor aCursor( *pPos, nullptr );
            if ( pBox->IsEmpty() )
            {
                // tdf#155747 remove table cursor
                pPos->GetDoc().GetDocShell()->GetWrtShell()->EnterStdMode();
                // TODO check the other cells of the column
                // before removing the column
                pPos->GetDoc().DeleteCol( aCursor );
                return;
            }
            else
            {
                SvxPrintItem aHasTextChangesOnly(RES_PRINT, false);
                pPos->GetDoc().SetBoxAttr( aCursor, aHasTextChangesOnly );
            }
        }

        // tracked row deletion

        const SwTableLine* pLine = pBox->GetUpper();
        const SvxPrintItem *pHasTextChangesOnlyProp =
                pLine->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
        // empty table row with property "HasTextChangesOnly" = false
        if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
        {
            if ( pLine->IsEmpty() )
            {
                SwCursor aCursor( *pPos, nullptr );
                pPos->GetDoc().DeleteRow( aCursor );
            }
            else
            {
                // update property "HasTextChangesOnly"
                SwRedlineTable::size_type nPos = 0;
                (void)pLine->UpdateTextChangesOnly(nPos);
            }
        }
    }

    // at rejection of a deletion in a table, remove the tracking of the table row
    // (also at accepting the last redline insertion of a tracked table row insertion)
    void lcl_RemoveTrackingOfTableRow( const SwPosition* pPos, bool bRejectDeletion )
    {
        const SwTableBox* pBox = pPos->GetNode().GetTableBox();
        if ( !pBox )
            return;

        // tracked column deletion

        const SvxPrintItem *pHasBoxTextChangesOnlyProp =
                pBox->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
        // table cell property "HasTextChangesOnly" is set and its value is false
        if ( pHasBoxTextChangesOnlyProp && !pHasBoxTextChangesOnlyProp->GetValue() )
        {
            SvxPrintItem aUnsetTracking(RES_PRINT, true);
            SwCursor aCursor( *pPos, nullptr );
            pPos->GetDoc().SetBoxAttr( aCursor, aUnsetTracking );
        }

        // tracked row deletion

        const SwTableLine* pLine = pBox->GetUpper();
        const SvxPrintItem *pHasTextChangesOnlyProp =
                pLine->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
        // table row property "HasTextChangesOnly" is set and its value is false
        if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
        {
            bool bNoMoreInsertion = false;
            if ( !bRejectDeletion )
            {
                SwRedlineTable::size_type nPos = 0;
                SwRedlineTable::size_type nInsert = pLine->UpdateTextChangesOnly(nPos, /*bUpdateProperty=*/false);

                if ( SwRedlineTable::npos == nInsert )
                    bNoMoreInsertion = true;
            }
            if ( bRejectDeletion || bNoMoreInsertion )
            {
                SvxPrintItem aUnsetTracking(RES_PRINT, true);
                SwCursor aCursor( *pPos, nullptr );
                pPos->GetDoc().SetRowNotTracked( aCursor, aUnsetTracking );
            }
        }
    }

    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(SwRangeRedline::Invalidation::Remove);

        switch( pRedl->GetType() )
        {
        case RedlineType::Insert:
        case RedlineType::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:
                    {
                        bool bInsert = RedlineType::Insert == pRedl->GetType();
                        SwPosition aPos(pRedl->Start()->GetNode());
                        rArr.DeleteAndDestroy( rPos-- );

                        // remove tracking of the table row, if needed
                        if ( bInsert )
                            lcl_RemoveTrackingOfTableRow( &aPos, /*bRejectDelete=*/false );
                    }
                    break;

                default:
                    bRet = false;
                }

                if( bReplace || ( bCheck && !pRedl->HasValidRange() ))
                {
                    // re-insert
                    rArr.Remove( pRedl );
                    rArr.Insert( pRedl );
                }
            }
            break;
        case RedlineType::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->GetNode().GetContentNode();
                    SwContentNode* pCEndNd = pDelEnd->GetNode().GetContentNode();
                    pRStt = pRedl->Start();
                    pREnd = pRedl->End();

                    // keep style of the empty paragraph after deletion of wholly paragraphs
                    if( pCSttNd && pCEndNd && pRStt && pREnd && pRStt->GetContentIndex() == 0 )
                        lcl_CopyStyle(*pREnd, *pRStt);

                    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 );
                        lcl_DeleteTrackedTableRow( aPam.End() );
                    }
                    else if (pCSttNd && !pCEndNd)
                        {
                            aPam.GetBound().nContent.Assign( nullptr, 0 );
                            aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
                            rDoc.getIDocumentContentOperations().DelFullPara( aPam );
                        }
                    else
                    {
                        rDoc.getIDocumentContentOperations().DeleteRange(aPam);
                    }
                    rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
                }
                else if( bDelRedl )
                    delete pRedl;
            }
            break;

        case RedlineType::FmtColl:
        case RedlineType::ParagraphFormat:
            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 ];
        SwDoc& rDoc = pRedl->GetDoc();
        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(SwRangeRedline::Invalidation::Remove);

        switch( pRedl->GetType() )
        {
        case RedlineType::Insert:
            {
                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->GetNode().GetContentNode();
                    SwContentNode* pCEndNd = pDelEnd->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 );
                        lcl_DeleteTrackedTableRow( aPam.End() );
                    }
                    else if (pCSttNd && !pCEndNd)
                        {
                            aPam.GetBound().nContent.Assign( nullptr, 0 );
                            aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
                            if (aPam.End()->GetNode().IsStartNode())
                            {   // end node will be deleted too! see nNodeDiff+1
                                aPam.End()->Adjust(SwNodeOffset(-1));
                            }
                            assert(!aPam.End()->GetNode().IsStartNode());
                            rDoc.getIDocumentContentOperations().DelFullPara( aPam );
                        }
                    else
                    {
                        rDoc.getIDocumentContentOperations().DeleteRange(aPam);
                    }
                    rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
                }
                else if( bDelRedl )
                    delete pRedl;
            }
            break;
        case RedlineType::Delete:
            {
                SwRangeRedline* pNew = nullptr;
                bool bCheck = false, bReplace = false;
                SwPaM const updatePaM(pSttRng ? *pSttRng : *pRedl->Start(),
                                      pEndRng ? *pEndRng : *pRedl->End());

                if( pRedl->GetExtraData() )
                    pRedl->GetExtraData()->Reject( *pRedl );

                // remove tracking of the table row, if needed
                lcl_RemoveTrackingOfTableRow( updatePaM.End(), /*bRejectDelete=*/true );

                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 );
                }

                sw::UpdateFramesForRemoveDeleteRedline(rDoc, updatePaM);
            }
            break;

        case RedlineType::Format:
        case RedlineType::FmtColl:
        case RedlineType::ParagraphFormat:
            {
                // tdf#52391 instead of hidden acception at the requested
                // rejection, remove direct text formatting to get the potential
                // original state of the text (FIXME if the original text
                // has already contained direct text formatting: unfortunately
                // ODF 1.2 doesn't support rejection of format-only changes)
                if ( pRedl->GetType() == RedlineType::Format )
                {
                    SwPaM aPam( *(pRedl->Start()), *(pRedl->End()) );
                    rDoc.ResetAttrs(aPam);
                }
                else if ( pRedl->GetType() == RedlineType::ParagraphFormat )
                {
                    // handle paragraph formatting changes
                    // (range is only a full paragraph or a part of it)
                    const SwPosition* pStart = pRedl->Start();
                    SwTextNode* pTNd = pStart->GetNode().GetTextNode();
                    if( pTNd )
                    {
                        // expand range to the whole paragraph
                        // and reset only the paragraph attributes
                        SwPaM aPam( *pTNd, pTNd->GetText().getLength() );
                        o3tl::sorted_vector<sal_uInt16> aResetAttrsArray;

                        static constexpr std::pair<sal_uInt16, sal_uInt16> aResetableSetRange[] = {
                            { RES_PARATR_BEGIN, RES_PARATR_END - 1 },
                            { RES_PARATR_LIST_BEGIN, RES_FRMATR_END - 1 },
                        };

                        for (const auto& [nBegin, nEnd] : aResetableSetRange)
                        {
                            for (sal_uInt16 i = nBegin; i <= nEnd; ++i)
                                aResetAttrsArray.insert( i );
                        }

                        rDoc.ResetAttrs(aPam, false, aResetAttrsArray);

                        // remove numbering
                        if ( pTNd->GetNumRule() )
                            rDoc.DelNumRules(aPam);
                    }
                }

                if( pRedl->GetExtraData() )
                    pRedl->GetExtraData()->Reject( *pRedl );

                rArr.DeleteAndDestroy( rPos-- );
            }
            break;

        default:
            bRet = false;
        }
        return bRet;
    }

    /// Given a redline that has another underlying redline, drop that underlying redline.
    /// Used to accept an insert or rejecting a delete, i.e. no changes to the text node strings.
    bool lcl_DeleteInnerRedline(const SwRedlineTable& rArr, const SwRedlineTable::size_type& rPos,
                                      int nDepth)
    {
        SwRangeRedline* pRedl = rArr[rPos];
        SwDoc& rDoc = pRedl->GetDoc();
        SwPaM const updatePaM(*pRedl->Start(), *pRedl->End());

        pRedl->PopAllDataAfter(nDepth);
        sw::UpdateFramesForRemoveDeleteRedline(rDoc, updatePaM);
        return true;
    }

    /// Given a redline that has an other underlying redline, drop the redline on top.
    /// Used to accept a format on top of insert/delete, no changes to the text node string.
    bool lcl_AcceptOuterFormat(SwRedlineTable& rArr, SwRedlineTable::size_type& rPos)
    {
        SwRangeRedline* pRedl = rArr[rPos];
        return pRedl->PopData();
    }

    /// Given a redline that has an other underlying redline, drop the redline on top & restore the
    /// old doc model. Used to reject a format on top of insert/delete.
    bool lcl_RejectOuterFormat(SwRedlineTable& rArr, SwRedlineTable::size_type& rPos)
    {
        SwRangeRedline* pRedl = rArr[rPos];
        SwDoc& rDoc = pRedl->GetDoc();
        SwPaM aPam(*(pRedl->Start()), *(pRedl->End()));
        rDoc.ResetAttrs(aPam);
        if (pRedl->GetExtraData())
            pRedl->GetExtraData()->Reject(*pRedl);
        return pRedl->PopData();
    }

    /// Given a redline that has two types and the underlying type is
    /// delete, reject the redline based on that underlying type. Used
    /// to accept a delete-then-format, i.e. this does change the text
    /// node string.
    bool lcl_AcceptInnerDelete(SwRangeRedline& rRedline, SwRedlineTable& rRedlines,
                               SwRedlineTable::size_type& rRedlineIndex, bool bCallDelete)
    {
        bool bRet = false;

        SwDoc& rDoc = rRedline.GetDoc();
        SwPaM aPam(*rRedline.Start(), *rRedline.End());
        bRet |= lcl_RejectRedline(rRedlines, rRedlineIndex, bCallDelete);
        // Handles undo/redo itself.
        rDoc.getIDocumentContentOperations().DeleteRange(aPam);

        return bRet;
    }

    typedef bool (*Fn_AcceptReject)( SwRedlineTable& rArr, SwRedlineTable::size_type&&nbsp;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* pStart = rPam.Start(),
                        * pEnd = rPam.End();
        const SwRangeRedline* pFnd = rArr.FindAtPosition( *pStart, n );
        if( pFnd &&     // Is new a part of it?
            ( *pFnd->Start() != *pStart || *pFnd->End() > *pEnd ))
        {
            // Only revoke the partial selection
            if( (*fn_AcceptReject)( rArr, n, bCallDelete, pStart, pEnd ))
                nCount++;
            ++n;
        }

        // tdf#119824 first we will accept only overlapping paragraph format changes
        // in the first loop to avoid potential content changes during Redo
        bool bHasParagraphFormatChange = false;
        forint m = 0 ; m < 2 && !bHasParagraphFormatChange; ++m )
        {
            for(SwRedlineTable::size_type o = n ; o < rArr.size(); ++o )
            {
                SwRangeRedline* pTmp = rArr[ o ];
                if( pTmp->HasMark() && pTmp->IsVisible() )
                {
                    if( *pTmp->End() <= *pEnd )
                    {
                        if( (m > 0 || RedlineType::ParagraphFormat == pTmp->GetType()) &&
                            (*fn_AcceptReject)( rArr, o, bCallDelete, nullptr, nullptr ))
                        {
                            bHasParagraphFormatChange = true;
                            nCount++;
                        }
                    }
                    else
                    {
                        if( *pTmp->Start() < *pEnd )
                        {
                            // Only revoke the partial selection
                            if( (m > 0 || RedlineType::ParagraphFormat == pTmp->GetType()) &&
                                (*fn_AcceptReject)( rArr, o, bCallDelete, pStart, pEnd ))
                            {
                                bHasParagraphFormatChange = true;
                                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.
        auto [pStart, pEnd] = rPam.StartEnd(); // SwPosition*
        SwDoc& rDoc = rPam.GetDoc();
        if( !pStart->GetContentIndex() &&
            !rDoc.GetNodes()[ pStart->GetNodeIndex() - 1 ]->IsContentNode() )
        {
            const SwRangeRedline* pRedl = rDoc.getIDocumentRedlineAccess().GetRedline( *pStart, nullptr );
            if( pRedl )
            {
                const SwPosition* pRStt = pRedl->Start();
                if( !pRStt->GetContentIndex() && pRStt->GetNodeIndex() ==
                    pStart->GetNodeIndex() - 1 )
                    *pStart = *pRStt;
            }
        }
        if( pEnd->GetNode().IsContentNode() &&
            !rDoc.GetNodes()[ pEnd->GetNodeIndex() + 1 ]->IsContentNode() &&
            pEnd->GetContentIndex() == pEnd->GetNode().GetContentNode()->Len()    )
        {
            const SwRangeRedline* pRedl = rDoc.getIDocumentRedlineAccess().GetRedline( *pEnd, nullptr );
            if( pRedl )
            {
                const SwPosition* pREnd = pRedl->End();
                if( !pREnd->GetContentIndex() && pREnd->GetNodeIndex() ==
                    pEnd->GetNodeIndex() + 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()->Assign(rDoc.GetNodes().GetEndOfContent());
            }
            m_rRedline.GetPoint()->Assign(rDoc.GetNodes().GetEndOfContent());
        }
        ~TemporaryRedlineUpdater()
        {
            static_cast<SwPaM&>(m_rRedline) = *m_pCursor;
        }
    };

/// Decides if it's OK to combine two types of redlines next to each other, e.g. insert and
/// delete-on-insert can be combined if accepting an insert.
bool CanCombineTypesForAcceptReject(SwRedlineData& rInnerData, SwRangeRedline&&nbsp;rOuterRedline)
{
    if (rInnerData.GetType() == RedlineType::Delete)
    {
        // Delete is OK to have 'format' on it, but 'insert' will be next to the 'delete'.
        return rOuterRedline.GetType() == RedlineType::Format
            && rOuterRedline.GetStackCount() > 1
            && rOuterRedline.GetType(1) == RedlineType::Delete;
    }

    if (rInnerData.GetType() != RedlineType::Insert)
    {
        return false;
    }

    switch (rOuterRedline.GetType())
    {
        case RedlineType::Delete:
        case RedlineType::Format:
            break;
        default:
            return false;
    }

    if (rOuterRedline.GetStackCount() <= 1)
    {
        return false;
    }

    if (rOuterRedline.GetType(1) != RedlineType::Insert)
    {
        return false;
    }

    return true;
}

/// Decides if it's OK to combine this rInnerData having 2 types with an outer rOuterRedline for
/// accept or reject purposes. E.g. format-on-delete and delete can be combined if accepting a
/// delete.
bool CanReverseCombineTypesForAcceptReject(SwRangeRedline& rOuterRedline, SwRedlineData& rInnerData)
{
    switch (rOuterRedline.GetType())
    {
        case RedlineType::Insert:
        case RedlineType::Delete:
            break;
        default:
            return false;
    }

    if (rInnerData.GetType() != RedlineType::Format)
    {
        return false;
    }

    const SwRedlineData* pInnerDataNext = rInnerData.Next();
    if (!pInnerDataNext)
    {
        return false;
    }

    switch (pInnerDataNext->GetType())
    {
        case RedlineType::Insert:
        case RedlineType::Delete:
            return pInnerDataNext->GetType() == rOuterRedline.GetType();
        default:
            return false;
    }
}
}

namespace sw
{

DocumentRedlineManager::DocumentRedlineManager(SwDoc& i_rSwdoc)
    : m_rDoc(i_rSwdoc)
    , meRedlineFlags(RedlineFlags::ShowInsert | RedlineFlags::ShowDelete)
    , mbIsRedlineMove(false)
    , mnAutoFormatRedlnCommentNo(0)
{
}

RedlineFlags DocumentRedlineManager::GetRedlineFlags(const SwViewShell* pViewShell) const
{
    if (!pViewShell)
    {
        SwDocShell* pDocShell = m_rDoc.GetDocShell();
        if (pDocShell)
        {
            pViewShell = pDocShell->GetWrtShell();
        }
    }

    RedlineFlags eRedlineFlags = meRedlineFlags;

    if (pViewShell)
    {
        // Recording can be per-view, the rest is per-document.
        eRedlineFlags = eRedlineFlags & ~RedlineFlags::On;
        if (pViewShell->GetViewOptions()->IsRedlineRecordingOn())
        {
            eRedlineFlags |= RedlineFlags::On;
        }
    }

    return eRedlineFlags;
}

void DocumentRedlineManager::SetRedlineFlags( RedlineFlags eMode, SfxRedlineRecordingMode eRedlineRecordingMode, bool bRecordModeChange )
{
    if( GetRedlineFlags() == eMode && !bRecordModeChange )
        return;

    if( (RedlineFlags::ShowMask & GetRedlineFlags()) != (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, bool); // Allow compiler warn if use of
                                                          // uninitialized ptr is possible

        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 )

        o3tl::sorted_vector<SwRootFrame *> hiddenLayouts;
        if (eShowMode == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
        {
            // sw_redlinehide: the problem here is that MoveFromSection
            // creates the frames wrongly (non-merged), because its own
            // SwRangeRedline has wrong positions until after the nodes
            // are all moved, so fix things up by force by re-creating
            // all merged frames from scratch.
            o3tl::sorted_vector<SwRootFrame *> const layouts(m_rDoc.GetAllLayouts());
            for (SwRootFrame *const pLayout : layouts)
            {
                if (pLayout->IsHideRedlines())
                {
                    pLayout->SetHideRedlines(false);
                    hiddenLayouts.insert(pLayout);
                }
            }
        }

        for (sal_uInt16 nLoop = 1; nLoop <= 2; ++nLoop)
            for (size_t i = 0; i < maRedlineTable.size(); )
            {
                SwRangeRedline *const pRedline = maRedlineTable[i];
                (pRedline->*pFnc)(nLoop, i, false);
                // a previous redline may have been deleted
                if (i < maRedlineTable.size() && maRedlineTable[i] == pRedline)
                    ++i;
            }

        //SwRangeRedline::MoveFromSection routinely changes
        //the keys that mpRedlineTable is sorted by
        maRedlineTable.Resort();

        CheckAnchoredFlyConsistency(m_rDoc);
        CHECK_REDLINE( *this )

        for (SwRootFrame *const pLayout : hiddenLayouts)
        {
            pLayout->SetHideRedlines(true);
        }

        m_rDoc.SetInXMLImport( bSaveInXMLImportFlag );
    }
    SetRedlineFlags_intern(eMode, eRedlineRecordingMode, bRecordModeChange);
    m_rDoc.getIDocumentState().SetModified();

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

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

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

void DocumentRedlineManager::SetRedlineFlags_intern(RedlineFlags eMode, SfxRedlineRecordingMode eRedlineRecordingMode, bool bRecordModeChange)
{
    SwDocShell* pDocShell = m_rDoc.GetDocShell();
    SwViewShell* pViewShell = pDocShell ? pDocShell->GetWrtShell() : nullptr;
    if (pViewShell && eRedlineRecordingMode == SfxRedlineRecordingMode::ViewAgnostic)
    {
        // Just set the requested flags on the model and on the current view, so setting flags &
        // restoring them result in the same state (no matter if that was this-view or all-views).
        auto bRedlineRecordingOn = bool(eMode & RedlineFlags::On);
        SwViewOption aOpt(*pViewShell->GetViewOptions());
        if (aOpt.IsRedlineRecordingOn() != bRedlineRecordingOn)
        {
            aOpt.SetRedlineRecordingOn(bRedlineRecordingOn);
            pViewShell->ApplyViewOptions(aOpt);
        }
    }
    else if (pViewShell)
    {
        bool bRecordAllViews = eRedlineRecordingMode == SfxRedlineRecordingMode::AllViews;
        // Recording may be per-view, the rest is per-document.
        for(SwViewShell& rSh : pViewShell->GetRingContainer())
        {
            auto bRedlineRecordingOn = bool(eMode & RedlineFlags::On);
            SwViewOption aOpt(*rSh.GetViewOptions());
            bool bOn = aOpt.IsRedlineRecordingOn();
            if (bRedlineRecordingOn)
            {
                // We'll want some kind of recording enabled.
                if (bRecordAllViews)
                {
                    // Enable for all views: turn it on everywhere.
                    bOn = true;
                }
                else
                {
                    // Enable it for this view was requested.
                    if (bRecordModeChange)
                    {
                        // Transitioning from "all views" to "this view", turn it off everywhere
                        // except in this view.
                        bOn = &rSh == pViewShell;
                    }
                    else if (&rSh == pViewShell)
                    {
                        // Transitioning from "no record": just touch the current view, leave
                        // others unchanged.
                        bOn = true;
                    }
                }
            }
            else
            {
                // Disable everywhere.
                bOn = false;
            }

            if (aOpt.IsRedlineRecordingOn() != bOn)
            {
                aOpt.SetRedlineRecordingOn(bOn);
                rSh.ApplyViewOptions(aOpt);
            }
        }
    }

    meRedlineFlags = eMode;
}

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

SwRedlineTable& DocumentRedlineManager::GetRedlineTable()
{
    return maRedlineTable;
}

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

SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable()
{
    return maExtraRedlineTable;
}

bool DocumentRedlineManager::IsInRedlines(const SwNode & rNode) const
{
    if (&rNode.GetNodes() != &m_rDoc.GetNodes())
        return false;

    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;
}

/// Data shared between DocumentRedlineManager::AppendRedline() and PreAppendInsertRedline().
class AppendRedlineContext
{
public:
    SwRangeRedline*& pNewRedl;
    SwPosition*& pStart;
    SwPosition*& pEnd;

    SwRangeRedline*& pRedl;
    SwPosition*& pRStt;
    SwPosition*& pREnd;

    const SwComparePosition eCmpPos;
    SwRedlineTable::size_type& n;
    bool& bMerged;
    bool& bDec;
    bool& bCompress;
    const bool bCallDelete;

    const sal_uInt32 nMoveIDToDelete;
    std::set<sal_uInt32>& deletedMoveIDs;
};

void DocumentRedlineManager::PreAppendForeignRedline(AppendRedlineContext& rCtx)
{
    // 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( rCtx.eCmpPos )
    {
    case SwComparePosition::Equal:
        {
            rCtx.pRedl->PushData( *rCtx.pNewRedl );
            delete rCtx.pNewRedl;
            rCtx.pNewRedl = nullptr;
            if( IsHideChanges( GetRedlineFlags() ))
            {
                rCtx.pRedl->Hide(0, maRedlineTable.GetPos(rCtx.pRedl));
            }
            rCtx.bCompress = true;

            if (rCtx.pNewRedl && rCtx.pNewRedl->GetType() == RedlineType::Delete)
            {
                // set IsMoved checking nearby redlines
                SwRedlineTable::size_type nRIdx = maRedlineTable.GetPos(rCtx.pRedl);
                if (nRIdx < maRedlineTable.size()) // in case above 're-insert' failed
                    maRedlineTable.isMoved(nRIdx);
            }

        }
        break;

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

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

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

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

    // insert the pNew part (if it exists)
    if( pNew )
    {
        maRedlineTable.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)
        rCtx.n = 0;
        rCtx.bDec = true;
    }
}

void DocumentRedlineManager::PreAppendInsertRedline(AppendRedlineContext& rCtx)
{
    switch( rCtx.pRedl->GetType() )
    {
    case RedlineType::Insert:
        if( rCtx.pRedl->IsOwnRedline( *rCtx.pNewRedl ) &&
            // don't join inserted characters with moved text
            !rCtx.pRedl->IsMoved() )
        {
            bool bDelete = false;
            bool bMaybeNotify = false;

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

                rCtx.bMerged = true;
                bDelete = true;
            }
            else if( (( SwComparePosition::Before == rCtx.eCmpPos &&
--> --------------------

--> maximum size reached

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

Messung V0.5
C=92 H=88 G=89

¤ Dauer der Verarbeitung: 0.11 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge