/* -*- 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 <config_wasm_strip.h>
#include <hintids.hxx>
#include <editeng/protitem.hxx>
#include <osl/diagnose.h>
#include <tools/gen.hxx>
#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
#include <com/sun/star/i18n/XBreakIterator.hpp>
#include <fmtcntnt.hxx>
#include <fmtanchr.hxx>
#include <frmfmt.hxx>
#include <txtftn.hxx>
#include <ftnfrm.hxx>
#include <doc.hxx>
#include <node.hxx>
#include <ndindex.hxx>
#include <numrule.hxx>
#include <swtable.hxx>
#include <ndtxt.hxx>
#include <pam.hxx>
#include <section.hxx>
#include <cntfrm.hxx>
#include <flyfrm.hxx>
#include <txtfrm.hxx>
#include <tabfrm.hxx>
#include <viewsh.hxx>
#include <paratr.hxx>
#include <ftnidx.hxx>
#include <fmtftn.hxx>
#include <fmthdft.hxx>
#include <frmatr.hxx>
#include <fmtautofmt.hxx>
#include <frmtool.hxx>
#include <pagefrm.hxx>
#include <node2lay.hxx>
#include <pagedesc.hxx>
#include <fmtpdsc.hxx>
#include <breakit.hxx>
#include <SwStyleNameMapper.hxx>
#include <scriptinfo.hxx>
#include <rootfrm.hxx>
#include <istyleaccess.hxx>
#include <IDocumentListItems.hxx>
#include <DocumentSettingManager.hxx>
#include <IDocumentLinksAdministration.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <calbck.hxx>
#include <ndole.hxx>
#include <memory>
#include <swcrsr.hxx>
#include <hints.hxx>
#include <frameformats.hxx>
#include <OnlineAccessibilityCheck.hxx>
#ifdef DBG_UTIL
#include <sal/backtrace.hxx>
#endif
 
using namespace ::com::sun::star::i18n;
 
namespace sw
{
 
void AccessibilityCheckStatus::reset()
{
    pCollection.reset();
}
 
}
 
/*
 * Some local helper functions for the attribute set handle of a content node.
 * Since the attribute set of a content node may not be modified directly,
 * we always have to create a new SwAttrSet, do the modifications, and get
 * a new handle from the style access
 */
 
namespace AttrSetHandleHelper
{
 
static void GetNewAutoStyle( std::shared_ptr<const SwAttrSet>& rpAttrSet,
                      const SwContentNode& rNode,
                      SwAttrSet const & rNewAttrSet )
{
    if( rNode.GetModifyAtAttr() )
        const_cast<SwAttrSet*>(rpAttrSet.get())->SetModifyAtAttr( nullptr );
    IStyleAccess& rSA = rpAttrSet->GetPool()->GetDoc()->GetIStyleAccess();
    rpAttrSet = rSA.getAutomaticStyle( rNewAttrSet, rNode.IsTextNode() ?
                                                     IStyleAccess::AUTO_STYLE_PARA :
                                                     IStyleAccess::AUTO_STYLE_NOTXT );
    const bool bSetModifyAtAttr = const_cast<SwAttrSet*>(rpAttrSet.get())->SetModifyAtAttr( &rNode );
    rNode.SetModifyAtAttr( bSetModifyAtAttr );
}
 
static void SetParent( std::shared_ptr<const SwAttrSet>& rpAttrSet,
                const SwContentNode& rNode,
                const SwFormat* pParentFormat,
                const SwFormat* pConditionalFormat )
{
    OSL_ENSURE( rpAttrSet, "no SwAttrSet" );
    OSL_ENSURE( pParentFormat || !pConditionalFormat, "ConditionalFormat without ParentFormat?" );
 
    const SwAttrSet* pParentSet = pParentFormat ? &pParentFormat->GetAttrSet() : nullptr;
 
    if ( pParentSet == rpAttrSet->GetParent() )
        return;
 
    SwAttrSet aNewSet( *rpAttrSet );
    aNewSet.SetParent( pParentSet );
    aNewSet.ClearItem( RES_FRMATR_STYLE_NAME );
    aNewSet.ClearItem( RES_FRMATR_CONDITIONAL_STYLE_NAME );
 
    if ( pParentFormat )
    {
        OUString sVal;
        SwStyleNameMapper::FillProgName( pParentFormat->GetName(), sVal, SwGetPoolIdFromName::TxtColl );
        const SfxStringItem aAnyFormatColl( RES_FRMATR_STYLE_NAME, sVal );
        aNewSet.Put( aAnyFormatColl );
 
        if ( pConditionalFormat != pParentFormat )
            SwStyleNameMapper::FillProgName( pConditionalFormat->GetName(), sVal, SwGetPoolIdFromName::TxtColl );
 
        const SfxStringItem aFormatColl( RES_FRMATR_CONDITIONAL_STYLE_NAME, sVal );
        aNewSet.Put( aFormatColl );
    }
 
    GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
}
 
static const SfxPoolItem* Put( std::shared_ptr<const SwAttrSet>& rpAttrSet,
                        const SwContentNode& rNode,
                        const SfxPoolItem& rAttr )
{
    SwAttrSet aNewSet( *rpAttrSet );
    const SfxPoolItem* pRet = aNewSet.Put( rAttr );
    if ( pRet )
        GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
    return pRet;
}
 
static bool Put( std::shared_ptr<const SwAttrSet>& rpAttrSet, const SwContentNode& rNode,
         const SfxItemSet& rSet )
{
    SwAttrSet aNewSet( *rpAttrSet );
 
    // #i76273# Robust
    std::optional<SfxItemSetFixed<RES_FRMATR_STYLE_NAME, RES_FRMATR_CONDITIONAL_STYLE_NAME>> pStyleNames;
    if ( SfxItemState::SET == rSet.GetItemState( RES_FRMATR_STYLE_NAME, false ) )
    {
        pStyleNames.emplace( *aNewSet.GetPool() );
        pStyleNames->Put( aNewSet );
    }
 
    const bool bRet = aNewSet.Put( rSet );
 
    // #i76273# Robust
    if ( pStyleNames )
    {
        aNewSet.Put( *pStyleNames );
    }
 
    if ( bRet )
        GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
 
    return bRet;
}
 
static bool Put_BC( std::shared_ptr<const SwAttrSet>& rpAttrSet,
            const SwContentNode& rNode, const SfxPoolItem& rAttr,
            SwAttrSet* pOld, SwAttrSet* pNew )
{
    SwAttrSet aNewSet( *rpAttrSet );
 
    // for a correct broadcast, we need to do a SetModifyAtAttr with the items
    // from aNewSet. The 'regular' SetModifyAtAttr is done in GetNewAutoStyle
    if( rNode.GetModifyAtAttr() )
        aNewSet.SetModifyAtAttr( &rNode );
 
    const bool bRet = aNewSet.Put_BC( rAttr, pOld, pNew );
 
    if ( bRet )
        GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
 
    return bRet;
}
 
static bool Put_BC( std::shared_ptr<const SwAttrSet>& rpAttrSet,
            const SwContentNode& rNode, const SfxItemSet& rSet,
            SwAttrSet* pOld, SwAttrSet* pNew )
{
    SwAttrSet aNewSet( *rpAttrSet );
 
    // #i76273# Robust
    std::optional<SfxItemSetFixed<RES_FRMATR_STYLE_NAME, RES_FRMATR_CONDITIONAL_STYLE_NAME>> pStyleNames;
    if ( SfxItemState::SET == rSet.GetItemState( RES_FRMATR_STYLE_NAME, false ) )
    {
        pStyleNames.emplace( *aNewSet.GetPool() );
        pStyleNames->Put( aNewSet );
    }
 
    // for a correct broadcast, we need to do a SetModifyAtAttr with the items
    // from aNewSet. The 'regular' SetModifyAtAttr is done in GetNewAutoStyle
    if( rNode.GetModifyAtAttr() )
        aNewSet.SetModifyAtAttr( &rNode );
 
    const bool bRet = aNewSet.Put_BC( rSet, pOld, pNew );
 
    // #i76273# Robust
    if ( pStyleNames )
    {
        aNewSet.Put( *pStyleNames );
    }
 
    if ( bRet )
        GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
 
    return bRet;
}
 
static sal_uInt16 ClearItem_BC( std::shared_ptr<const SwAttrSet>& rpAttrSet,
                     const SwContentNode& rNode, sal_uInt16 nWhich,
                     SwAttrSet* pOld, SwAttrSet* pNew )
{
    SwAttrSet aNewSet( *rpAttrSet );
    if( rNode.GetModifyAtAttr() )
        aNewSet.SetModifyAtAttr( &rNode );
    const sal_uInt16 nRet = aNewSet.ClearItem_BC( nWhich, pOld, pNew );
    if ( nRet )
        GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
    return nRet;
}
 
static sal_uInt16 ClearItem_BC( std::shared_ptr<const SwAttrSet>& rpAttrSet,
                     const SwContentNode& rNode,
                     sal_uInt16 nWhich1, sal_uInt16 nWhich2,
                     SwAttrSet* pOld, SwAttrSet* pNew )
{
    SwAttrSet aNewSet( *rpAttrSet );
    if( rNode.GetModifyAtAttr() )
        aNewSet.SetModifyAtAttr( &rNode );
    const sal_uInt16 nRet = aNewSet.ClearItem_BC( nWhich1, nWhich2, pOld, pNew );
    if ( nRet )
        GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
    return nRet;
}
 
}
 
/** Returns the section level at the position given by aIndex.
 *
 * We use the following logic:
 * S = Start, E = End, C = ContentNode
 * Level   0 = E
 *         1 = S E
 *         2 = SC
 *
 * All EndNodes of the BaseSection have level 0
 * All StartNodes of the BaseSection have level 1
 */
sal_uInt16 SwNode::GetSectionLevel() const
{
    // EndNode of a BaseSection? They are always 0!
    if( IsEndNode() && SwNodeOffset(0) == m_pStartOfSection->StartOfSectionIndex() )
        return 0;
 
    sal_uInt16 nLevel;
    const SwNode* pNode = IsStartNode() ? this : m_pStartOfSection;
    for( nLevel = 1; SwNodeOffset(0) != pNode->StartOfSectionIndex(); ++nLevel )
        pNode = pNode->m_pStartOfSection;
    return IsEndNode() ? nLevel-1 : nLevel;
}
 
