Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/sw/source/core/txtnode/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 204 kB image not shown  

Quelle  ndtxt.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 <hintids.hxx>
#include <hints.hxx>

#include <comphelper/configuration.hxx>
#include <comphelper/lok.hxx>
#include <comphelper/string.hxx>
#include <editeng/fontitem.hxx>
#include <editeng/escapementitem.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/rsiditem.hxx>
#include <sal/log.hxx>
#include <osl/diagnose.h>
#include <anchoredobject.hxx>
#include <txtfld.hxx>
#include <txtinet.hxx>
#include <fmtanchr.hxx>
#include <fmtinfmt.hxx>
#include <fmtrfmrk.hxx>
#include <txttxmrk.hxx>
#include <fchrfmt.hxx>
#include <txtftn.hxx>
#include <fmtflcnt.hxx>
#include <fmtfld.hxx>
#include <frmatr.hxx>
#include <ftnidx.hxx>
#include <ftninfo.hxx>
#include <fmtftn.hxx>
#include <charfmt.hxx>
#include <ndtxt.hxx>
#include <doc.hxx>
#include <IDocumentUndoRedo.hxx>
#include <IDocumentSettingAccess.hxx>
#include <IDocumentListsAccess.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <docary.hxx>
#include <docufld.hxx>
#include <pam.hxx>
#include <fldbas.hxx>
#include <paratr.hxx>
#include <txtfrm.hxx>
#include <ftnfrm.hxx>
#include <pagefrm.hxx>
#include <rootfrm.hxx>
#include <expfld.hxx>
#include <section.hxx>
#include <mvsave.hxx>
#include <SwGrammarMarkUp.hxx>
#include <redline.hxx>
#include <IMark.hxx>
#include <scriptinfo.hxx>
#include <istyleaccess.hxx>
#include <SwStyleNameMapper.hxx>
#include <numrule.hxx>
#include <docsh.hxx>
#include <SwNodeNum.hxx>
#include <svl/grabbagitem.hxx>
#include <svl/intitem.hxx>
#include <sortedobjs.hxx>
#include <calbck.hxx>
#include <attrhint.hxx>
#include <memory>
#include <unoparagraph.hxx>
#include <unotext.hxx>
#include <wrtsh.hxx>
#include <fmtpdsc.hxx>
#include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
#include <svl/itemiter.hxx>
#include <undobj.hxx>
#include <formatflysplit.hxx>
#include <fmtcntnt.hxx>
#include <poolfmt.hxx>
#include <names.hxx>

using namespace ::com::sun::star;

typedef std::vector<SwTextAttr*> SwpHts;

namespace sw {
    class TextNodeNotificationSuppressor {
        SwTextNode& m_rNode;
        bool m_bWasNotifiable;
        public:
            TextNodeNotificationSuppressor(SwTextNode& rNode)
                : m_rNode(rNode)
                , m_bWasNotifiable(rNode.m_bNotifiable)
            {
                m_rNode.m_bNotifiable = false;
            }
            ~TextNodeNotificationSuppressor()
            {
                m_rNode.m_bNotifiable = m_bWasNotifiable;
            }
    };
}

// unfortunately everyone can change Hints without ensuring order or the linking between them
#ifdef DBG_UTIL
#define CHECK_SWPHINTS(pNd)  { if( pNd->GetpSwpHints() && \
                                   !pNd->GetDoc().IsInReading() ) \
                                  pNd->GetpSwpHints()->Check(true); }
#define CHECK_SWPHINTS_IF_FRM(pNd)  { if( pNd->GetpSwpHints() && \
                                   !pNd->GetDoc().IsInReading() ) \
    pNd->GetpSwpHints()->Check(getLayoutFrame(nullptr, nullptr, nullptr) != nullptr); }
#else
#define CHECK_SWPHINTS(pNd)
#define CHECK_SWPHINTS_IF_FRM(pNd)
#endif

SwTextNode *SwNodes::MakeTextNode( const SwNode& rWhere,
                                 SwTextFormatColl *pColl, bool const bNewFrames)
{
    OSL_ENSURE( pColl, "Collection pointer is 0." );

    SwTextNode *pNode = new SwTextNode( rWhere, pColl, nullptr );

    SwNodeIndex aIdx( *pNode );

    // if there is no layout or it is in a hidden section, MakeFrames is not needed
    const SwSectionNode* pSectNd;
    if (!bNewFrames ||
        !GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell() ||
        ( nullptr != (pSectNd = pNode->FindSectionNode()) &&
            pSectNd->GetSection().IsHiddenFlag() ))
        return pNode;

    SwNodeIndex aTmp( rWhere );
    do {
        // max. 2 loops:
        // 1. take the successor
        // 2. take the predecessor

        SwNode * pNd = & aTmp.GetNode();
        switch (pNd->GetNodeType())
        {
        case SwNodeType::Table:
            static_cast<SwTableNode*>(pNd)->MakeFramesForAdjacentContentNode(aIdx);
            return pNode;

        case SwNodeType::Section:
            ifstatic_cast<SwSectionNode*>(pNd)->GetSection().IsHidden() ||
                static_cast<SwSectionNode*>(pNd)->IsContentHidden() )
            {
                pNd = FindPrvNxtFrameNode( *pNode, pNode );
                if( !pNd )
                    return pNode;
                aTmp = *pNd;
                break;
            }
            static_cast<SwSectionNode*>(pNd)->MakeFramesForAdjacentContentNode(aIdx);
            return pNode;

        case SwNodeType::Text:
        case SwNodeType::Grf:
        case SwNodeType::Ole:
            static_cast<SwContentNode*>(pNd)->MakeFramesForAdjacentContentNode(*pNode);
            return pNode;

        case SwNodeType::End:
            if( pNd->StartOfSectionNode()->IsSectionNode() &&
                aTmp.GetIndex() < rWhere.GetIndex() )
            {
                if( pNd->StartOfSectionNode()->GetSectionNode()->GetSection().IsHiddenFlag())
                {
                    if( !GoPrevSection( &aTmp, truefalse ) ||
                        aTmp.GetNode().FindTableNode() !=
                            pNode->FindTableNode() )
                        return pNode;
                }
                else
                    aTmp = *pNd->StartOfSectionNode();
                break;
            }
            else if( pNd->StartOfSectionNode()->IsTableNode() &&
                    aTmp.GetIndex() < rWhere.GetIndex() )
            {
                // after a table node
                aTmp = *pNd->StartOfSectionNode();
                break;
            }
            [[fallthrough]];
        default:
            if( &rWhere == &aTmp.GetNode() )
                aTmp -= SwNodeOffset(2);
            else
                return pNode;
            break;
        }
    } whiletrue );
}

SwTextNode::SwTextNode( const SwNode& rWhere, SwTextFormatColl *pTextColl, const SfxItemSet* pAutoAttr )
:   SwContentNode( rWhere, SwNodeType::Text, pTextColl ),
    m_bContainsHiddenChars(false),
    m_bHiddenCharsHidePara(false),
    m_bRecalcHiddenCharFlags(false),
    m_bLastOutlineState( false ),
    m_bNotifiable( true ),
    mbEmptyListStyleSetDueToSetOutlineLevelAttr( false ),
    mbInSetOrResetAttr( false ),
    m_bInUndo(false)
{
    {
        sw::TextNodeNotificationSuppressor(*this);

        if( pAutoAttr )
            SetAttr( *pAutoAttr );

        if (!IsInList() && GetNumRule() && !GetListId().isEmpty())
        {
            // #i101516#
            // apply paragraph style's assigned outline style list level as
            // list level of the paragraph, if it has none set already.
            if ( !HasAttrListLevel() &&
                 pTextColl && pTextColl->IsAssignedToListLevelOfOutlineStyle() )
            {
                SetAttrListLevel( pTextColl->GetAssignedOutlineStyleLevel() );
            }
            AddToList();
        }

        // call method <UpdateOutlineNode(..)> only for the document nodes array
        if (GetNodes().IsDocNodes())
            GetNodes().UpdateOutlineNode(*this);
    }

    m_bContainsHiddenChars = m_bHiddenCharsHidePara = false;
    m_bRecalcHiddenCharFlags = true;
}

