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

Quelle  flowfrm.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 <sal/config.h>
#include <sal/log.hxx>
#include <osl/diagnose.h>
#include <svx/svdobj.hxx>

#include <anchoredobject.hxx>
#include <bodyfrm.hxx>
#include <swtable.hxx>
#include <rootfrm.hxx>
#include <pagefrm.hxx>
#include <viewimp.hxx>
#include <viewopt.hxx>
#include <frmatr.hxx>
#include <frmtool.hxx>
#include <IDocumentFieldsAccess.hxx>
#include <editeng/formatbreakitem.hxx>
#include <editeng/keepitem.hxx>
#include <fmtanchr.hxx>
#include <fmtsrnd.hxx>
#include <fmtpdsc.hxx>
#include <editeng/ulspitem.hxx>
#include <tgrditem.hxx>
#include <txtftn.hxx>
#include <fmtftn.hxx>
#include <editeng/pgrditem.hxx>
#include <paratr.hxx>
#include <ftnfrm.hxx>
#include <txtfrm.hxx>
#include <notxtfrm.hxx>
#include <tabfrm.hxx>
#include <rowfrm.hxx>
#include <pagedesc.hxx>
#include <layact.hxx>
#include <flyfrm.hxx>
#include <sectfrm.hxx>
#include <section.hxx>
#include <dbg_lay.hxx>
#include <lineinfo.hxx>
#include <fmtclbl.hxx>
#include <sortedobjs.hxx>
#include <layouter.hxx>
#include <fmtfollowtextflow.hxx>
#include <calbck.hxx>
#include <IDocumentSettingAccess.hxx>
#include <IDocumentDrawModelAccess.hxx>
#include <pam.hxx>
#include <ndtxt.hxx>
#include <flyfrms.hxx>

bool SwFlowFrame::s_bMoveBwdJump = false;

SwFlowFrame::SwFlowFrame( SwFrame &rFrame ) :
    m_rThis( rFrame ),
    m_pFollow( nullptr ),
    m_pPrecede( nullptr ),
    m_bLockJoin( false ),
    m_bUndersized( false ),
    m_bFlyLock( false )
{}

SwFlowFrame::~SwFlowFrame()
{
    if (m_pFollow)
    {
        m_pFollow->m_pPrecede = nullptr;
    }
    if (m_pPrecede)
    {
        m_pPrecede->m_pFollow = nullptr;
    }
}

void SwFlowFrame::SetFollow(SwFlowFrame *const pFollow)
{
    if (m_pFollow)
    {
        assert(this == m_pFollow->m_pPrecede);
        m_pFollow->m_pPrecede = nullptr;
    }
    m_pFollow = pFollow;
    if (m_pFollow != nullptr)
    {
        if (m_pFollow->m_pPrecede) // re-chaining pFollow?
        {
            assert(m_pFollow == m_pFollow->m_pPrecede->m_pFollow);
            m_pFollow->m_pPrecede->m_pFollow = nullptr;
        }
        m_pFollow->m_pPrecede = this;
    }
}

/// @return true if any follow has the JoinLocked flag
bool SwFlowFrame::HasLockedFollow() const
{
    const SwFlowFrame* pFrame = GetFollow();
    while( pFrame )
    {
        if( pFrame->IsJoinLocked() )
            return true;
        pFrame = pFrame->GetFollow();
    }
    return false;
}

bool SwFlowFrame::IsKeepFwdMoveAllowed( bool bIgnoreMyOwnKeepValue )
{
    // If all the predecessors up to the first of the chain have
    // the 'keep' attribute set, and the first of the chain's
    // IsFwdMoveAllowed returns false, then we're not allowed to move.
    SwFrame *pFrame = &m_rThis;
    if ( !pFrame->IsInFootnote() ) {
        if ( bIgnoreMyOwnKeepValue && pFrame->GetIndPrev() )
            pFrame = pFrame->GetIndPrev();
        do
        {
            if (pFrame->GetAttrSet()->GetKeep().GetValue()
                || pFrame->IsHiddenNow())
                pFrame = pFrame->GetIndPrev();
            else
                return true;
        } while ( pFrame );
    }
                  //See IsFwdMoveAllowed()
    bool bRet = false;
    if ( pFrame && pFrame->GetIndPrev() )
        bRet = true;
    return bRet;
}

void SwFlowFrame::CheckKeep()
{
    // Kick off the "last" predecessor with a 'keep' attribute, because
    // it's possible for the whole troop to move back.
    SwFrame *pPre = m_rThis.GetIndPrev();
    assert(pPre);
    while (pPre && pPre->IsHiddenNow())
    {
        pPre = pPre->GetIndPrev();
    }
    if (!pPre)
    {
        return;
    }
    if( pPre->IsSctFrame() )
    {
        SwFrame *pLast = static_cast<SwSectionFrame*>(pPre)->FindLastContent();
        while (pLast && pLast->IsHiddenNow())
        {
            pLast = pLast->GetIndPrev();
        }
        if( pLast && pLast->FindSctFrame() == pPre )
            pPre = pLast;
        else
            return;
    }
    SwFrame* pTmp{pPre};
    bool bKeep;
    while ( (bKeep = pPre->GetAttrSet()->GetKeep().GetValue()) &&
            nullptr != (pTmp = pTmp->GetIndPrev()) )
    {
        if (pTmp->IsHiddenNow())
        {
            continue;
        }
        if( pTmp->IsSctFrame() )
        {
            SwFrame *pLast = static_cast<SwSectionFrame*>(pTmp)->FindLastContent();
            while (pLast && pLast->IsHiddenNow())
            {
                pLast = pLast->GetIndPrev();
            }
            if( pLast && pLast->FindSctFrame() == pTmp )
                pTmp = pLast;
            else
                break;
        }
        pPre = pTmp;
    }
    if ( bKeep )
        pPre->InvalidatePos();
}

namespace
{
/**
 * Determines if the next content frame after rThis will require the full area of the parent body
 * frame.
 */

bool IsNextContentFullPage(const SwFrame& rThis)
{
    const SwFrame* pNext = rThis.FindNextCnt();
    if (!pNext)
    {
        return false;
    }

    const SwSortedObjs* pNextDrawObjs = pNext->GetDrawObjs();
    if (!pNextDrawObjs || !pNextDrawObjs->size())
    {
        return false;
    }

    for (const auto& pDrawObj : *pNextDrawObjs)
    {
        if (!pDrawObj)
        {
            continue;
        }

        SwTwips nDrawObjHeight = pDrawObj->GetObjRectWithSpaces().Height();
        const SwPageFrame* pPageFrame = pDrawObj->GetPageFrame();
        if (!pPageFrame)
        {
            continue;
        }

        SwTwips nBodyHeight = pPageFrame->GetLower()->getFrameArea().Height();
        if (nDrawObjHeight < nBodyHeight)
        {
            continue;
        }

        const SwFormatSurround& rSurround = pDrawObj->GetFrameFormat()->GetSurround();
        if (rSurround.GetSurround() != text::WrapTextMode_NONE)
        {
            continue;
        }

        // At this point the height of the draw object will use all the vertical available space,
        // and also no wrapping will be performed, so all horizontal space will be taken as well.
        return true;
    }

    return false;
}
}