#ifdef DBG_UTIL
tools::Long SwNode::s_nSerial = 0;
#endif
 
/// only used by SwContentNodeTmp in SwTextNode::Update
SwNode::SwNode()
    : m_nNodeType( SwNodeType::Start )
    , m_nAFormatNumLvl( 0 )
    , m_bIgnoreDontExpand( false)
    , m_eMerge(Merge::None)
#ifdef DBG_UTIL
    , m_nSerial( s_nSerial++)
#endif
    , m_pStartOfSection( nullptr )
{}
 
SwNode::SwNode( const SwNode& rWhere, const SwNodeType nNdType )
    : m_nNodeType( nNdType )
    , m_nAFormatNumLvl( 0 )
    , m_bIgnoreDontExpand( false)
    , m_eMerge(Merge::None)
#ifdef DBG_UTIL
    , m_nSerial( s_nSerial++)
#endif
    , m_pStartOfSection( nullptr )
{
    SwNodeOffset nWhereOffset = rWhere.GetIndex();
    if( !nWhereOffset )
        return;
 
    SwNodes& rNodes = const_cast<SwNodes&> (rWhere.GetNodes());
    SwNode* pNd = rNodes[ nWhereOffset -1 ];
    rNodes.InsertNode( this, nWhereOffset );
    m_pStartOfSection = pNd->GetStartNode();
    if( nullptr == m_pStartOfSection )
    {
        m_pStartOfSection = pNd->m_pStartOfSection;
        if( pNd->GetEndNode() )     // Skip EndNode ? Section
        {
            pNd = m_pStartOfSection;
            m_pStartOfSection = pNd->m_pStartOfSection;
        }
    }
}
 
/** Inserts a node into the rNodes array at the rWhere position
 *
 * @param rNodes the variable array in that the node will be inserted
 * @param nPos position within the array where the node will be inserted
 * @param nNdType the type of node to insert
 */
SwNode::SwNode( SwNodes& rNodes, SwNodeOffset nPos, const SwNodeType nNdType )
    : m_nNodeType( nNdType )
    , m_nAFormatNumLvl( 0 )
    , m_bIgnoreDontExpand( false)
    , m_eMerge(Merge::None)
#ifdef DBG_UTIL
    , m_nSerial( s_nSerial++)
#endif
    , m_pStartOfSection( nullptr )
{
    if( !nPos )
        return;
 
    SwNode* pNd = rNodes[ nPos - 1 ];
    rNodes.InsertNode( this, nPos );
    m_pStartOfSection = pNd->GetStartNode();
    if( nullptr == m_pStartOfSection )
    {
        m_pStartOfSection = pNd->m_pStartOfSection;
        if( pNd->GetEndNode() )     // Skip EndNode ? Section!
        {
            pNd = m_pStartOfSection;
            m_pStartOfSection = pNd->m_pStartOfSection;
        }
    }
}
 
SwNode::~SwNode()
{
    assert(m_aAnchoredFlys.empty() || GetDoc().IsInDtor()); // must all be deleted
    InvalidateInSwCache(RES_OBJECTDYING);
    assert(!IsInCache());
}
 
/// Find the TableNode in which it is located.
/// If we're not in a table: return 0
SwTableNode* SwNode::FindTableNode()
{
    if( IsTableNode() )
        return GetTableNode();
    SwStartNode* pTmp = m_pStartOfSection;
    while( !pTmp->IsTableNode() && pTmp->GetIndex() )
        pTmp = pTmp->m_pStartOfSection;
    return pTmp->GetTableNode();
}
 
/// Is the node located in the visible area of the Shell?
bool SwNode::IsInVisibleArea( SwViewShell const * pSh ) const
{
    bool bRet = false;
    const SwContentNode* pNd;
 
    if( SwNodeType::Start & m_nNodeType )
    {
        SwNodeIndex aIdx( *this );
        pNd = SwNodes::GoNext(&aIdx);
    }
    else if( SwNodeType::End & m_nNodeType )
    {
        SwNodeIndex aIdx( *EndOfSectionNode() );
        pNd = SwNodes::GoPrevious( &aIdx );
    }
    else
        pNd = GetContentNode();
 
    if( !pSh )
        // Get the Shell from the Doc
        pSh = GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
 
    if( pSh )
    {
        const SwFrame* pFrame;
        if (pNd && nullptr != (pFrame = pNd->getLayoutFrame(pSh->GetLayout(), nullptr, nullptr)))
        {
 
            if ( pFrame->IsInTab() )
                pFrame = pFrame->FindTabFrame();
 
            if( !pFrame->isFrameAreaDefinitionValid() )
            {
                do
                {
                    pFrame = pFrame->FindPrev();
                }
                while ( pFrame && !pFrame->isFrameAreaDefinitionValid() );
            }
 
            if( !pFrame || pSh->VisArea().Overlaps( pFrame->getFrameArea() ) )
                bRet = true;
        }
    }
 
    return bRet;
}
 
bool SwNode::IsInProtectSect() const
{
    const SwNode* pNd = SwNodeType::Section == m_nNodeType ? m_pStartOfSection : this;
    const SwSectionNode* pSectNd = pNd->FindSectionNode();
    return pSectNd && pSectNd->GetSection().IsProtectFlag();
}
 
/// Does the node contain anything protected?
/// I.e.: Area/Frame/Table rows/... including the Anchor for
/// Frames/Footnotes/...
bool SwNode::IsProtect() const
{
    const SwNode* pNd = SwNodeType::Section == m_nNodeType ? m_pStartOfSection : this;
    const SwStartNode* pSttNd = pNd->FindSectionNode();
    if( pSttNd && static_cast<const SwSectionNode*>(pSttNd)->GetSection().IsProtectFlag() )
        return true;
 
    pSttNd = FindTableBoxStartNode();
    if( nullptr != pSttNd )
    {
        SwContentFrame* pCFrame;
        if( IsContentNode() && nullptr != (pCFrame = static_cast<const SwContentNode*>(this)->getLayoutFrame( GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() ) ))
            return pCFrame->IsProtected();
 
        const SwTableBox* pBox = pSttNd->FindTableNode()->GetTable().
                                        GetTableBox( pSttNd->GetIndex() );
        //Robust #149568
        if( pBox && pBox->GetFrameFormat()->GetProtect().IsContentProtected() )
            return true;
    }
 
    SwFrameFormat* pFlyFormat = GetFlyFormat();
    if( pFlyFormat )
    {
        if (pFlyFormat->GetProtect().IsContentProtected())
            return true;
        const SwFormatAnchor& rAnchor = pFlyFormat->GetAnchor();
        const SwNode* pAnchorNode = rAnchor.GetAnchorNode();
        if (!pAnchorNode)
            return false;
        return pAnchorNode != this && pAnchorNode->IsProtect();
    }
 
    pSttNd = FindFootnoteStartNode();
    if( nullptr != pSttNd )
    {
        const SwTextFootnote* pTFootnote = GetDoc().GetFootnoteIdxs().SeekEntry(
                                *pSttNd );
        if( pTFootnote )
            return pTFootnote->GetTextNode().IsProtect();
    }
 
    return false;
}
 