SwTextNode::~SwTextNode()
{
    // delete only removes the pointer not the array elements!
    if ( m_pSwpHints )
    {
        // do not delete attributes twice when those delete their content
        std::unique_ptr<SwpHints> pTmpHints(std::move(m_pSwpHints));

        for( size_t j = pTmpHints->Count(); j; )
        {
            // first remove the attribute from the array otherwise
            // if would delete itself
            DestroyAttr( pTmpHints->Get( --j ) );
        }
    }

    // must be removed from outline nodes by now
#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
    SwOutlineNodes::size_type foo;
    assert(!GetNodes().GetOutLineNds().Seek_Entry(this, &foo));
#endif

    RemoveFromList();

    DelFrames(nullptr); // must be called here while it's still a SwTextNode
    DelFrames_TextNodePart();

    // If this Node should have Outline Numbering but that state hasn't been
    // crystallized by SwNodes::UpdateOutlineNode yet, and so it currently isn't
    // added to SwNodes::m_aOutlineNodes, then set LastOutlineState so it won't
    // be added if ResetAttr() triggers UpdateOutlineNode() during destruction,
    // and avoid leaving a dangling pointer in m_aOutlineNodes.
    if (IsOutline() && !m_bLastOutlineState)
        m_bLastOutlineState = true;

#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
    if (!GetDoc().IsInDtor())
#endif
    {
        ResetAttr(RES_PAGEDESC);
    }
    InvalidateInSwCache();
}

void SwTextNode::FileLoadedInitHints()
{
    if (m_pSwpHints)
    {
        m_pSwpHints->MergePortions(*this);
    }
}

SwContentFrame *SwTextNode::MakeFrame( SwFrame* pSib )
{
    SwContentFrame *pFrame = sw::MakeTextFrame(*this, pSib, sw::FrameMode::New);
    return pFrame;
}

sal_Int32 SwTextNode::Len() const
{
    return m_Text.getLength();
}

// After a split node, it's necessary to actualize the ref-pointer of the ftnfrms.
static void lcl_ChangeFootnoteRef( SwTextNode &rNode )
{
    SwpHints *pSwpHints = rNode.GetpSwpHints();
    if( !(pSwpHints && rNode.GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell()) )
        return;

    SwContentFrame* pFrame = nullptr;
    // OD 07.11.2002 #104840# - local variable to remember first footnote
    // of node <rNode> in order to invalidate position of its first content.
    // Thus, in its <MakeAll()> it will checked its position relative to its reference.
    SwFootnoteFrame* pFirstFootnoteOfNode = nullptr;
    for( size_t j = pSwpHints->Count(); j; )
    {
        SwTextAttr* pHt = pSwpHints->Get(--j);
        if (RES_TXTATR_FTN == pHt->Which())
        {
            if( !pFrame )
            {
                pFrame = SwIterator<SwContentFrame, SwTextNode, sw::IteratorMode::UnwrapMulti>(rNode).First();
                if (!pFrame)
                    return;
            }
            SwTextFootnote *pAttr = static_cast<SwTextFootnote*>(pHt);
            OSL_ENSURE( pAttr->GetStartNode(), "FootnoteAtr without StartNode." );
            SwNodeIndex aIdx( *pAttr->GetStartNode(), 1 );
            SwContentNode *pNd = aIdx.GetNode().GetContentNode();
            if ( !pNd )
                pNd = SwNodes::GoNextSection(&aIdx, truefalse);
            if ( !pNd )
                continue;

            SwIterator<SwContentFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*pNd);
            SwContentFrame* pContent = aIter.First();
            if( pContent )
            {
                OSL_ENSURE( pContent->getRootFrame() == pFrame->getRootFrame(),
                        "lcl_ChangeFootnoteRef: Layout double?" );
                SwFootnoteFrame *pFootnote = pContent->FindFootnoteFrame();
                if( pFootnote && pFootnote->GetAttr() == pAttr )
                {
                    while( pFootnote->GetMaster() )
                        pFootnote = pFootnote->GetMaster();
                    // #104840# - remember footnote frame
                    pFirstFootnoteOfNode = pFootnote;
                    while ( pFootnote )
                    {
                        pFootnote->SetRef( pFrame );
                        pFootnote = pFootnote->GetFollow();
                        static_cast<SwTextFrame*>(pFrame)->SetFootnote( true );
                    }
                }
#if OSL_DEBUG_LEVEL > 0
                while( nullptr != (pContent = aIter.Next()) )
                {
                    SwFootnoteFrame *pDbgFootnote = pContent->FindFootnoteFrame();
                    OSL_ENSURE( !pDbgFootnote || pDbgFootnote->GetRef() == pFrame,
                            "lcl_ChangeFootnoteRef: Who's that guy?" );
                }
#endif
            }
        }
    } // end of for-loop on <SwpHints>
    // #104840# - invalidate
    if ( pFirstFootnoteOfNode )
    {
        SwContentFrame* pContent = pFirstFootnoteOfNode->ContainsContent();
        if ( pContent )
        {
            pContent->InvalidatePos_();
        }
    }
}