bool SwFlowFrame::IsKeep(SvxFormatKeepItem const& rKeep,
        SvxFormatBreakItem const& rBreak,
        bool const bCheckIfLastRowShouldKeep) const
{
    assert(m_rThis.IsTextFrame() ? !static_cast<SwTextFrame const&>(m_rThis).IsHiddenNowImpl() : !m_rThis.IsHiddenNow()); // check it before?
    // 1. The keep attribute is ignored inside footnotes
    // 2. For compatibility reasons, the keep attribute is
    //    ignored for frames inside table cells
    // 3. If bBreakCheck is set to true, this function only checks
    //    if there are any break after attributes set at rAttrs
    //    or break before attributes set for the next content (or next table)
    // 4. Keep is ignored if the next frame will require its own page.
    bool bKeep = bCheckIfLastRowShouldKeep ||
                 (  !m_rThis.IsInFootnote() &&
                    ( !m_rThis.IsInTab() || m_rThis.IsTabFrame() ) &&
                    rKeep.GetValue() && !IsNextContentFullPage(m_rThis));

    if (bKeep && m_rThis.IsTextFrame())
    {
        auto& rTextFrame = static_cast<SwTextFrame&>(m_rThis);
        if (rTextFrame.HasNonLastSplitFlyDrawObj())
        {
            // Allow split for the non-last anchors of a split fly, even if rKeep.GetValue() is
            // true.
            bKeep = false;
        }
    }

    OSL_ENSURE( !bCheckIfLastRowShouldKeep || m_rThis.IsTabFrame(),
            "IsKeep with bCheckIfLastRowShouldKeep should only be used for tabfrms" );

    // Ignore keep attribute if there are break situations:
    if ( bKeep )
    {
        switch (rBreak.GetBreak())
        {
            case SvxBreak::ColumnAfter:
            case SvxBreak::ColumnBoth:
            case SvxBreak::PageAfter:
            case SvxBreak::PageBoth:
            {
                bKeep = false;
                break;
            }
            defaultbreak;
        }
        if ( bKeep )
        {
            SwFrame *pNxt;
            if( nullptr != (pNxt = m_rThis.FindNextCnt()) &&
                (!m_pFollow || pNxt != &m_pFollow->GetFrame()))
            {
                // The last row of a table only keeps with the next content
                // it they are in the same section:
                if ( bCheckIfLastRowShouldKeep )
                {
                    const SwSection* pThisSection = nullptr;
                    const SwSection* pNextSection = nullptr;
                    const SwSectionFrame* pThisSectionFrame = m_rThis.FindSctFrame();
                    const SwSectionFrame* pNextSectionFrame = pNxt->FindSctFrame();

                    if ( pThisSectionFrame )
                        pThisSection = pThisSectionFrame->GetSection();

                    if ( pNextSectionFrame )
                        pNextSection = pNextSectionFrame->GetSection();

                    if ( pThisSection != pNextSection )
                        bKeep = false;
                }

                if ( bKeep )
                {
                    SvxFormatBreakItem const* pBreak;
                    SwFormatPageDesc const* pPageDesc;
                    SwTabFrame* pTab = pNxt->IsInTab() ? pNxt->FindTabFrame() : nullptr;
                    if (pTab && (!m_rThis.IsInTab() || m_rThis.FindTabFrame() != pTab))
                    {
                        const SwAttrSet *const pSet = &pTab->GetFormat()->GetAttrSet();
                        pBreak = &pSet->GetBreak();
                        pPageDesc = &pSet->GetPageDesc();
                    }
                    else
                    {
                        pBreak = &pNxt->GetBreakItem();
                        pPageDesc = &pNxt->GetPageDescItem();
                    }

                    if (pPageDesc->GetPageDesc())
                        bKeep = false;
                    else switch (pBreak->GetBreak())
                    {
                        case SvxBreak::ColumnBefore:
                        case SvxBreak::ColumnBoth:
                        case SvxBreak::PageBefore:
                        case SvxBreak::PageBoth:
                            bKeep = false;
                            break;
                        defaultbreak;
                    }
                }
            }
        }
    }
    return bKeep;
}

SwFrame * SwFlowFrame::FindPrevIgnoreHidden() const
{
    SwFrame * pRet{m_rThis.FindPrev()};
    while (pRet && pRet->IsHiddenNow())
    {
        pRet = pRet->FindPrev();
    }
    return pRet;
}

SwFrame * SwFlowFrame::FindNextIgnoreHidden() const
{
    SwFrame * pRet{m_rThis.FindNext()};
    while (pRet && pRet->IsHiddenNow())
    {
        pRet = pRet->FindNext();
    }
    return pRet;
}

sal_uInt8 SwFlowFrame::BwdMoveNecessary( const SwPageFrame *pPage, const SwRect &rRect )
{
    // The return value helps deciding whether we need to flow back (3),
    // or whether we can use the good old WouldFit (0, 1), or if
    // it's reasonable to relocate and test-format (2).

    // Bit 1 in this case means that there are objects anchored to myself,
    // bit 2 means that I have to evade other objects.

    // If a SurroundObj that desires to be wrapped around overlaps with the
    // Rect, it's required to flow (because we can't guess the relationships).
    // However it's possible for a test formatting to happen.
    // If the SurroundObj is a Fly and I'm a Lower, or the Fly is a Lower of
    // mine, then it doesn't matter.
    // If the SurroundObj is anchored in a character bound Fly, and I'm not
    // a Lower of that character bound Fly myself, then the Fly doesn't matter.

    // If the object is anchored with me, i can ignore it, because
    // it's likely that it will follow me with the flow. A test formatting is
    // not allowed in that case, however!
    sal_uInt8 nRet = 0;
    SwFlowFrame *pTmp = this;
    do
    {   // If there are objects hanging either on me or on a follow, we can't
        // do a test formatting, because paragraph bound objects wouldn't
        // be properly considered, and character bound objects shouldn't
        // be test formatted at all.
        if( pTmp->GetFrame().GetDrawObjs() )
            nRet = 1;
        pTmp = pTmp->GetFollow();
    } while ( !nRet && pTmp );
    const SwSortedObjs *pObjs = pPage ? pPage->GetSortedObjs() : nullptr;
    if (pObjs)
    {

        const SwSortedObjs &rObjs = *pObjs;
        SwNodeOffset nIndex = NODE_OFFSET_MAX;
        for ( size_t i = 0; nRet < 3 && i < rObjs.size(); ++i )
        {

            SwAnchoredObject* pObj = rObjs[i];
            const SwFrameFormat* pFormat = pObj->GetFrameFormat();
            const SwRect aRect( pObj->GetObjRect() );
            if ( aRect.Overlaps( rRect ) &&
                 pFormat->GetSurround().GetSurround() != css::text::WrapTextMode_THROUGH )
            {
                if( m_rThis.IsLayoutFrame() && //Fly Lower of This?
                    Is_Lower_Of( &m_rThis, pObj->GetDrawObj() ) )
                    continue;
                ifauto pFly = pObj->DynCastFlyFrame() )
                {
                    if ( pFly->IsAnLower( &m_rThis ) )//This Lower of Fly?
                        continue;
                }

                const SwFrame* pAnchor = pObj->GetAnchorFrame();
                if ( pAnchor == &m_rThis )
                {
                    nRet |= 1;
                    continue;
                }

                // Don't do this if the object is anchored behind me in the text
                // flow, because then I wouldn't evade it.
                if ( ::IsFrameInSameContext( pAnchor, &m_rThis ) )
                {
                    if ( pFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PARA )
                    {
                        // The index of the other one can be retrieved using the anchor attribute.
                        SwNodeOffset nTmpIndex = pFormat->GetAnchor().GetAnchorNode()->GetIndex();
                        // Now we're going to check whether the current paragraph before
                        // the anchor of the displacing object sits in the text. If this
                        // is the case, we don't try to evade it.
                        // The index is being determined via SwFormatAnchor, because it's
                        // getting quite expensive otherwise.
                        if( NODE_OFFSET_MAX == nIndex )
                        {
                            const SwNode *pNode;
                            if (m_rThis.IsTextFrame())
                                pNode = static_cast<SwTextFrame&>(m_rThis).GetTextNodeFirst();
                            else if (m_rThis.IsNoTextFrame())
                                pNode = static_cast<SwNoTextFrame&>(m_rThis).GetNode();
                            else if( m_rThis.IsSctFrame() )
                                pNode = static_cast<SwSectionFormat*>(static_cast<SwSectionFrame&>(m_rThis).
                                        GetFormat())->GetSectionNode();
                            else
                            {
                                assert(!m_rThis.IsContentFrame());
                                OSL_ENSURE( m_rThis.IsTabFrame(), "new FowFrame?" );
                                pNode = static_cast<SwTabFrame&>(m_rThis).GetTable()->
                                    GetTabSortBoxes()[0]->GetSttNd()->FindTableNode();
                            }
                            nIndex = pNode->GetIndex();
                        }
                        if (nIndex < nTmpIndex &&
                            (!m_rThis.IsTextFrame() ||
                             !FrameContainsNode(static_cast<SwTextFrame&>(m_rThis), nTmpIndex)))
                        {
                            continue;
                        }
                    }
                }
                else
                    continue;

                nRet |= 2;
            }
        }
    }
    return nRet;
}