/// Find the PageDesc that is used to format this node. If the Layout is available,
/// we search through that. Else we can only do it the hard way by searching onwards through the nodes.
const SwPageDesc* SwNode::FindPageDesc( SwNodeOffset* pPgDescNdIdx ) const
{
    if ( !GetNodes().IsDocNodes() )
    {
        return nullptr;
    }
 
    const SwPageDesc* pPgDesc = nullptr;
 
    const SwContentNode* pNode;
    if( SwNodeType::Start & m_nNodeType )
    {
        SwNodeIndex aIdx( *this );
        pNode = SwNodes::GoNext(&aIdx);
    }
    else if( SwNodeType::End & m_nNodeType )
    {
        SwNodeIndex aIdx( *EndOfSectionNode() );
        pNode = SwNodes::GoPrevious( &aIdx );
    }
    else
    {
        pNode = GetContentNode();
        if( pNode )
            pPgDesc = pNode->GetAttr( RES_PAGEDESC ).GetPageDesc();
    }
 
    // Are we going through the layout?
    if( !pPgDesc )
    {
        const SwFrame* pFrame;
        const SwPageFrame* pPage;
        if (pNode && nullptr != (pFrame = pNode->getLayoutFrame(pNode->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr)) &&
            nullptr != ( pPage = pFrame->FindPageFrame() ) )
        {
            pPgDesc = pPage->GetPageDesc();
            if ( pPgDescNdIdx )
            {
                *pPgDescNdIdx = pNode->GetIndex();
            }
        }
    }
 
    if( !pPgDesc )
    {
        // Thus via the nodes array
        const SwDoc& rDoc = GetDoc();
        const SwNode* pNd = this;
        const SwStartNode* pSttNd;
        if( pNd->GetIndex() < GetNodes().GetEndOfExtras().GetIndex() &&
            nullptr != ( pSttNd = pNd->FindFlyStartNode() ) )
        {
            // Find the right Anchor first
            const SwFrameFormat* pFormat = nullptr;
            const sw::SpzFrameFormats& rFormats = *rDoc.GetSpzFrameFormats();
 
            for(sw::SpzFrameFormat* pSpz: rFormats)
            {
                const SwFormatContent& rContent = pSpz->GetContent();
                if( rContent.GetContentIdx() &&
                    &rContent.GetContentIdx()->GetNode() == static_cast<SwNode const *>(pSttNd) )
                {
                    pFormat = pSpz;
                    break;
                }
            }
 
            if( pFormat )
            {
                const SwFormatAnchor* pAnchor = &pFormat->GetAnchor();
                if ((RndStdIds::FLY_AT_PAGE != pAnchor->GetAnchorId()) &&
                    pAnchor->GetAnchorNode() )
                {
                    pNd = pAnchor->GetAnchorNode();
                    const SwNode* pFlyNd = pNd->FindFlyStartNode();
                    while( pFlyNd )
                    {
                        // Get up through the Anchor
                        size_t n;
                        for( n = 0; n < rFormats.size(); ++n )
                        {
                            const SwFrameFormat* pFrameFormat = rFormats[ n ];
                            const SwNodeIndex* pIdx = pFrameFormat->GetContent().
                                                        GetContentIdx();
                            if( pIdx && pFlyNd == &pIdx->GetNode() )
                            {
                                if( pFormat == pFrameFormat )
                                {
                                    pNd = pFlyNd;
                                    pFlyNd = nullptr;
                                    break;
                                }
                                pAnchor = &pFrameFormat->GetAnchor();
                                if ((RndStdIds::FLY_AT_PAGE == pAnchor->GetAnchorId()) ||
                                    !pAnchor->GetAnchorNode() )
                                {
                                    pFlyNd = nullptr;
                                    break;
                                }
 
                                pFlyNd = pAnchor->GetAnchorNode()->FindFlyStartNode();
                                break;
                            }
                        }
                        if( n >= rFormats.size() )
                        {
                            OSL_ENSURE( false, "FlySection, but no Format found" );
                            return nullptr;
                        }
                    }
                }
            }
            // pNd should now contain the correct Anchor or it's still this
        }
 
        if( pNd->GetIndex() < GetNodes().GetEndOfExtras().GetIndex() )
        {
            if( pNd->GetIndex() > GetNodes().GetEndOfAutotext().GetIndex() )
            {
                pPgDesc = &rDoc.GetPageDesc( 0 );
                pNd = nullptr;
            }
            else
            {
                // Find the Body text node
                if( nullptr != ( pSttNd = pNd->FindHeaderStartNode() ) ||
                    nullptr != ( pSttNd = pNd->FindFooterStartNode() ))
                {
                    // Then find this StartNode in the PageDescs
                    sal_uInt16 nId;
                    UseOnPage eAskUse;
                    if( SwHeaderStartNode == pSttNd->GetStartNodeType())
                    {
                        nId = RES_HEADER;
                        eAskUse = UseOnPage::HeaderShare;
                    }
                    else
                    {
                        nId = RES_FOOTER;
                        eAskUse = UseOnPage::FooterShare;
                    }
 
                    for( size_t n = rDoc.GetPageDescCnt(); n && !pPgDesc; )
                    {
                        const SwPageDesc& rPgDsc = rDoc.GetPageDesc( --n );
                        const SwFrameFormat* pFormat = &rPgDsc.GetMaster();
                        int nStt = 0, nLast = 1;
                        if( !( eAskUse & rPgDsc.ReadUseOn() )) ++nLast;
 
                        for( ; nStt < nLast; ++nStt, pFormat = &rPgDsc.GetLeft() )
                        {
                            const SwFrameFormat * pHdFtFormat = nId == RES_HEADER
                                ? static_cast<SwFormatHeader const &>(
                                    pFormat->GetFormatAttr(nId)).GetHeaderFormat()
                                : static_cast<SwFormatFooter const &>(
                                    pFormat->GetFormatAttr(nId)).GetFooterFormat();
                            if( pHdFtFormat )
                            {
                                const SwFormatContent& rContent = pHdFtFormat->GetContent();
                                if( rContent.GetContentIdx() &&
                                    &rContent.GetContentIdx()->GetNode() ==
                                    static_cast<SwNode const *>(pSttNd) )
                                {
                                    pPgDesc = &rPgDsc;
                                    break;
                                }
                            }
                        }
                    }
 
                    if( !pPgDesc )
                        pPgDesc = &rDoc.GetPageDesc( 0 );
                    pNd = nullptr;
                }
                else if( nullptr != ( pSttNd = pNd->FindFootnoteStartNode() ))
                {
                    // the Anchor can only be in the Body text
                    const SwTextFootnote* pTextFootnote;
                    const SwFootnoteIdxs& rFootnoteArr = rDoc.GetFootnoteIdxs();
                    for( size_t n = 0; n < rFootnoteArr.size(); ++n )
                        if( nullptr != ( pTextFootnote = rFootnoteArr[ n ])->GetStartNode() &&
                            static_cast<SwNode const *>(pSttNd) ==
                            &pTextFootnote->GetStartNode()->GetNode() )
                        {
                            pNd = &pTextFootnote->GetTextNode();
                            break;
                        }
                }
                else
                {
                    // Can only be a page-bound Fly (or something newer).
                    // we can only return the standard here
                    OSL_ENSURE( pNd->FindFlyStartNode(),
                            "Where is this Node?" );
 
                    pPgDesc = &rDoc.GetPageDesc( 0 );
                    pNd = nullptr;
                }
            }
        }
 
        if( pNd )
        {
            SwFindNearestNode aInfo( *pNd );
            // Over all Nodes of all PageDescs
            for (SwRootFrame* pRootFrame : const_cast<SwDoc&>(rDoc).GetAllLayouts())
            {
                const SwPageFrame* pPageFrameIter = pRootFrame->GetLastPage();
                while (pPageFrameIter)
                {
                    const SwContentFrame* pContentFrame = pPageFrameIter->FindFirstBodyContent();
                    if (pContentFrame)
                    {
                        const SwFormatPageDesc& rFormatPageDesc = pContentFrame->GetPageDescItem();
                        if ( const sw::BroadcastingModify* pMod = rFormatPageDesc.GetDefinedIn() )
                        {
                            if( auto pContentNode = dynamic_cast<const SwContentNode*>( pMod) )
                                aInfo.CheckNode( *pContentNode );
                            else if( auto pFormat = dynamic_cast<const SwFormat*>( pMod) )
                                pFormat->GetInfo( aInfo );
                        }
                    }
                    pPageFrameIter = static_cast<const SwPageFrame*>(pPageFrameIter->GetPrev());
                }
            }
 
            pNd = aInfo.GetFoundNode();
            if( nullptr != pNd )
            {
                if( pNd->IsContentNode() )
                    pPgDesc = pNd->GetContentNode()->GetAttr( RES_PAGEDESC ).GetPageDesc();
                else if( pNd->IsTableNode() )
                    pPgDesc = pNd->GetTableNode()->GetTable().
                            GetFrameFormat()->GetPageDesc().GetPageDesc();
                else if( pNd->IsSectionNode() )
                    pPgDesc = pNd->GetSectionNode()->GetSection().
                            GetFormat()->GetPageDesc().GetPageDesc();
                if ( pPgDescNdIdx )
                {
                    *pPgDescNdIdx = pNd->GetIndex();
                }
            }
            if( !pPgDesc )
                pPgDesc = &rDoc.GetPageDesc( 0 );
        }
    }
    return pPgDesc;
}
 
/// If the node is located in a Fly, we return it formatted accordingly
SwFrameFormat* SwNode::GetFlyFormat() const
{
    SwFrameFormat* pRet = nullptr;
    const SwNode* pSttNd = FindFlyStartNode();
    if( pSttNd )
    {
        if( IsContentNode() )
        {
            SwContentFrame* pFrame = SwIterator<SwContentFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*static_cast<const SwContentNode*>(this)).First();
            if( pFrame && pFrame->FindFlyFrame())
                pRet = pFrame->FindFlyFrame()->GetFormat();
        }
        if( !pRet )
        {
            // The hard way through the Doc is our last way out
            const sw::SpzFrameFormats& rSpzs = *GetDoc().GetSpzFrameFormats();
            for(sw::SpzFrameFormat* pSpz: rSpzs)
            {
                // Only Writer fly frames can contain Writer nodes.
                if (pSpz->Which() != RES_FLYFRMFMT)
                    continue;
                const SwFormatContent& rContent = pSpz->GetContent();
                if( rContent.GetContentIdx() &&
                    &rContent.GetContentIdx()->GetNode() == pSttNd )
                {
                    pRet = pSpz;
                    break;
                }
            }
        }
    }
    return pRet;
}
 
SwTableBox* SwNode::GetTableBox() const
{
    SwTableBox* pBox = nullptr;
    const SwNode* pSttNd = FindTableBoxStartNode();
    if( pSttNd )
        pBox = const_cast<SwTableBox*>(pSttNd->FindTableNode()->GetTable().GetTableBox(
                                                    pSttNd->GetIndex() ));
    return pBox;
}
 
SwStartNode* SwNode::FindSttNodeByType( SwStartNodeType eTyp )
{
    SwStartNode* pTmp = IsStartNode() ? static_cast<SwStartNode*>(this) : m_pStartOfSection;
 
    while( eTyp != pTmp->GetStartNodeType() && pTmp->GetIndex() )
        pTmp = pTmp->m_pStartOfSection;
    return eTyp == pTmp->GetStartNodeType() ? pTmp : nullptr;
}
 
const SwTextNode* SwNode::FindOutlineNodeOfLevel(sal_uInt8 const nLvl,
        SwRootFrame const*const pLayout) const
{
    const SwTextNode* pRet = nullptr;
    const SwOutlineNodes& rONds = GetNodes().GetOutLineNds();
    if( MAXLEVEL > nLvl && !rONds.empty() )
    {
        SwOutlineNodes::size_type nPos;
        bool bCheckFirst = false;
        if (!rONds.Seek_Entry(this, &nPos))
        {
            if (nPos == 0)
                bCheckFirst = true;
        }
        else
        {
            ++nPos;
        }
 
        if( bCheckFirst )
        {
            // The first OutlineNode comes after the one asking.
            // Test if both are on the same page.
            // If not it's invalid.
            for (nPos = 0; nPos < rONds.size(); ++nPos)
            {
                pRet = rONds[nPos]->GetTextNode();
                if (!pLayout || sw::IsParaPropsNode(*pLayout, *pRet))
                {
                    break;
                }
            }
            if (nPos == rONds.size())
            {
                return nullptr;
            }
 
            const SwContentNode* pCNd = GetContentNode();
 
            Point aPt( 0, 0 );
            std::pair<Point, bool> const tmp(aPt, false);
            assert(pRet);
            const SwFrame* pFrame = pRet->getLayoutFrame(pRet->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp),
                       * pMyFrame = pCNd ? pCNd->getLayoutFrame(pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp) : nullptr;
            const SwPageFrame* pPgFrame = pFrame ? pFrame->FindPageFrame() : nullptr;
            if( pPgFrame && pMyFrame &&
                pPgFrame->getFrameArea().Top() > pMyFrame->getFrameArea().Top() )
            {
                // The one asking precedes the Page, thus its invalid
                pRet = nullptr;
            }
        }
        else
        {
            for ( ; 0 < nPos; --nPos)
            {
                SwTextNode const*const pNode = rONds[nPos - 1]->GetTextNode();
                if ((nPos == 1 /*as before*/ || pNode->GetAttrOutlineLevel() - 1 <= nLvl)
                    && (!pLayout || sw::IsParaPropsNode(*pLayout, *pNode)))
                {
                    pRet = pNode;
                    break;
                }
            }
        }
    }
    return pRet;
}
 