namespace sw {

// check if there are flys on the existing frames (now on "pNode")
// that need to be moved to the new frames of "this"
void MoveMergedFlysAndFootnotes(std::vector<SwTextFrame*> const& rFrames,
        SwTextNode const& rFirstNode, SwTextNode & rSecondNode,
        bool isSplitNode)
{
    if (!isSplitNode)
    {
        lcl_ChangeFootnoteRef(rSecondNode);
    }
    for (SwNodeOffset nIndex = rSecondNode.GetIndex() + 1; ; ++nIndex)
    {
        SwNode *const pTmp(rSecondNode.GetNodes()[nIndex]);
        if (pTmp->IsCreateFrameWhenHidingRedlines() || pTmp->IsEndNode())
        {
            break;
        }
        else if (pTmp->IsStartNode())
        {
            nIndex = pTmp->EndOfSectionIndex();
        }
        else if (pTmp->GetRedlineMergeFlag() == SwNode::Merge::NonFirst
              && pTmp->IsTextNode())
        {
            lcl_ChangeFootnoteRef(*pTmp->GetTextNode());
        }
    }
    for (SwTextFrame *const pFrame : rFrames)
    {
        if (SwSortedObjs *const pObjs = pFrame->GetDrawObjs())
        {
            std::vector<SwAnchoredObject*> objs;
            objs.reserve(pObjs->size());
            for (SwAnchoredObject *const pObj : *pObjs)
            {
                objs.push_back(pObj);
            }
            for (SwAnchoredObject *const pObj : objs)
            {
                SwFrameFormat* pFormat(pObj->GetFrameFormat());
                SwFormatAnchor const& rAnchor(pFormat->GetAnchor());
                if (rFirstNode.GetIndex() < rAnchor.GetAnchorNode()->GetIndex())
                {
                    // move it to the new frame of "this"
                    pFormat->CallSwClientNotify(sw::LegacyModifyHint(&rAnchor, &rAnchor));
                    // note pObjs will be deleted if it becomes empty
                    assert(!pFrame->GetDrawObjs() || !pObjs->Contains(*pObj));
                }
            }
        }
    }
}

// namespace

SwTextNode *SwTextNode::SplitContentNode(const SwPosition & rPos,
        std::function<void (SwTextNode *, sw::mark::RestoreMode, bool AtStart)> const*const pContentIndexRestore)
{
    bool isHide(false);
    SwNode::Merge const eOldMergeFlag(GetRedlineMergeFlag());
    bool parentIsOutline = IsOutline();

    // create a node "in front" of me
    const sal_Int32 nSplitPos = rPos.GetContentIndex();
    const sal_Int32 nTextLen = m_Text.getLength();
    SwTextNode* const pNode =
        MakeNewTextNode( rPos.GetNode(), false, nSplitPos==nTextLen );

    // the first paragraph gets the XmlId,
    // _except_ if it is empty and the second is not empty
    if (nSplitPos != 0) {
        pNode->RegisterAsCopyOf(*thistrue);
        if (nSplitPos == nTextLen)
        {
            RemoveMetadataReference();
            // NB: SwUndoSplitNode will call pNode->JoinNext,
            // which is sufficient even in this case!
        }
    }

    ResetAttr( RES_PARATR_LIST_ISRESTART );
    ResetAttr( RES_PARATR_LIST_RESTARTVALUE );
    ResetAttr( RES_PARATR_LIST_ISCOUNTED );
    if ( GetNumRule() == nullptr || (parentIsOutline && !IsOutline()) )
    {
        ResetAttr( RES_PARATR_LIST_ID );
        ResetAttr( RES_PARATR_LIST_LEVEL );
    }

    bool bSplitFly = false;
    std::optional<std::vector<SwFrameFormat*>> oFlys = sw::GetFlysAnchoredAt(GetDoc(), GetIndex(), false);
    if (oFlys.has_value())
    {
        // See if one of the flys is a split fly. If so, we need to keep
        // the potentially split text frames unchanged and create a new
        // text frame at the end.
        for (const auto& rFly : *oFlys)
        {
            if (rFly->GetFlySplit().GetValue())
            {
                bSplitFly = true;
                break;
            }
        }
    }

    if ( HasWriterListeners() && !m_Text.isEmpty() && ((nTextLen / 2) < nSplitPos || bSplitFly) )
    {
        // optimization for SplitNode: If a split is at the end of a node then
        // move the frames from the current to the new one and create new ones
        // for the current one.

        // If fly frames are moved, they don't need to destroy their layout
        // frames.  Set a flag that is checked in SwTextFlyCnt::SetAnchor.
        if ( HasHints() )
        {
            pNode->GetOrCreateSwpHints().SetInSplitNode(true);
        }

        // Move the first part of the content to the new node and delete
        // it in the old node.
        SwContentIndex aIdx( this );
        CutText( pNode, aIdx, nSplitPos );

        if( GetWrong() )
        {
            pNode->SetWrong( GetWrong()->SplitList( nSplitPos ) );
        }
        SetWrongDirty(sw::WrongState::TODO);

        if( GetGrammarCheck() )
        {
            pNode->SetGrammarCheck( GetGrammarCheck()->SplitGrammarList( nSplitPos ) );
        }
        SetGrammarCheckDirty( true );

        SetWordCountDirty( true );

        if( GetSmartTags() )
        {
            pNode->SetSmartTags( GetSmartTags()->SplitList( nSplitPos ) );
        }
        SetSmartTagDirty( true );

        resetAndQueueAccessibilityCheck();
        pNode->resetAndQueueAccessibilityCheck();

        if ( pNode->HasHints() )
        {
            if ( pNode->m_pSwpHints->CanBeDeleted() )
            {
                pNode->m_pSwpHints.reset();
            }
            else
            {
                pNode->m_pSwpHints->SetInSplitNode(false);
            }

            // All fly frames anchored as char that are moved to the new
            // node must have their layout frames deleted.
            // This comment is sort of silly because we actually delete the
            // layout frames of those which were not moved?
            // JP 01.10.96: delete all empty and not-to-be-expanded attributes
            if ( HasHints() )
            {
                for ( size_t j = m_pSwpHints->Count(); j; )
                {
                    SwTextAttr* const pHt = m_pSwpHints->Get( --j );
                    if ( RES_TXTATR_FLYCNT == pHt ->Which() )
                    {
                        pHt->GetFlyCnt().GetFrameFormat()->DelFrames();
                    }
                    else if ( pHt->DontExpand() )
                    {
                        const sal_Int32* const pEnd = pHt->GetEnd();
                        if (pEnd && pHt->GetStart() == *pEnd )
                        {
                            // delete it!
                            m_pSwpHints->DeleteAtPos( j );
                            DestroyAttr( pHt );
                        }
                    }
                }
            }

        }

        if (pContentIndexRestore)
        {   // call before making frames and before RegisterToNode
            (*pContentIndexRestore)(pNode, sw::mark::RestoreMode::NonFlys, false);
        }
        if (eOldMergeFlag != SwNode::Merge::None)
        {   // clear before making frames and before RegisterToNode
            SetRedlineMergeFlag(SwNode::Merge::None);
        }   // now RegisterToNode will set merge flags in both nodes properly!

        std::vector<SwTextFrame*> frames;
        SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*this);
        for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
        {
            if (pFrame->getRootFrame()->HasMergedParas())
            {
                isHide = true;
            }
            frames.push_back(pFrame);
        }
        for (SwTextFrame * pFrame : frames)
        {
            pFrame->RegisterToNode( *pNode );
            if (!pFrame->IsFollow() && pFrame->GetOffset())
            {
                pFrame->SetOffset( TextFrameIndex(0) );
            }
        }

        InvalidateInSwCache();

        if ( HasHints() )
        {
            MoveTextAttr_To_AttrSet();
            pNode->MoveTextAttr_To_AttrSet();
        }
        // in case there are frames, the RegisterToNode has set the merge flag
        pNode->MakeFramesForAdjacentContentNode(*this);
        lcl_ChangeFootnoteRef( *this );
        if (pContentIndexRestore)
        {   // call after making frames; listeners will take care of adding to the right frame
            (*pContentIndexRestore)(pNode, sw::mark::RestoreMode::Flys, false);
        }
        if (eOldMergeFlag != SwNode::Merge::None)
        {
            MoveMergedFlysAndFootnotes(frames, *pNode, *thistrue);
        }
    }
    else
    {
        std::unique_ptr<SwWrongList> pList = ReleaseWrong();
        SetWrongDirty(sw::WrongState::TODO);

        std::unique_ptr<SwGrammarMarkUp> pList3 = ReleaseGrammarCheck();
        SetGrammarCheckDirty( true );

        SetWordCountDirty( true );

        std::unique_ptr<SwWrongList> pList2 = ReleaseSmartTags();
        SetSmartTagDirty( true );

        SwContentIndex aIdx( this );
        CutText( pNode, aIdx, nSplitPos );

        // JP 01.10.96: delete all empty and not-to-be-expanded attributes
        if ( HasHints() )
        {
            for ( size_t j = m_pSwpHints->Count(); j; )
            {
                SwTextAttr* const pHt = m_pSwpHints->Get( --j );
                const sal_Int32* const pEnd = pHt->GetEnd();
                if ( pHt->DontExpand() && pEnd && (pHt->GetStart() == *pEnd) )
                {
                    // delete it!
                    m_pSwpHints->DeleteAtPos( j );
                    DestroyAttr( pHt );
                }
            }
            MoveTextAttr_To_AttrSet();
            pNode->MoveTextAttr_To_AttrSet();
        }

        if( pList )
        {
            pNode->SetWrong( pList->SplitList( nSplitPos ) );
            SetWrong( std::move(pList) );
        }

        if( pList3 )
        {
            pNode->SetGrammarCheck( pList3->SplitGrammarList( nSplitPos ) );
            SetGrammarCheck( std::move(pList3) );
        }

        if( pList2 )
        {
            pNode->SetSmartTags( pList2->SplitList( nSplitPos ) );
            SetSmartTags( std::move(pList2) );
        }

        resetAndQueueAccessibilityCheck();
        pNode->resetAndQueueAccessibilityCheck();

        if (pContentIndexRestore)
        {   // call before making frames and before RegisterToNode
            (*pContentIndexRestore)(pNode, sw::mark::RestoreMode::NonFlys, false);
        }

        std::vector<SwTextFrame*> frames;
        SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*this);
        for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
        {
            frames.push_back(pFrame);
            if (pFrame->getRootFrame()->HasMergedParas())
            {
                isHide = true;
            }
        }
        bool bNonMerged(false);
        bool bRecreateThis(false);
        for (SwTextFrame * pFrame : frames)
        {
            // sw_redlinehide: for this to work properly with hidden nodes,
            // the frame needs to listen on them too.
            // also: have to check the frame; this->GetRedlineMergeFlag()
            // is None in case there's a delete redline inside the paragraph,
            // but that could still result in a merged frame after split...
            if (pFrame->GetMergedPara())
            {
                // Can't special case this == First here - that could (if
                // both nodes are still merged by redline) lead to
                // duplicate frames on "this".
                // Update the extents with new node; also inits merge flag,
                // so the MakeFramesForAdjacentContentNode below respects it
                pFrame->RegisterToNode(*pNode);
                if (nSplitPos == 0)
                {
                    // in this case, it was not
                    // invalidated because Cut didn't sent it any hints,
                    // so we have to invalidate it here!
                    pFrame->Prepare(PrepareHint::Clear, nullptr, false);
                }
                if (!pFrame->GetMergedPara() ||
                    !pFrame->GetMergedPara()->listener.IsListeningTo(this))
                {
                    // it's no longer listening - need to recreate frame
                    // (note this is idempotent, can be done once per frame)
                    SetRedlineMergeFlag(SwNode::Merge::None);
                    bRecreateThis = true;
                }
            }
            else
            {
                bNonMerged = true;
            }
        }
        assert(!(bNonMerged && bRecreateThis)); // 2 layouts not handled yet - maybe best to simply use the other branch then?
        if (!frames.empty() && bNonMerged)
        {
            // the existing frame on "this" should have been updated by Cut
            MakeFramesForAdjacentContentNode(*pNode);
            lcl_ChangeFootnoteRef(*pNode);
        }
        else if (bRecreateThis)
        {
            assert(pNode->HasWriterListeners()); // was just moved there
            pNode->MakeFramesForAdjacentContentNode(*this);
            lcl_ChangeFootnoteRef(*this);
        }