/// A specialized form of Cut(), which relocates a whole chain (this and the following,
/// in particular). During this process, only the minimum operations and notifications are done.
SwLayoutFrame *SwFlowFrame::CutTree( SwFrame *pStart )
{
    // Cut the Start and all the neighbours; they are chained together and
    // a handle to the first one is returned. Residuals are invalidated
    // as appropriate.

    SwLayoutFrame *pLay = pStart->GetUpper();
    if ( pLay->IsInFootnote() )
        pLay = pLay->FindFootnoteFrame();

    // i#58846
    // <pPrepare( PrepareHint::QuoVadis )> only for frames in footnotes
    if( pStart->IsInFootnote() )
    {
        SwFrame* pTmp = pStart->GetIndPrev();
        if( pTmp )
            pTmp->Prepare( PrepareHint::QuoVadis );
    }

    // Just cut quickly and take care that we don't cause problems with the
    // left-behinds. The pointers of the chain being cut can point who-knows where.
    if ( pStart == pStart->GetUpper()->Lower() )
        pStart->GetUpper()->m_pLower = nullptr;
    if ( pStart->GetPrev() )
    {
        pStart->GetPrev()->mpNext = nullptr;
        pStart->mpPrev = nullptr;
    }

    if ( pLay->IsFootnoteFrame() )
    {
        if ( !pLay->Lower() && !pLay->IsColLocked() &&
             !static_cast<SwFootnoteFrame*>(pLay)->IsBackMoveLocked() )
        {
            // tdf#101821 don't delete it while iterating over it
            if (!pLay->IsDeleteForbidden())
            {
                pLay->Cut();
                SwFrame::DestroyFrame(pLay);
            }
            // else: assume there is code on the stack to clean up empty
            // footnote frames
            // (don't go into the else branch below, it produces a disconnected
            // footnote with null upper that can be returned by
            // SwFootnoteBossFrame::FindFootnote() causing null pointer deref
            // in SwTextFrame::ConnectFootnote()
        }
        else
        {
            bool bUnlock = !static_cast<SwFootnoteFrame*>(pLay)->IsBackMoveLocked();
            static_cast<SwFootnoteFrame*>(pLay)->LockBackMove();
            pLay->InvalidateSize();
            pLay->Calc(pLay->getRootFrame()->GetCurrShell()->GetOut());
            SwContentFrame *pCnt = pLay->ContainsContent();
            while ( pCnt && pLay->IsAnLower( pCnt ) )
            {
                // It's possible for the ContentFrame to be locked, and we don't want
                // to end up in an endless page migration, so we're not even
                // going to call Calc!
                OSL_ENSURE( pCnt->IsTextFrame(), "The Graphic has landed." );
                if ( static_cast<SwTextFrame*>(pCnt)->IsLocked() ||
                     static_cast<SwTextFrame*>(pCnt)->GetFollow() == pStart )
                    break;
                pCnt->Calc(pCnt->getRootFrame()->GetCurrShell()->GetOut());
                pCnt = pCnt->GetNextContentFrame();
            }
            if( bUnlock )
                static_cast<SwFootnoteFrame*>(pLay)->UnlockBackMove();
        }
        pLay = nullptr;
    }
    return pLay;
}

/// A specialized form of Paste(), which relocates a whole chain (this and the following,
/// in particular). During this process, only the minimum operations and notifications are done.
bool SwFlowFrame::PasteTree( SwFrame *pStart, SwLayoutFrame *pParent, SwFrame *pSibling,
                           SwFrame *pOldParent )
{
    // returns true if there's a LayoutFrame in the chain.
    bool bRet = false;

    // The chain beginning with pStart is inserted before pSibling
    // under the parent. We take care to invalidate as required.

    // I'm receiving a finished chain. We need to update the pointers for
    // the beginning of the chain, then all the uppers and finally the end.
    // On the way there, we invalidate as required.
    if ( pSibling )
    {
        pStart->mpPrev = pSibling->GetPrev();
        if ( nullptr != pStart->mpPrev )
            pStart->GetPrev()->mpNext = pStart;
        else
            pParent->m_pLower = pStart;
        pSibling->InvalidatePos_();
        pSibling->InvalidatePrt_();
    }
    else
    {
        pStart->mpPrev = pParent->Lower();
        if ( nullptr == pStart->mpPrev )
            pParent->m_pLower = pStart;
        else
        //i#100782
        //If the pParent has more than 1 child nodes, former design will
        //ignore them directly without any collection work. It will make some
        //dangling pointers. This lead the crash...
        //The new design will find the last child of pParent in loop way, and
        //add the pStart after the last child.
        //  pParent->Lower()->pNext = pStart;
        {
            SwFrame* pTemp = pParent->m_pLower;
            while (pTemp)
            {
                if (pTemp->mpNext)
                    pTemp = pTemp->mpNext;
                else
                {
                    pStart->mpPrev = pTemp;
                    pTemp->mpNext = pStart;
                    break;
                }
            }
        }


        // i#27145
        if ( pParent->IsSctFrame() )
        {
            // We have no sibling because pParent is a section frame and
            // has just been created to contain some content. The printing
            // area of the frame behind pParent has to be invalidated, so
            // that the correct distance between pParent and the next frame
            // can be calculated.
            pParent->InvalidateNextPrtArea();
        }
    }
    SwFrame *pFloat = pStart;
    SwFrame *pLst = nullptr;
    SwRectFnSet aRectFnSet(pParent);
    SwTwips nGrowVal = 0;
    do
    {   pFloat->mpUpper = pParent;
        pFloat->InvalidateAll_();
        pFloat->InvalidateInfFlags();
        pFloat->CheckDirChange();

        // I'm a friend of the TextFrame and thus am allowed to do many things.
        // The CacheIdx idea seems to be a bit risky!
        if ( pFloat->IsTextFrame() )
        {
            if ( static_cast<SwTextFrame*>(pFloat)->GetCacheIdx() != USHRT_MAX )
                static_cast<SwTextFrame*>(pFloat)->Init();    // I'm his friend.
        }
        else
            bRet = true;

        nGrowVal = o3tl::saturating_add(nGrowVal, aRectFnSet.GetHeight(pFloat->getFrameArea()));
        if ( pFloat->GetNext() )
            pFloat = pFloat->GetNext();
        else
        {
            pLst = pFloat;
            pFloat = nullptr;
        }
    } while ( pFloat );

    if ( pSibling )
    {
        pLst->mpNext = pSibling;
        pSibling->mpPrev = pLst;
        if( pSibling->IsInFootnote() )
        {
            if( pSibling->IsSctFrame() )
                pSibling = static_cast<SwSectionFrame*>(pSibling)->ContainsAny();
            if( pSibling )
                pSibling->Prepare( PrepareHint::ErgoSum );
        }
    }
    if ( nGrowVal )
    {
        if ( pOldParent && pOldParent->IsBodyFrame() ) // For variable page height while browsing
            pOldParent->Shrink( nGrowVal );
        pParent->Grow( nGrowVal );
    }

    if ( pParent->IsFootnoteFrame() )
        static_cast<SwFootnoteFrame*>(pParent)->InvalidateNxtFootnoteCnts( pParent->FindPageFrame() );
    return bRet;
}