static bool IsValidNextPrevNd( const SwNode& rNd )
{
    return SwNodeType::Table == rNd.GetNodeType() ||
           ( SwNodeType::ContentMask & rNd.GetNodeType() ) ||
            ( SwNodeType::End == rNd.GetNodeType() && rNd.StartOfSectionNode() &&
            SwNodeType::Table == rNd.StartOfSectionNode()->GetNodeType() );
}
 
sal_uInt8 SwNode::HasPrevNextLayNode() const
{
    // assumption: <this> node is a node inside the document nodes array section.
 
    sal_uInt8 nRet = 0;
    if( IsValidNextPrevNd( *this ))
    {
        SwNodeIndex aIdx( *this, -1 );
        // #i77805# - skip section start and end nodes
        while ( aIdx.GetNode().IsSectionNode() ||
                ( aIdx.GetNode().IsEndNode() &&
                  aIdx.GetNode().StartOfSectionNode()->IsSectionNode() ) )
        {
            --aIdx;
        }
        if( IsValidNextPrevNd( aIdx.GetNode() ))
            nRet |= ND_HAS_PREV_LAYNODE;
        // #i77805# - skip section start and end nodes
        aIdx.Assign(*this, +1);
        while ( aIdx.GetNode().IsSectionNode() ||
                ( aIdx.GetNode().IsEndNode() &&
                  aIdx.GetNode().StartOfSectionNode()->IsSectionNode() ) )
        {
            ++aIdx;
        }
        if( IsValidNextPrevNd( aIdx.GetNode() ))
            nRet |= ND_HAS_NEXT_LAYNODE;
    }
    return nRet;
}
 
void SwNode::dumpAsXml(xmlTextWriterPtr pWriter) const
{
    const char* pName = "???";
    switch (GetNodeType())
    {
    case SwNodeType::End:
        pName = "end";
        break;
    case SwNodeType::Start:
    case SwNodeType::Text:
    case SwNodeType::Ole:
        abort(); // overridden
    case SwNodeType::Table:
        pName = "table";
        break;
    case SwNodeType::Grf:
        pName = "grf";
        break;
    default: break;
    }
    (void)xmlTextWriterStartElement(pWriter, BAD_CAST(pName));
 
    (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
    (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("type"), BAD_CAST(OString::number(static_cast<sal_uInt8>(GetNodeType())).getStr()));
    (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), BAD_CAST(OString::number(sal_Int32(GetIndex())).getStr()));
 
    switch (GetNodeType())
    {
        case SwNodeType::Grf:
        {
            auto pNoTextNode = static_cast<const SwNoTextNode*>(this);
            const tools::PolyPolygon* pContour = pNoTextNode->HasContour();
            if (pContour)
            {
                (void)xmlTextWriterStartElement(pWriter, BAD_CAST("pContour"));
                for (sal_uInt16 i = 0; i < pContour->Count(); ++i)
                {
                    (void)xmlTextWriterStartElement(pWriter, BAD_CAST("polygon"));
                    (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"),
                                                BAD_CAST(OString::number(i).getStr()));
                    const tools::Polygon& rPolygon = pContour->GetObject(i);
                    for (sal_uInt16 j = 0; j < rPolygon.GetSize(); ++j)
                    {
                        (void)xmlTextWriterStartElement(pWriter, BAD_CAST("point"));
                        (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"),
                                                    BAD_CAST(OString::number(j).getStr()));
                        const Point& rPoint = rPolygon.GetPoint(j);
                        (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("x"),
                                                    BAD_CAST(OString::number(rPoint.X()).getStr()));
                        (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("y"),
                                                    BAD_CAST(OString::number(rPoint.Y()).getStr()));
                        (void)xmlTextWriterEndElement(pWriter);
                    }
                    (void)xmlTextWriterEndElement(pWriter);
                }
                (void)xmlTextWriterEndElement(pWriter);
            }
        }
        break;
        default:
            break;
    }
 
    (void)xmlTextWriterEndElement(pWriter);
    if (GetNodeType() == SwNodeType::End)
        (void)xmlTextWriterEndElement(pWriter); // end start node
}
 
SwStartNode::SwStartNode( const SwNode& rWhere, const SwNodeType nNdType,
                            SwStartNodeType eSttNd )
    : SwNode( rWhere, nNdType ), m_eStartNodeType( eSttNd )
{
    if( !rWhere.GetIndex() )
    {
        SwNodes& rNodes = const_cast<SwNodes&> (rWhere.GetNodes());
        rNodes.InsertNode( this, rWhere.GetIndex() );
        m_pStartOfSection = this;
    }
    // Just do this temporarily until the EndNode is inserted
    m_pEndOfSection = reinterpret_cast<SwEndNode*>(this);
}
 
SwStartNode::SwStartNode( SwNodes& rNodes, SwNodeOffset nPos )
    : SwNode( rNodes, nPos, SwNodeType::Start ), m_eStartNodeType( SwNormalStartNode )
{
    if( !nPos )
    {
        rNodes.InsertNode( this, nPos );
        m_pStartOfSection = this;
    }
    // Just do this temporarily until the EndNode is inserted
    m_pEndOfSection = reinterpret_cast<SwEndNode*>(this);
}
 
void SwStartNode::CheckSectionCondColl() const
{
    SwNodeIndex aIdx( *this );
    SwNodeOffset nEndIdx = EndOfSectionIndex();
    SwContentNode* pCNd;
    while (nullptr != (pCNd = SwNodes::GoNext(&aIdx)) && pCNd->GetIndex() < nEndIdx)
        pCNd->ChkCondColl();
}
 
void SwStartNode::dumpAsXml(xmlTextWriterPtr pWriter) const
{
    const char* pName = "???";
    switch(GetStartNodeType())
    {
        case SwNormalStartNode:
            pName = "start";
            break;
        case SwTableBoxStartNode:
            pName = "tablebox";
            break;
        case SwFlyStartNode:
            pName = "fly";
            break;
        case SwFootnoteStartNode:
            pName = "footnote";
            break;
        case SwHeaderStartNode:
            pName = "header";
            break;
        case SwFooterStartNode:
            pName = "footer";
            break;
    }
 
    (void)xmlTextWriterStartElement(pWriter, BAD_CAST(pName));
    (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
    (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("type"), BAD_CAST(OString::number(static_cast<sal_uInt8>(GetNodeType())).getStr()));
    (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), BAD_CAST(OString::number(sal_Int32(GetIndex())).getStr()));
 
    // (void)xmlTextWriterEndElement(pWriter); - it is a start node, so don't end, will make xml better nested
}
 
 
/** Insert a node into the array
 *
 * The StartOfSection pointer is set to the given node.
 *
 * The EndOfSection pointer of the corresponding start node is set to this node.
 *
 * @param rWhere position where the node should be inserted
 * @param rSttNd the start note of the section
 */
 
SwEndNode::SwEndNode( const SwNode& rWhere, SwStartNode& rSttNd )
    : SwNode( rWhere, SwNodeType::End )
{
    m_pStartOfSection = &rSttNd;
    m_pStartOfSection->m_pEndOfSection = this;
}
 
SwEndNode::SwEndNode( SwNodes& rNds, SwNodeOffset nPos, SwStartNode& rSttNd )
    : SwNode( rNds, nPos, SwNodeType::End )
{
    m_pStartOfSection = &rSttNd;
    m_pStartOfSection->m_pEndOfSection = this;
}
 
/// only used by SwContentNodeTmp in SwTextNode::Update
SwContentNode::SwContentNode()
    : SwNode()
    , m_aCondCollListener( *this )
    , m_pCondColl( nullptr )
    , mbSetModifyAtAttr( false )
{}
 
SwContentNode::SwContentNode( const SwNode& rWhere, const SwNodeType nNdType,
                            SwFormatColl *pColl )
    : SwNode( rWhere, nNdType )
    , m_aCondCollListener( *this )
    , m_pCondColl( nullptr )
    , mbSetModifyAtAttr( false )
{
    if(pColl)
        pColl->Add(*this);
}
 
SwContentNode::~SwContentNode()
{
    // The base class SwClient of SwFrame excludes itself from the dependency list!
    // Thus, we need to delete all Frames in the dependency list.
    if (!IsTextNode()) // see ~SwTextNode
    {
        DelFrames(nullptr);
    }
 
    m_aCondCollListener.EndListeningAll();
    m_pCondColl = nullptr;
 
    if ( mpAttrSet && mbSetModifyAtAttr )
        const_cast<SwAttrSet*>(mpAttrSet.get())->SetModifyAtAttr( nullptr );
    InvalidateInSwCache(RES_OBJECTDYING);
}
 
void SwContentNode::UpdateAttr(const SwUpdateAttr& rUpdate)
{
    if (GetNodes().IsDocNodes()
            && IsTextNode()
            && RES_ATTRSET_CHG == rUpdate.getWhichAttr())
        static_cast<SwTextNode*>(this)->SetCalcHiddenCharFlags();
    CallSwClientNotify(sw::LegacyModifyHint(&rUpdate, &rUpdate));
}
 