        if (pContentIndexRestore)
        {   // call after making frames; listeners will take care of adding to the right frame
            (*pContentIndexRestore)(pNode, sw::mark::RestoreMode::Flys, nSplitPos == 0);
        }

        if (bRecreateThis)
        {
            MoveMergedFlysAndFootnotes(frames, *pNode, *thistrue);
        }
    }

    // pNode is the previous node, 'this' is the next node from the split.
    if (nSplitPos == nTextLen && m_pSwpHints)
    {
        // We just created an empty next node: avoid unwanted superscript in the new node if it's
        // there.
        ResetAttr(RES_CHRATR_ESCAPEMENT);
    }

#ifndef NDEBUG
    if (isHide) // otherwise flags won't be set anyway
    {
        // First
        // -> First,NonFirst
        // -> First,Hidden
        // -> None,First
        // Hidden
        // -> Hidden,Hidden (if still inside merge rl)
        // -> NonFirst,First (if redline was split)
        // NonFirst
        // -> NonFirst,First (if split after end of "incoming" redline &
        //                    before start of "outgoing" redline)
        // -> NonFirst,None (if split after end of "incoming" redline)
        // -> NonFirst,Hidden (if split after start of "outgoing" redline)
        // -> Hidden, NonFirst (if split before end of "incoming" redline)
        // None
        // -> None,None
        // -> First,NonFirst (if splitting inside a delete redline)
        SwNode::Merge const eFirst(pNode->GetRedlineMergeFlag());
        SwNode::Merge const eSecond(GetRedlineMergeFlag());
        switch (eOldMergeFlag)
        {
            case Merge::First:
                assert((eFirst == Merge::First && eSecond == Merge::NonFirst)
                    || (eFirst == Merge::First && eSecond == Merge::Hidden)
                    || (eFirst == Merge::None && eSecond == Merge::First));
            break;
            case Merge::Hidden:
                assert((eFirst == Merge::Hidden && eSecond == Merge::Hidden)
                    || (eFirst == Merge::NonFirst && eSecond == Merge::First)
                        // next ones can happen temp. in UndoDelete :(
                    || (eFirst == Merge::Hidden && eSecond == Merge::NonFirst)
                    || (eFirst == Merge::NonFirst && eSecond == Merge::None));
            break;
            case Merge::NonFirst:
                assert((eFirst == Merge::NonFirst && eSecond == Merge::First)
                    || (eFirst == Merge::NonFirst && eSecond == Merge::None)
                    || (eFirst == Merge::NonFirst && eSecond == Merge::Hidden)
                    || (eFirst == Merge::Hidden && eSecond == Merge::NonFirst));
            break;
            case Merge::None:
                assert((eFirst == Merge::None && eSecond == Merge::None)
                    || (eFirst == Merge::First && eSecond == Merge::NonFirst));
            break;
        }
    }
#else
    (void) isHide;
#endif

    {
        // Send Hint for PageDesc. This should be done in the Layout when
        // pasting the frames, but that causes other problems that look
        // expensive to solve.
        const SwFormatPageDesc *pItem;
        if(HasWriterListeners() && (pItem = pNode->GetSwAttrSet().GetItemIfSet(RES_PAGEDESC)))
            pNode->TriggerNodeUpdate(sw::LegacyModifyHint(pItem, pItem));
    }
    return pNode;
}

void SwTextNode::MoveTextAttr_To_AttrSet()
{
    OSL_ENSURE( m_pSwpHints, "MoveTextAttr_To_AttrSet without SwpHints?" );
    for ( size_t i = 0; m_pSwpHints && i < m_pSwpHints->Count(); ++i )
    {
        SwTextAttr *pHt = m_pSwpHints->Get(i);

        if( pHt->GetStart() )
            break;

        const sal_Int32* pHtEndIdx = pHt->GetEnd();

        if( !pHtEndIdx )
            continue;

        if (*pHtEndIdx < m_Text.getLength() || pHt->IsCharFormatAttr())
            break;

        if (!pHt->IsDontMoveAttr())
        {
            bool isInserted(false);
            if (pHt->Which() == RES_TXTATR_AUTOFMT)
            {
                isInserted = SetAttr(*pHt->GetAutoFormat().GetStyleHandle());
            }
            else
            {
                isInserted = SetAttr(pHt->GetAttr());
            }
            if (isInserted)
            {
                m_pSwpHints->DeleteAtPos(i);
                DestroyAttr( pHt );
                --i;
            }
        }
    }

}

namespace sw {

/// if first node is deleted & second survives, then the first node's frame
/// will be deleted too; prevent this by moving the frame to the second node
/// if necessary.
void MoveDeletedPrevFrames(const SwTextNode & rDeletedPrev, SwTextNode & rNode)
{
    std::vector<SwTextFrame*> frames;
    SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(rDeletedPrev);
    for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
    {
        if (pFrame->getRootFrame()->HasMergedParas())
        {
            frames.push_back(pFrame);
        }
    }
    {
        auto frames2(frames);
        SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIt(rNode);
        for (SwTextFrame* pFrame = aIt.First(); pFrame; pFrame = aIt.Next())
        {
            if (pFrame->getRootFrame()->HasMergedParas())
            {
                auto const it(std::find(frames2.begin(), frames2.end(), pFrame));
                assert(it != frames2.end());
                frames2.erase(it);
            }
        }
        assert(frames2.empty());
    }
    for (SwTextFrame *const pFrame : frames)
    {
        pFrame->RegisterToNode(rNode, true);
    }
}

// typical Join:
// None,Node->None
// None,First->First
// First,NonFirst->First
// NonFirst,First->NonFirst
// NonFirst,None->NonFirst

/// if first node is First, its frames may need to be moved, never deleted.
/// if first node is NonFirst, second node's own frames (First/None) must be deleted
void CheckResetRedlineMergeFlag(SwTextNode & rNode, Recreate const eRecreateMerged)
{
    if (eRecreateMerged != sw::Recreate::No)
    {
        SwTextNode * pMergeNode(&rNode);
        if (eRecreateMerged == sw::Recreate::Predecessor
            // tdf#135018 check that there is a predecessor node, i.e. rNode
            // isn't the first node after the body start node
            && rNode.GetNodes()[rNode.GetIndex() - 1]->StartOfSectionIndex() != SwNodeOffset(0))
        {
            for (SwNodeOffset i = rNode.GetIndex() - 1; ; --i)
            {
                SwNode *const pNode(rNode.GetNodes()[i]);
                assert(!pNode->IsStartNode());
                if (pNode->IsEndNode())
                {
                    i = pNode->StartOfSectionIndex();
                }
                else if (pNode->IsTextNode())
                {
                    pMergeNode = pNode->GetTextNode(); // use predecessor to merge
                    break;
                }
            }
        }
        std::vector<SwTextFrame*> frames;
        SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pMergeNode);
        for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
        {
            if (pFrame->getRootFrame()->HasMergedParas())
            {
                frames.push_back(pFrame);
            }
        }
        auto eMode(sw::FrameMode::Existing);
        for (SwTextFrame * pFrame : frames)
        {
            SwTextNode & rFirstNode(pFrame->GetMergedPara()
                ? *pFrame->GetMergedPara()->pFirstNode
                : *pMergeNode);
            assert(rFirstNode.GetIndex() <= rNode.GetIndex());
            pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
                        *pFrame, rFirstNode, eMode));
            // there is no merged para in case the deleted node had one but
            // nothing was actually hidden
            if (pFrame->GetMergedPara())
            {
                assert(pFrame->GetMergedPara()->listener.IsListeningTo(&rNode));
                assert(rNode.GetIndex() <= pFrame->GetMergedPara()->pLastNode->GetIndex());
                // tdf#135978 Join: recreate fly frames anchored to subsequent nodes
                if (eRecreateMerged == sw::Recreate::ThisNode)
                {
                    AddRemoveFlysAnchoredToFrameStartingAtNode(*pFrame, rNode, nullptr);
                }
            }
            eMode = sw::FrameMode::New// Existing is not idempotent!
        }
    }
    else if (rNode.GetRedlineMergeFlag() != SwNode::Merge::None)
    {
        SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(rNode);
        for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
        {
            if (auto const pMergedPara = pFrame->GetMergedPara())
            {
                if (pMergedPara->pFirstNode == pMergedPara->pLastNode)
                {
                    assert(pMergedPara->pFirstNode == &rNode);
                    rNode.SetRedlineMergeFlag(SwNode::Merge::None);
                }
                break// checking once is enough
            }
            else if (pFrame->getRootFrame()->HasMergedParas())
            {
                rNode.SetRedlineMergeFlag(SwNode::Merge::None);
                break// checking once is enough
            }
        }
    }
}