void SwFlowFrame::MoveSubTree( SwLayoutFrame* pParent, SwFrame* pSibling )
{
    OSL_ENSURE( pParent, "No parent given." );
    OSL_ENSURE( m_rThis.GetUpper(), "Where are we coming from?" );

    // Be economical with notifications if an action is running.
    SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
    const SwViewShellImp *pImp = pSh ? pSh->Imp() : nullptr;
    const bool bComplete = pImp && pImp->IsAction() && pImp->GetLayAction().IsComplete();

    if ( !bComplete )
    {
        SwFrame *pPre = m_rThis.GetIndPrev();
        if ( pPre )
        {
            pPre->SetRetouche();
            // follow-up of i#26250
            // invalidate printing area of previous frame, if it's in a table
            if ( pPre->GetUpper()->IsInTab() )
            {
                pPre->InvalidatePrt_();
            }
            pPre->InvalidatePage();
        }
        else
        {
            m_rThis.GetUpper()->SetCompletePaint();
            m_rThis.GetUpper()->InvalidatePage();
        }
    }

    SwPageFrame *pOldPage = m_rThis.FindPageFrame();

    SwLayoutFrame *pOldParent;
    bool bInvaLay;

    {
        //JoinLock pParent for the lifetime of the Cut/Paste call to avoid
        //SwSectionFrame::MergeNext removing the pParent we're trying to reparent
        //into
        FlowFrameJoinLockGuard aJoinGuard(pParent);
        SwFrameDeleteGuard aDeleteGuard(pParent);
        pOldParent = CutTree( &m_rThis );
        bInvaLay = PasteTree( &m_rThis, pParent, pSibling, pOldParent );
    }

    // If, by cutting & pasting, an empty SectionFrame came into existence, it should
    // disappear automatically.
    SwSectionFrame *pSct;

    SwFlyFrame* pFly = nullptr;
    if ( pOldParent && !pOldParent->Lower() &&
         ( pOldParent->IsInSct() &&
           !(pSct = pOldParent->FindSctFrame())->ContainsContent() &&
           !pSct->ContainsAny( true ) ) )
    {
            pSct->DelEmpty( false );
    }
    else if (pOldParent && !pOldParent->Lower()
             && (pOldParent->IsInFly() && !(pFly = pOldParent->FindFlyFrame())->ContainsContent()
                 && !pFly->ContainsAny()))
    {
        if (pFly->IsFlySplitAllowed())
        {
            // Master fly is empty now that we pasted the content to the follow, mark it for
            // deletion.
            auto pFlyAtContent = static_cast<SwFlyAtContentFrame*>(pFly);
            pFlyAtContent->DelEmpty();
        }
    }

    // If we're in a column section, we'd rather not call Calc "from below"
    if( !m_rThis.IsInSct() &&
        ( !m_rThis.IsInTab() || ( m_rThis.IsTabFrame() && !m_rThis.GetUpper()->IsInTab() ) ) )
        m_rThis.GetUpper()->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
    else if( m_rThis.GetUpper()->IsSctFrame() )
    {
        SwSectionFrame* pTmpSct = static_cast<SwSectionFrame*>(m_rThis.GetUpper());
        bool bOld = pTmpSct->IsContentLocked();
        pTmpSct->SetContentLock( true );
        pTmpSct->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
        if( !bOld )
            pTmpSct->SetContentLock( false );
    }
    SwPageFrame *pPage = m_rThis.FindPageFrame();

    if ( pOldPage != pPage )
    {
        m_rThis.InvalidatePage( pPage );
        if ( m_rThis.IsLayoutFrame() )
        {
            SwContentFrame *pCnt = static_cast<SwLayoutFrame*>(&m_rThis)->ContainsContent();
            if ( pCnt )
                pCnt->InvalidatePage( pPage );
        }
        else if ( pSh && pSh->GetDoc()->GetLineNumberInfo().IsRestartEachPage()
                  && pPage->FindFirstBodyContent() == &m_rThis )
        {
            m_rThis.InvalidateLineNum_();
        }
    }
    if ( bInvaLay || (pSibling && pSibling->IsLayoutFrame()) )
        m_rThis.GetUpper()->InvalidatePage( pPage );
}

bool SwFlowFrame::IsAnFollow( const SwFlowFrame *pAssumed ) const
{
    const SwFlowFrame *pFoll = this;
    do
    {   if ( pAssumed == pFoll )
            return true;
        pFoll = pFoll->GetFollow();
    } while ( pFoll );
    return false;
}

SwTextFrame* SwContentFrame::FindMaster() const
{
    OSL_ENSURE( IsFollow(), "SwContentFrame::FindMaster(): !IsFollow" );

    const SwContentFrame* pPrec = static_cast<const SwContentFrame*>(SwFlowFrame::GetPrecede());

    if ( pPrec && pPrec->HasFollow() && pPrec->GetFollow() == this )
    {
        OSL_ENSURE( pPrec->IsTextFrame(), "NoTextFrame with follow found" );
        return const_cast<SwTextFrame*>(static_castconst SwTextFrame* >(pPrec));
    }

    OSL_FAIL( "Follow is lost in Space." );
    return nullptr;
}

SwSectionFrame* SwSectionFrame::FindMaster() const
{
    OSL_ENSURE( IsFollow(), "SwSectionFrame::FindMaster(): !IsFollow" );

    if (!m_pSection)
        return nullptr;

    SwIterator<SwSectionFrame,SwFormat> aIter( *m_pSection->GetFormat() );
    SwSectionFrame* pSect = aIter.First();
    while ( pSect )
    {
        if (pSect->GetFollow() == this)
            return pSect;
        pSect = aIter.Next();
    }

    OSL_FAIL( "Follow is lost in Space." );
    return nullptr;
}

SwTabFrame* SwTabFrame::FindMaster( bool bFirstMaster ) const
{
    OSL_ENSURE( IsFollow(), "SwTabFrame::FindMaster(): !IsFollow" );

    SwIterator<SwTabFrame,SwFormat> aIter( *GetTable()->GetFrameFormat() );
    SwTabFrame* pTab = aIter.First();
    while ( pTab )
    {
            if ( bFirstMaster )
            {
                // Optimization. This makes code like this obsolete:
                // while ( pTab->IsFollow() )
                //     pTab = pTab->FindMaster();

                if ( !pTab->IsFollow() )
                {
                    SwTabFrame* pNxt = pTab;
                    while ( pNxt )
                    {
                        if ( pNxt->GetFollow() == this )
                            return pTab;
                        pNxt = pNxt->GetFollow();
                    }
                }
            }
            else
            {
                if ( pTab->GetFollow() == this )
                    return pTab;
            }

            pTab = aIter.Next();
    }

    OSL_FAIL( "Follow is lost in Space." );
    return nullptr;
}

/**
 * Returns the next/previous Layout leaf that's NOT below this (or even is this itself).
 * Also, that leaf must be in the same text flow as the pAnch origin frame (Body, Footnote)
 */

const SwLayoutFrame *SwFrame::GetLeaf( MakePageType eMakePage, bool bFwd,
                                   const SwFrame *pAnch ) const
{
    // No flow, no joy...
    if ( !(IsInDocBody() || IsInFootnote() || IsInFly()) )
        return nullptr;

    const SwFrame *pLeaf = this;
    bool bFound = false;

    do
    {   pLeaf = const_cast<SwFrame*>(pLeaf)->GetLeaf( eMakePage, bFwd );

        if ( pLeaf &&
            (!IsLayoutFrame() || !static_cast<const SwLayoutFrame*>(this)->IsAnLower( pLeaf )))
        {
            if ( pAnch->IsInDocBody() == pLeaf->IsInDocBody() &&
                 pAnch->IsInFootnote()     == pLeaf->IsInFootnote() )
            {
                bFound = true;
            }
        }
    } while ( !bFound && pLeaf );

    return static_cast<const SwLayoutFrame*>(pLeaf);
}

SwLayoutFrame *SwFrame::GetLeaf( MakePageType eMakePage, bool bFwd )
{
    if ( IsInFootnote() )
        return bFwd ? GetNextFootnoteLeaf( eMakePage ) : GetPrevFootnoteLeaf( eMakePage );

    // i#53323
    // A frame could be inside a table AND inside a section.
    // Thus, it has to be determined, which is the first parent.
    bool bInTab( IsInTab() );
    bool bInSct( IsInSct() );
    if ( bInTab && bInSct )
    {
        const SwFrame* pUpperFrame( GetUpper() );
        while ( pUpperFrame )
        {
            if ( pUpperFrame->IsTabFrame() )
            {
                // the table is the first.
                bInSct = false;
                break;
            }
            else if ( pUpperFrame->IsSctFrame() )
            {
                // the section is the first.
                bInTab = false;
                break;
            }

            pUpperFrame = pUpperFrame->GetUpper();
        }
    }

    if ( bInTab && ( !IsTabFrame() || GetUpper()->IsCellFrame() ) ) // TABLE IN TABLE
        return bFwd ? GetNextCellLeaf() : GetPrevCellLeaf();

    if ( bInSct )
        return bFwd ? GetNextSctLeaf( eMakePage ) : GetPrevSctLeaf();

    if (IsInFly() && FindFlyFrame()->IsFlySplitAllowed())
    {
        if (bFwd)
        {
            return GetNextFlyLeaf(eMakePage);
        }
        else
        {
            return GetPrevFlyLeaf();
        }
    }

    return bFwd ? GetNextLeaf( eMakePage ) : GetPrevLeaf();
}