void SwContentNode::SwClientNotify( const SwModify&, const SfxHint& rHint)
{
    if (rHint.GetId() == SfxHintId::SwLegacyModify)
    {
        auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
        const sal_uInt16 nWhich = pLegacyHint->GetWhich();
        InvalidateInSwCache(nWhich);
 
        bool bSetParent = false;
        bool bCalcHidden = false;
        SwFormatColl* pFormatColl = nullptr;
        switch(nWhich)
        {
            case RES_OBJECTDYING:
                {
                    SwFormat* pFormat = pLegacyHint->m_pNew
                            ? static_cast<SwFormat*>(static_cast<const SwPtrMsgPoolItem*>(pLegacyHint->m_pNew)->pObject)
                            : nullptr;
                    // Do not mangle pointers if it is the upper-most format!
                    if(pFormat && GetRegisteredIn() == pFormat)
                    {
                        // As ~SwFormat calls CheckRegistrationFormat before
                        // ~SwModify, which sends the RES_OBJECTDYING, we should never
                        // reach this point.
                        assert(false);
                    }
                }
                break;
 
            case RES_FMT_CHG:
                // If the Format parent was switched, register the Attrset at the new one
                // Skip own Modify!
                if(GetpSwAttrSet()
                        && pLegacyHint->m_pNew
                        && static_cast<const SwFormatChg*>(pLegacyHint->m_pNew)->pChangedFormat == GetRegisteredIn())
                {
                    pFormatColl = GetFormatColl();
                    bSetParent = true;
                }
                break;
 
            case RES_ATTRSET_CHG:
                if (GetNodes().IsDocNodes()
                        && IsTextNode()
                        && pLegacyHint->m_pOld
                        && SfxItemState::SET == pLegacyHint->m_pOld->StaticWhichCast(RES_ATTRSET_CHG).GetChgSet()->GetItemState(RES_CHRATR_HIDDEN, false))
                    bCalcHidden = true;
                break;
 
            case RES_UPDATE_ATTR:
                // RES_UPDATE_ATTR _should_ always contain a SwUpdateAttr hint in old and new.
                // However, faking one with just a basic SfxPoolItem setting a WhichId has been observed.
                // This makes the crude "WhichId" type divert from the true type, which is bad.
                // Thus we are asserting here, but falling back to an proper
                // hint instead. so that we at least will not spread such poison further.
#ifdef DBG_UTIL
                if (!SfxPoolItem::areSame(pLegacyHint->m_pNew, pLegacyHint->m_pOld))
                {
                    auto pBT = sal::backtrace_get(20);
                    SAL_WARN("sw.core", "UpdateAttr not matching! " << sal::backtrace_to_string(pBT.get()));
                }
#endif
                assert(SfxPoolItem::areSame(pLegacyHint->m_pNew, pLegacyHint->m_pOld));
                assert(dynamic_cast<const SwUpdateAttr*>(pLegacyHint->m_pNew));
                const SwUpdateAttr aFallbackHint(0,0,0);
                const SwUpdateAttr& rUpdateAttr = pLegacyHint->m_pNew ? *static_cast<const SwUpdateAttr*>(pLegacyHint->m_pNew) : aFallbackHint;
                UpdateAttr(rUpdateAttr);
                return;
        }
        if(bSetParent && GetpSwAttrSet())
            AttrSetHandleHelper::SetParent(mpAttrSet, *this, pFormatColl, pFormatColl);
        if(bCalcHidden)
            static_cast<SwTextNode*>(this)->SetCalcHiddenCharFlags();
        CallSwClientNotify(rHint);
    }
    else if (rHint.GetId() == SfxHintId::SwAutoFormatUsedHint)
    {
        static_cast<const sw::AutoFormatUsedHint&>(rHint).CheckNode(this);
        return;
    }
    else if (rHint.GetId() == SfxHintId::SwModifyChanged)
    {
        auto pModifyChangedHint = static_cast<const sw::ModifyChangedHint*>(&rHint);
        m_pCondColl = const_cast<SwFormatColl*>(static_cast<const SwFormatColl*>(pModifyChangedHint->m_pNew));
    }
    else if(rHint.GetId() == SfxHintId::SwCondCollCondChg)
    {
        auto pCondCollCondChgHint = static_cast<const sw::CondCollCondChg*>(&rHint);
        ChkCondColl(&pCondCollCondChgHint->m_rColl);
    }
}
 
bool SwContentNode::InvalidateNumRule()
{
    SwNumRule* pRule = nullptr;
    const SfxPoolItem* pItem;
    if( GetNodes().IsDocNodes() &&
        nullptr != ( pItem = GetNoCondAttr( RES_PARATR_NUMRULE, true )) &&
        !static_cast<const SwNumRuleItem*>(pItem)->GetValue().isEmpty() &&
        nullptr != (pRule = GetDoc().FindNumRulePtr(
                                static_cast<const SwNumRuleItem*>(pItem)->GetValue() ) ) )
    {
        pRule->Invalidate();
    }
    return nullptr != pRule;
}
 
SwContentFrame *SwContentNode::getLayoutFrame( const SwRootFrame* _pRoot,
    const SwPosition *const pPos,
    std::pair<Point, bool> const*const pViewPosAndCalcFrame) const
{
    return static_cast<SwContentFrame*>( ::GetFrameOfModify( _pRoot, *this, FRM_CNTNT,
                                            pPos, pViewPosAndCalcFrame));
}
 
SwRect SwContentNode::FindLayoutRect( const bool bPrtArea, const Point* pPoint ) const
{
    SwRect aRet;
    std::pair<Point, bool> tmp;
    if (pPoint)
    {
        tmp.first = *pPoint;
        tmp.second = false;
    }
    SwContentFrame* pFrame = static_cast<SwContentFrame*>( ::GetFrameOfModify( nullptr, *this,
                                FRM_CNTNT, nullptr, pPoint ? &tmp : nullptr) );
    if( pFrame )
        aRet = bPrtArea ? pFrame->getFramePrintArea() : pFrame->getFrameArea();
    return aRet;
}
 
SwRect SwContentNode::FindPageFrameRect() const
{
    SwRect aRet;
    SwFrame* pFrame = ::GetFrameOfModify( nullptr, *this, FRM_CNTNT );
    if( pFrame && nullptr != ( pFrame = pFrame->FindPageFrame() ))
        aRet = pFrame->getFrameArea();
    return aRet;
}
 
sal_Int32 SwContentNode::Len() const { return 0; }
 
SwFormatColl *SwContentNode::ChgFormatColl( SwFormatColl *pNewColl )
{
    OSL_ENSURE( pNewColl, "Collectionpointer is 0." );
    SwFormatColl *pOldColl = GetFormatColl();
 
    if( pNewColl != pOldColl )
    {
        pNewColl->Add(*this);
 
        // Set the Parent of out AutoAttributes to the new Collection
        if( GetpSwAttrSet() )
            AttrSetHandleHelper::SetParent( mpAttrSet, *this, pNewColl, pNewColl );
 
        SetCondFormatColl( nullptr );
 
        if( !IsModifyLocked() )
        {
            assert(dynamic_cast<SwTextFormatColl*>(pNewColl));
            ChkCondColl(static_cast<SwTextFormatColl*>(pNewColl));
            SwFormatChg aTmp1( pOldColl );
            SwFormatChg aTmp2( pNewColl );
            CallSwClientNotify( sw::LegacyModifyHint(&aTmp1, &aTmp2) );
        }
    }
    InvalidateInSwCache(RES_ATTRSET_CHG);
    return pOldColl;
}
 
bool SwContentNode::GoNext(SwPosition* pPos, SwCursorSkipMode nMode ) const
{
    if (!GoNext(&pPos->nContent, nMode))
        return false;
    if (pPos->nContent.GetContentNode() != &pPos->GetNode())
        pPos->nNode.Assign(*pPos->nContent.GetContentNode());
    return true;
}
 
bool SwContentNode::GoNext(SwContentIndex * pIdx, SwCursorSkipMode nMode ) const
{
    bool bRet = true;
    if( pIdx->GetIndex() < Len() )
    {
        if( !IsTextNode() )
            ++(*pIdx);
        else
        {
            const SwTextNode& rTNd = *GetTextNode();
            sal_Int32 nPos = pIdx->GetIndex();
            assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
            sal_Int32 nDone = 0;
            sal_uInt16 nItrMode = ( SwCursorSkipMode::Cells & nMode ) ?
                                    CharacterIteratorMode::SKIPCELL :
                                    CharacterIteratorMode::SKIPCONTROLCHARACTER;
            nPos = g_pBreakIt->GetBreakIter()->nextCharacters( rTNd.GetText(), nPos,
                               g_pBreakIt->GetLocale( rTNd.GetLang( nPos ) ),
                               nItrMode, 1, nDone );
 
            // Check if nPos is inside hidden text range:
            if ( SwCursorSkipMode::Hidden & nMode )
            {
                sal_Int32 nHiddenStart;
                sal_Int32 nHiddenEnd;
                SwScriptInfo::GetBoundsOfHiddenRange( rTNd, nPos, nHiddenStart, nHiddenEnd );
                if ( nHiddenStart != COMPLETE_STRING && nHiddenStart != nPos )
                     nPos = nHiddenEnd;
            }
 
            if( 1 == nDone )
                *pIdx = nPos;
            else
                bRet = false;
        }
    }
    else
        bRet = false;
    return bRet;
}
 
bool SwContentNode::GoPrevious(SwContentIndex * pIdx, SwCursorSkipMode nMode ) const
{
    bool bRet = true;
    if( pIdx->GetIndex() > 0 )
    {
        if( !IsTextNode() )
            --(*pIdx);
        else
        {
            const SwTextNode& rTNd = *GetTextNode();
            sal_Int32 nPos = pIdx->GetIndex();
            assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
            sal_Int32 nDone = 0;
            sal_uInt16 nItrMode = ( SwCursorSkipMode::Cells & nMode ) ?
                                    CharacterIteratorMode::SKIPCELL :
                                    CharacterIteratorMode::SKIPCONTROLCHARACTER;
            nPos = g_pBreakIt->GetBreakIter()->previousCharacters( rTNd.GetText(), nPos,
                               g_pBreakIt->GetLocale( rTNd.GetLang( nPos ) ),
                               nItrMode, 1, nDone );
 
            // Check if nPos is inside hidden text range:
            if ( SwCursorSkipMode::Hidden & nMode )
            {
                sal_Int32 nHiddenStart;
                sal_Int32 nHiddenEnd;
                SwScriptInfo::GetBoundsOfHiddenRange( rTNd, nPos, nHiddenStart, nHiddenEnd );
                if ( nHiddenStart != COMPLETE_STRING )
                     nPos = nHiddenStart;
            }
 
            if( 1 == nDone )
                *pIdx = nPos;
            else
                bRet = false;
        }
    }
    else
        bRet = false;
    return bRet;
}
 
/**
 * Creates all Views for the Doc for this Node.
 * The created ContentFrames are attached to the corresponding Layout.
 */