bool HasNumberingWhichNeedsLayoutUpdate(const SwTextNode& rTextNode)
{
    const SwNodeNum* pNodeNum = rTextNode.GetNum();
    if (!pNodeNum)
    {
        return false;
    }

    const SwNumRule* pNumRule = pNodeNum->GetNumRule();
    if (!pNumRule)
    {
        return false;
    }

    const SwNumFormat* pFormat
        = pNumRule->GetNumFormat(o3tl::narrowing<sal_uInt16>(rTextNode.GetAttrListLevel()));
    if (!pFormat)
    {
        return false;
    }

    switch (pFormat->GetNumberingType())
    {
        case SVX_NUM_NUMBER_NONE:
        case SVX_NUM_CHAR_SPECIAL:
        case SVX_NUM_BITMAP:
            return false;
        default:
            return true;
    }
}
// namespace

SwContentNode *SwTextNode::JoinNext()
{
    SwNodes& rNds = GetNodes();
    SwNodeIndex aIdx( *this );
    if( SwContentNode::CanJoinNext( &aIdx ) )
    {
        SwDoc& rDoc = rNds.GetDoc();
        const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
        pContentStore->Save(rDoc, aIdx.GetIndex(), SAL_MAX_INT32);
        SwTextNode *pTextNode = aIdx.GetNode().GetTextNode();
        sal_Int32 nOldLen = m_Text.getLength();

        // METADATA: merge
        JoinMetadatable(*pTextNode, !Len(), !pTextNode->Len());

        std::unique_ptr<SwWrongList> pList = ReleaseWrong();
        if( pList )
        {
            pList->JoinList( pTextNode->GetWrong(), nOldLen );
            SetWrongDirty(sw::WrongState::TODO);
        }
        else
        {
            pList = pTextNode->ReleaseWrong();
            if( pList )
            {
                pList->Move( 0, nOldLen );
                SetWrongDirty(sw::WrongState::TODO);
            }
        }

        std::unique_ptr<SwGrammarMarkUp> pList3 = ReleaseGrammarCheck();
        if( pList3 )
        {
            pList3->JoinGrammarList( pTextNode->GetGrammarCheck(), nOldLen );
            SetGrammarCheckDirty( true );
        }
        else
        {
            pList3 = pTextNode->ReleaseGrammarCheck();
            if( pList3 )
            {
                pList3->MoveGrammar( 0, nOldLen );
                SetGrammarCheckDirty( true );
            }
        }

        std::unique_ptr<SwWrongList> pList2 = ReleaseSmartTags();
        if( pList2 )
        {
            pList2->JoinList( pTextNode->GetSmartTags(), nOldLen );
            SetSmartTagDirty( true );
        }
        else
        {
            pList2 = pTextNode->ReleaseSmartTags();
            if( pList2 )
            {
                pList2->Move( 0, nOldLen );
                SetSmartTagDirty( true );
            }
        }

        { // scope for SwContentIndex
            pTextNode->CutText( this, SwContentIndex(pTextNode), pTextNode->Len() );
        }
        // move all Bookmarks/TOXMarks
        if( !pContentStore->Empty())
            pContentStore->Restore( rDoc, GetIndex(), nOldLen );

        if( pTextNode->HasAnyIndex() )
        {
            // move all ShellCursor/StackCursor/UnoCursor out of delete range
            rDoc.CorrAbs( aIdx.GetNode(), SwPosition( *this ), nOldLen, true );
        }
        SwNode::Merge const eOldMergeFlag(pTextNode->GetRedlineMergeFlag());
        auto eRecreateMerged(eOldMergeFlag == SwNode::Merge::First
                    ? sw::Recreate::ThisNode
                    : sw::Recreate::No);
        if (eRecreateMerged == sw::Recreate::No)
        {
            // tdf#137318 if a delete is inside one node, flag is still None!
            SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pTextNode);
            for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
            {
                if (pFrame->GetMergedPara())
                {
                    eRecreateMerged = sw::Recreate::ThisNode;
                    break;
                }
            }
        }
        bool bOldHasNumberingWhichNeedsLayoutUpdate = HasNumberingWhichNeedsLayoutUpdate(*pTextNode);

        rNds.Delete(aIdx);
        SetWrong( std::move(pList) );
        SetGrammarCheck( std::move(pList3) );
        SetSmartTags( std::move(pList2) );

        resetAndQueueAccessibilityCheck();

        if (bOldHasNumberingWhichNeedsLayoutUpdate || HasNumberingWhichNeedsLayoutUpdate(*this))
        {
            // Repaint all text frames that belong to this numbering to avoid outdated generated
            // numbers.
            InvalidateNumRule();
        }

        CheckResetRedlineMergeFlag(*this, eRecreateMerged);
    }
    else {
        OSL_FAIL( "No TextNode." );
    }

    return this;
}