namespace sw {

bool HasPageBreakBefore(SwPageFrame const& rPage)
{
    SwFrame const* pFlow(rPage.FindFirstBodyContent());
    if (!pFlow)
    {
        return false;
    }
    while (pFlow->GetUpper()->IsInTab())
    {
        pFlow = pFlow->GetUpper()->FindTabFrame();
    }
    return pFlow->GetPageDescItem().GetPageDesc()
        || pFlow->GetBreakItem().GetBreak() == SvxBreak::PageBefore
        || pFlow->GetBreakItem().GetBreak() == SvxBreak::PageBoth;
};

// namespace sw

bool SwFrame::WrongPageDesc( SwPageFrame* pNew )
{
    // Now it's getting a bit complicated:

    // Maybe I'm bringing a Pagedesc myself; in that case,
    // the pagedesc of the next page needs to correspond.
    // Otherwise, I'll have to dig a bit deeper to see where
    // the following Pagedesc is coming from.
    // If the following page itself tells me that it's pagedesc
    // is wrong, I can happily exchange it.
    // If the page however thinks that it's pagedesc is correct,
    // this doesn't mean it's useful to me:
    // If the first BodyContent asks for a PageDesc or a PageBreak,
    // I'll have to insert a new page - except the desired page is
    // the correct one.
    // If I inserted a new page, the problems only get started:
    // because then it's likely for the next page to have been
    // wrong and having been swapped because of that.
    // This in turn means that I have a new (and correct) page,
    // but the conditions to swap still apply.
    // Way out of the situation: Try to preliminarily insert a
    // new page once (empty pages are already inserted by InsertPage()
    // if required)

    //My Pagedesc doesn't count if I'm a follow!
    const SwPageDesc *pDesc = nullptr;
    std::optional<sal_uInt16> oTmp;
    SwFlowFrame *pFlow = SwFlowFrame::CastFlowFrame( this );
    if ( !pFlow || !pFlow->IsFollow() )
    {
        const SwFormatPageDesc &rFormatDesc = GetPageDescItem();
        pDesc = rFormatDesc.GetPageDesc();
        if( pDesc )
        {
            if( !pDesc->GetRightFormat() )
                oTmp = 2;
            else if( !pDesc->GetLeftFormat() )
                oTmp = 1;
            else if( rFormatDesc.GetNumOffset() )
                oTmp = rFormatDesc.GetNumOffset();
        }
    }

    // Does the Content bring a Pagedesc or do we need the
    // virtual page number of the new layout leaf?
    // PageDesc isn't allowed with Follows
    const bool isRightPage = oTmp ? sw::IsRightPageByNumber(*mpRoot, *oTmp) : pNew->OnRightPage();
    if ( !pDesc )
        pDesc = pNew->FindPageDesc();

    bool bFirst = pNew->OnFirstPage();

    const SwFlowFrame *pNewFlow = pNew->FindFirstBodyContent();
    // Did we find ourselves?
    if( pNewFlow == pFlow )
        pNewFlow = nullptr;
    if ( pNewFlow && pNewFlow->GetFrame().IsInTab() )
        pNewFlow = pNewFlow->GetFrame().FindTabFrame();
    const SwPageDesc *pNewDesc= ( pNewFlow && !pNewFlow->IsFollow() )
            ? pNewFlow->GetFrame().GetPageDescItem().GetPageDesc()
            : nullptr;

    SAL_INFO( "sw.pageframe""WrongPageDesc p: " << pNew << " phys: " << pNew->GetPhyPageNum() );
    SAL_INFO( "sw.pageframe""WrongPageDesc " << pNew->GetPageDesc() << " " << pDesc );
    SAL_INFO( "sw.pageframe""WrongPageDesc right: " << isRightPage
              << " first: " << bFirst << " " << pNew->GetFormat() << " == "
              << (isRightPage ? pDesc->GetRightFormat(bFirst) : pDesc->GetLeftFormat(bFirst)) << " "
              << (isRightPage ? pDesc->GetLeftFormat(bFirst) : pDesc->GetRightFormat(bFirst)) );

    return (pNewDesc && pNewDesc == pDesc);
}

/// Returns the next layout leaf in which we can move the frame.
SwLayoutFrame *SwFrame::GetNextLeaf( MakePageType eMakePage )
{
    OSL_ENSURE( !IsInFootnote(), "GetNextLeaf(), don't call me for Footnote." );
    OSL_ENSURE( !IsInSct(), "GetNextLeaf(), don't call me for Sections." );

    const bool bBody = IsInDocBody();  // If I'm coming from the DocBody,
                                           // I want to end up in the body.

    // It doesn't make sense to insert pages, as we only want to search the
    // chain.
    if( IsInFly() )
        eMakePage = MAKEPAGE_NONE;

    // For tables, we just take the big leap. A simple GetNext would
    // iterate through the first cells and, in turn, all other cells.
    SwLayoutFrame *pLayLeaf = nullptr;
    if ( IsTabFrame() )
    {
        SwFrame *const pTmp = static_cast<SwTabFrame*>(this)->FindLastContentOrTable();
        if ( pTmp )
            pLayLeaf = pTmp->GetUpper();
    }
    if ( !pLayLeaf )
        pLayLeaf = GetNextLayoutLeaf();

    SwLayoutFrame *pOldLayLeaf = nullptr;           // Make sure that we don't have to
                                            // start searching from top when we
                                            // have a freshly created page.
    bool bNewPg = false;    // Only insert a new page once.

    while ( true )
    {
        if ( pLayLeaf )
        {
            // There's yet another LayoutFrame. Let's see if it's ready to host
            // me as well.
            // It only needs to be of the same kind like my starting point
            // (DocBody or Footnote respectively)
            if ( pLayLeaf->FindPageFrame()->IsFootnotePage() )
            {   // If I ended up at the end note pages, we're done.
                pLayLeaf = nullptr;
                continue;
            }
            if ( (bBody && !pLayLeaf->IsInDocBody()) || pLayLeaf->IsInTab()
                 || pLayLeaf->IsInSct() )
            {
                // They don't want me! Try again
                pOldLayLeaf = pLayLeaf;
                pLayLeaf = pLayLeaf->GetNextLayoutLeaf();
                continue;
            }

            // I'm wanted, therefore I'm done. However, it may still be that,
            // during a page break, the page type isn't the desired one. In that
            // case we have to insert a page of the correct type.

            if( !IsFlowFrame() && ( eMakePage == MAKEPAGE_NONE ||
                eMakePage==MAKEPAGE_APPEND || eMakePage==MAKEPAGE_NOSECTION ) )
                return pLayLeaf;

            SwPageFrame *pNew = pLayLeaf->FindPageFrame();
            const SwViewShell *pSh = getRootFrame()->GetCurrShell();
            // The pagedesc check does not make sense for frames in fly frames
            if ( pNew != FindPageFrame() && !bNewPg && !IsInFly() &&
                 // i#46683
                 // Do not consider page descriptions in browse mode (since
                 // MoveBwd ignored them)
                 !(pSh && pSh->GetViewOptions()->getBrowseMode() ) )
            {
                if( WrongPageDesc( pNew ) )
                {
                    SwFootnoteContFrame *pCont = pNew->FindFootnoteCont();
                    if( pCont )
                    {
                        // If the reference of the first footnote of this page
                        // lies before the page, we'd rather not insert a new page.

                        SwFootnoteFrame *pFootnote = static_cast<SwFootnoteFrame*>(pCont->Lower());
                        if( pFootnote && pFootnote->GetRef() )
                        {
                            const sal_uInt16 nRefNum = pNew->GetPhyPageNum();
                            if( pFootnote->GetRef()->GetPhyPageNum() < nRefNum )
                                break;
                        }
                    }
                    //Gotcha! The following page is wrong, therefore we need to
                    //insert a new one.
                    if ( eMakePage == MAKEPAGE_INSERT )
                    {
                        bNewPg = true;

                        SwPageFrame *pPg = pOldLayLeaf ?
                                    pOldLayLeaf->FindPageFrame() : nullptr;
                        if ( pPg && pPg->IsEmptyPage() )
                            // Don't insert behind. Insert before the EmptyPage.
                            pPg = static_cast<SwPageFrame*>(pPg->GetPrev());

                        if ( !pPg || pPg == pNew )
                            pPg = FindPageFrame();

                        InsertPage( pPg, false );
                        pLayLeaf = GetNextLayoutLeaf();
                        pOldLayLeaf = nullptr;
                        continue;
                    }
                    else
                        pLayLeaf = nullptr;
                }
            }
            break;
        }
        else
        {
            // There's no other matching LayoutFrame, so we have to insert
            // a new page.
            if ( eMakePage == MAKEPAGE_APPEND || eMakePage == MAKEPAGE_INSERT )
            {
                InsertPage(
                    pOldLayLeaf ? pOldLayLeaf->FindPageFrame() : FindPageFrame(),
                    false );

                // And again from the start.
                pLayLeaf = pOldLayLeaf ? pOldLayLeaf : GetNextLayoutLeaf();
            }
            else
                break;
        }
    }
    return pLayLeaf;
}

/// Returns the previous layout leaf where we can move the frame.
SwLayoutFrame *SwFrame::GetPrevLeaf()
{
    OSL_ENSURE( !IsInFootnote(), "GetPrevLeaf(), don't call me for Footnote." );

    const bool bBody = IsInDocBody();  // If I'm coming from the DocBody,
                                           // I want to end up in the body.
    const bool bFly  = IsInFly();

    SwLayoutFrame *pLayLeaf = GetPrevLayoutLeaf();
    SwLayoutFrame *pPrevLeaf = nullptr;

    while ( pLayLeaf )
    {
        if ( pLayLeaf->IsInTab() ||     // Never go into tables.
             pLayLeaf->IsInSct() )      // Same goes for sections!
            pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
        else if ( bBody && pLayLeaf->IsInDocBody() )
        {
            if ( pLayLeaf->Lower() )
                break;
            pPrevLeaf = pLayLeaf;
            pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
            if ( pLayLeaf )
                SwFlowFrame::SetMoveBwdJump( true );
        }
        else if ( bFly )
            break;  //Contents in Flys should accept any layout leaf.
        else
            pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
    }
    return pLayLeaf ? pLayLeaf : pPrevLeaf;
}

bool SwFlowFrame::IsPrevObjMove() const
{
    // true:   The FlowFrame must respect the a border of the predecessor, also needs
    //         to insert a break if required.

    //!!!!!!!!!!!Hack!!!!!!!!!!!
    const SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
    if( pSh && pSh->GetViewOptions()->getBrowseMode() )
        return false;

    SwFrame *const pPre{FindPrevIgnoreHidden()};

    if ( pPre && pPre->GetDrawObjs() )
    {
        OSL_ENSURE( SwFlowFrame::CastFlowFrame( pPre ), "new flowfrm?" );
        if( SwFlowFrame::CastFlowFrame( pPre )->IsAnFollow( this ) )
            return false;
        if (SwFlowFrame::CastFlowFrame(pPre)->IsJoinLocked())
        {
            SwBorderAttrAccess baa(SwFrame::GetCache(), pPre);
            SwBorderAttrs const& rAttrs(*baa.Get());
            if (SwFlowFrame::CastFlowFrame(pPre)->IsKeep(rAttrs.GetAttrSet().GetKeep(), pPre->GetBreakItem()))
            {   // pPre is currently being formatted - maybe it moved back but
                // its objects still have the old page's body as
                // mpVertPosOrientFrame and SwContentFrame::MakeAll() is calling
                // pNxt->Calc() in this case so allow this frame to move back
                return false// too, else pPre is forced to move forward again.
            }
        }
        SwLayoutFrame* pPreUp = pPre->GetUpper();
        // If the upper is a SectionFrame, or a column of a SectionFrame, we're
        // allowed to protrude out of it.  However, we need to respect the
        // Upper of the SectionFrame.
        if( pPreUp->IsInSct() )
        {
            if( pPreUp->IsSctFrame() )
                pPreUp = pPreUp->GetUpper();
            else if( pPreUp->IsColBodyFrame() &&
                     pPreUp->GetUpper()->GetUpper()->IsSctFrame() )
                pPreUp = pPreUp->GetUpper()->GetUpper()->GetUpper();
        }
        // i#26945 - re-factoring
        // use <GetVertPosOrientFrame()> to determine, if object has followed the
        // text flow to the next layout frame
        for (SwAnchoredObject* pObj : *pPre->GetDrawObjs())
        {
            const SwFrameFormat* pObjFormat = pObj->GetFrameFormat();
            // Do not consider hidden objects
            // i#26945 - do not consider object, which
            // doesn't follow the text flow.
            if ( pObjFormat->GetDoc().getIDocumentDrawModelAccess().IsVisibleLayerId(
                                            pObj->GetDrawObj()->GetLayer() ) &&
                 pObjFormat->GetFollowTextFlow().GetValue() )
            {
                const SwLayoutFrame* pVertPosOrientFrame = pObj->GetVertPosOrientFrame();
                if ( pVertPosOrientFrame &&
                     pPreUp != pVertPosOrientFrame &&
                     !pPreUp->IsAnLower( pVertPosOrientFrame ) )
                {
                    return true;
                }
            }
        }
    }
    return false;
}

/**
|*      If there's a hard page break before the Frame AND there's a
|*      predecessor on the same page, true is returned (we need to create a
|*      new PageBreak). Otherwise, returns false.
|*      If bAct is set to true, this function returns true if
|*      there's a PageBreak.
|*      Of course, we don't evaluate the hard page break for follows.
|*      The page break is in its own FrameFormat (BEFORE) or in the FrameFormat of the
|*      predecessor (AFTER). If there's no predecessor on the page, we don't
|*      need to think further.
|*      Also, a page break (or the need for one) is also present if
|*      the FrameFormat contains a PageDesc.
|*      The implementation works only on ContentFrames! - the definition
|*      of the predecessor is not clear for LayoutFrames.
|*/

bool SwFlowFrame::IsPageBreak( bool bAct ) const
{
    if ( !IsFollow() && m_rThis.IsInDocBody() &&
         ( !m_rThis.IsInTab() || ( m_rThis.IsTabFrame() && !m_rThis.GetUpper()->IsInTab() ) ) ) // i66968
    {
        const SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
        if( pSh && pSh->GetViewOptions()->getBrowseMode() )
            return false;

        // Determine predecessor
        const SwFrame *pPrev = m_rThis.FindPrev();
        while (pPrev && (!pPrev->IsInDocBody() || pPrev->IsHiddenNow()))
            pPrev = pPrev->FindPrev();

        if ( pPrev )
        {
            OSL_ENSURE( pPrev->IsInDocBody(), "IsPageBreak: Not in DocBody?" );
            if ( bAct )
            {   if ( m_rThis.FindPageFrame() == pPrev->FindPageFrame() )
                    return false;
            }
            else
            {   if ( m_rThis.FindPageFrame() != pPrev->FindPageFrame() )
                    return false;
            }

            //for compatibility, also break at column break if no columns exist
            const IDocumentSettingAccess& rIDSA = m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess();
            const bool bTreatSingleColumnBreakAsPageBreak = rIDSA.get(DocumentSettingId::TREAT_SINGLE_COLUMN_BREAK_AS_PAGE_BREAK);
            const SvxBreak eBreak = m_rThis.GetBreakItem().GetBreak();
            if ( eBreak == SvxBreak::PageBefore ||
                 eBreak == SvxBreak::PageBoth ||
                 ( bTreatSingleColumnBreakAsPageBreak && eBreak == SvxBreak::ColumnBefore && !m_rThis.FindColFrame() ))
                return true;
            else
            {
                const SvxBreak ePrB = pPrev->GetBreakItem().GetBreak();
                if ( ePrB == SvxBreak::PageAfter ||
                     ePrB == SvxBreak::PageBoth  ||
                    m_rThis.GetPageDescItem().GetPageDesc())
                {
                    return true;
                }
            }
        }
    }
    return false;
}

/**
|*      If there's a hard column break before the Frame AND there is
|*      a predecessor in the same column, we return true (we need to create
|*      a ColBreak). Otherwise, we return false.
|*      If bAct is set to true, we return true if there's a ColBreak.
|*      Of course, we don't evaluate the hard column break for follows.
|*
|*      The column break is in its own FrameFormat (BEFORE) or in the FrameFormat of the
|*      predecessor (AFTER). If there's no predecessor in the column, we don't
|*      need to think further.
|*      The implementation works only on ContentFrames! - the definition
|*      of the predecessor is not clear for LayoutFrames.
|*/

bool SwFlowFrame::IsColBreak( bool bAct ) const
{
    if ( !IsFollow() && (m_rThis.IsMoveable() || bAct) )
    {
        const SwFrame *pCol = m_rThis.FindColFrame();
        if ( pCol )
        {
            // Determine predecessor
            const SwFrame *pPrev = m_rThis.FindPrev();
            while( pPrev && ( ( !pPrev->IsInDocBody() && !m_rThis.IsInFly() && !m_rThis.FindFooterOrHeader() ) ||
                   pPrev->IsHiddenNow() ) )
                    pPrev = pPrev->FindPrev();

            if ( pPrev )
            {
                if ( bAct )
                {   if ( pCol == pPrev->FindColFrame() )
                        return false;
                }
                else
                {   if ( pCol != pPrev->FindColFrame() )
                        return false;
                }

                const SvxBreak eBreak = m_rThis.GetBreakItem().GetBreak();
                if ( eBreak == SvxBreak::ColumnBefore ||
                     eBreak == SvxBreak::ColumnBoth )
                    return true;
                else
                {
                    const SvxBreak ePrB = pPrev->GetBreakItem().GetBreak();
                    if ( ePrB == SvxBreak::ColumnAfter ||
                         ePrB == SvxBreak::ColumnBoth )
                        return true;
                }
            }
        }
    }
    return false;
}

// Skip hidden paragraphs and empty sections on the same level
static const SwFrame* skipHiddenSiblingFrames_(const SwFrame* pFrame)
{
    while (pFrame && pFrame->IsHiddenNow())
        pFrame = pFrame->GetPrev();
    return pFrame;
}

bool SwFlowFrame::HasParaSpaceAtPages( bool bSct ) const
{
    if( m_rThis.IsInSct() )
    {
        const SwFrame* pTmp = m_rThis.GetUpper();
        while( pTmp )
        {
            if( pTmp->IsCellFrame() || pTmp->IsFlyFrame() ||
                pTmp->IsFooterFrame() || pTmp->IsHeaderFrame() ||
                ( pTmp->IsFootnoteFrame() && !static_cast<const SwFootnoteFrame*>(pTmp)->GetMaster() ) )
                return true;
            if( pTmp->IsPageFrame() )
                return !pTmp->GetPrev() || IsPageBreak(true);
            if( pTmp->IsColumnFrame() && pTmp->GetPrev() )
                return IsColBreak( true );
            if (pTmp->IsSctFrame() && (!bSct || skipHiddenSiblingFrames_(pTmp->GetPrev())))
                return false;
            pTmp = pTmp->GetUpper();
        }
        OSL_FAIL( "HasParaSpaceAtPages: Where's my page?" );
        return false;
    }
    if( !m_rThis.IsInDocBody() || ( m_rThis.IsInTab() && !m_rThis.IsTabFrame()) ||
        IsPageBreak( true ) || ( m_rThis.FindColFrame() && IsColBreak( true ) ) )
        return true;
    const SwFrame* pTmp = m_rThis.FindColFrame();
    if( pTmp )
    {
        if( pTmp->GetPrev() )
            return false;
    }
    else
        pTmp = &m_rThis;
    pTmp = pTmp->FindPageFrame();
    return pTmp && !pTmp->GetPrev();
}

// Skip hidden paragraphs and empty sections
static const SwFrame* skipHiddenFrames_(const SwFrame* pFrame)
{
    do
    {
        pFrame = skipHiddenSiblingFrames_(pFrame);
        if (!pFrame || !pFrame->IsSctFrame())
            return pFrame;
        // Special case: found previous frame is a section
        // Search for the last content in the section
        auto pSectFrame = static_cast<const SwSectionFrame*>(pFrame);
        pFrame = pSectFrame->FindLastContent();
        // If the last content is in a table _inside_ the section,
        // take the table herself.
        // Correction: Check directly, if table is inside table, instead of indirectly
        // by checking, if section isn't inside a table
        if (pFrame && pFrame->IsInTab())
        {
            const SwTabFrame* pTableFrame = pFrame->FindTabFrame();
            if (pSectFrame->IsAnLower(pTableFrame))
                return pTableFrame;
        }
    } while (true);
}

/** helper method to determine previous frame for calculation of the
    upper space

     i#11860
*/

const SwFrame* SwFlowFrame::GetPrevFrameForUpperSpaceCalc_( const SwFrame* _pProposedPrevFrame ) const
{
    const SwFrame* pPrevFrame
        = skipHiddenFrames_(_pProposedPrevFrame ? _pProposedPrevFrame : m_rThis.GetPrev());
    if (pPrevFrame || !m_rThis.IsInFootnote()
        || !(m_rThis.IsSctFrame() || !m_rThis.IsInSct() || !m_rThis.FindSctFrame()->IsInFootnote()))
        return pPrevFrame;

    // Special case: no direct previous frame is found but frame is in footnote
    // Search for a previous frame in previous footnote,
    // if frame isn't in a section, which is also in the footnote
    const SwFootnoteFrame* pPrevFootnoteFrame =
            static_cast<const SwFootnoteFrame*>(m_rThis.FindFootnoteFrame()->GetPrev());
    if ( pPrevFootnoteFrame )
        return skipHiddenFrames_(pPrevFootnoteFrame->GetLastLower());

    return nullptr;
}

// This should be renamed to something like lcl_UseULSpacing
/// Compare styles attached to these text frames.
static bool lcl_IdenticalStyles(const SwFrame* pPrevFrame, const SwFrame* pFrame,
                                bool bAllowAcrossSections = false)
{
    if (!pFrame || !pFrame->IsTextFrame())
        return false;

    // Identical styles only applies if "the paragraphs belong to the same content area".
    if (!bAllowAcrossSections && pPrevFrame && pPrevFrame->FindSctFrame() != pFrame->FindSctFrame())
        return false;

    SwTextFormatColl *pPrevFormatColl = nullptr;
    if (pPrevFrame && pPrevFrame->IsTextFrame())
    {
        const SwTextFrame *pTextFrame = static_castconst SwTextFrame * >( pPrevFrame );
        pPrevFormatColl = dynamic_cast<SwTextFormatColl*>(
            pTextFrame->GetTextNodeForParaProps()->GetFormatColl());
    }

    const SwTextFrame* pTextFrame = static_cast<const SwTextFrame*>(pFrame);
    SwTextFormatColl* const pFormatColl
        = dynamic_cast<SwTextFormatColl*>(pTextFrame->GetTextNodeForParaProps()->GetFormatColl());
    return pPrevFormatColl == pFormatColl;
}

static bool lcl_getContextualSpacing(const SwFrame* pPrevFrame)
{
    bool bRet;
    SwBorderAttrAccess aAccess(SwFrame::GetCache(), pPrevFrame);
    const SwBorderAttrs *pAttrs = aAccess.Get();

    bRet = pAttrs->GetULSpace().GetContext();

    return bRet;
}

/// Implement top-of-the-page layout anomalies needed to match MS Word
static void lcl_PartiallyCollapseUpper(const SwFrame& rFrame, SwTwips& rUpper)
{
    const SwTextFrame* pTextFrame = rFrame.DynCastTextFrame();
    if (!pTextFrame || !rFrame.IsInDocBody() || rFrame.IsInTab() || rFrame.IsInFly())
        return;

    // re-used existing compat values to identify whether MSO-compatible layout is needed
    const IDocumentSettingAccess& rIDSA = pTextFrame->GetDoc().getIDocumentSettingAccess();
    const bool bCompat15 = rIDSA.get(DocumentSettingId::TAB_OVER_SPACING); // MSO 2013+
    const bool bCompat14 = rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN); // <= MSO 2010
    if (!bCompat15 && !bCompat14)
        return;