void SwContentNode::MakeFramesForAdjacentContentNode(SwContentNode& rNode)
{
    OSL_ENSURE( &rNode != this,
            "No ContentNode or CopyNode and new Node identical." );
 
    if( !HasWriterListeners() || &rNode == this )   // Do we actually have Frames?
        return;
 
    SwFrame *pFrame;
    SwLayoutFrame *pUpper;
    // Create Frames for Nodes which come after the Table?
    OSL_ENSURE( FindTableNode() == rNode.FindTableNode(), "Table confusion" );
 
    SwNode2Layout aNode2Layout( *this, rNode.GetIndex() );
 
    while( nullptr != (pUpper = aNode2Layout.UpperFrame( pFrame, rNode )) )
    {
        if (pUpper->getRootFrame()->HasMergedParas()
            && !rNode.IsCreateFrameWhenHidingRedlines())
        {
            continue;
        }
        SwFrame *pNew = rNode.MakeFrame( pUpper );
        pNew->Paste( pUpper, pFrame );
        // #i27138#
        // notify accessibility paragraphs objects about changed
        // CONTENT_FLOWS_FROM/_TO relation.
        // Relation CONTENT_FLOWS_FROM for next paragraph will change
        // and relation CONTENT_FLOWS_TO for previous paragraph will change.
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
        if ( pNew->IsTextFrame() )
        {
            SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() );
            if ( pViewShell && pViewShell->GetLayout() &&
                 pViewShell->GetLayout()->IsAnyShellAccessible() )
            {
                auto pNext = pNew->FindNextCnt( true );
                auto pPrev = pNew->FindPrevCnt();
                pViewShell->InvalidateAccessibleParaFlowRelation(
                            pNext ? pNext->DynCastTextFrame() : nullptr,
                            pPrev ? pPrev->DynCastTextFrame() : nullptr );
            }
        }
#endif
    }
}
 
/**
 * Deletes all Views from the Doc for this Node.
 * The ContentFrames are removed from the corresponding Layout.
 */
void SwContentNode::DelFrames(SwRootFrame const*const pLayout)
{
    if( !HasWriterListeners() )
        return;
 
    SwIterator<SwContentFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*this);
    for( SwContentFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
    {
        if (pLayout && pLayout != pFrame->getRootFrame())
        {
            continue; // skip it
        }
        if (pFrame->IsTextFrame())
        {
            if (sw::MergedPara * pMerged =
                    static_cast<SwTextFrame *>(pFrame)->GetMergedPara())
            {
                if (this != pMerged->pFirstNode)
                {
                    // SwNodes::RemoveNode iterates *backwards* - so
                    // ensure there are no more extents pointing to this
                    // node as SwFrame::InvalidatePage() will access them.
                    // Note: cannot send via SwClientNotify from dtor
                    // because that would access deleted wrong-lists
                    sw::UpdateMergedParaForDelete(*pMerged, true,
                            *static_cast<SwTextNode*>(this), 0, Len());
                    if (this == pMerged->pParaPropsNode)
                    {
                        // otherwise pointer should have been updated to a different node
                        assert(this == pMerged->pLastNode);
                        assert(pMerged->extents.empty());
                        for (SwNodeOffset i = pMerged->pLastNode->GetIndex() - 1;;
                                --i)
                        {
                            assert(pMerged->pFirstNode->GetIndex() <= i);
                            SwNode *const pNode(GetNodes()[i]);
                            if (pNode->IsTextNode()
                                && pNode->GetRedlineMergeFlag() != Merge::Hidden)
                            {
                                pMerged->pParaPropsNode = pNode->GetTextNode();
                                break;
                            }
                            else if (pMerged->pFirstNode->GetIndex() == i)
                            {   // this can only happen when called from CheckParaRedlineMerge()
                                // and the pMerged will be deleted anyway
                                pMerged->pParaPropsNode = pMerged->pFirstNode;
                                break;
                            }
                        }
                        assert(pMerged->listener.IsListeningTo(pMerged->pParaPropsNode));
                    }
                    assert(GetIndex() <= pMerged->pLastNode->GetIndex());
                    if (this == pMerged->pLastNode)
                    {
                        // tdf#130680 find the previous node that is a
                        // listener of pMerged; see CheckParaRedlineMerge()
                        for (SwNodeOffset i = GetIndex() - 1;
                             this == pMerged->pLastNode; --i)
                        {
                            SwNode *const pNode = GetNodes()[i];
                            if (pNode->IsTextNode())
                            {
                                pMerged->pLastNode = pNode->GetTextNode();
                            }
                            else if (SwEndNode const*const pEnd = pNode->GetEndNode())
                            {
                                SwStartNode const*const pStart(pEnd->StartOfSectionNode());
                                i = pStart->GetIndex(); // skip table or section
                            }
                        }
                        assert(pMerged->pFirstNode->GetIndex() <= pMerged->pLastNode->GetIndex());
                        assert(pMerged->listener.IsListeningTo(pMerged->pLastNode));
                    }
                    // avoid re-parenting mess (ModifyChangedHint)
                    pMerged->listener.EndListening(this);
                    continue; // don't delete
                }
            }
        // #i27138#
        // notify accessibility paragraphs objects about changed
        // CONTENT_FLOWS_FROM/_TO relation.
        // Relation CONTENT_FLOWS_FROM for current next paragraph will change
        // and relation CONTENT_FLOWS_TO for current previous paragraph will change.
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
            SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() );
            if ( pViewShell && pViewShell->GetLayout() &&
                 pViewShell->GetLayout()->IsAnyShellAccessible() )
            {
                auto pNext = pFrame->FindNextCnt( true );
                auto pPrev = pFrame->FindPrevCnt();
                pViewShell->InvalidateAccessibleParaFlowRelation(
                            pNext ? pNext->DynCastTextFrame() : nullptr,
                            pPrev ? pPrev->DynCastTextFrame() : nullptr );
            }
#endif
        }
 
        if( pFrame->IsFollow() )
        {
            SwContentFrame* pMaster = pFrame->FindMaster();
            pMaster->SetFollow( pFrame->GetFollow() );
        }
        pFrame->SetFollow( nullptr );//So it doesn't get funny ideas.
                                //Otherwise it could be possible that a follow
                                //gets destroyed before its master. Following
                                //the now invalid pointer will then lead to an
                                //illegal memory access. The chain can be
                                //crushed here because we'll destroy all of it
                                //anyway.
 
        if( pFrame->GetUpper() && pFrame->IsInFootnote() && !pFrame->GetIndNext() &&
            !pFrame->GetIndPrev() )
        {
            SwFootnoteFrame *pFootnote = pFrame->FindFootnoteFrame();
            OSL_ENSURE( pFootnote, "You promised a FootnoteFrame?" );
            SwContentFrame* pCFrame;
            if( !pFootnote->GetFollow() && !pFootnote->GetMaster() &&
                nullptr != ( pCFrame = pFootnote->GetRefFromAttr()) && pCFrame->IsFollow() )
            {
                OSL_ENSURE( pCFrame->IsTextFrame(), "NoTextFrame has Footnote?" );
                pCFrame->FindMaster()->Prepare( PrepareHint::FootnoteInvalidationGone );
            }
        }
        pFrame->Cut();
        SwFrame::DestroyFrame(pFrame);
    }
}
 
SwContentNode *SwContentNode::JoinNext()
{
    return this;
}
 
 
/// Get info from Modify
bool SwContentNode::GetInfo( SwFindNearestNode& rInfo ) const
{
    if( GetAttr( RES_PAGEDESC ).GetPageDesc() )
        rInfo.CheckNode( *this );
    return true;
}
 
/// @param rAttr the attribute to set
bool SwContentNode::SetAttr(const SfxPoolItem& rAttr )
{
    if( !GetpSwAttrSet() ) // Have the Nodes created by the corresponding AttrSets
        NewAttrSet( GetDoc().GetAttrPool() );
 
    OSL_ENSURE( GetpSwAttrSet(), "Why did't we create an AttrSet?");
 
    InvalidateInSwCache(RES_ATTRSET_CHG);
 
    bool bRet = false;
    // If Modify is locked, we do not send any Modifys
    if( IsModifyLocked() ||
        ( !HasWriterListeners() &&  RES_PARATR_NUMRULE != rAttr.Which() ))
    {
        bRet = nullptr != AttrSetHandleHelper::Put( mpAttrSet, *this, rAttr );
    }
    else
    {
        SwAttrSet aOld( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() ),
                  aNew( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() );
        bRet = AttrSetHandleHelper::Put_BC( mpAttrSet, *this, rAttr, &aOld, &aNew );
        if( bRet )
            sw::ClientNotifyAttrChg(*this, *GetpSwAttrSet(), aOld, aNew);
    }
    return bRet;
}
 
bool SwContentNode::SetAttr( const SfxItemSet& rSet )
{
    InvalidateInSwCache(RES_ATTRSET_CHG);
 
    if( const SwFormatAutoFormat* pFnd = rSet.GetItemIfSet( RES_AUTO_STYLE, false ) )
    {
        OSL_ENSURE( rSet.Count() == 1, "SetAutoStyle mixed with other attributes?!" );
 
        // If there already is an attribute set (usually containing a numbering
        // item), we have to merge the attribute of the new set into the old set:
        bool bSetParent = true;
        if ( GetpSwAttrSet() )
        {
            bSetParent = false;
            AttrSetHandleHelper::Put( mpAttrSet, *this, *pFnd->GetStyleHandle() );
        }
        else
        {
            std::shared_ptr<SfxItemSet> pItemSet = pFnd->GetStyleHandle();
            mpAttrSet = std::dynamic_pointer_cast<SwAttrSet>(pItemSet);
            assert(bool(pItemSet) == bool(mpAttrSet) && "types do not match");
        }
 
        if ( bSetParent )
        {
            // If the content node has a conditional style, we have to set the
            // string item containing the correct conditional style name (the
            // style name property has already been set during the import!)
            // In case we do not have a conditional style, we make use of the
            // fact that nobody else uses the attribute set behind the handle.
            // FME 2007-07-10 #i78124# If autostyle does not have a parent,
            // the string is empty.
            const SfxStringItem* pNameItem = nullptr;
            if ( nullptr != GetCondFormatColl() ||
                 !(pNameItem = mpAttrSet->GetItemIfSet( RES_FRMATR_STYLE_NAME, false )) ||
                 pNameItem->GetValue().isEmpty() )
                AttrSetHandleHelper::SetParent( mpAttrSet, *this, &GetAnyFormatColl(), GetFormatColl() );
            else
                const_cast<SwAttrSet*>(mpAttrSet.get())->SetParent( &GetFormatColl()->GetAttrSet() );
        }
 
        return true;
    }
 
    if( !GetpSwAttrSet() ) // Have the AttrsSets created by the corresponding Nodes
        NewAttrSet( GetDoc().GetAttrPool() );
 
    bool bRet = false;
    // If Modify is locked, do not send any Modifys
    if ( IsModifyLocked() ||
         ( !HasWriterListeners() &&
           SfxItemState::SET != rSet.GetItemState( RES_PARATR_NUMRULE, false ) ) )
    {
        // Some special treatment for Attributes
        bRet = AttrSetHandleHelper::Put( mpAttrSet, *this, rSet );
    }
    else
    {
        SwAttrSet aOld( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() ),
                  aNew( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() );
        bRet = AttrSetHandleHelper::Put_BC( mpAttrSet, *this, rSet, &aOld, &aNew );
        if( bRet )
            sw::ClientNotifyAttrChg(*this, *GetpSwAttrSet(), aOld, aNew);
    }
    return bRet;
}
 