void SwTextNode::JoinPrev()
{
    SwNodes& rNds = GetNodes();
    SwNodeIndex aIdx( *this );
    if( SwContentNode::CanJoinPrev( &aIdx ) )
    {
        SwDoc& rDoc = rNds.GetDoc();
        const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
        pContentStore->Save( rDoc, aIdx.GetIndex(), SAL_MAX_INT32);
        SwTextNode *pTextNode = aIdx.GetNode().GetTextNode();
        const sal_Int32 nLen = pTextNode->Len();

        std::unique_ptr<SwWrongList> pList = pTextNode->ReleaseWrong();
        if( pList )
        {
            pList->JoinList( GetWrong(), Len() );
            SetWrongDirty(sw::WrongState::TODO);
            ClearWrong();
        }
        else
        {
            pList = ReleaseWrong();
            if( pList )
            {
                pList->Move( 0, nLen );
                SetWrongDirty(sw::WrongState::TODO);
            }
        }

        std::unique_ptr<SwGrammarMarkUp> pList3 = pTextNode->ReleaseGrammarCheck();
        if( pList3 )
        {
            pList3->JoinGrammarList( GetGrammarCheck(), Len() );
            SetGrammarCheckDirty( true );
            ClearGrammarCheck();
        }
        else
        {
            pList3 = ReleaseGrammarCheck();
            if( pList3 )
            {
                pList3->MoveGrammar( 0, nLen );
                SetGrammarCheckDirty( true );
            }
        }

        std::unique_ptr<SwWrongList> pList2 = pTextNode->ReleaseSmartTags();
        if( pList2 )
        {
            pList2->JoinList( GetSmartTags(), Len() );
            SetSmartTagDirty( true );
            ClearSmartTags();
        }
        else
        {
            pList2 = ReleaseSmartTags();
            if( pList2 )
            {
                pList2->Move( 0, nLen );
                SetSmartTagDirty( true );
            }
        }

        { // scope for SwContentIndex
            pTextNode->CutText( this, SwContentIndex(this), SwContentIndex(pTextNode), nLen );
        }
        // move all Bookmarks/TOXMarks
        if( !pContentStore->Empty() )
            pContentStore->Restore( rDoc, GetIndex() );

        if( pTextNode->HasAnyIndex() )
        {
            // move all ShellCursor/StackCursor/UnoCursor out of delete range
            rDoc.CorrAbs( aIdx.GetNode(), SwPosition( *this ), nLen, true );
        }
        SwNode::Merge const eOldMergeFlag(pTextNode->GetRedlineMergeFlag());
        if (eOldMergeFlag == SwNode::Merge::First
            && !IsCreateFrameWhenHidingRedlines())
        {
            sw::MoveDeletedPrevFrames(*pTextNode, *this);
        }
        rNds.Delete(aIdx);
        SetWrong( std::move(pList) );
        SetGrammarCheck( std::move(pList3) );
        SetSmartTags( std::move(pList2) );
        resetAndQueueAccessibilityCheck();
        InvalidateNumRule();
        sw::CheckResetRedlineMergeFlag(*this,
                eOldMergeFlag == SwNode::Merge::NonFirst
                    ? sw::Recreate::Predecessor
                    : sw::Recreate::No);
    }
    else {
        OSL_FAIL( "No TextNode." );
    }
}

// create an AttrSet with ranges for Frame-/Para/Char-attributes
void SwTextNode::NewAttrSet( SwAttrPool& rPool )
{
    OSL_ENSURE( !mpAttrSet, "AttrSet is set after all" );
    SwAttrSet aNewAttrSet( rPool, aTextNodeSetRange );

    // put names of parent style and conditional style:
    const SwFormatColl* pAnyFormatColl = &GetAnyFormatColl();
    const SwFormatColl* pFormatColl = GetFormatColl();
    ProgName sVal;
    SwStyleNameMapper::FillProgName( pAnyFormatColl->GetName(), sVal, SwGetPoolIdFromName::TxtColl );
    SfxStringItem aAnyFormatColl( RES_FRMATR_STYLE_NAME, sVal.toString() );
    if ( pFormatColl != pAnyFormatColl )
        SwStyleNameMapper::FillProgName( pFormatColl->GetName(), sVal, SwGetPoolIdFromName::TxtColl );
    SfxStringItem aFormatColl( RES_FRMATR_CONDITIONAL_STYLE_NAME, sVal.toString() );
    aNewAttrSet.Put( aAnyFormatColl );
    aNewAttrSet.Put( aFormatColl );

    aNewAttrSet.SetParent( &pAnyFormatColl->GetAttrSet() );
    mpAttrSet = GetDoc().GetIStyleAccess().getAutomaticStyle( aNewAttrSet, IStyleAccess::AUTO_STYLE_PARA, &sVal );
}

namespace
{
class SwContentNodeTmp : public SwContentNode
{
public:
    SwContentNodeTmp() : SwContentNode() {}
    virtual void NewAttrSet(SwAttrPool&) override {}
    virtual SwContentFrame *MakeFrame(SwFrame*) override { return nullptr; }
    virtual SwContentNode* MakeCopy(SwDoc&, SwNode&, bool /*bNewFrames*/) const override { return nullptr; };
};
};