    // are we even allowed to consolidate the below and above spacing between paragraphs?
    if (rIDSA.get(DocumentSettingId::PARA_SPACE_MAX)) // DontUseHTMLAutoSpacing
        return;

    const SwContentFrame* pPrevPara = pTextFrame->FindPrevCnt();
    while (pPrevPara && pPrevPara->IsHiddenNow())
        pPrevPara = pPrevPara->FindPrevCnt();

    // Anything related to tables is skipped simply to avoid potential disaster
    if (!pPrevPara || pPrevPara->IsInTab())
        return;

    // MSO skips space between same-style paragraphs even at a sectionPageBreak.
    const bool bContextualSpacing = pTextFrame->GetAttrSet()->GetULSpace().GetContext();
    const bool bIdenticalStyles
        = bContextualSpacing
            && lcl_IdenticalStyles(pPrevPara, pTextFrame, /*AllowAcrossSections*/ true);
    if (bIdenticalStyles)
        rUpper = 0;
    else
    {
        // MSO is also hyper-consistent about consolidating
        // the lower-space from the previous paragraph
        // with the upper spacing of this paragraph
        const SwTwips nPrevLowerSpace
            = pPrevPara->GetAttrSet()->GetULSpace().GetLower();
        rUpper = std::max<SwTwips>(rUpper - nPrevLowerSpace, 0);
    }
}