// With nWhich it takes the Hint from the Delta array
bool SwContentNode::ResetAttr( sal_uInt16 nWhich1, sal_uInt16 nWhich2 )
{
    if( !GetpSwAttrSet() )
        return false;
 
    InvalidateInSwCache(RES_ATTRSET_CHG);
 
    // If Modify is locked, do not send out any Modifys
    if( IsModifyLocked() )
    {
        sal_uInt16 nDel = 0;
        if ( !nWhich2 || nWhich2 < nWhich1 )
        {
            nDel = ClearItemsFromAttrSet( { nWhich1 } );
        }
        else
            nDel = AttrSetHandleHelper::ClearItem_BC( mpAttrSet, *this, nWhich1, nWhich2, nullptr, nullptr );
 
        if( !GetpSwAttrSet()->Count() ) // Empty? Delete
            mpAttrSet.reset();
        return 0 != nDel;
    }
 
    // No valid area defined?
    if( !nWhich2 || nWhich2 < nWhich1 )
        nWhich2 = nWhich1; // Then set only this Item to 1st Id
 
    SwAttrSet aOld( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() ),
              aNew( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() );
    bool bRet = 0 != AttrSetHandleHelper::ClearItem_BC( mpAttrSet, *this, nWhich1, nWhich2, &aOld, &aNew );
 
    if( bRet )
    {
        sw::ClientNotifyAttrChg(*this, *GetpSwAttrSet(), aOld, aNew);
 
        if( !GetpSwAttrSet()->Count() ) // Empty?, delete it
            mpAttrSet.reset();
    }
    return bRet;
}
 
bool SwContentNode::ResetAttr( const std::vector<sal_uInt16>& rWhichArr )
{
    if( !GetpSwAttrSet() )
        return false;
 
    InvalidateInSwCache(RES_ATTRSET_CHG);
    // If Modify is locked, do not send out any Modifys
    sal_uInt16 nDel = 0;
    if( IsModifyLocked() )
    {
        nDel = ClearItemsFromAttrSet( rWhichArr );
    }
    else
    {
        SwAttrSet aOld( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() ),
                  aNew( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() );
 
        for ( const auto& rWhich : rWhichArr )
            if( AttrSetHandleHelper::ClearItem_BC( mpAttrSet, *this, rWhich, &aOld, &aNew ))
                ++nDel;
 
        if( nDel )
            sw::ClientNotifyAttrChg(*this, *GetpSwAttrSet(), aOld, aNew);
    }
    if( !GetpSwAttrSet()->Count() ) // Empty?, delete it
        mpAttrSet.reset();
    return 0 != nDel ;
}
 
sal_uInt16 SwContentNode::ResetAllAttr()
{
    if( !GetpSwAttrSet() )
        return 0;
    InvalidateInSwCache(RES_ATTRSET_CHG);
 
    // If Modify is locked, do not send out any Modifys
    if( IsModifyLocked() )
    {
        sal_uInt16 nDel = ClearItemsFromAttrSet( { 0 } );
        if( !GetpSwAttrSet()->Count() ) // Empty? Delete
            mpAttrSet.reset();
        return nDel;
    }
 
    SwAttrSet aOld( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() ),
              aNew( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() );
    bool bRet = 0 != AttrSetHandleHelper::ClearItem_BC( mpAttrSet, *this, 0, &aOld, &aNew );
 
    if( bRet )
    {
        sw::ClientNotifyAttrChg(*this, *GetpSwAttrSet(), aOld, aNew);
        if( !GetpSwAttrSet()->Count() ) // Empty? Delete
            mpAttrSet.reset();
    }
    return aNew.Count();
}
 
bool SwContentNode::GetAttr( SfxItemSet& rSet ) const
{
    if( rSet.Count() )
        rSet.ClearItem();
 
    const SwAttrSet& rAttrSet = GetSwAttrSet();
    return rSet.Set( rAttrSet );
}
 
sal_uInt16 SwContentNode::ClearItemsFromAttrSet( const std::vector<sal_uInt16>& rWhichIds )
{
    sal_uInt16 nRet = 0;
    if ( rWhichIds.empty() )
        return nRet;
 
    OSL_ENSURE( GetpSwAttrSet(), "no item set" );
    SwAttrSet aNewAttrSet( *GetpSwAttrSet() );
    for ( const auto& rWhichId : rWhichIds )
    {
        nRet = nRet + aNewAttrSet.ClearItem( rWhichId );
    }
    if ( nRet )
        AttrSetHandleHelper::GetNewAutoStyle( mpAttrSet, *this, aNewAttrSet );
 
    return nRet;
}
 
const SfxPoolItem* SwContentNode::GetNoCondAttr( sal_uInt16 nWhich,
                                               bool bInParents ) const
{
    const SfxPoolItem* pFnd = nullptr;
    if( m_pCondColl && m_pCondColl->GetRegisteredIn() )
    {
        if( !GetpSwAttrSet() || ( SfxItemState::SET != GetpSwAttrSet()->GetItemState(
                    nWhich, false, &pFnd ) && bInParents ))
        {
            (void)static_cast<const SwFormat*>(GetRegisteredIn())->GetItemState( nWhich, bInParents, &pFnd );
        }
    }
    // undo change of issue #i51029#
    // Note: <GetSwAttrSet()> returns <mpAttrSet>, if set, otherwise it returns
    //       the attribute set of the paragraph style, which is valid for the
    //       content node - see file <node.hxx>
    else
    {
        GetSwAttrSet().GetItemState( nWhich, bInParents, &pFnd );
    }
    return pFnd;
}
 
static bool lcl_CheckMaxLength(SwNode const& rPrev, SwNode const& rNext)
{
    if (rPrev.GetNodeType() != rNext.GetNodeType())
    {
        return false;
    }
    if (!rPrev.IsTextNode())
    {
        return true;
    }
 
    // Check if a node can contain the other (order is not significant)
    return rPrev.GetTextNode()->GetSpaceLeft() > rNext.GetTextNode()->Len();
}
 
/// Can we join two Nodes?
/// We can return the 2nd position in pIdx.
bool SwContentNode::CanJoinNext( SwNodeIndex* pIdx ) const
{
    const SwNodes& rNds = GetNodes();
    SwNodeIndex aIdx( *this, 1 );
 
    const SwNode* pNd = this;
    while( aIdx < rNds.Count()-1 &&
        (( pNd = &aIdx.GetNode())->IsSectionNode() ||
            ( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsSectionNode() )))
        ++aIdx;
 
    if (rNds.Count()-1 == aIdx.GetIndex())
        return false;
    if (!lcl_CheckMaxLength(*this, *pNd))
    {
        return false;
    }
    if( pIdx )
        *pIdx = aIdx;
    return true;
}
 
/// Can we join two Nodes?
/// We can return the 2nd position in pIdx.
bool SwContentNode::CanJoinNext( SwPosition* pIdx ) const
{
    const SwNodes& rNds = GetNodes();
    SwNodeIndex aIdx( *this, 1 );
 
    const SwNode* pNd = this;
    while( aIdx < rNds.Count()-1 &&
        (( pNd = &aIdx.GetNode())->IsSectionNode() ||
            ( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsSectionNode() )))
        ++aIdx;
 
    if (rNds.Count()-1 == aIdx.GetIndex())
        return false;
    if (!lcl_CheckMaxLength(*this, *pNd))
    {
        return false;
    }
    if( pIdx )
        pIdx->Assign(aIdx);
    return true;
}
 
/// Can we join two Nodes?
/// We can return the 2nd position in pIdx.
bool SwContentNode::CanJoinPrev( SwNodeIndex* pIdx ) const
{
    SwNodeIndex aIdx( *this, -1 );
 
    const SwNode* pNd = this;
    while( aIdx.GetIndex() &&
        (( pNd = &aIdx.GetNode())->IsSectionNode() ||
            ( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsSectionNode() )))
        --aIdx;
 
    if (SwNodeOffset(0) == aIdx.GetIndex())
        return false;
    if (!lcl_CheckMaxLength(*pNd, *this))
    {
        return false;
    }
    if( pIdx )
        *pIdx = aIdx;
    return true;
}
 
void SwContentNode::SetCondFormatColl(SwFormatColl* pColl)
{
    if( !((!pColl && m_pCondColl) || ( pColl && !m_pCondColl ) ||
        ( pColl && pColl != m_pCondColl->GetRegisteredIn() )) )
        return;
 
    SwFormatColl* pOldColl = GetCondFormatColl();
    m_aCondCollListener.EndListeningAll();
    if(pColl)
        m_aCondCollListener.StartListening(pColl);
    m_pCondColl = pColl;
    if(GetpSwAttrSet())
        AttrSetHandleHelper::SetParent(mpAttrSet, *this, &GetAnyFormatColl(), GetFormatColl());
 
    if(!IsModifyLocked())
    {
        SwFormatChg aTmp1(pOldColl ? pOldColl : GetFormatColl());
        SwFormatChg aTmp2(pColl ? pColl : GetFormatColl());
        CallSwClientNotify(sw::LegacyModifyHint(&aTmp1, &aTmp2));
    }
    InvalidateInSwCache(RES_ATTRSET_CHG);
}
 