// override SwContentIndexReg::Update => text hints do not need SwContentIndex for start/end!
void SwTextNode::Update(
    SwContentIndex const & rPos,
    const sal_Int32 nChangeLen,
    UpdateMode const eMode)
{
    assert(rPos.GetContentNode() == this);
    SetAutoCompleteWordDirty( true );

    SwpHts aCollector;
    const sal_Int32 nChangePos = rPos.GetIndex();

    if ( HasHints() )
    {
        if (eMode & UpdateMode::Negative)
        {
            std::vector<SwTextInputField*> aTextInputFields;

            const sal_Int32 nChangeEnd = nChangePos + nChangeLen;
            for ( size_t n = 0; n < m_pSwpHints->Count(); ++n )
            {
                bool bTextAttrChanged = false;
                bool bStartOfTextAttrChanged = false;
                SwTextAttr * const pHint = m_pSwpHints->GetWithoutResorting(n);
                if ( pHint->GetStart() > nChangePos )
                {
                    if ( pHint->GetStart() > nChangeEnd )
                    {
                         pHint->SetStart( pHint->GetStart() - nChangeLen );
                    }
                    else
                    {
                         pHint->SetStart( nChangePos );
                    }
                    bStartOfTextAttrChanged = true;
                }

                const sal_Int32 * const pEnd = pHint->GetEnd();
                if (pEnd && *pEnd > nChangePos )
                {
                    if( *pEnd > nChangeEnd )
                    {
                        pHint->SetEnd(*pEnd - nChangeLen);
                    }
                    else
                    {
                        pHint->SetEnd(nChangePos);
                    }
                    bTextAttrChanged = !bStartOfTextAttrChanged;
                }

                if ( bTextAttrChanged
                     && pHint->Which() == RES_TXTATR_INPUTFIELD )
                {
                    SwTextInputField* pTextInputField = dynamic_cast<SwTextInputField*>(pHint);
                    if ( pTextInputField )
                        aTextInputFields.push_back(pTextInputField);
                }
            }

            //wait until all the attribute positions are correct
            //before updating the field contents
            for (SwTextInputField* pTextInputField : aTextInputFields)
            {
                pTextInputField->UpdateFieldContent();
            }

            m_pSwpHints->MergePortions( *this );
        }
        else
        {
            bool bNoExp = false;
            bool bResort = false;
            bool bMergePortionsNeeded = false;
            const int coArrSz = RES_TXTATR_WITHEND_END - RES_CHRATR_BEGIN;
            std::vector<SwTextInputField*> aTextInputFields;

            bool aDontExp[ coArrSz ] = {};

            for ( size_t n = 0; n < m_pSwpHints->Count(); ++n )
            {
                bool bTextAttrChanged = false;
                SwTextAttr * const pHint = m_pSwpHints->GetWithoutResorting(n);
                const sal_Int32 * const pEnd = pHint->GetEnd();
                if ( pHint->GetStart() >= nChangePos )
                {
                    pHint->SetStart( pHint->GetStart() + nChangeLen );
                    if ( pEnd )
                    {
                        pHint->SetEnd(*pEnd + nChangeLen);
                    }
                }
                else if ( pEnd && (*pEnd >= nChangePos) )
                {
                    if ( (*pEnd > nChangePos) || IsIgnoreDontExpand() )
                    {
                        pHint->SetEnd(*pEnd + nChangeLen);
                        bTextAttrChanged = true;
                    }
                    else // *pEnd == nChangePos
                    {
                        const sal_uInt16 nWhich = pHint->Which();

                        OSL_ENSURE(!isCHRATR(nWhich), "Update: char attr hint?");
                        if (!(isCHRATR(nWhich) || isTXTATR_WITHEND(nWhich)))
                            continue;

                        const sal_uInt16 nWhPos = nWhich - RES_CHRATR_BEGIN;

                        if( aDontExp[ nWhPos ] )
                            continue;

                        if ( pHint->DontExpand() )
                        {
                            pHint->SetDontExpand( false );
                            bResort = true;
                            // could have a continuation with IgnoreStart()...
                            if (pHint->IsFormatIgnoreEnd())
                            {
                                bMergePortionsNeeded = true;
                            }
                            if ( pHint->IsCharFormatAttr() )
                            {
                                bNoExp = true;
                                aDontExp[ RES_TXTATR_CHARFMT - RES_CHRATR_BEGIN ] = true;
                                aDontExp[ RES_TXTATR_INETFMT - RES_CHRATR_BEGIN ] = true;
                            }
                            else
                                aDontExp[ nWhPos ] = true;
                        }
                        else if( bNoExp )
                        {
                            auto it = std::find_if(aCollector.begin(), aCollector.end(),
                                [nWhich](const SwTextAttr *pTmp) { return nWhich == pTmp->Which(); });
                            if (it != aCollector.end())
                            {
                                SwTextAttr *pTmp = *it;
                                aCollector.erase( it );
                                SwTextAttr::Destroy( pTmp );
                            }
                            SwTextAttr * const pTmp =
                            MakeTextAttr( GetDoc(),
                                pHint->GetAttr(), nChangePos, nChangePos + nChangeLen);
                            aCollector.push_back( pTmp );
                        }
                        else
                        {
                            pHint->SetEnd(*pEnd + nChangeLen);
                            bTextAttrChanged = true;
                        }
                    }
                }

                if ( bTextAttrChanged
                     && pHint->Which() == RES_TXTATR_INPUTFIELD )
                {
                    SwTextInputField* pTextInputField = dynamic_cast<SwTextInputField*>(pHint);
                    if ( pTextInputField )
                        aTextInputFields.push_back(pTextInputField);
                }
            }

            //wait until all the attribute positions are correct
            //before updating the field contents
            for (SwTextInputField* pTextInputField : aTextInputFields)
            {
                pTextInputField->UpdateFieldContent();
            }

            if (bMergePortionsNeeded)
            {
                m_pSwpHints->MergePortions(*this); // does Resort too
            }
            else if (bResort)
            {
                m_pSwpHints->Resort();
            }
        }
    }

    bool bSortMarks = false;
    SwContentNodeTmp aTmpIdxReg;
    if (!(eMode & UpdateMode::Negative) && !(eMode & UpdateMode::Delete))
    {
        o3tl::sorted_vector<SwRangeRedline*> vMyRedlines;
        // walk the list of SwContentIndex attached to me and see if any of them are redlines
        const SwContentIndex* pContentNodeIndex = GetFirstIndex();
        while (pContentNodeIndex)
        {
            if (pContentNodeIndex->GetOwner() && pContentNodeIndex->GetOwner()->GetOwnerType() == SwContentIndexOwnerType::Redline)
            {
                auto pRedl = static_cast<SwRangeRedline*>(pContentNodeIndex->GetOwner());
                if (pRedl && (pRedl->HasMark() || this == &pRedl->GetPoint()->GetNode()))
                    vMyRedlines.insert(pRedl);
            }
            pContentNodeIndex = pContentNodeIndex->GetNext();
        }
        for (SwRangeRedline* pRedl : vMyRedlines)
        {
            if ( pRedl->HasMark() )
            {
                SwPosition* const pEnd = pRedl->End();
                if ( *this == pEnd->GetNode() &&
                     *pRedl->GetPoint() != *pRedl->GetMark() )
                {
                    SwContentIndex & rIdx = pEnd->nContent;
                    if (nChangePos == rIdx.GetIndex())
                    {
                        rIdx.Assign( &aTmpIdxReg, rIdx.GetIndex() );
                    }
                }
            }
            else if ( this == &pRedl->GetPoint()->GetNode() )
            {
                SwContentIndex & rIdx = pRedl->GetPoint()->nContent;
                if (nChangePos == rIdx.GetIndex())
                {
                    rIdx.Assign( &aTmpIdxReg, rIdx.GetIndex() );
                }
                // the unused position must not be on a SwTextNode
                bool const isOneUsed(&pRedl->GetBound() == pRedl->GetPoint());
                assert(!pRedl->GetBound(!isOneUsed).GetNode().IsTextNode());
                assert(!pRedl->GetBound(!isOneUsed).GetContentNode()); (void)isOneUsed;
            }
        }

        // Bookmarks must never grow to either side, when editing (directly)
        // to the left or right (i#29942)! Exception: if the bookmark has
        // 2 positions and start == end, then expand it (tdf#96479)
        if (!(eMode & UpdateMode::Replace)) // Exception: Replace
        {
            bool bAtLeastOneBookmarkMoved = false;
            bool bAtLeastOneExpandedBookmarkAtInsertionPosition = false;
            // A text node already knows its marks via its SwContentIndexes.
            o3tl::sorted_vector<const sw::mark::MarkBase*> aSeenMarks;
            const SwContentIndex* next;
            for (const SwContentIndex* pIndex = GetFirstIndex(); pIndex; pIndex = next )
            {
                next = pIndex->GetNext();
                if (!pIndex->GetOwner() || pIndex->GetOwner()->GetOwnerType() != SwContentIndexOwnerType::Mark)
                    continue;
                auto const pMark = static_cast<sw::mark::MarkBase const*>(pIndex->GetOwner());
                // filter out ones that cannot match to reduce the max size of aSeenMarks
                const SwPosition* pMarkPos1 = &pMark->GetMarkPos();
                const SwPosition* pMarkPos2 = pMark->IsExpanded() ? &pMark->GetOtherMarkPos() : nullptr;
                if (pMarkPos1->nContent.GetIndex() != rPos.GetIndex()
                    && (pMarkPos2 == nullptr || pMarkPos2->nContent.GetIndex() != rPos.GetIndex()))
                    continue;
                // Only handle bookmarks once, if they start and end at this node as well.
                if (!aSeenMarks.insert(pMark).second)
                    continue;
                const SwPosition* pEnd = &pMark->GetMarkEnd();
                SwContentIndex & rEndIdx = const_cast<SwContentIndex&>(pEnd->nContent);
                if( *this == pEnd->GetNode() &&
                    rPos.GetIndex() == rEndIdx.GetIndex() )
                {
                    if (&rEndIdx == next) // nasty corner case:
                    {   // don't switch to iterating aTmpIdxReg!
                        next = rEndIdx.GetNext();
                    }
                    // tdf#96479: if start == end, ignore the other position
                    // so it is moved!
                    rEndIdx.Assign( &aTmpIdxReg, rEndIdx.GetIndex() );
                    bAtLeastOneBookmarkMoved = true;
                }
                else if ( !bAtLeastOneExpandedBookmarkAtInsertionPosition )
                {
                    if ( pMark->IsExpanded() )
                    {
                        const SwPosition* pStart = &pMark->GetMarkStart();
                        if ( this == &pStart->GetNode()
                             && rPos.GetIndex() == pStart->GetContentIndex() )
                        {
                            bAtLeastOneExpandedBookmarkAtInsertionPosition = true;
                        }
                    }
                }
            }

            bSortMarks = bAtLeastOneBookmarkMoved && bAtLeastOneExpandedBookmarkAtInsertionPosition;
        }

        // at-char anchored flys shouldn't be moved, either.
        if (!m_bInUndo)
        {
            std::vector<SwFrameFormat*> const& rFlys(GetAnchoredFlys());
            for (size_t i = 0; i != rFlys.size(); ++i)
            {
                SwFrameFormat const*const pFormat = rFlys[i];
                const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
                const SwNode* pAnchorNode = rAnchor.GetAnchorNode();
                if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR && pAnchorNode)
                {
                    // The fly is at-char anchored and has an anchor position.
                    SwContentIndex& rEndIdx = const_cast<SwContentIndex&>(rAnchor.GetContentAnchor()->nContent);
                    if (*pAnchorNode == *this && rEndIdx.GetIndex() == rPos.GetIndex())
                    {
                        // The anchor position is exactly our insert position.
                        rEndIdx.Assign(&aTmpIdxReg, rEndIdx.GetIndex());
                    }
                }
            }
        }

        // The cursors of other shells shouldn't be moved, either.
        if (SwDocShell* pDocShell = GetDoc().GetDocShell())
        {
            if (pDocShell->GetWrtShell())
            {
                for (SwViewShell& rShell : pDocShell->GetWrtShell()->GetRingContainer())
                {
                    auto pWrtShell = dynamic_cast<SwWrtShell*>(&rShell);
                    if (!pWrtShell || pWrtShell == dynamic_cast<SwWrtShell*>(GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell()))
                        continue;

                    SwShellCursor* pCursor = pWrtShell->GetCursor_();
                    if (!pCursor)
                        continue;

                    SwContentIndex& rIndex = pCursor->Start()->nContent;
                    if (pCursor->Start()->GetNode() == *this && rIndex.GetIndex() == rPos.GetIndex())
                    {
                        // The cursor position of this other shell is exactly our insert position.
                        rIndex.Assign(&aTmpIdxReg, rIndex.GetIndex());
                    }
                }
            }
        }
    }

    // base class
    SwContentIndexReg::Update(rPos, nChangeLen, eMode);

    for ( SwTextAttr* pAttr : aCollector )
    {
        m_pSwpHints->TryInsertHint( pAttr, *this );
    }

    aTmpIdxReg.MoveTo( *this );
    if ( bSortMarks )
    {
        getIDocumentMarkAccess()->assureSortedMarkContainers();
    }

    //Any drawing objects anchored into this text node may be sorted by their
    //anchor position which may have changed here, so resort them
    SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> iter(*this);
    for (SwTextFrame* pFrame = iter.First(); pFrame; pFrame = iter.Next())
    {
        SwSortedObjs * pSortedObjs(pFrame->GetDrawObjs());
        if (pSortedObjs)
        {
            pSortedObjs->UpdateAll();
        }
        // also sort the objs on the page frame
        if (SwPageFrame *pPage = pFrame->FindPageFrame())
            pSortedObjs = pPage->GetSortedObjs();

        if (pSortedObjs) // doesn't exist yet if called for inserting as-char fly
        {
            pSortedObjs->UpdateAll();
        }
    }

    // Update the paragraph signatures.
    if (SwEditShell* pEditShell = GetDoc().GetEditShell())
    {
        pEditShell->ValidateParagraphSignatures(thistrue);
    }

    // Inform LOK clients about change in position of redlines (if any)
    // Don't emit notifications during save: redline flags are temporarily changed during save, but
    // it's not useful to let clients know about such changes.
    if (!comphelper::LibreOfficeKit::isActive() || GetDoc().IsInWriting())
        return;

    const SwRedlineTable& rTable = GetDoc().getIDocumentRedlineAccess().GetRedlineTable();
    for (SwRedlineTable::size_type nRedlnPos = 0; nRedlnPos < rTable.size(); ++nRedlnPos)
    {
        SwRangeRedline* pRedln = rTable[nRedlnPos];
        if (pRedln->HasMark())
        {
            if (*this == pRedln->End()->GetNode() && *pRedln->GetPoint() != *pRedln->GetMark())
            {
                // Redline is changed only when some change occurs before it
                if (nChangePos <= pRedln->Start()->GetContentIndex())
                {
                    SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify, pRedln);
                }
            }
        }
        else if (this == &pRedln->GetPoint()->GetNode())
            SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify, pRedln);
    }
}