SwTwips SwFlowFrame::CalcUpperSpace( const SwBorderAttrs *pAttrs,
                                   const SwFrame* pPr,
                                   const bool _bConsiderGrid ) const
{
    if (m_rThis.IsHiddenNow())
        return 0;

    const SwFrame* pPrevFrame = GetPrevFrameForUpperSpaceCalc_( pPr );

    std::optional<SwBorderAttrAccess> oAccess;
    SwFrame* pOwn;
    if( !pAttrs )
    {
        if( m_rThis.IsSctFrame() )
        {
            SwSectionFrame* pFoll = &static_cast<SwSectionFrame&>(m_rThis);
            do
                pOwn = pFoll->ContainsAny();
            while( !pOwn && nullptr != ( pFoll = pFoll->GetFollow() ) );
            if( !pOwn )
                return 0;
        }
        else
            pOwn = &m_rThis;
        oAccess.emplace(SwFrame::GetCache(), pOwn);
        pAttrs = oAccess->Get();
    }
    else
    {
        pOwn = &m_rThis;
    }
    SwTwips nUpper = 0;

    {
        const IDocumentSettingAccess& rIDSA = m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess();
        if( pPrevFrame )
        {
            const bool bUseFormerLineSpacing = rIDSA.get(DocumentSettingId::OLD_LINE_SPACING);
            const bool bContextualSpacingThis = pAttrs->GetULSpace().GetContext();
            const bool bContextualSpacingPrev = lcl_getContextualSpacing(pPrevFrame);
            bool bIdenticalStyles = lcl_IdenticalStyles(pPrevFrame, &m_rThis);

            const bool bContextualSpacing = bContextualSpacingThis
                                         && bContextualSpacingPrev
                                         && bIdenticalStyles;

            // tdf#125893 always ignore own top margin setting of the actual paragraph
            // with contextual spacing, if the previous paragraph is identical
            const bool bHalfContextualSpacing = !bContextualSpacing
                                         && bContextualSpacingThis
                                         && !bContextualSpacingPrev
                                         && bIdenticalStyles;

            // tdf#134463 always ignore own bottom margin setting of the previous paragraph
            // with contextual spacing, if the actual paragraph is identical
            const bool bHalfContextualSpacingPrev = !bContextualSpacing
                                         && !bContextualSpacingThis
                                         && bContextualSpacingPrev
                                         && bIdenticalStyles;

            // i#11860 - use new method to determine needed spacing
            // values of found previous frame and use these values.
            SwTwips nPrevLowerSpace = 0;
            SwTwips nPrevLineSpacing = 0;
            // i#102458
            bool bPrevLineSpacingProportional = false;
            GetSpacingValuesOfFrame( (*pPrevFrame),
                                   nPrevLowerSpace, nPrevLineSpacing,
                                   bPrevLineSpacingProportional,
                                   bIdenticalStyles);
            if( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX) )
            {
                // FIXME: apply bHalfContextualSpacing for better portability?
                nUpper = bContextualSpacing ? 0 : nPrevLowerSpace + pAttrs->GetULSpace().GetUpper();
                SwTwips nAdd = nPrevLineSpacing;
                // i#11859 - consideration of the line spacing
                //      for the upper spacing of a text frame
                if ( bUseFormerLineSpacing )
                {
                    // former consideration
                    if ( pOwn->IsTextFrame() )
                    {
                        nAdd = std::max( nAdd, SwTwips(static_cast<SwTextFrame*>(pOwn)->GetLineSpace()) );
                    }
                    nUpper += nAdd;
                }
                else
                {
                    // new consideration:
                    //      Only the proportional line spacing of the previous
                    //      text frame is considered for the upper spacing and
                    //      the line spacing values are add up instead of
                    //      building its maximum.
                    if ( pOwn->IsTextFrame() )
                    {
                        // i#102458
                        // Correction:
                        // A proportional line spacing of the previous text frame
                        // is added up to an own leading line spacing.
                        // Otherwise, the maximum of the leading line spacing
                        // of the previous text frame and the own leading line
                        // spacing is built.
                        if ( bPrevLineSpacingProportional )
                        {
                            nAdd += static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true );
                        }
                        else
                        {
                            nAdd = std::max( nAdd, SwTwips(static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true )) );
                        }
                    }
                    nUpper += nAdd;
                }
            }
            else
            {
                nUpper = bContextualSpacing ? 0 : std::max(
                                bHalfContextualSpacingPrev ? 0 : static_cast<tools::Long>(nPrevLowerSpace),
                                bHalfContextualSpacing     ? 0 : static_cast<tools::Long>(pAttrs->GetULSpace().GetUpper()) );

                // i#11859 - consideration of the line spacing
                //      for the upper spacing of a text frame
                if ( bUseFormerLineSpacing )
                {
                    // former consideration
                    if ( pOwn->IsTextFrame() )
                        nUpper = std::max( nUpper, SwTwips(static_cast<SwTextFrame*>(pOwn)->GetLineSpace()) );
                    if ( nPrevLineSpacing != 0 )
                    {
                        nUpper = std::max( nUpper, nPrevLineSpacing );
                    }
                }
                else
                {
                    // new consideration:
                    //      Only the proportional line spacing of the previous
                    //      text frame is considered for the upper spacing and
                    //      the line spacing values are add up and added to
                    //      the paragraph spacing instead of building the
                    //      maximum of the line spacings and the paragraph spacing.
                    SwTwips nAdd = nPrevLineSpacing;
                    if ( pOwn->IsTextFrame() )
                    {
                        // i#102458
                        // Correction:
                        // A proportional line spacing of the previous text frame
                        // is added up to an own leading line spacing.
                        // Otherwise, the maximum of the leading line spacing
                        // of the previous text frame and the own leading line
                        // spacing is built.
                        if ( bPrevLineSpacingProportional )
                        {
                            nAdd += static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true );
                        }
                        else
                        {
                            nAdd = std::max( nAdd, SwTwips(static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true )) );
                        }
                    }
                    nUpper += nAdd;
                }
            }
        }
        else if ( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES) &&
                  CastFlowFrame( pOwn )->HasParaSpaceAtPages( m_rThis.IsSctFrame() ) )
        {
            nUpper = pAttrs->GetULSpace().GetUpper();

            if (m_rThis.IsCollapseUpper())
            {
                nUpper = 0;
            }
            else
                lcl_PartiallyCollapseUpper(*pOwn, nUpper); // possibly modifies nUpper
        }
    }

    // i#25029 - pass previous frame <pPrevFrame>
--> --------------------

--> maximum size reached

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

Messung V0.5
C=96 H=92 G=93

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