bool SwContentNode::IsAnyCondition( SwCollCondition& rTmp ) const
{
    const SwNodes& rNds = GetNodes();
    {
        Master_CollCondition nCond = Master_CollCondition::NONE;
        const SwStartNode* pSttNd = StartOfSectionNode();
        while( pSttNd )
        {
            switch( pSttNd->GetNodeType() )
            {
            case SwNodeType::Table:      nCond = Master_CollCondition::PARA_IN_TABLEBODY; break;
            case SwNodeType::Section:    nCond = Master_CollCondition::PARA_IN_SECTION; break;
 
            default:
                switch( pSttNd->GetStartNodeType() )
                {
                case SwTableBoxStartNode:
                    {
                        nCond = Master_CollCondition::PARA_IN_TABLEBODY;
                        const SwTableNode* pTableNd = pSttNd->FindTableNode();
                        const SwTableBox* pBox;
                        if( pTableNd && nullptr != ( pBox = pTableNd->GetTable().
                            GetTableBox(pSttNd->GetIndex()) ) &&
                            pBox->IsInHeadline( &pTableNd->GetTable() ) )
                            nCond = Master_CollCondition::PARA_IN_TABLEHEAD;
                    }
                    break;
                case SwFlyStartNode:        nCond = Master_CollCondition::PARA_IN_FRAME; break;
                case SwFootnoteStartNode:
                    {
                        nCond = Master_CollCondition::PARA_IN_FOOTNOTE;
                        const SwFootnoteIdxs& rFootnoteArr = rNds.GetDoc().GetFootnoteIdxs();
                        const SwTextFootnote* pTextFootnote;
                        const SwNode* pSrchNd = pSttNd;
 
                        for( size_t n = 0; n < rFootnoteArr.size(); ++n )
                            if( nullptr != ( pTextFootnote = rFootnoteArr[ n ])->GetStartNode() &&
                                pSrchNd == &pTextFootnote->GetStartNode()->GetNode() )
                            {
                                if( pTextFootnote->GetFootnote().IsEndNote() )
                                    nCond = Master_CollCondition::PARA_IN_ENDNOTE;
                                break;
                            }
                    }
                    break;
                case SwHeaderStartNode:     nCond = Master_CollCondition::PARA_IN_HEADER; break;
                case SwFooterStartNode:     nCond = Master_CollCondition::PARA_IN_FOOTER; break;
                case SwNormalStartNode:     break;
                }
            }
 
            if( nCond != Master_CollCondition::NONE )
            {
                rTmp.SetCondition( nCond, 0 );
                return true;
            }
            pSttNd = pSttNd->GetIndex()
                        ? pSttNd->StartOfSectionNode()
                        : nullptr;
        }
    }
 
    {
        SwOutlineNodes::size_type nPos;
        const SwOutlineNodes& rOutlNds = rNds.GetOutLineNds();
        if( !rOutlNds.empty() )
        {
            if (!rOutlNds.Seek_Entry(this, &nPos) && nPos)
                --nPos;
            if( nPos < rOutlNds.size() &&
                rOutlNds[ nPos ]->GetIndex() < GetIndex() )
            {
                SwTextNode* pOutlNd = rOutlNds[ nPos ]->GetTextNode();
 
                if( pOutlNd->IsOutline())
                {
                    rTmp.SetCondition( Master_CollCondition::PARA_IN_OUTLINE, pOutlNd->GetAttrOutlineLevel() - 1 );
                    return true;
                }
            }
        }
    }
 
    return false;
}
 
void SwContentNode::ChkCondColl(const SwTextFormatColl* pColl)
{
    if(pColl != GetRegisteredIn())
    {
        SAL_INFO("sw.core", "Not our cond collection, skipping check of Cond Colls.");
        return;
    }
    if(&GetNodes() != &GetDoc().GetNodes())
    {
        SAL_WARN("sw.core", "Nodes amiss, skipping check of Cond Colls.");
        return;
    }
    // Check, just to be sure
    if( RES_CONDTXTFMTCOLL != GetFormatColl()->Which() )
        return;
 
    SwCollCondition aTmp( nullptr, Master_CollCondition::NONE, 0 );
    const SwCollCondition* pCColl;
 
    bool bDone = false;
 
    if( IsAnyCondition( aTmp ))
    {
        pCColl = static_cast<SwConditionTextFormatColl*>(GetFormatColl())
            ->HasCondition( aTmp );
 
        if (pCColl)
        {
            SetCondFormatColl( pCColl->GetTextFormatColl() );
            bDone = true;
        }
    }
 
    if (bDone)
        return;
 
    if( IsTextNode() && static_cast<SwTextNode*>(this)->GetNumRule())
    {
        // Is at which Level in a list?
        aTmp.SetCondition( Master_CollCondition::PARA_IN_LIST,
                        static_cast<SwTextNode*>(this)->GetActualListLevel() );
        pCColl = static_cast<SwConditionTextFormatColl*>(GetFormatColl())->
                        HasCondition( aTmp );
    }
    else
        pCColl = nullptr;
 
    if( pCColl )
        SetCondFormatColl( pCColl->GetTextFormatColl() );
    else if( m_pCondColl )
        SetCondFormatColl( nullptr );
}
 
// #i42921#
SvxFrameDirection SwContentNode::GetTextDirection( const SwPosition& rPos,
                                     const Point* pPt ) const
{
    SvxFrameDirection nRet = SvxFrameDirection::Unknown;
 
    Point aPt;
    if( pPt )
        aPt = *pPt;
 
    // #i72024# - No format of the frame, because this can cause recursive layout actions
    std::pair<Point, bool> const tmp(aPt, false);
    SwFrame* pFrame = getLayoutFrame(GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), &rPos, &tmp);
 
    if ( pFrame )
    {
        if ( pFrame->IsVertical() )
        {
            if (pFrame->IsVertLRBT())
                nRet = SvxFrameDirection::Vertical_LR_BT;
            else if (pFrame->IsRightToLeft())
                nRet = SvxFrameDirection::Vertical_LR_TB;
            else
                nRet = SvxFrameDirection::Vertical_RL_TB;
        }
        else
        {
            if ( pFrame->IsRightToLeft() )
                nRet = SvxFrameDirection::Horizontal_RL_TB;
            else
                nRet = SvxFrameDirection::Horizontal_LR_TB;
        }
    }
 
    return nRet;
}
 
std::unique_ptr<SwOLENodes> SwContentNode::CreateOLENodesArray( const SwFormatColl& rColl, bool bOnlyWithInvalidSize )
{
    std::unique_ptr<SwOLENodes> pNodes;
    SwIterator<SwContentNode,SwFormatColl> aIter( rColl );
    for( SwContentNode* pNd = aIter.First(); pNd; pNd = aIter.Next() )
    {
        SwOLENode *pONd = pNd->GetOLENode();
        if ( pONd && (!bOnlyWithInvalidSize || pONd->IsOLESizeInvalid()) )
        {
            if ( !pNodes  )
                pNodes.reset(new SwOLENodes);
            pNodes->push_back( pONd );
        }
    }
 
    return pNodes;
}
 
drawinglayer::attribute::SdrAllFillAttributesHelperPtr SwContentNode::getSdrAllFillAttributesHelper() const
{
    return drawinglayer::attribute::SdrAllFillAttributesHelperPtr();
}
 
/*
 * Document Interface Access
 */
const IDocumentSettingAccess* SwNode::getIDocumentSettingAccess() const { return &GetDoc().GetDocumentSettingManager(); }
const IDocumentDeviceAccess& SwNode::getIDocumentDeviceAccess() const { return GetDoc().getIDocumentDeviceAccess(); }
const IDocumentRedlineAccess& SwNode::getIDocumentRedlineAccess() const { return GetDoc().getIDocumentRedlineAccess(); }
const IDocumentStylePoolAccess& SwNode::getIDocumentStylePoolAccess() const { return GetDoc().getIDocumentStylePoolAccess(); }
const IDocumentDrawModelAccess& SwNode::getIDocumentDrawModelAccess() const { return GetDoc().getIDocumentDrawModelAccess(); }
const IDocumentLayoutAccess& SwNode::getIDocumentLayoutAccess() const { return GetDoc().getIDocumentLayoutAccess(); }
IDocumentLayoutAccess& SwNode::getIDocumentLayoutAccess() { return GetDoc().getIDocumentLayoutAccess(); }
const IDocumentLinksAdministration& SwNode::getIDocumentLinksAdministration() const { return GetDoc().getIDocumentLinksAdministration(); }
IDocumentLinksAdministration& SwNode::getIDocumentLinksAdministration() { return GetDoc().getIDocumentLinksAdministration(); }
const IDocumentFieldsAccess& SwNode::getIDocumentFieldsAccess() const { return GetDoc().getIDocumentFieldsAccess(); }
IDocumentFieldsAccess& SwNode::getIDocumentFieldsAccess() { return GetDoc().getIDocumentFieldsAccess(); }
IDocumentContentOperations& SwNode::getIDocumentContentOperations() { return GetDoc().getIDocumentContentOperations(); }
IDocumentListItems& SwNode::getIDocumentListItems() { return GetDoc().getIDocumentListItems(); } // #i83479#
 
const IDocumentMarkAccess* SwNode::getIDocumentMarkAccess() const { return GetDoc().getIDocumentMarkAccess(); }
IStyleAccess& SwNode::getIDocumentStyleAccess() { return GetDoc().GetIStyleAccess(); }
 
bool SwNode::IsInRedlines() const
{
    const SwDoc& rDoc = GetDoc();
 
    return rDoc.getIDocumentRedlineAccess().IsInRedlines(*this);
}
 
void SwNode::AddAnchoredFly(SwFrameFormat *const pFlyFormat)
{
    assert(pFlyFormat);
    assert(pFlyFormat->GetAnchor(false).GetAnchorNode() == this);
    // check node type, cf. SwFormatAnchor::SetAnchor()
    assert(IsTextNode() || IsStartNode() || IsTableNode());
    m_aAnchoredFlys.push_back(pFlyFormat);
}
 
void SwNode::RemoveAnchoredFly(SwFrameFormat *const pFlyFormat)
{
    assert(pFlyFormat);
    // cannot assert this in Remove because it is called when new anchor is already set
//    assert(&pFlyFormat->GetAnchor(false).GetContentAnchor()->GetNode() == this);
    assert(IsTextNode() || IsStartNode() || IsTableNode());
    auto it(std::find(m_aAnchoredFlys.begin(), m_aAnchoredFlys.end(), pFlyFormat));
    assert(it != m_aAnchoredFlys.end());
    m_aAnchoredFlys.erase(it);
}
 
void SwNode::resetAndQueueAccessibilityCheck(bool bIssueObjectNameChanged)
{
    GetDoc().getOnlineAccessibilityCheck()->resetAndQueue(this, bIssueObjectNameChanged);
}
 
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'Assign' is required to be utilized.

V530 The return value of function 'Assign' is required to be utilized.

V728 An excessive check can be simplified. The '(A && !B) || (!A && B)' expression is equivalent to the 'bool(A) != bool(B)' expression.