void SwTextNode::ChgTextCollUpdateNum(const SwTextFormatColl* pOldColl,
                                      const SwTextFormatColl* pNewColl,
                                      bool bSetListLevel)
{
    SwDoc& rDoc = GetDoc();
    // query the OutlineLevel and if it changed, notify the Nodes-Array!
    const int nOldLevel = pOldColl && pOldColl->IsAssignedToListLevelOfOutlineStyle()
                              ? pOldColl->GetAssignedOutlineStyleLevel()
                              : MAXLEVEL;
    const int nNewLevel = pNewColl && pNewColl->IsAssignedToListLevelOfOutlineStyle() ?
                     pNewColl->GetAssignedOutlineStyleLevel() : MAXLEVEL;

    if ( MAXLEVEL != nNewLevel && -1 != nNewLevel && bSetListLevel )
    {
        SetAttrListLevel(nNewLevel);
    }
    rDoc.GetNodes().UpdateOutlineNode(*this);

    SwNodes& rNds = GetNodes();
    // If Level 0 (Chapter), update the footnotes!
    if( ( !nNewLevel || !nOldLevel) && !rDoc.GetFootnoteIdxs().empty() &&
        FTNNUM_CHAPTER == rDoc.GetFootnoteInfo().m_eNum &&
        rNds.IsDocNodes() )
    {
        rDoc.GetFootnoteIdxs().UpdateFootnote( *rNds[GetIndex()] );
    }

    if( pNewColl && RES_CONDTXTFMTCOLL == pNewColl->Which() )
    {
        // check the condition of the text node again
        ChkCondColl();
    }
}

// If positioned exactly at the end of a CharStyle or Hyperlink,
// set its DontExpand flag.
bool SwTextNode::DontExpandFormat( sal_Int32 nIdx, bool bFlag,
                                bool bFormatToTextAttributes )
{
    if (bFormatToTextAttributes && nIdx == m_Text.getLength())
    {
        FormatToTextAttr( this );
    }

    bool bRet = false;
    if ( HasHints() )
    {
        m_pSwpHints->SortIfNeedBe();
        int nPos = m_pSwpHints->GetLastPosSortedByEnd(nIdx);
        for ( ; nPos >= 0; --nPos)
        {
            SwTextAttr *pTmp = m_pSwpHints->GetSortedByEnd( nPos );
            const sal_Int32 *pEnd = pTmp->GetEnd();
            if( !pEnd )
                continue;
            assert( *pEnd <= nIdx );
            if( nIdx != *pEnd )
                break;
            if( bFlag != pTmp->DontExpand() && !pTmp->IsLockExpandFlag()
                     && *pEnd > pTmp->GetStart())
            {
                bRet = true;
                m_pSwpHints->NoteInHistory( pTmp );
                pTmp->SetDontExpand( bFlag );
            }
        }
    }
    return bRet;
}

static bool lcl_GetTextAttrDefault(sal_Int32 nIndex, sal_Int32 nHintStart, sal_Int32 nHintEnd)
{
    return ((nHintStart <= nIndex) && (nIndex <  nHintEnd));
}
static bool lcl_GetTextAttrExpand(sal_Int32 nIndex, sal_Int32 nHintStart, sal_Int32 nHintEnd)
{
    return ((nHintStart <  nIndex) && (nIndex <= nHintEnd));
}
static bool lcl_GetTextAttrParent(sal_Int32 nIndex, sal_Int32 nHintStart, sal_Int32 nHintEnd)
{
    return ((nHintStart <  nIndex) && (nIndex <  nHintEnd));
}

static void
lcl_GetTextAttrs(
    std::vector<SwTextAttr *> *const pVector,
    SwTextAttr **const ppTextAttr,
    SwpHints const *const pSwpHints,
    sal_Int32 const nIndex, sal_uInt16 const nWhich,
    ::sw::GetTextAttrMode const eMode)
{
    assert(nWhich >= RES_TXTATR_BEGIN && nWhich < RES_TXTATR_END);
    if (!pSwpHints)
        return;
    size_t const nSize = pSwpHints->Count();
    sal_Int32 nPreviousIndex(0); // index of last hint with nWhich
    bool (*pMatchFunc)(sal_Int32, sal_Int32, sal_Int32)=nullptr;
    switch (eMode)
    {
        case ::sw::GetTextAttrMode::Default: pMatchFunc = &lcl_GetTextAttrDefault;
        break;
        case ::sw::GetTextAttrMode::Expand:  pMatchFunc = &lcl_GetTextAttrExpand;
        break;
        case ::sw::GetTextAttrMode::Parent:  pMatchFunc = &lcl_GetTextAttrParent;
        break;
        default: assert(false);
    }

    for( size_t i = pSwpHints->GetFirstPosSortedByWhichAndStart(nWhich); i < nSize; ++i )
    {
        SwTextAttr *const pHint = pSwpHints->GetSortedByWhichAndStart(i);
        if (pHint->Which() != nWhich)
            break// hints are sorted by which&start, so we are done...

        sal_Int32 const nHintStart = pHint->GetStart();
        if (nIndex < nHintStart)
            break// hints are sorted by which&start, so we are done...

        sal_Int32 const*const pEndIdx = pHint->GetEnd();
        // cannot have hint with no end and no dummy char
        assert(pEndIdx || pHint->HasDummyChar());
        // If EXPAND is set, simulate the text input behavior, i.e.
--> --------------------

--> maximum size reached

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

Messung V0.5
C=90 H=89 G=89

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