/* -*- 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 <hints.hxx>
#include <comphelper/flagguard.hxx>
#include <tools/line.hxx>
#include <editeng/opaqitem.hxx>
#include <editeng/protitem.hxx>
#include <vcl/settings.hxx>
#include <fmtpdsc.hxx>
#include <fmtsrnd.hxx>
#include <pagedesc.hxx>
#include <pagefrm.hxx>
#include <rootfrm.hxx>
#include <ftnfrm.hxx>
#include <flyfrm.hxx>
#include <tabfrm.hxx>
#include <rowfrm.hxx>
#include <cellfrm.hxx>
#include <txtfrm.hxx>
#include <notxtfrm.hxx>
#include <viewopt.hxx>
#include <DocumentSettingManager.hxx>
#include <viscrs.hxx>
#include <dflyobj.hxx>
#include <crstate.hxx>
#include <dcontact.hxx>
#include <sortedobjs.hxx>
#include <txatbase.hxx>
#include <fmtfld.hxx>
#include <fldbas.hxx>
#include <frmatr.hxx>
#include <frmtool.hxx>
#include "../text/inftxt.hxx"
#include "../text/itrpaint.hxx"
#include <ndtxt.hxx>
#include <undobj.hxx>
#include <flyfrms.hxx>
#include <sectfrm.hxx>
 
#include <swselectionlist.hxx>
#include <comphelper/lok.hxx>
#include <osl/diagnose.h>
 
namespace {
    bool lcl_GetModelPositionForViewPoint_Objects( const SwPageFrame* pPageFrame, bool bSearchBackground,
           SwPosition *pPos, Point const & rPoint, SwCursorMoveState* pCMS  )
    {
        bool bRet = false;
        Point aPoint( rPoint );
        SwOrderIter aIter( pPageFrame );
        aIter.Top();
        while ( aIter() )
        {
            const SwVirtFlyDrawObj* pObj =
                                static_cast<const SwVirtFlyDrawObj*>(aIter());
            if (const SwContact* pContact = ::GetUserCall( aIter() ))
            {
                const SwAnchoredObject* pAnchoredObj = pContact->GetAnchoredObj( aIter() );
                const SwFrameFormat* pObjFormat = pAnchoredObj->GetFrameFormat();
                const SwFormatSurround& rSurround = pObjFormat->GetSurround();
                const SvxOpaqueItem& rOpaque = pObjFormat->GetOpaque();
                bool bInBackground = ( rSurround.GetSurround() == css::text::WrapTextMode_THROUGH ) && !rOpaque.GetValue();
 
                bool bBackgroundMatches = bInBackground == bSearchBackground;
 
                const SwFlyFrame* pFly = pObj ? pObj->GetFlyFrame() : nullptr;
                if ( pFly && bBackgroundMatches &&
                     ( ( pCMS && pCMS->m_bSetInReadOnly ) ||
                       !pFly->IsProtected() ) &&
                     pFly->GetModelPositionForViewPoint( pPos, aPoint, pCMS ) )
                {
                    bRet = true;
                    break;
                }
 
                if ( pCMS && pCMS->m_bStop )
                    return false;
            }
            aIter.Prev();
        }
        return bRet;
    }
 
    double lcl_getDistance( const SwRect& rRect, const Point& rPoint )
    {
        double nDist = 0.0;
 
        // If the point is inside the rectangle, then distance is 0
        // Otherwise, compute the distance to the center of the rectangle.
        if ( !rRect.Contains( rPoint ) )
        {
            tools::Line aLine( rPoint, rRect.Center( ) );
            nDist = aLine.GetLength( );
        }
 
        return nDist;
    }
}
 
namespace {
 
//For SwFlyFrame::GetModelPositionForViewPoint
class SwCursorOszControl
{
public:
    // So the compiler can initialize the class already. No DTOR and member
    // as public members
    const SwFlyFrame* m_pEntry;
    const SwFlyFrame* m_pStack1;
    const SwFlyFrame* m_pStack2;
 
    bool ChkOsz( const SwFlyFrame *pFly )
        {
            bool bRet = true;
            if (pFly != m_pStack1 && pFly != m_pStack2)
            {
                m_pStack1 = m_pStack2;
                m_pStack2 = pFly;
                bRet  = false;
            }
            return bRet;
        }
 
    void Entry( const SwFlyFrame *pFly )
        {
            if (!m_pEntry)
                m_pEntry = m_pStack1 = pFly;
        }
 
    void Exit( const SwFlyFrame *pFly )
        {
            if (pFly == m_pEntry)
                m_pEntry = m_pStack1 = m_pStack2 = nullptr;
        }
};
 
}
 
static SwCursorOszControl g_OszCtrl = { nullptr, nullptr, nullptr };
 
/** Searches the ContentFrame owning the PrtArea containing the point. */
bool SwLayoutFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
                               SwCursorMoveState* pCMS, bool ) const
{
    vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
    bool bRet = false;
    const SwFrame *pFrame = Lower();
    while ( !bRet && pFrame )
    {
        pFrame->Calc(pRenderContext);
 
        // #i43742# New function
        const bool bContentCheck = pFrame->IsTextFrame() && pCMS && pCMS->m_bContentCheck;
        const SwRect aPaintRect( bContentCheck ?
                                 pFrame->UnionFrame() :
                                 pFrame->GetPaintArea() );
 
        auto pTextFrame = pFrame->DynCastTextFrame();
        bool bSplitFly = false;
        if (pTextFrame && pTextFrame->HasNonLastSplitFlyDrawObj())
        {
            // Don't consider a non-last anchor of the split fly, so the view point can be corrected
            // to go to the nearest fly, instead of the last anchor on a later page.
            bSplitFly = true;
        }
 
        if ( aPaintRect.Contains( rPoint ) &&
             ( bContentCheck || pFrame->GetModelPositionForViewPoint( pPos, rPoint, pCMS ) ) && !bSplitFly )
            bRet = true;
        else
            pFrame = pFrame->GetNext();
        if ( pCMS && pCMS->m_bStop )
            return false;
    }
    return bRet;
}
 
/** Searches for the page containing the searched point. */
bool SwPageFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
                             SwCursorMoveState* pCMS, bool bTestBackground ) const
{
    Point aPoint(std::clamp(rPoint.X(), getFrameArea().Left(), getFrameArea().Right()),
                 std::clamp(rPoint.Y(), getFrameArea().Top(), getFrameArea().Bottom()));
 
    bool bRet = false;
    //Could it be a free flying one?
    //If his content should be protected, we can't set the Cursor in it, thus
    //all changes should be impossible.
    if ( GetSortedObjs() )
    {
        bRet = lcl_GetModelPositionForViewPoint_Objects( this, false, pPos, rPoint, pCMS );
    }
 
    if ( !bRet )
    {
        SwPosition aBackPos( *pPos );
        SwPosition aTextPos( *pPos );
 
        //We fix the StartPoint if no Content below the page 'answers' and then
        //start all over again one page before the current one.
        //However we can't use Flys in such a case.
        if (!SwLayoutFrame::GetModelPositionForViewPoint(&aTextPos, aPoint, pCMS))
        {
            if ( pCMS && (pCMS->m_bStop || pCMS->m_bExactOnly) )
            {
                pCMS->m_bStop = true;
                return false;
            }
 
            const SwContentFrame *pCnt = GetContentPos( aPoint, false, false, pCMS, false );
 
            auto pTextFrame = pCnt ? pCnt->DynCastTextFrame() : nullptr;
            if (pTextFrame)
            {
                SwFlyAtContentFrame* pFly = pTextFrame->HasNonLastSplitFlyDrawObj();
                if (pFly)
                {
                    // No exact match, looking for a nearest doc model position. Consider our fly
                    // frame.
                    pCnt = pFly->GetContentPos( aPoint, false, false, pCMS, false );
                }
            }
 
            SAL_WARN_IF(!pCnt, "sw.layout", "Cursor is gone to a Black hole");
            if (!pCnt)
                return false;
 
            // GetContentPos may have modified pCMS
            if ( pCMS && pCMS->m_bStop )
                return false;
 
            bool bTextRet = false;
 
            if( pCMS && pCMS->m_pFill && pCnt->IsTextFrame() )
                bTextRet = pCnt->GetModelPositionForViewPoint( &aTextPos, rPoint, pCMS );
            else
                bTextRet = pCnt->GetModelPositionForViewPoint( &aTextPos, aPoint, pCMS );
 
            if ( !bTextRet )
            {
                // Set point to pCnt, delete mark
                // this may happen, if pCnt is hidden
                if (pCnt->IsTextFrame())
                {
                    aTextPos = static_cast<SwTextFrame const*>(pCnt)->MapViewToModelPos(TextFrameIndex(0));
                }
                else
                {
                    assert(pCnt->IsNoTextFrame());
                    aTextPos.Assign( *static_cast<SwNoTextFrame const*>(pCnt)->GetNode() );
                }
            }
        }
 
        SwContentNode* pContentNode = aTextPos.GetNode().GetContentNode();
        bool bConsiderBackground = true;
        // If the text position is a clickable field, then that should have priority.
        if (pContentNode && pContentNode->IsTextNode())
        {
            SwTextNode* pTextNd = pContentNode->GetTextNode();
            SwTextAttr* pTextAttr = pTextNd->GetTextAttrForCharAt(aTextPos.GetContentIndex(), RES_TXTATR_FIELD);
            if (pTextAttr)
            {
                const SwField* pField = pTextAttr->GetFormatField().GetField();
                if (pField->IsClickable())
                    bConsiderBackground = false;
            }
        }
 
        bool bBackRet = false;
        // Check objects in the background if nothing else matched
        if ( GetSortedObjs() )
        {
            bBackRet = lcl_GetModelPositionForViewPoint_Objects( this, true, &aBackPos, rPoint, pCMS );
        }
 
        if (bConsiderBackground && bTestBackground && bBackRet)
        {
            (*pPos) = aBackPos;
        }
        else if (!bBackRet)
        {
            (*pPos) = aTextPos;
        }
        else // bBackRet && !(bConsiderBackground && bTestBackground)
        {
            /* In order to provide a selection as accurate as possible when we have both
             * text and background object, then we compute the distance between both
             * would-be positions and the click point. The shortest distance wins.
             */
            double nTextDistance = 0;
            bool bValidTextDistance = false;
            if (pContentNode)
            {
                SwContentFrame* pTextFrame = pContentNode->getLayoutFrame( getRootFrame( ) );
 
                // try this again but prefer the "previous" position
                SwCursorMoveState aMoveState;
                SwCursorMoveState *const pState(pCMS ? pCMS : &aMoveState);
                comphelper::FlagRestorationGuard g(
                        pState->m_bPosMatchesBounds, true);
                SwPosition prevTextPos(*pPos);
                if (SwLayoutFrame::GetModelPositionForViewPoint(&prevTextPos, aPoint, pState))
                {
                    SwRect aTextRect;
                    pTextFrame->GetCharRect(aTextRect, prevTextPos);
 
                    if (prevTextPos.GetContentIndex() < pContentNode->Len())
                    {
                        // aRextRect is just a line on the left edge of the
                        // previous character; to get a better measure from
                        // lcl_getDistance, extend that to a rectangle over
                        // the entire character.
                        SwPosition nextTextPos(prevTextPos);
                        nextTextPos.AdjustContent(+1);
                        SwRect nextTextRect;
                        pTextFrame->GetCharRect(nextTextRect, nextTextPos);
                        SwRectFnSet aRectFnSet(pTextFrame);
                        if (aRectFnSet.GetTop(aTextRect) ==
                            aRectFnSet.GetTop(nextTextRect)) // same line?
                        {
                            // need to handle mixed RTL/LTR portions somehow
                            if (aRectFnSet.GetLeft(aTextRect) <
                                aRectFnSet.GetLeft(nextTextRect))
                            {
                                aRectFnSet.SetRight( aTextRect,
                                    aRectFnSet.GetLeft(nextTextRect));
                            }
                            else // RTL
                            {
                                aRectFnSet.SetLeft( aTextRect,
                                    aRectFnSet.GetLeft(nextTextRect));
                            }
                        }
                    }
 
                    nTextDistance = lcl_getDistance(aTextRect, rPoint);
                    bValidTextDistance = true;
                }
            }
 
            double nBackDistance = 0;
            bool bValidBackDistance = false;
            SwContentNode* pBackNd = aBackPos.GetNode( ).GetContentNode( );
            if ( pBackNd && bConsiderBackground)
            {
                // FIXME There are still cases were we don't have the proper node here.
                SwContentFrame* pBackFrame = pBackNd->getLayoutFrame( getRootFrame( ) );
                if (pBackFrame)
                {
                    SwRect rBackRect;
                    pBackFrame->GetCharRect( rBackRect, aBackPos );
 
                    nBackDistance = lcl_getDistance( rBackRect, rPoint );
                    bValidBackDistance = true;
                }
            }
 
            if ( bValidTextDistance && bValidBackDistance && basegfx::fTools::more( nTextDistance, nBackDistance ) )
            {
                (*pPos) = aBackPos;
            }
            else
            {
                (*pPos) = aTextPos;
            }
        }
    }
 
    rPoint = aPoint;
    return true;
}
 
bool SwLayoutFrame::FillSelection( SwSelectionList& rList, const SwRect& rRect ) const
{
    if( rRect.Overlaps(GetPaintArea()) )
    {
        const SwFrame* pFrame = Lower();
        while( pFrame )
        {
            pFrame->FillSelection( rList, rRect );
            pFrame = pFrame->GetNext();
        }
    }
    return false;
}
 
bool SwPageFrame::FillSelection( SwSelectionList& rList, const SwRect& rRect ) const
{
    bool bRet = false;
    if( rRect.Overlaps(GetPaintArea()) )
    {
        bRet = SwLayoutFrame::FillSelection( rList, rRect );
        if( GetSortedObjs() )
        {
            const SwSortedObjs &rObjs = *GetSortedObjs();
            for (SwAnchoredObject* pAnchoredObj : rObjs)
            {
                const SwFlyFrame* pFly = pAnchoredObj->DynCastFlyFrame();
                if( !pFly )
                    continue;
                if( pFly->FillSelection( rList, rRect ) )
                    bRet = true;
            }
        }
    }
    return bRet;
}
 
bool SwRootFrame::FillSelection( SwSelectionList& aSelList, const SwRect& rRect) const
{
    const SwFrame *pPage = Lower();
    const tools::Long nBottom = rRect.Bottom();
    while( pPage )
    {
        if( pPage->getFrameArea().Top() < nBottom )
        {
            if( pPage->getFrameArea().Bottom() > rRect.Top() )
                pPage->FillSelection( aSelList, rRect );
            pPage = pPage->GetNext();
        }
        else
            pPage = nullptr;
    }
    return !aSelList.isEmpty();
}
 
/** Primary passes the call to the first page.
 *
 *  @return false, if the passed Point gets changed
 */
bool SwRootFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
                             SwCursorMoveState* pCMS, bool bTestBackground ) const
{
    const bool bOldAction = IsCallbackActionEnabled();
    const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( false );
    OSL_ENSURE( (Lower() && Lower()->IsPageFrame()), "No PageFrame found." );
    if( pCMS && pCMS->m_pFill )
        pCMS->m_bFillRet = false;
    Point aOldPoint = rPoint;
 
    // search for page containing rPoint. The borders around the pages are considered
    const SwPageFrame* pPage = GetPageAtPos( rPoint, nullptr, true );
 
    // #i95626#
    // special handling for <rPoint> beyond root frames area
    if ( !pPage &&
         rPoint.X() > getFrameArea().Right() &&
         rPoint.Y() > getFrameArea().Bottom() )
    {
        pPage = dynamic_cast<const SwPageFrame*>(Lower());
        while ( pPage && pPage->GetNext() )
        {
            pPage = dynamic_cast<const SwPageFrame*>(pPage->GetNext());
        }
    }
    if ( pPage )
    {
        pPage->SwPageFrame::GetModelPositionForViewPoint( pPos, rPoint, pCMS, bTestBackground );
    }
 
    const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( bOldAction );
    if( pCMS )
    {
        if( pCMS->m_bStop )
            return false;
        if( pCMS->m_pFill )
            return pCMS->m_bFillRet;
    }
    return aOldPoint == rPoint;
}
 
/**
 * If this is about a Content-carrying cell the Cursor will be force inserted into one of the ContentFrames
 * if there are no other options.
 *
 * There is no entry for protected cells.
 */
bool SwCellFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
                             SwCursorMoveState* pCMS, bool ) const
{
    vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
    // cell frame does not necessarily have a lower (split table cell)
    if ( !Lower() )
        return false;
 
    if ( !(pCMS && pCMS->m_bSetInReadOnly) &&
         GetFormat()->GetProtect().IsContentProtected() )
        return false;
 
    if ( pCMS && pCMS->m_eState == CursorMoveState::TableSel )
    {
        const SwTabFrame *pTab = FindTabFrame();
        if ( pTab->IsFollow() && pTab->IsInHeadline( *this ) )
        {
            pCMS->m_bStop = true;
            return false;
        }
    }
 
    if (Lower()->IsLayoutFrame())
        return SwLayoutFrame::GetModelPositionForViewPoint(pPos, rPoint, pCMS);
 
    Calc(pRenderContext);
    bool bRet = false;
 
    const SwFrame *pFrame = Lower();
    while (pFrame && !bRet)
    {
        pFrame->Calc(pRenderContext);
        if (pFrame->getFrameArea().Contains(rPoint))
        {
            bRet = pFrame->GetModelPositionForViewPoint(pPos, rPoint, pCMS);
            if (pCMS && pCMS->m_bStop)
                return false;
        }
        pFrame = pFrame->GetNext();
    }
    if (!bRet)
    {
        const bool bFill = pCMS && pCMS->m_pFill;
        Point aPoint(rPoint);
        const SwContentFrame *pCnt = GetContentPos(rPoint, true);
        if (bFill && pCnt->IsTextFrame())
        {
            rPoint = aPoint;
        }
        pCnt->GetModelPositionForViewPoint(pPos, rPoint, pCMS);
    }
    return true;
}
 
//Problem: If two Flys have the same size and share the same position then
//they end inside each other.
//Because we recursively check if a Point doesn't randomly lie inside another
//fly which lies completely inside the current Fly we could trigger an endless
//loop with the mentioned situation above.
//Using the helper class SwCursorOszControl we prevent the recursion. During
//a recursion GetModelPositionForViewPoint picks the one which lies on top.
bool SwFlyFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
                            SwCursorMoveState* pCMS, bool ) const
{
    vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
    g_OszCtrl.Entry( this );
 
    //If the Points lies inside the Fly, we try hard to set the Cursor inside it.
    //However if the Point sits inside a Fly which is completely located inside
    //the current one, we call GetModelPositionForViewPoint for it.
    Calc(pRenderContext);
    bool bInside = getFrameArea().Contains( rPoint ) && Lower();
    bool bRet = false;
 
    //If a Frame contains a graphic, but only text was requested, it basically
    //won't accept the Cursor.
    if ( bInside && pCMS && pCMS->m_eState == CursorMoveState::SetOnlyText &&
         (!Lower() || Lower()->IsNoTextFrame()) )
        bInside = false;
 
    const SwPageFrame *pPage = FindPageFrame();
    if ( bInside && pPage && pPage->GetSortedObjs() )
    {
        SwOrderIter aIter( pPage );
        aIter.Top();
        while ( aIter() && !bRet )
        {
            const SwVirtFlyDrawObj* pObj = static_cast<const SwVirtFlyDrawObj*>(aIter());
            const SwFlyFrame* pFly = pObj ? pObj->GetFlyFrame() : nullptr;
            if ( pFly && pFly->getFrameArea().Contains( rPoint ) &&
                 getFrameArea().Contains( pFly->getFrameArea() ) )
            {
                if (g_OszCtrl.ChkOsz(pFly))
                    break;
                bRet = pFly->GetModelPositionForViewPoint( pPos, rPoint, pCMS );
                if ( bRet )
                    break;
                if ( pCMS && pCMS->m_bStop )
                    return false;
            }
            aIter.Next();
        }
    }
 
    while ( bInside && !bRet )
    {
        const SwFrame *pFrame = Lower();
        while ( pFrame && !bRet )
        {
            pFrame->Calc(pRenderContext);
            if ( pFrame->getFrameArea().Contains( rPoint ) )
            {
                bRet = pFrame->GetModelPositionForViewPoint( pPos, rPoint, pCMS );
                if ( pCMS && pCMS->m_bStop )
                    return false;
            }
            pFrame = pFrame->GetNext();
        }
        if ( !bRet )
        {
            const bool bFill = pCMS && pCMS->m_pFill;
            Point aPoint( rPoint );
            const SwContentFrame *pCnt = GetContentPos( rPoint, true, false, pCMS );
            if ( pCMS && pCMS->m_bStop )
                return false;
            if( bFill && pCnt->IsTextFrame() )
            {
                rPoint = aPoint;
            }
            pCnt->GetModelPositionForViewPoint( pPos, rPoint, pCMS );
            bRet = true;
        }
    }
    g_OszCtrl.Exit( this );
    return bRet;
}
 
/** Layout dependent cursor travelling */
bool SwNoTextFrame::LeftMargin(SwPaM *pPam) const
{
    if( &pPam->GetPointNode() != GetNode() )
        return false;
    pPam->GetPoint()->AssignStartIndex(*GetNode());
    return true;
}
 
bool SwNoTextFrame::RightMargin(SwPaM *pPam, bool) const
{
    if( &pPam->GetPointNode() != GetNode() )
        return false;
    pPam->GetPoint()->AssignEndIndex(*GetNode());
    return true;
}
 
static const SwContentFrame *lcl_GetNxtCnt( const SwContentFrame* pCnt )
{
    return pCnt->GetNextContentFrame();
}
 
static const SwContentFrame *lcl_GetPrvCnt( const SwContentFrame* pCnt )
{
    return pCnt->GetPrevContentFrame();
}
 
typedef const SwContentFrame *(*GetNxtPrvCnt)( const SwContentFrame* );
 
/// Frame in repeated headline?
static bool lcl_IsInRepeatedHeadline( const SwFrame *pFrame,
                                    const SwTabFrame** ppTFrame = nullptr )
{
    const SwTabFrame *pTab = pFrame->FindTabFrame();
    if( ppTFrame )
        *ppTFrame = pTab;
    return pTab && pTab->IsFollow() && pTab->IsInHeadline( *pFrame );
}
 
/// Skip protected table cells. Optionally also skip repeated headlines.
//MA 1998-01-26: Chg also skip other protected areas
//FME: Skip follow flow cells
static const SwContentFrame * lcl_MissProtectedFrames( const SwContentFrame *pCnt,
                                                   GetNxtPrvCnt fnNxtPrv,
                                                   bool bMissHeadline,
                                                   bool bInReadOnly,
                                                   bool bMissFollowFlowLine )
{
    if ( pCnt && pCnt->IsInTab() )
    {
        bool bProtect = true;
        while ( pCnt && bProtect )
        {
            const SwLayoutFrame *pCell = pCnt->GetUpper();
            while ( pCell && !pCell->IsCellFrame() )
                pCell = pCell->GetUpper();
            if ( !pCell ||
                    ( ( bInReadOnly || !pCell->GetFormat()->GetProtect().IsContentProtected() ) &&
                      ( !bMissHeadline || !lcl_IsInRepeatedHeadline( pCell ) ) &&
                      ( !bMissFollowFlowLine || !pCell->IsInFollowFlowRow() ) &&
                        !pCell->IsCoveredCell() ) )
                bProtect = false;
            else
                pCnt = (*fnNxtPrv)( pCnt );
        }
    }
    else if ( !bInReadOnly )
        while ( pCnt && pCnt->IsProtected() )
            pCnt = (*fnNxtPrv)( pCnt );
 
    return pCnt;
}
 
static bool lcl_UpDown( SwPaM *pPam, const SwContentFrame *pStart,
                    GetNxtPrvCnt fnNxtPrv, bool bInReadOnly )
{
    OSL_ENSURE( FrameContainsNode(*pStart, pPam->GetPointNode().GetIndex()),
            "lcl_UpDown doesn't work for others." );
 
    const SwContentFrame *pCnt = nullptr;
 
    //We have to cheat a little bit during a table selection: Go to the
    //beginning of the cell while going up and go to the end of the cell while
    //going down.
    bool bTableSel = false;
    if ( pStart->IsInTab() &&
        pPam->GetPointNode().StartOfSectionNode() !=
        pPam->GetMarkNode().StartOfSectionNode() )
    {
        bTableSel = true;
        const SwLayoutFrame  *pCell = pStart->GetUpper();
        while ( !pCell->IsCellFrame() )
            pCell = pCell->GetUpper();
 
        // Check, if cell has a Prev/Follow cell:
        const bool bFwd = ( fnNxtPrv == lcl_GetNxtCnt );
        const SwLayoutFrame* pTmpCell = bFwd ?
            static_cast<const SwCellFrame*>(pCell)->GetFollowCell() :
            static_cast<const SwCellFrame*>(pCell)->GetPreviousCell();
 
        const SwContentFrame* pTmpStart = pStart;
        while ( pTmpCell && nullptr != ( pTmpStart = pTmpCell->ContainsContent() ) )
        {
            pCell = pTmpCell;
            pTmpCell = bFwd ?
                static_cast<const SwCellFrame*>(pCell)->GetFollowCell() :
                static_cast<const SwCellFrame*>(pCell)->GetPreviousCell();
        }
        const SwContentFrame *pNxt = pCnt = pTmpStart;
 
        while ( pCell->IsAnLower( pNxt ) )
        {
            pCnt = pNxt;
            pNxt = (*fnNxtPrv)( pNxt );
        }
    }
 
    pCnt = (*fnNxtPrv)( pCnt ? pCnt : pStart );
    pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
 
    const SwTabFrame *pStTab = pStart->FindTabFrame();
    const SwTabFrame *pTable = nullptr;
    const bool bTab = pStTab || (pCnt && pCnt->IsInTab());
    bool bEnd = !bTab;
 
    const SwFrame* pVertRefFrame = pStart;
    if ( bTableSel && pStTab )
        pVertRefFrame = pStTab;
    SwRectFnSet aRectFnSet(pVertRefFrame);
 
    SwTwips nX = 0;
    if ( bTab )
    {
        // pStart or pCnt is inside a table. nX will be used for travelling:
        SwRect aRect( pStart->getFrameArea() );
        pStart->GetCharRect( aRect, *pPam->GetPoint() );
        Point aCenter = aRect.Center();
        nX = aRectFnSet.IsVert() ? aCenter.Y() : aCenter.X();
 
        pTable = pCnt ? pCnt->FindTabFrame() : nullptr;
        if ( !pTable )
            pTable = pStTab;
 
        if ( pStTab &&
            !pStTab->GetUpper()->IsInTab() &&
            !pTable->GetUpper()->IsInTab() )
        {
            const SwFrame *pCell = pStart->GetUpper();
            while ( pCell && !pCell->IsCellFrame() )
                pCell = pCell->GetUpper();
            OSL_ENSURE( pCell, "could not find the cell" );
            nX = aRectFnSet.XInc(aRectFnSet.GetLeft(pCell->getFrameArea()),
                                 aRectFnSet.GetWidth(pCell->getFrameArea()) / 2);
 
            //The flow leads from one table to the next. The X-value needs to be
            //corrected based on the middle of the starting cell by the amount
            //of the offset of the tables.
            if ( pStTab != pTable )
            {
                nX += aRectFnSet.GetLeft(pTable->getFrameArea()) -
                      aRectFnSet.GetLeft(pStTab->getFrameArea());
            }
        }
 
        // Restrict nX to the left and right borders of pTab:
        // (is this really necessary?)
        if (pTable && !pTable->GetUpper()->IsInTab())
        {
            const bool bRTL = pTable->IsRightToLeft();
            const tools::Long nPrtLeft = bRTL ?
                                aRectFnSet.GetPrtRight(*pTable) :
                                aRectFnSet.GetPrtLeft(*pTable);
            if (bRTL != (aRectFnSet.XDiff(nPrtLeft, nX) > 0))
                nX = nPrtLeft;
            else
            {
                const tools::Long nPrtRight = bRTL ?
                                    aRectFnSet.GetPrtLeft(*pTable) :
                                    aRectFnSet.GetPrtRight(*pTable);
                if (bRTL != (aRectFnSet.XDiff(nX, nPrtRight) > 0))
                    nX = nPrtRight;
            }
        }
    }
 
    do
    {
        //If I'm in the DocumentBody, I want to stay there.
        if ( pStart->IsInDocBody() )
        {
            while (pCnt && (!pCnt->IsInDocBody() || pCnt->IsHiddenNow()))
            {
                pCnt = (*fnNxtPrv)( pCnt );
                pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
            }
        }
 
        //If I'm in the FootNoteArea, I try to reach the next FootNoteArea in
        //case of necessity.
        else if ( pStart->IsInFootnote() )
        {
            while (pCnt && (!pCnt->IsInFootnote() || pCnt->IsHiddenNow()))
            {
                pCnt = (*fnNxtPrv)( pCnt );
                pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
            }
        }
 
        //In Flys we can go ahead blindly as long as we find a Content.
        else if ( pStart->IsInFly() )
        {
            if (pCnt && pCnt->IsHiddenNow())
            {
                pCnt = (*fnNxtPrv)( pCnt );
                pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
            }
        }
 
        //Otherwise I'll just refuse to leave to current area.
        else if ( pCnt )
        {
            const SwFrame *pUp = pStart->GetUpper();
            while (pUp && pUp->GetUpper() && !(pUp->GetType() & FRM_HEADFOOT))
                pUp = pUp->GetUpper();
            bool bSame = false;
            const SwFrame *pCntUp = pCnt->GetUpper();
            while ( pCntUp && !bSame )
            {
                if ( pUp == pCntUp )
                    bSame = true;
                else
                    pCntUp = pCntUp->GetUpper();
            }
            if ( !bSame )
                pCnt = nullptr;
            else if (pCnt->IsHiddenNow()) // i73332
            {
                pCnt = (*fnNxtPrv)( pCnt );
                pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
            }
        }
 
        if ( bTab )
        {
            if ( !pCnt )
                bEnd = true;
            else
            {
                const SwTabFrame *pTab = pCnt->FindTabFrame();
                if( !pTab )
                    bEnd = true;
                else
                {
                    if ( pTab != pTable )
                    {
                        //The flow leads from one table to the next. The X-value
                        //needs to be corrected by the amount of the offset of
                        //the tables
                        if ( pTable &&
                              !pTab->GetUpper()->IsInTab() &&
                            !pTable->GetUpper()->IsInTab() )
                            nX += pTab->getFrameArea().Left() - pTable->getFrameArea().Left();
                        pTable = pTab;
                    }
                    const SwLayoutFrame *pCell = pCnt->GetUpper();
                    while ( pCell && !pCell->IsCellFrame() )
                        pCell = pCell->GetUpper();
 
                    Point aInsideCell;
                    Point aInsideCnt;
                    if ( pCell )
                    {
                        tools::Long nTmpTop = aRectFnSet.GetTop(pCell->getFrameArea());
                        if ( aRectFnSet.IsVert() )
                        {
                            if ( nTmpTop )
                                nTmpTop = aRectFnSet.XInc(nTmpTop, -1);
 
                            aInsideCell = Point( nTmpTop, nX );
                        }
                        else
                            aInsideCell = Point( nX, nTmpTop );
                    }
 
                    tools::Long nTmpTop = aRectFnSet.GetTop(pCnt->getFrameArea());
                    if ( aRectFnSet.IsVert() )
                    {
                        if ( nTmpTop )
                            nTmpTop = aRectFnSet.XInc(nTmpTop, -1);
 
                        aInsideCnt = Point( nTmpTop, nX );
                    }
                    else
                        aInsideCnt = Point( nX, nTmpTop );
 
                    if ( pCell && pCell->getFrameArea().Contains( aInsideCell ) )
                    {
                        bEnd = true;
                        //Get the right Content out of the cell.
                        if ( !pCnt->getFrameArea().Contains( aInsideCnt ) )
                        {
                            pCnt = pCell->ContainsContent();
                            if ( fnNxtPrv == lcl_GetPrvCnt )
                                while ( pCell->IsAnLower(pCnt->GetNextContentFrame()) )
                                    pCnt = pCnt->GetNextContentFrame();
                        }
                    }
                    else if ( pCnt->getFrameArea().Contains( aInsideCnt ) )
                        bEnd = true;
                }
            }
            if ( !bEnd )
            {
                pCnt = (*fnNxtPrv)( pCnt );
                pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
            }
        }
 
    } while (!bEnd || (pCnt && pCnt->IsHiddenNow()));
 
    if (pCnt == nullptr)
    {
        return false;
    }
    if (pCnt->IsTextFrame())
    {
        SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pCnt));
        *pPam->GetPoint() = pFrame->MapViewToModelPos(TextFrameIndex(
                fnNxtPrv == lcl_GetPrvCnt
                    ? pFrame->GetText().getLength()
                    : 0));
    }
    else
    {   // set the Point on the Content-Node
        assert(pCnt->IsNoTextFrame());
        const SwContentNode* const pCNd = static_cast<SwNoTextFrame const*>(pCnt)->GetNode();
        if ( fnNxtPrv == lcl_GetPrvCnt )
            pPam->GetPoint()->AssignEndIndex(*pCNd);
        else
            pPam->GetPoint()->AssignStartIndex(*pCNd);
    }
    return true;
}
 
bool SwContentFrame::UnitUp( SwPaM* pPam, const SwTwips, bool bInReadOnly ) const
{
    return ::lcl_UpDown( pPam, this, lcl_GetPrvCnt, bInReadOnly );
}
 
bool SwContentFrame::UnitDown( SwPaM* pPam, const SwTwips, bool bInReadOnly ) const
{
    return ::lcl_UpDown( pPam, this, lcl_GetNxtCnt, bInReadOnly );
}
 
/** Returns the number of the current page.
 *
 * If the method gets a PaM then the current page is the one in which the PaM sits. Otherwise the
 * current page is the first one inside the VisibleArea. We only work on available pages!
 */
sal_uInt16 SwRootFrame::GetCurrPage( const SwPaM *pActualCursor ) const
{
    assert(pActualCursor && "got no page cursor");
    SwFrame const*const pActFrame = pActualCursor->GetPoint()->GetNode().
                                    GetContentNode()->getLayoutFrame(this,
                                                    pActualCursor->GetPoint());
    return pActFrame->FindPageFrame()->GetPhyPageNum();
}
 
/** Returns a PaM which sits at the beginning of the requested page.
 *
 * Formatting is done as far as necessary.
 * The PaM sits on the last page, if the page number was chosen too big.
 *
 * @return Null, if the operation was not possible.
 */
sal_uInt16 SwRootFrame::SetCurrPage( SwCursor* pToSet, sal_uInt16 nPageNum )
{
    vcl::RenderContext* pRenderContext = GetCurrShell() ? GetCurrShell()->GetOut() : nullptr;
    OSL_ENSURE( Lower() && Lower()->IsPageFrame(), "No page available." );
 
    SwPageFrame *pPage = static_cast<SwPageFrame*>(Lower());
    bool bEnd =false;
    while ( !bEnd && pPage->GetPhyPageNum() != nPageNum )
    {   if ( pPage->GetNext() )
            pPage = static_cast<SwPageFrame*>(pPage->GetNext());
        else
        {   //Search the first ContentFrame and format until a new page is started
            //or until the ContentFrame are all done.
            const SwContentFrame *pContent = pPage->ContainsContent();
            while ( pContent && pPage->IsAnLower( pContent ) )
            {
                pContent->Calc(pRenderContext);
                pContent = pContent->GetNextContentFrame();
            }
            //Either this is a new page or we found the last page.
            if ( pPage->GetNext() )
                pPage = static_cast<SwPageFrame*>(pPage->GetNext());
            else
                bEnd = true;
        }
    }
    //pPage now points to the 'requested' page. Now we have to create the PaM
    //on the beginning of the first ContentFrame in the body-text.
    //If this is a footnote-page, the PaM will be set in the first footnote.
    const SwContentFrame *pContent = pPage->ContainsContent();
    if ( pPage->IsFootnotePage() )
        while ( pContent && !pContent->IsInFootnote() )
            pContent = pContent->GetNextContentFrame();
    else
        while ( pContent && !pContent->IsInDocBody() )
            pContent = pContent->GetNextContentFrame();
    if ( pContent )
    {
        assert(pContent->IsTextFrame());
        SwTextFrame const*const pFrame(static_cast<const SwTextFrame*>(pContent));
        *pToSet->GetPoint() = pFrame->MapViewToModelPos(pFrame->GetOffset());
 
        SwShellCursor* pSCursor = dynamic_cast<SwShellCursor*>(pToSet);
        if( pSCursor )
        {
            Point &rPt = pSCursor->GetPtPos();
            rPt = pContent->getFrameArea().Pos();
            rPt += pContent->getFramePrintArea().Pos();
        }
        return pPage->GetPhyPageNum();
    }
    return 0;
}
 
SwContentFrame *GetFirstSub( const SwLayoutFrame *pLayout )
{
    return const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(pLayout))->FindFirstBodyContent();
}
 
SwContentFrame *GetLastSub( const SwLayoutFrame *pLayout )
{
    return const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(pLayout))->FindLastBodyContent();
}
 
SwLayoutFrame *GetNextFrame( const SwLayoutFrame *pFrame )
{
    SwLayoutFrame *pNext =
        (pFrame->GetNext() && pFrame->GetNext()->IsLayoutFrame()) ?
                                            const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pFrame->GetNext())) : nullptr;
    // #i39402# in case of an empty page
    if(pNext && !pNext->ContainsContent())
        pNext = (pNext->GetNext() && pNext->GetNext()->IsLayoutFrame()) ?
                                            static_cast<SwLayoutFrame*>(pNext->GetNext()) : nullptr;
    return pNext;
}
 
SwLayoutFrame *GetThisFrame( const SwLayoutFrame *pFrame )
{
    return const_cast<SwLayoutFrame*>(pFrame);
}
 
SwLayoutFrame *GetPrevFrame( const SwLayoutFrame *pFrame )
{
    SwLayoutFrame *pPrev =
        (pFrame->GetPrev() && pFrame->GetPrev()->IsLayoutFrame()) ?
                                            const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pFrame->GetPrev())) : nullptr;
    // #i39402# in case of an empty page
    if(pPrev && !pPrev->ContainsContent())
        pPrev = (pPrev->GetPrev() && pPrev->GetPrev()->IsLayoutFrame()) ?
                                            static_cast<SwLayoutFrame*>(pPrev->GetPrev()) : nullptr;
    return pPrev;
}
 
/**
 * Returns the first/last Contentframe (controlled using the parameter fnPosPage)
 * of the current/previous/next page (controlled using the parameter fnWhichPage).
 */
bool GetFrameInPage( const SwContentFrame *pCnt, SwWhichPage fnWhichPage,
                   SwPosPage fnPosPage, SwPaM *pPam )
{
    //First find the requested page, at first the current, then the one which
    //was requests through fnWichPage.
    const SwLayoutFrame *pLayoutFrame = pCnt->FindPageFrame();
    if ( !pLayoutFrame || (nullptr == (pLayoutFrame = (*fnWhichPage)(pLayoutFrame))) )
        return false;
 
    //Now the desired ContentFrame below the page
    pCnt = (*fnPosPage)(pLayoutFrame);
    if( nullptr == pCnt )
        return false;
    else
    {
        // repeated headlines in tables
        if ( pCnt->IsInTab() && fnPosPage == GetFirstSub )
        {
            const SwTabFrame* pTab = pCnt->FindTabFrame();
            if ( pTab->IsFollow() )
            {
                if ( pTab->IsInHeadline( *pCnt ) )
                {
                    SwLayoutFrame* pRow = pTab->GetFirstNonHeadlineRow();
                    if ( pRow )
                    {
                        // We are in the first line of a follow table
                        // with repeated headings.
                        // To actually make a "real" move we take the first content
                        // of the next row
                        pCnt = pRow->ContainsContent();
                        if ( ! pCnt )
                            return false;
                    }
                }
            }
        }
 
        assert(pCnt->IsTextFrame());
        SwTextFrame const*const pFrame(static_cast<const SwTextFrame*>(pCnt));
        TextFrameIndex const nIdx((fnPosPage == GetFirstSub)
            ? pFrame->GetOffset()
            : (pFrame->GetFollow())
                ? pFrame->GetFollow()->GetOffset() - TextFrameIndex(1)
                : TextFrameIndex(pFrame->GetText().getLength()));
        *pPam->GetPoint() = pFrame->MapViewToModelPos(nIdx);
        return true;
    }
}
 
static sal_uInt64 CalcDiff(const Point &rPt1, const Point &rPt2)
{
    //Calculate the distance between the two points.
    //'delta' X^2 + 'delta'Y^2 = 'distance'^2
    sal_uInt64 dX = std::max( rPt1.X(), rPt2.X() ) -
               std::min( rPt1.X(), rPt2.X() ),
          dY = std::max( rPt1.Y(), rPt2.Y() ) -
               std::min( rPt1.Y(), rPt2.Y() );
    return (dX * dX) + (dY * dY);
}
 
/** Check if the point lies inside the page part in which also the ContentFrame lies.
 *
 * In this context header, page body, footer and footnote-container count as page part.
 * This will suit the purpose that the ContentFrame which lies in the "right" page part will be
 * accepted instead of one which doesn't lie there although his distance to the point is shorter.
 */
static const SwLayoutFrame* lcl_Inside( const SwContentFrame *pCnt, Point const & rPt )
{
    const SwLayoutFrame* pUp = pCnt->GetUpper();
    while( pUp )
    {
        if( pUp->IsPageBodyFrame() || pUp->IsFooterFrame() || pUp->IsHeaderFrame() )
        {
            if( rPt.Y() >= pUp->getFrameArea().Top() && rPt.Y() <= pUp->getFrameArea().Bottom() )
                return pUp;
            return nullptr;
        }
        if( pUp->IsFootnoteContFrame() )
            return pUp->getFrameArea().Contains( rPt ) ? pUp : nullptr;
        pUp = pUp->GetUpper();
    }
    return nullptr;
}
 
/** Search for the nearest Content to pass.
 *
 * Considers the previous, the current and the next page.
 * If no content is found, the area gets expanded until one is found.
 *
 * @return The 'semantically correct' position inside the PrtArea of the found ContentFrame.
 */
const SwContentFrame *SwLayoutFrame::GetContentPos( Point& rPoint,
                                            const bool bDontLeave,
                                            const bool bBodyOnly,
                                            SwCursorMoveState *pCMS,
                                            const bool bDefaultExpand ) const
{
    //Determine the first ContentFrame.
    const SwLayoutFrame *pStart = (!bDontLeave && bDefaultExpand && GetPrev()) ?
                                    static_cast<const SwLayoutFrame*>(GetPrev()) : this;
    const SwContentFrame *pContent = pStart->ContainsContent();
 
    if ( !pContent && (GetPrev() && !bDontLeave) )
        pContent = ContainsContent();
 
    if ( bBodyOnly )
        while ( pContent && !pContent->IsInDocBody() )
            pContent = pContent->GetNextContentFrame();
 
    const SwContentFrame *pActual= pContent;
    const SwLayoutFrame *pInside = nullptr;
    sal_uInt16 nMaxPage = GetPhyPageNum() + (bDefaultExpand ? 1 : 0);
    Point aPoint = rPoint;
    sal_uInt64 nDistance = SAL_MAX_UINT64;
 
    while ( true )  //A loop to be sure we always find one.
    {
        while ( pContent &&
                ((!bDontLeave || IsAnLower( pContent )) &&
                (pContent->GetPhyPageNum() <= nMaxPage)) )
        {
            if ( pContent->getFrameArea().Width() &&
                 ( !bBodyOnly || pContent->IsInDocBody() ) )
            {
                //If the Content lies in a protected area (cell, Footnote, section),
                //we search the next Content which is not protected.
                const SwContentFrame *pComp = pContent;
                pContent = ::lcl_MissProtectedFrames( pContent, lcl_GetNxtCnt, false,
                                        pCMS && pCMS->m_bSetInReadOnly, false );
                if ( pComp != pContent )
                    continue;
 
                if (!pContent->IsHiddenNow())
                {
                    SwRect aContentFrame( pContent->UnionFrame() );
                    if ( aContentFrame.Contains( rPoint ) )
                    {
                        pActual = pContent;
                        aPoint = rPoint;
                        break;
                    }
                    //The distance from rPoint to the nearest Point of pContent
                    //will now be calculated.
                    Point aContentPoint( rPoint );
 
                    //First set the vertical position
                    if ( aContentFrame.Top() > aContentPoint.Y() )
                        aContentPoint.setY( aContentFrame.Top() );
                    else if ( aContentFrame.Bottom() < aContentPoint.Y() )
                        aContentPoint.setY( aContentFrame.Bottom() );
 
                    //Now the horizontal position
                    if ( aContentFrame.Left() > aContentPoint.X() )
                        aContentPoint.setX( aContentFrame.Left() );
                    else if ( aContentFrame.Right() < aContentPoint.X() )
                        aContentPoint.setX( aContentFrame.Right() );
 
                    // pInside is a page area in which the point lies. As soon
                    // as pInside != 0 only frames are accepted which are
                    // placed inside.
                    if( !pInside || ( pInside->IsAnLower( pContent ) &&
                        ( !pContent->IsInFootnote() || pInside->IsFootnoteContFrame() ) ) )
                    {
                        const sal_uInt64 nDiff = ::CalcDiff(aContentPoint, rPoint);
                        bool bBetter = nDiff < nDistance;  // This one is nearer
                        if( !pInside )
                        {
                            pInside = lcl_Inside( pContent, rPoint );
                            if( pInside )  // In the "right" page area
                                bBetter = true;
                        }
                        if( bBetter )
                        {
                            aPoint = aContentPoint;
                            nDistance = nDiff;
                            pActual = pContent;
                        }
                    }
                }
            }
            pContent = pContent->GetNextContentFrame();
            if ( bBodyOnly )
                while ( pContent && !pContent->IsInDocBody() )
                    pContent = pContent->GetNextContentFrame();
        }
        if ( !pActual )
        {   //If we not yet found one we have to expand the searched
            //area, sometime we will find one!
            //MA 1997-01-09: Opt for many empty pages - if we only search inside
            //the body, we can expand the searched area sufficiently in one step.
            if ( bBodyOnly )
            {
                while ( !pContent && pStart->GetPrev() )
                {
                    ++nMaxPage;
                    if( !pStart->GetPrev()->IsLayoutFrame() )
                        return nullptr;
                    pStart = static_cast<const SwLayoutFrame*>(pStart->GetPrev());
                    if( pStart->IsInDocBody() )
                        pContent = pStart->ContainsContent();
                    else
                    {
                        const SwPageFrame *pPage = pStart->FindPageFrame();
                        if( !pPage )
                            return nullptr;
                        pContent = pPage->FindFirstBodyContent();
                    }
                }
                if ( !pContent )  // Somewhere down the road we have to start with one!
                {
                    const SwPageFrame *pPage = pStart->FindPageFrame();
                    if( !pPage )
                        return nullptr;
                    pContent = pPage->GetUpper()->ContainsContent();
                    while ( pContent && !pContent->IsInDocBody() )
                        pContent = pContent->GetNextContentFrame();
                    if ( !pContent )
                        return nullptr;   // There is no document content yet!
                }
            }
            else
            {
                ++nMaxPage;
                if ( pStart->GetPrev() )
                {
                    if( !pStart->GetPrev()->IsLayoutFrame() )
                        return nullptr;
                    pStart = static_cast<const SwLayoutFrame*>(pStart->GetPrev());
                    pContent = pStart->ContainsContent();
                }
                else // Somewhere down the road we have to start with one!
                {
                    const SwPageFrame *pPage = pStart->FindPageFrame();
                    if( !pPage )
                        return nullptr;
                    pContent = pPage->GetUpper()->ContainsContent();
                }
            }
            pActual = pContent;
        }
        else
            break;
    }
 
    OSL_ENSURE( pActual, "no Content found." );
    OSL_ENSURE( !bBodyOnly || pActual->IsInDocBody(), "Content not in Body." );
 
    //Special case for selecting tables not in repeated TableHeadlines.
    if ( pActual->IsInTab() && pCMS && pCMS->m_eState == CursorMoveState::TableSel )
    {
        const SwTabFrame *pTab = pActual->FindTabFrame();
        if ( pTab->IsFollow() && pTab->IsInHeadline( *pActual ) )
        {
            pCMS->m_bStop = true;
            return nullptr;
        }
    }
 
    //A small correction at the first/last
    Size aActualSize( pActual->getFramePrintArea().SSize() );
    if ( aActualSize.Height() > pActual->GetUpper()->getFramePrintArea().Height() )
        aActualSize.setHeight( pActual->GetUpper()->getFramePrintArea().Height() );
 
    SwRectFnSet aRectFnSet(pActual);
    if ( !pActual->GetPrev() &&
         aRectFnSet.YDiff( aRectFnSet.GetPrtTop(*pActual),
                              aRectFnSet.IsVert() ? rPoint.X() : rPoint.Y() ) > 0 )
    {
        aPoint.setY( pActual->getFrameArea().Top() + pActual->getFramePrintArea().Top() );
        aPoint.setX( pActual->getFrameArea().Left() +
                        ( pActual->IsRightToLeft() || aRectFnSet.IsVert() ?
                          pActual->getFramePrintArea().Right() :
                          pActual->getFramePrintArea().Left() ) );
    }
    else if ( !pActual->GetNext() &&
              aRectFnSet.YDiff( aRectFnSet.GetPrtBottom(*pActual),
                                   aRectFnSet.IsVert() ? rPoint.X() : rPoint.Y() ) < 0 )
    {
        aPoint.setY( pActual->getFrameArea().Top() + pActual->getFramePrintArea().Bottom() );
        aPoint.setX( pActual->getFrameArea().Left() +
                        ( pActual->IsRightToLeft() || aRectFnSet.IsVert() ?
                          pActual->getFramePrintArea().Left() :
                          pActual->getFramePrintArea().Right() ) );
    }
 
    //Bring the Point into the PrtArea
    const SwRect aRect( pActual->getFrameArea().Pos() + pActual->getFramePrintArea().Pos(),
                        aActualSize );
    if ( aPoint.Y() < aRect.Top() )
        aPoint.setY( aRect.Top() );
    else if ( aPoint.Y() > aRect.Bottom() )
        aPoint.setY( aRect.Bottom() );
    if ( aPoint.X() < aRect.Left() )
        aPoint.setX( aRect.Left() );
    else if ( aPoint.X() > aRect.Right() )
        aPoint.setX( aRect.Right() );
    rPoint = aPoint;
    return pActual;
}
 
/** Same as SwLayoutFrame::GetContentPos(). Specialized for fields and border. */
void SwPageFrame::GetContentPosition( const Point &rPt, SwPosition &rPos ) const
{
    //Determine the first ContentFrame.
    const SwContentFrame *pContent = ContainsContent();
    if ( pContent )
    {
        //Look back one more (if possible).
        const SwContentFrame *pTmp = pContent->GetPrevContentFrame();
        while ( pTmp && !pTmp->IsInDocBody() )
            pTmp = pTmp->GetPrevContentFrame();
        if ( pTmp )
            pContent = pTmp;
    }
    else
        pContent = GetUpper()->ContainsContent();
 
    const SwContentFrame *pAct = pContent;
    Point aAct       = rPt;
    sal_uInt64 nDist = SAL_MAX_UINT64;
 
    while ( pContent )
    {
        SwRect aContentFrame( pContent->UnionFrame() );
        if ( aContentFrame.Contains( rPt ) )
        {
            //This is the nearest one.
            pAct = pContent;
            break;
        }
 
        //Calculate the distance from rPt to the nearest point of pContent.
        Point aPoint( rPt );
 
        //Calculate the vertical position first
        if ( aContentFrame.Top() > rPt.Y() )
            aPoint.setY( aContentFrame.Top() );
        else if ( aContentFrame.Bottom() < rPt.Y() )
            aPoint.setY( aContentFrame.Bottom() );
 
        //And now the horizontal position
        if ( aContentFrame.Left() > rPt.X() )
            aPoint.setX( aContentFrame.Left() );
        else if ( aContentFrame.Right() < rPt.X() )
            aPoint.setX( aContentFrame.Right() );
 
        const sal_uInt64 nDiff = ::CalcDiff( aPoint, rPt );
        if ( nDiff < nDist )
        {
            aAct    = aPoint;
            nDist   = nDiff;
            pAct    = pContent;
        }
        else if ( aContentFrame.Top() > getFrameArea().Bottom() )
            //In terms of fields, it's not possible to be closer any more!
            break;
 
        pContent = pContent->GetNextContentFrame();
        while ( pContent && !pContent->IsInDocBody() )
            pContent = pContent->GetNextContentFrame();
    }
 
    //Bring the point into the PrtArea.
    assert(pAct);
    const SwRect aRect( pAct->getFrameArea().Pos() + pAct->getFramePrintArea().Pos(), pAct->getFramePrintArea().SSize() );
    if ( aAct.Y() < aRect.Top() )
        aAct.setY( aRect.Top() );
    else if ( aAct.Y() > aRect.Bottom() )
        aAct.setY( aRect.Bottom() );
    if ( aAct.X() < aRect.Left() )
        aAct.setX( aRect.Left() );
    else if ( aAct.X() > aRect.Right() )
        aAct.setX( aRect.Right() );
 
    if (!pAct->isFrameAreaDefinitionValid() ||
        (pAct->IsTextFrame() && !static_cast<SwTextFrame const*>(pAct)->HasPara()))
    {
        // ContentFrame not formatted -> always on node-beginning
        // tdf#100635 also if the SwTextFrame would require reformatting,
        // which is unwanted in case this is called from text formatting code
        rPos = static_cast<SwTextFrame const*>(pAct)->MapViewToModelPos(TextFrameIndex(0));
    }
    else
    {
        SwCursorMoveState aTmpState( CursorMoveState::SetOnlyText );
        pAct->GetModelPositionForViewPoint( &rPos, aAct, &aTmpState );
    }
}
 
/** Search the nearest Content to the passed point.
 *
 * Only search inside the BodyText.
 * @note Only the nearest vertically one will be searched.
 * @note JP 11.10.2001: only in tables we try to find the right column - Bug 72294
 */
Point SwRootFrame::GetNextPrevContentPos( const Point& rPoint, bool bNext ) const
{
    vcl::RenderContext* pRenderContext = GetCurrShell() ? GetCurrShell()->GetOut() : nullptr;
    // #123110# - disable creation of an action by a callback
    // event during processing of this method. Needed because formatting is
    // triggered by this method.
    DisableCallbackAction aDisableCallbackAction(const_cast<SwRootFrame&>(*this));
    //Search the first ContentFrame and his successor in the body area.
    //To be efficient (and not formatting too much) we'll start at the correct
    //page.
    const SwLayoutFrame *pPage = static_cast<const SwLayoutFrame*>(Lower());
    if( pPage )
        while( pPage->GetNext() && pPage->getFrameArea().Bottom() < rPoint.Y() )
            pPage = static_cast<const SwLayoutFrame*>(pPage->GetNext());
 
    const SwContentFrame *pCnt = pPage ? pPage->ContainsContent() : ContainsContent();
    while ( pCnt && !pCnt->IsInDocBody() )
        pCnt = pCnt->GetNextContentFrame();
 
    if ( !pCnt )
        return Point( 0, 0 );
 
    pCnt->Calc(pRenderContext);
    if( !bNext )
    {
        // As long as the point lies before the first ContentFrame and there are
        // still precedent pages I'll go to the next page.
        assert(pPage);
        while ( rPoint.Y() < pCnt->getFrameArea().Top() && pPage->GetPrev() )
        {
            pPage = static_cast<const SwLayoutFrame*>(pPage->GetPrev());
            pCnt = pPage->ContainsContent();
            while ( !pCnt )
            {
                pPage = static_cast<const SwLayoutFrame*>(pPage->GetPrev());
                if ( pPage )
                    pCnt = pPage->ContainsContent();
                else
                {
                    pCnt = ContainsContent();
                    return pCnt ? pCnt->UnionFrame().Pos() : Point();
                }
            }
            pCnt->Calc(pRenderContext);
        }
    }
 
    //Does the point lie above the first ContentFrame?
    if ( rPoint.Y() < pCnt->getFrameArea().Top() && !lcl_IsInRepeatedHeadline( pCnt ) )
        return pCnt->UnionFrame().Pos();
 
    Point aRet(0, 0);
    do
    {
        //Does the point lie in the current ContentFrame?
        SwRect aContentFrame( pCnt->UnionFrame() );
        if ( aContentFrame.Contains( rPoint ) && !lcl_IsInRepeatedHeadline( pCnt ))
        {
            aRet = rPoint;
            break;
        }
 
        //Is the current one the last ContentFrame?
        //If the next ContentFrame lies behind the point, then the current on is the
        //one we searched.
        const SwContentFrame *pNxt = pCnt->GetNextContentFrame();
        while ( pNxt && !pNxt->IsInDocBody() )
            pNxt = pNxt->GetNextContentFrame();
 
        //Does the point lie behind the last ContentFrame?
        if ( !pNxt )
        {
            aRet = Point( aContentFrame.Right(), aContentFrame.Bottom() );
            break;
        }
 
        //If the next ContentFrame lies behind the point then it is the one we
        //searched.
        const SwTabFrame* pTFrame;
        pNxt->Calc(pRenderContext);
        if( pNxt->getFrameArea().Top() > rPoint.Y() &&
            !lcl_IsInRepeatedHeadline( pCnt, &pTFrame ) &&
            ( !pTFrame || pNxt->getFrameArea().Left() > rPoint.X() ))
        {
            if (bNext)
                aRet = pNxt->getFrameArea().Pos();
            else
                aRet = Point( aContentFrame.Right(), aContentFrame.Bottom() );
            break;
        }
        pCnt = pNxt;
    }
    while (pCnt);
    return aRet;
}
 
/** Returns the absolute document position of the desired page.
 *
 * Formatting is done only as far as needed and only if bFormat=true.
 * Pos is set to the one of the last page, if the page number was chosen too big.
 *
 * @return Null, if the operation failed.
 */
Point SwRootFrame::GetPagePos( sal_uInt16 nPageNum ) const
{
    OSL_ENSURE( Lower() && Lower()->IsPageFrame(), "No page available." );
 
    const SwPageFrame *pPage = static_cast<const SwPageFrame*>(Lower());
    while ( true )
    {
        if ( pPage->GetPhyPageNum() >= nPageNum || !pPage->GetNext() )
            break;
        pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
    }
    return pPage->getFrameArea().Pos();
}
 
/** get page frame by physical page number
 *
 * @return pointer to the page frame with the given physical page number
 */
SwPageFrame* SwRootFrame::GetPageByPageNum( sal_uInt16 _nPageNum ) const
{
    const SwPageFrame* pPageFrame = static_cast<const SwPageFrame*>( Lower() );
    while ( pPageFrame && pPageFrame->GetPhyPageNum() < _nPageNum )
    {
          pPageFrame = static_cast<const SwPageFrame*>( pPageFrame->GetNext() );
    }
 
    if ( pPageFrame && pPageFrame->GetPhyPageNum() == _nPageNum )
    {
        return const_cast<SwPageFrame*>( pPageFrame );
    }
    else
    {
        return nullptr;
    }
}
 
/**
 * @return true, when the given physical pagenumber doesn't exist or this page is an empty page.
 */
bool SwRootFrame::IsDummyPage( sal_uInt16 nPageNum ) const
{
    if( !Lower() || !nPageNum || nPageNum > GetPageNum() )
        return true;
 
    const SwPageFrame *pPage = static_cast<const SwPageFrame*>(Lower());
    while( pPage && nPageNum < pPage->GetPhyPageNum() )
        pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
    return !pPage || pPage->IsEmptyPage();
}
 
/** Is the Frame or rather the Section in which it lies protected?
 *
 * Also Fly in Fly in ... and Footnotes
 */
bool SwFrame::IsProtected() const
{
    if (IsTextFrame())
    {
        const SwDoc *pDoc = &static_cast<const SwTextFrame*>(this)->GetDoc();
        bool isFormProtected=pDoc->GetDocumentSettingManager().get(DocumentSettingId::PROTECT_FORM );
        if (isFormProtected)
        {
            return false; // TODO a hack for now, well deal with it later, I we return true here we have a "double" locking
        }
    }
    //The Frame can be protected in borders, cells or sections.
    //Also goes up FlyFrames recursive and from footnote to anchor.
    const SwFrame *pFrame = this;
    do
    {
        if (pFrame->IsTextFrame())
        {   // sw_redlinehide: redlines can't overlap section nodes, so any node will do
            if (static_cast<SwTextFrame const*>(pFrame)->GetTextNodeFirst()->IsInProtectSect())
            {
                return true;
            }
        }
        else if ( pFrame->IsContentFrame() )
        {
            assert(pFrame->IsNoTextFrame());
            if (static_cast<const SwNoTextFrame*>(pFrame)->GetNode() &&
                static_cast<const SwNoTextFrame*>(pFrame)->GetNode()->IsInProtectSect())
            {
                return true;
            }
        }
        else
        {
            if ( static_cast<const SwLayoutFrame*>(pFrame)->GetFormat() &&
                 static_cast<const SwLayoutFrame*>(pFrame)->GetFormat()->
                 GetProtect().IsContentProtected() )
                return true;
            if ( pFrame->IsCoveredCell() )
                return true;
        }
        if ( pFrame->IsFlyFrame() )
        {
            //In a chain the protection of the content can be specified by the
            //master of the chain.
            if ( static_cast<const SwFlyFrame*>(pFrame)->GetPrevLink() )
            {
                const SwFlyFrame *pMaster = static_cast<const SwFlyFrame*>(pFrame);
                do
                {   pMaster = pMaster->GetPrevLink();
                } while ( pMaster->GetPrevLink() );
                if ( pMaster->IsProtected() )
                    return true;
            }
            pFrame = static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame();
        }
        else if ( pFrame->IsFootnoteFrame() )
            pFrame = static_cast<const SwFootnoteFrame*>(pFrame)->GetRef();
        else
            pFrame = pFrame->GetUpper();
 
    } while ( pFrame );
 
    return false;
}
 
// virtual
bool SwFrame::IsHiddenNow() const
{
    if (const auto* pSectFrame = FindSctFrame())
        return pSectFrame->IsHiddenNow();
 
    return false;
}
 
void SwFrame::MakeValidZeroHeight()
{
    SwRectFnSet aRectFnSet(this);
    {
        SwFrameAreaDefinition::FramePrintAreaWriteAccess area(*this);
        aRectFnSet.SetHeight(area, 0);
    }
    ShrinkFrame(aRectFnSet.GetHeight(getFrameArea()));
    if (IsLayoutFrame()) // ShrinkFrame might do nothing!
    {
        SwFrameAreaDefinition::FrameAreaWriteAccess area(*this);
        aRectFnSet.SetHeight(area, 0);
    }
    setFrameAreaSizeValid(true);
    setFramePrintAreaValid(true);
    setFrameAreaPositionValid(false);
}
 
/** @return the physical page number */
sal_uInt16 SwFrame::GetPhyPageNum() const
{
    const SwPageFrame *pPage = FindPageFrame();
    return pPage ? pPage->GetPhyPageNum() : 0;
}
 
/** Decides if the page want to be a right page or not.
 *
 * If the first content of the page has a page descriptor, we take the follow
 * of the page descriptor of the last not empty page. If this descriptor allows
 * only right(left) pages and the page isn't an empty page then it wants to be
 * such right(left) page. If the descriptor allows right and left pages, we
 * look for a number offset in the first content. If there is one, odd number
 * results right pages (or left pages if document starts with even number),
 * even number results left pages (or right pages if document starts with even
 * number).
 * If there is no number offset, we take the physical page number instead,
 * but a previous empty page doesn't count.
 */
bool SwFrame::WannaRightPage() const
{
    const SwPageFrame *pPage = FindPageFrame();
    if ( !pPage || !pPage->GetUpper() )
        return true;
 
    const SwFrame *pFlow = pPage->FindFirstBodyContent();
    const SwPageDesc *pDesc = nullptr;
    ::std::optional<sal_uInt16> oPgNum;
    if ( pFlow )
    {
        if ( pFlow->IsInTab() )
            pFlow = pFlow->FindTabFrame();
        const SwFlowFrame *pTmp = SwFlowFrame::CastFlowFrame( pFlow );
        if ( !pTmp->IsFollow() )
        {
            const SwFormatPageDesc& rPgDesc = pFlow->GetPageDescItem();
            pDesc = rPgDesc.GetPageDesc();
            oPgNum = rPgDesc.GetNumOffset();
        }
    }
    if ( !pDesc )
    {
        const SwPageFrame* pPrv = static_cast<const SwPageFrame*>(pPage->GetPrev());
        if( pPrv && pPrv->IsEmptyPage() )
            pPrv = static_cast<const SwPageFrame*>(pPrv->GetPrev());
        if( pPrv )
            pDesc = pPrv->GetPageDesc()->GetFollow();
        else
        {
            const SwDoc* pDoc = pPage->GetFormat()->GetDoc();
            pDesc = &pDoc->GetPageDesc( 0 );
        }
    }
    OSL_ENSURE( pDesc, "No pagedescriptor" );
    bool isRightPage;
    if( oPgNum )
        isRightPage = sw::IsRightPageByNumber(*mpRoot, *oPgNum);
    else
    {
        isRightPage = pPage->OnRightPage();
        if( pPage->GetPrev() && static_cast<const SwPageFrame*>(pPage->GetPrev())->IsEmptyPage() )
            isRightPage = !isRightPage;
    }
    if( !pPage->IsEmptyPage() )
    {
        assert(pDesc && "No pagedescriptor");
        if( !pDesc->GetRightFormat() )
            isRightPage = false;
        else if( !pDesc->GetLeftFormat() )
            isRightPage = true;
    }
    return isRightPage;
}
 
bool SwFrame::OnFirstPage() const
{
    bool bRet = false;
    const SwPageFrame *pPage = FindPageFrame();
 
    if (pPage)
    {
        const SwPageFrame* pPrevFrame = dynamic_cast<const SwPageFrame*>(pPage->GetPrev());
        if (pPrevFrame)
        {
            // first page of layout may be empty page, but only if it starts with "Left Page" style
            const SwPageDesc* pDesc = pPage->GetPageDesc();
            bRet = pPrevFrame->GetPageDesc() != pDesc;
        }
        else
            bRet = true;
    }
    return bRet;
}
 
void SwFrame::Calc(vcl::RenderContext* pRenderContext) const
{
    if ( !isFrameAreaPositionValid() || !isFramePrintAreaValid() || !isFrameAreaSizeValid() )
    {
        const_cast<SwFrame*>(this)->PrepareMake(pRenderContext);
    }
}
 
Point SwFrame::GetRelPos() const
{
    Point aRet( getFrameArea().Pos() );
    // here we cast since SwLayoutFrame is declared only as forwarded
    aRet -= GetUpper()->getFramePrintArea().Pos();
    aRet -= GetUpper()->getFrameArea().Pos();
    return aRet;
}
 
/** @return the virtual page number with the offset. */
sal_uInt16 SwFrame::GetVirtPageNum() const
{
    const SwPageFrame *pPage = FindPageFrame();
    if ( !pPage || !pPage->GetUpper() )
        return 0;
 
    sal_uInt16 nPhyPage = pPage->GetPhyPageNum();
    const SwRootFrame* pRootFrame = static_cast<const SwRootFrame*>(pPage->GetUpper());
    if ( !pRootFrame->IsVirtPageNum() )
        return nPhyPage;
 
    //Search the nearest section using the virtual page number.
    const SwFrame *pFoundFrame = nullptr;
    const SwPageFrame* pPageFrameIter = pPage;
    while (pPageFrameIter)
    {
        const SwContentFrame* pContentFrame = pPageFrameIter->FindFirstBodyContent();
        if (pContentFrame)
        {
            const SwFormatPageDesc& rFormatPageDesc = pContentFrame->GetPageDescItem();
 
            if ( rFormatPageDesc.GetNumOffset() && rFormatPageDesc.GetDefinedIn() )
            {
                const sw::BroadcastingModify* pMod = rFormatPageDesc.GetDefinedIn();
                sw::VirtPageNumHint aHint(pPage);
                pMod->CallSwClientNotify(aHint);
                if(aHint.GetPage())
                {
                    pFoundFrame = aHint.GetFrame();
                    break;
                }
            }
        }
        pPageFrameIter = static_cast<const SwPageFrame*>(pPageFrameIter->GetPrev());
    }
    if ( pFoundFrame )
    {
        ::std::optional<sal_uInt16> oNumOffset = pFoundFrame->GetPageDescItem().GetNumOffset();
        if (oNumOffset)
        {
            return nPhyPage - pFoundFrame->GetPhyPageNum() + *oNumOffset;
        }
        else
        {
            return nPhyPage - pFoundFrame->GetPhyPageNum();
        }
    }
    return nPhyPage;
}
 
/** Determines and sets those cells which are enclosed by the selection. */
bool SwRootFrame::MakeTableCursors( SwTableCursor& rTableCursor )
{
    //Find Union-Rects and tables (Follows) of the selection.
    OSL_ENSURE( rTableCursor.GetPointContentNode() && rTableCursor.GetMarkContentNode(),
            "Tabselection not on Cnt." );
 
    bool bRet = false;
 
    // For new table models there's no need to ask the layout...
    if( rTableCursor.NewTableSelection() )
        return true;
 
    Point aPtPt, aMkPt;
    {
        SwShellCursor* pShCursor = dynamic_cast<SwShellCursor*>(&rTableCursor);
 
        if( pShCursor )
        {
            aPtPt = pShCursor->GetPtPos();
            aMkPt = pShCursor->GetMkPos();
        }
    }
 
    // #151012# Made code robust here
    const SwContentNode* pTmpStartNode = rTableCursor.GetPointContentNode();
    const SwContentNode* pTmpEndNode   = rTableCursor.GetMarkContentNode();
 
    std::pair<Point, bool> tmp(aPtPt, false);
    const SwFrame *const pTmpStartFrame = pTmpStartNode ? pTmpStartNode->getLayoutFrame(this, nullptr, &tmp) : nullptr;
    tmp.first = aMkPt;
    const SwFrame *const pTmpEndFrame = pTmpEndNode ? pTmpEndNode->getLayoutFrame(this, nullptr, &tmp) : nullptr;
 
    const SwLayoutFrame* pStart = pTmpStartFrame ? pTmpStartFrame->GetUpper() : nullptr;
    const SwLayoutFrame* pEnd   = pTmpEndFrame   ? pTmpEndFrame->GetUpper() : nullptr;
 
    OSL_ENSURE( pStart && pEnd, "MakeTableCursors: Good to have the code robust here!" );
 
    /* #109590# Only change table boxes if the frames are
        valid. Needed because otherwise the table cursor after moving
        table cells by dnd resulted in an empty tables cursor.  */
    if ( pStart && pEnd && pStart->isFrameAreaDefinitionValid() && pEnd->isFrameAreaDefinitionValid())
    {
        SwSelUnions aUnions;
        ::MakeSelUnions( aUnions, pStart, pEnd );
 
        SwSelBoxes aNew;
 
        const bool bReadOnlyAvailable = rTableCursor.IsReadOnlyAvailable();
 
        for (SwSelUnion & rUnion : aUnions)
        {
            const SwTabFrame *pTable = rUnion.GetTable();
 
            // Skip any repeated headlines in the follow:
            const SwLayoutFrame* pRow = pTable->IsFollow() ?
                                pTable->GetFirstNonHeadlineRow() :
                                static_cast<const SwLayoutFrame*>(pTable->Lower());
 
            while ( pRow )
            {
                if ( pRow->getFrameArea().Overlaps( rUnion.GetUnion() ) )
                {
                    const SwLayoutFrame *pCell = pRow->FirstCell();
 
                    while ( pCell && pRow->IsAnLower( pCell ) )
                    {
                        OSL_ENSURE( pCell->IsCellFrame(), "Frame without cell" );
                        if( IsFrameInTableSel( rUnion.GetUnion(), pCell ) &&
                            (bReadOnlyAvailable ||
                             !pCell->GetFormat()->GetProtect().IsContentProtected()))
                        {
                            SwTableBox* pInsBox = const_cast<SwTableBox*>(
                                static_cast<const SwCellFrame*>(pCell)->GetTabBox());
                            aNew.insert( pInsBox );
                        }
                        if ( pCell->GetNext() )
                        {
                            pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext());
                            if ( pCell->Lower() && pCell->Lower()->IsRowFrame() )
                                pCell = pCell->FirstCell();
                        }
                        else
                        {
                            const SwLayoutFrame* pLastCell = pCell;
                            do
                            {
                                pCell = pCell->GetNextLayoutLeaf();
                            } while ( pCell && pLastCell->IsAnLower( pCell ) );
                            // For sections with columns
                            if( pCell && pCell->IsInTab() )
                            {
                                while( !pCell->IsCellFrame() )
                                {
                                    pCell = pCell->GetUpper();
                                    assert(pCell && "Where's my cell?");
                                }
                            }
                        }
                    }
                }
                pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext());
            }
        }
 
        rTableCursor.ActualizeSelection( aNew );
        bRet = true;
    }
 
    return bRet;
}
 
static void Sub( SwRegionRects& rRegion, const SwRect& rRect )
{
    if( rRect.Width() > 1 && rRect.Height() > 1 &&
        rRect.Overlaps( rRegion.GetOrigin() ))
        rRegion -= rRect;
}
 
static void Add( SwRegionRects& rRegion, const SwRect& rRect )
{
    if( rRect.Width() > 1 && rRect.Height() > 1 )
        rRegion += rRect;
}
 
/*
 * The following situations can happen (simplified version, before
 * CJK/CTL features were added):
 *  1. Start and end in same line of text and in the same frame
 *     -> one rectangle out of start and end; and we're okay
 *  2. Start and end in same frame
 *     -> expand start to the right, end to the left and if more than two
 *        lines of text are involved - calculate the in-between area
 *  3. Start and end in different frames
 *     -> expand start to the right until frame-end, calculate Rect
 *        expand end to the left until frame-start, calculate Rect
 *        and if more than two frames are involved add the PrtArea of all
 *        frames which lie in between
 *
 * Big reorganization because of FlyFrame - those need to be excluded.
 * Exceptions:  - The Fly in which the selection took place (if it took place
 *                 in a Fly)
 *              - The Flys which are below the text (in z-order)
 *              - The Flys which are anchored to somewhere inside the selection.
 * Functioning: First a SwRegion with a root gets initialized.
 *              Out of the region the selected areas are cut out. The
 *              section gets compressed and finally inverted and thereby the
 *              rectangles are available for highlighting.
 *              In the end the Flys are cut out of the region.
 */
void SwRootFrame::CalcFrameRects(SwShellCursor const& rCursor, SwRects & rRects, RectsMode const eMode)
{
    auto [pStartPos, pEndPos] = rCursor.StartEnd(); // SwPosition*
 
    SwViewShell *pSh = GetCurrShell();
 
    bool bIgnoreVisArea = true;
    if (pSh)
        bIgnoreVisArea = pSh->GetViewOptions()->IsPDFExport() || comphelper::LibreOfficeKit::isActive();
 
    SwRegionRects aRegion( !bIgnoreVisArea ?
                           pSh->VisArea() :
                           getFrameArea() );
    if( !pStartPos->GetNode().IsContentNode() ||
        !pStartPos->GetNode().GetContentNode()->getLayoutFrame(this) ||
        ( pStartPos->GetNode() != pEndPos->GetNode() &&
          ( !pEndPos->GetNode().IsContentNode() ||
            !pEndPos->GetNode().GetContentNode()->getLayoutFrame(this) ) ) )
    {
        return;
    }
 
    DisableCallbackAction a(*this); // the GetCharRect below may format
 
    //First obtain the ContentFrames for the start and the end - those are needed
    //anyway.
    std::pair<Point, bool> tmp(rCursor.GetSttPos(), true);
    SwContentFrame* pStartFrame = pStartPos->GetNode().
        GetContentNode()->getLayoutFrame(this, pStartPos, &tmp);
 
    tmp.first = rCursor.GetEndPos();
    SwContentFrame* pEndFrame   = pEndPos->GetNode().
        GetContentNode()->getLayoutFrame(this, pEndPos, &tmp);
 
    assert(pStartFrame && pEndFrame && "No ContentFrames found.");
    //tdf#119224 start and end are expected to exist for the scope of this function
    SwFrameDeleteGuard aStartFrameGuard(pStartFrame), aEndFrameGuard(pEndFrame);
 
    // Do not subtract FlyFrames that contain selected Frames.
    SwSortedObjs aSortObjs;
    if ( pStartFrame->IsInFly() )
    {
        SwAnchoredObject* pObj = pStartFrame->FindFlyFrame();
        OSL_ENSURE( pObj, "No Start Object." );
        if (pObj) aSortObjs.Insert( *pObj );
        SwAnchoredObject* pObj2 = pEndFrame->FindFlyFrame();
        OSL_ENSURE( pObj2, "SwRootFrame::CalcFrameRects(..) - FlyFrame missing - looks like an invalid selection" );
        if ( pObj2 != nullptr && pObj2 != pObj )
        {
            aSortObjs.Insert(*pObj2);
        }
    }
 
    // if a selection which is not allowed exists, we correct what is not
    // allowed (header/footer/table-headline start/end on different pages).
    do {    // middle check loop
        const SwLayoutFrame* pSttLFrame = pStartFrame->GetUpper();
        const SwFrameType cHdFtTableHd = SwFrameType::Header | SwFrameType::Footer | SwFrameType::Tab;
        while( pSttLFrame &&
            ! (cHdFtTableHd & pSttLFrame->GetType() ))
            pSttLFrame = pSttLFrame->GetUpper();
        if( !pSttLFrame )
            break;
        const SwLayoutFrame* pEndLFrame = pEndFrame->GetUpper();
        while( pEndLFrame &&
            ! (cHdFtTableHd & pEndLFrame->GetType() ))
            pEndLFrame = pEndLFrame->GetUpper();
        if( !pEndLFrame )
            break;
 
        OSL_ENSURE( pEndLFrame->GetType() == pSttLFrame->GetType(),
            "Selection over different content" );
        switch( pSttLFrame->GetType() )
        {
        case SwFrameType::Header:
        case SwFrameType::Footer:
            // On different pages? Then always on the start-page
            if( pEndLFrame->FindPageFrame() != pSttLFrame->FindPageFrame() )
            {
                // Set end- to the start-ContentFrame
                if( pStartPos == rCursor.GetPoint() )
                    pEndFrame = pStartFrame;
                else
                    pStartFrame = pEndFrame;
            }
            break;
        case SwFrameType::Tab:
            // On different pages? Then check for table-headline
            {
                const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>(pSttLFrame);
                if( ( pTabFrame->GetFollow() ||
                    static_cast<const SwTabFrame*>(pEndLFrame)->GetFollow() ) &&
                    pTabFrame->GetTable()->GetRowsToRepeat() > 0 &&
                    pTabFrame->GetLower() != static_cast<const SwTabFrame*>(pEndLFrame)->GetLower() &&
                    ( lcl_IsInRepeatedHeadline( pStartFrame ) ||
                    lcl_IsInRepeatedHeadline( pEndFrame ) ) )
                {
                    // Set end- to the start-ContentFrame
                    if( pStartPos == rCursor.GetPoint() )
                        pEndFrame = pStartFrame;
                    else
                        pStartFrame = pEndFrame;
                }
            }
            break;
        default: break;
        }
    } while( false );
 
    SwCursorMoveState aTmpState( CursorMoveState::NONE );
    aTmpState.m_b2Lines = true;
    aTmpState.m_bNoScroll = true;
    aTmpState.m_nCursorBidiLevel = pStartFrame->IsRightToLeft() ? 1 : 0;
 
    //ContentRects to Start- and EndFrames.
    SwRect aStRect, aEndRect;
    pStartFrame->GetCharRect( aStRect, *pStartPos, &aTmpState );
    std::unique_ptr<Sw2LinesPos> pSt2Pos = std::move(aTmpState.m_p2Lines);
    aTmpState.m_nCursorBidiLevel = pEndFrame->IsRightToLeft() ? 1 : 0;
 
    pEndFrame->GetCharRect( aEndRect, *pEndPos, &aTmpState );
    std::unique_ptr<Sw2LinesPos> pEnd2Pos = std::move(aTmpState.m_p2Lines);
 
    SwRect aStFrame ( pStartFrame->UnionFrame( true ) );
    aStFrame.Intersection( pStartFrame->GetPaintArea() );
    SwRect aEndFrame( pStartFrame == pEndFrame ? aStFrame : pEndFrame->UnionFrame( true ) );
    if( pStartFrame != pEndFrame )
    {
        aEndFrame.Intersection( pEndFrame->GetPaintArea() );
    }
    SwRectFnSet aRectFnSet(pStartFrame);
    const bool bR2L = pStartFrame->IsRightToLeft();
    const bool bEndR2L = pEndFrame->IsRightToLeft();
    const bool bB2T = pStartFrame->IsVertLRBT();
 
    // If there's no doubleline portion involved or start and end are both
    // in the same doubleline portion, all works fine, but otherwise
    // we need the following...
    if( pSt2Pos != pEnd2Pos && ( !pSt2Pos || !pEnd2Pos ||
        pSt2Pos->aPortion != pEnd2Pos->aPortion ) )
    {
        // If we have a start(end) position inside a doubleline portion
        // the surrounded part of the doubleline portion is subtracted
        // from the region and the aStRect(aEndRect) is set to the
        // end(start) of the doubleline portion.
        if( pSt2Pos )
        {
            SwRect aTmp( aStRect );
 
            // BiDi-Portions are swimming against the current.
            const bool bPorR2L = ( MultiPortionType::BIDI == pSt2Pos->nMultiType ) ?
                ! bR2L :
            bR2L;
 
            if( MultiPortionType::BIDI == pSt2Pos->nMultiType &&
                aRectFnSet.GetWidth(pSt2Pos->aPortion2) )
            {
                // nested bidi portion
                tools::Long nRightAbs = aRectFnSet.GetRight(pSt2Pos->aPortion);
                nRightAbs -= aRectFnSet.GetLeft(pSt2Pos->aPortion2);
                tools::Long nLeftAbs = nRightAbs - aRectFnSet.GetWidth(pSt2Pos->aPortion2);
 
                aRectFnSet.SetRight( aTmp, nRightAbs );
 
                if ( ! pEnd2Pos || pEnd2Pos->aPortion != pSt2Pos->aPortion )
                {
                    SwRect aTmp2( pSt2Pos->aPortion );
                    aRectFnSet.SetRight( aTmp2, nLeftAbs );
                    aTmp2.Intersection( aEndFrame );
                    Sub( aRegion, aTmp2 );
                }
            }
            else
            {
                if( bPorR2L )
                    aRectFnSet.SetLeft( aTmp, aRectFnSet.GetLeft(pSt2Pos->aPortion) );
                else
                    aRectFnSet.SetRight( aTmp, aRectFnSet.GetRight(pSt2Pos->aPortion) );
            }
 
            if( MultiPortionType::ROT_90 == pSt2Pos->nMultiType ||
                aRectFnSet.GetTop(pSt2Pos->aPortion) ==
                aRectFnSet.GetTop(aTmp) )
            {
                aRectFnSet.SetTop( aTmp, aRectFnSet.GetTop(pSt2Pos->aLine) );
            }
 
            aTmp.Intersection( aStFrame );
            Sub( aRegion, aTmp );
 
            SwTwips nTmp = aRectFnSet.GetBottom(pSt2Pos->aLine);
            if( MultiPortionType::ROT_90 != pSt2Pos->nMultiType &&
                aRectFnSet.BottomDist( aStRect, nTmp ) > 0 )
            {
                aRectFnSet.SetTop( aTmp, aRectFnSet.GetBottom(aTmp) );
                aRectFnSet.SetBottom( aTmp, nTmp );
                if( aRectFnSet.BottomDist( aStRect, aRectFnSet.GetBottom(pSt2Pos->aPortion) ) > 0 )
                {
                    if( bPorR2L )
                        aRectFnSet.SetRight( aTmp, aRectFnSet.GetRight(pSt2Pos->aPortion) );
                    else
                        aRectFnSet.SetLeft( aTmp, aRectFnSet.GetLeft(pSt2Pos->aPortion) );
                }
                aTmp.Intersection( aStFrame );
                Sub( aRegion, aTmp );
            }
 
            aStRect = pSt2Pos->aLine;
            aRectFnSet.SetLeft( aStRect, bR2L ?
                    aRectFnSet.GetLeft(pSt2Pos->aPortion) :
                    aRectFnSet.GetRight(pSt2Pos->aPortion) );
            aRectFnSet.SetWidth( aStRect, 1 );
        }
 
        if( pEnd2Pos )
        {
            SwRectFnSet fnRectX(pEndFrame);
            SwRect aTmp( aEndRect );
 
            // BiDi-Portions are swimming against the current.
            const bool bPorR2L = ( MultiPortionType::BIDI == pEnd2Pos->nMultiType ) ?
                                       ! bEndR2L :
                                         bEndR2L;
 
            if( MultiPortionType::BIDI == pEnd2Pos->nMultiType &&
                fnRectX.GetWidth(pEnd2Pos->aPortion2) )
            {
                // nested bidi portion
                tools::Long nRightAbs = fnRectX.GetRight(pEnd2Pos->aPortion);
                nRightAbs = nRightAbs - fnRectX.GetLeft(pEnd2Pos->aPortion2);
                tools::Long nLeftAbs = nRightAbs - fnRectX.GetWidth(pEnd2Pos->aPortion2);
 
                fnRectX.SetLeft( aTmp, nLeftAbs );
 
                if ( ! pSt2Pos || pSt2Pos->aPortion != pEnd2Pos->aPortion )
                {
                    SwRect aTmp2( pEnd2Pos->aPortion );
                    fnRectX.SetLeft( aTmp2, nRightAbs );
                    aTmp2.Intersection( aEndFrame );
                    Sub( aRegion, aTmp2 );
                }
            }
            else
            {
                if ( bPorR2L )
                    fnRectX.SetRight( aTmp, fnRectX.GetRight(pEnd2Pos->aPortion) );
                else
                    fnRectX.SetLeft( aTmp, fnRectX.GetLeft(pEnd2Pos->aPortion) );
            }
 
            if( MultiPortionType::ROT_90 == pEnd2Pos->nMultiType ||
                fnRectX.GetBottom(pEnd2Pos->aPortion) ==
                fnRectX.GetBottom(aEndRect) )
            {
                fnRectX.SetBottom( aTmp, fnRectX.GetBottom(pEnd2Pos->aLine) );
            }
 
            aTmp.Intersection( aEndFrame );
            Sub( aRegion, aTmp );
 
            // The next statement means neither ruby nor rotate(90):
            if( MultiPortionType::RUBY != pEnd2Pos->nMultiType && MultiPortionType::ROT_90 != pEnd2Pos->nMultiType )
            {
                SwTwips nTmp = fnRectX.GetTop(pEnd2Pos->aLine);
                if( fnRectX.GetTop(aEndRect) != nTmp )
                {
                    fnRectX.SetBottom( aTmp, fnRectX.GetTop(aTmp) );
                    fnRectX.SetTop( aTmp, nTmp );
                    if( fnRectX.GetTop(aEndRect) !=
                        fnRectX.GetTop(pEnd2Pos->aPortion) )
                    {
                        if( bPorR2L )
                            fnRectX.SetLeft( aTmp, fnRectX.GetLeft(pEnd2Pos->aPortion) );
                        else
                            fnRectX.SetRight( aTmp, fnRectX.GetRight(pEnd2Pos->aPortion) );
                    }
                    aTmp.Intersection( aEndFrame );
                    Sub( aRegion, aTmp );
                }
            }
 
            aEndRect = pEnd2Pos->aLine;
            fnRectX.SetLeft( aEndRect, bEndR2L ?
                    fnRectX.GetRight(pEnd2Pos->aPortion) :
                    fnRectX.GetLeft(pEnd2Pos->aPortion) );
            fnRectX.SetWidth( aEndRect, 1 );
        }
    }
    else if( pSt2Pos && pEnd2Pos &&
             MultiPortionType::BIDI == pSt2Pos->nMultiType &&
             MultiPortionType::BIDI == pEnd2Pos->nMultiType &&
             pSt2Pos->aPortion == pEnd2Pos->aPortion &&
             pSt2Pos->aPortion2 != pEnd2Pos->aPortion2 )
    {
        // This is the ugly special case, where the selection starts and
        // ends in the same bidi portion but one start or end is inside a
        // nested bidi portion.
 
        if ( aRectFnSet.GetWidth(pSt2Pos->aPortion2) )
        {
            SwRect aTmp( aStRect );
            tools::Long nRightAbs = aRectFnSet.GetRight(pSt2Pos->aPortion);
            nRightAbs -= aRectFnSet.GetLeft(pSt2Pos->aPortion2);
            tools::Long nLeftAbs = nRightAbs - aRectFnSet.GetWidth(pSt2Pos->aPortion2);
 
            aRectFnSet.SetRight( aTmp, nRightAbs );
            aTmp.Intersection( aStFrame );
            Sub( aRegion, aTmp );
 
            aStRect = pSt2Pos->aLine;
            aRectFnSet.SetLeft( aStRect, bR2L ? nRightAbs : nLeftAbs );
            aRectFnSet.SetWidth( aStRect, 1 );
        }
 
        SwRectFnSet fnRectX(pEndFrame);
        if ( fnRectX.GetWidth(pEnd2Pos->aPortion2) )
        {
            SwRect aTmp( aEndRect );
            tools::Long nRightAbs = fnRectX.GetRight(pEnd2Pos->aPortion);
            nRightAbs -= fnRectX.GetLeft(pEnd2Pos->aPortion2);
            tools::Long nLeftAbs = nRightAbs - fnRectX.GetWidth(pEnd2Pos->aPortion2);
 
            fnRectX.SetLeft( aTmp, nLeftAbs );
            aTmp.Intersection( aEndFrame );
            Sub( aRegion, aTmp );
 
            aEndRect = pEnd2Pos->aLine;
            fnRectX.SetLeft( aEndRect, bEndR2L ? nLeftAbs : nRightAbs );
            fnRectX.SetWidth( aEndRect, 1 );
        }
    }
 
    // The charrect may be outside the paintarea (for cursortravelling)
    // but the selection has to be restricted to the paintarea
    if( aStRect.Left() < aStFrame.Left() )
        aStRect.Left( aStFrame.Left() );
    else if( aStRect.Left() > aStFrame.Right() )
        aStRect.Left( aStFrame.Right() );
    SwTwips nTmp = aStRect.Right();
    if( nTmp < aStFrame.Left() )
        aStRect.Right( aStFrame.Left() );
    else if( nTmp > aStFrame.Right() )
        aStRect.Right( aStFrame.Right() );
    if( aEndRect.Left() < aEndFrame.Left() )
        aEndRect.Left( aEndFrame.Left() );
    else if( aEndRect.Left() > aEndFrame.Right() )
        aEndRect.Left( aEndFrame.Right() );
    nTmp = aEndRect.Right();
    if( nTmp < aEndFrame.Left() )
        aEndRect.Right( aEndFrame.Left() );
    else if( nTmp > aEndFrame.Right() )
        aEndRect.Right( aEndFrame.Right() );
 
    if( pStartFrame == pEndFrame )
    {
        bool bSameRotatedOrBidi = pSt2Pos && pEnd2Pos &&
            ( MultiPortionType::BIDI == pSt2Pos->nMultiType  ||
              MultiPortionType::ROT_270 == pSt2Pos->nMultiType ||
              MultiPortionType::ROT_90  == pSt2Pos->nMultiType ) &&
            pSt2Pos->aPortion == pEnd2Pos->aPortion;
        // case 1: (Same frame and same line)
        if( bSameRotatedOrBidi ||
            aRectFnSet.GetTop(aStRect) == aRectFnSet.GetTop(aEndRect) )
        {
            Point aTmpSt( aStRect.Pos() );
            Point aTmpEnd( aEndRect.Right(), aEndRect.Bottom() );
            if (bSameRotatedOrBidi || bR2L || bB2T)
            {
                if( aTmpSt.Y() > aTmpEnd.Y() )
                {
                    tools::Long nTmpY = aTmpEnd.Y();
                    aTmpEnd.setY( aTmpSt.Y() );
                    aTmpSt.setY( nTmpY );
                }
                if( aTmpSt.X() > aTmpEnd.X() )
                {
                    tools::Long nTmpX = aTmpEnd.X();
                    aTmpEnd.setX( aTmpSt.X() );
                    aTmpSt.setX( nTmpX );
                }
            }
 
            SwRect aTmp( aTmpSt, aTmpEnd );
            // If content is selected which doesn't take space (e.g. PostIts,
            // RefMarks, TOXMarks), then at least set the width of the Cursor.
            if( 1 == aRectFnSet.GetWidth(aTmp) &&
                pStartPos->GetContentIndex() !=
                pEndPos->GetContentIndex() )
            {
                OutputDevice* pOut = pSh->GetOut();
                tools::Long nCursorWidth = pOut->GetSettings().GetStyleSettings().
                    GetCursorSize();
                aRectFnSet.SetWidth( aTmp, pOut->PixelToLogic(
                    Size( nCursorWidth, 0 ) ).Width() );
            }
            aTmp.Intersection( aStFrame );
            Sub( aRegion, aTmp );
        }
        //case 2: (Same frame, but not the same line)
        else
        {
            SwTwips lLeft, lRight;
            if( pSt2Pos && pEnd2Pos && pSt2Pos->aPortion == pEnd2Pos->aPortion )
            {
                lLeft = aRectFnSet.GetLeft(pSt2Pos->aPortion);
                lRight = aRectFnSet.GetRight(pSt2Pos->aPortion);
            }
            else
            {
                lLeft = aRectFnSet.GetLeft(pStartFrame->getFrameArea()) +
                    aRectFnSet.GetLeft(pStartFrame->getFramePrintArea());
                lRight = aRectFnSet.GetRight(aEndFrame);
            }
            if( lLeft < aRectFnSet.GetLeft(aStFrame) )
                lLeft = aRectFnSet.GetLeft(aStFrame);
            if( lRight > aRectFnSet.GetRight(aStFrame) )
                lRight = aRectFnSet.GetRight(aStFrame);
            SwRect aSubRect( aStRect );
            //First line
            if( bR2L )
                aRectFnSet.SetLeft( aSubRect, lLeft );
            else
                aRectFnSet.SetRight( aSubRect, lRight );
            Sub( aRegion, aSubRect );
 
            //If there's at least a twips between start- and endline,
            //the whole area between will be added.
            SwTwips aTmpBottom = aRectFnSet.GetBottom(aStRect);
            SwTwips aTmpTop = aRectFnSet.GetTop(aEndRect);
            if( aTmpBottom != aTmpTop )
            {
                aRectFnSet.SetLeft( aSubRect, lLeft );
                aRectFnSet.SetRight( aSubRect, lRight );
                aRectFnSet.SetTop( aSubRect, aTmpBottom );
                aRectFnSet.SetBottom( aSubRect, aTmpTop );
                Sub( aRegion, aSubRect );
            }
            //and the last line
            aSubRect = aEndRect;
            if( bR2L )
                aRectFnSet.SetRight( aSubRect, lRight );
            else
                aRectFnSet.SetLeft( aSubRect, lLeft );
            Sub( aRegion, aSubRect );
        }
    }
    //case 3: (Different frames, maybe with other frames between)
    else
    {
        //The startframe first...
        SwRect aSubRect( aStRect );
        if( bR2L )
            aRectFnSet.SetLeft( aSubRect, aRectFnSet.GetLeft(aStFrame));
        else
            aRectFnSet.SetRight( aSubRect, aRectFnSet.GetRight(aStFrame));
        Sub( aRegion, aSubRect );
        SwTwips nTmpTwips = aRectFnSet.GetBottom(aStRect);
        if( aRectFnSet.GetBottom(aStFrame) != nTmpTwips )
        {
            aSubRect = aStFrame;
            aRectFnSet.SetTop( aSubRect, nTmpTwips );
            Sub( aRegion, aSubRect );
        }
 
        //Now the frames between, if there are any
        bool const bBody = pStartFrame->IsInDocBody();
        const SwTableBox* pCellBox = pStartFrame->GetUpper()->IsCellFrame() ?
            static_cast<const SwCellFrame*>(pStartFrame->GetUpper())->GetTabBox() : nullptr;
        assert(pSh);
        if (pSh->IsSelectAll())
            pCellBox = nullptr;
 
        const SwContentFrame *pContent = pStartFrame->GetNextContentFrame();
        SwRect aPrvRect;
 
        OSL_ENSURE( pContent,
            "<SwRootFrame::CalcFrameRects(..)> - no content frame. This is a serious defect" );
        while ( pContent && pContent != pEndFrame )
        {
            if ( pContent->IsInFly() )
            {
                const SwAnchoredObject* pObj = pContent->FindFlyFrame();
                if (!aSortObjs.Contains(*pObj))
                {   // is this even possible, assuming valid cursor pos.?
                    aSortObjs.Insert( *const_cast<SwAnchoredObject*>(pObj) );
                }
            }
 
            // Consider only frames which have the same IsInDocBody value like pStartFrame
            // If pStartFrame is inside a SwCellFrame, consider only frames which are inside the
            // same cell frame (or its follow cell)
            const SwTableBox* pTmpCellBox = pContent->GetUpper()->IsCellFrame() ?
                static_cast<const SwCellFrame*>(pContent->GetUpper())->GetTabBox() : nullptr;
            if (pSh->IsSelectAll())
                pTmpCellBox = nullptr;
            if ( bBody == pContent->IsInDocBody() &&
                ( !pCellBox || pCellBox == pTmpCellBox ) )
            {
                SwRect aCRect( pContent->UnionFrame( true ) );
                aCRect.Intersection( pContent->GetPaintArea() );
                if( aCRect.Overlaps( aRegion.GetOrigin() ))
                {
                    SwRect aTmp( aPrvRect );
                    aTmp.Union( aCRect );
                    if ( (aPrvRect.Height() * aPrvRect.Width() +
                        aCRect.Height()   * aCRect.Width()) ==
                        (aTmp.Height() * aTmp.Width()) )
                    {
                        aPrvRect.Union( aCRect );
                    }
                    else
                    {
                        if ( aPrvRect.HasArea() )
                            Sub( aRegion, aPrvRect );
                        aPrvRect = aCRect;
                    }
                }
            }
            pContent = pContent->GetNextContentFrame();
            OSL_ENSURE( pContent,
                "<SwRootFrame::CalcFrameRects(..)> - no content frame. This is a serious defect!" );
        }
        if ( aPrvRect.HasArea() )
            Sub( aRegion, aPrvRect );
 
        // At last the endframe...
        aRectFnSet.Refresh(pEndFrame);
        nTmpTwips = aRectFnSet.GetTop(aEndRect);
        if( aRectFnSet.GetTop(aEndFrame) != nTmpTwips )
        {
            aSubRect = aEndFrame;
            aRectFnSet.SetBottom( aSubRect, nTmpTwips );
            Sub( aRegion, aSubRect );
        }
        aSubRect = aEndRect;
        if( bEndR2L )
            aRectFnSet.SetRight(aSubRect, aRectFnSet.GetRight(aEndFrame));
        else
            aRectFnSet.SetLeft( aSubRect, aRectFnSet.GetLeft(aEndFrame) );
        Sub( aRegion, aSubRect );
    }
 
    aRegion.Invert();
    pSt2Pos.reset();
    pEnd2Pos.reset();
 
    // Cut out Flys in the foreground. We don't cut out a Fly when:
    // - it's a Lower of StartFrame/EndFrame (FLY_AS_CHAR and all other Flys
    //   which sit in it recursively)
    // - it's lower in the Z-order than the fly that contains the StartFrame
    //   (i.e. the one with the StartFrame is painted on top of it)
    // - it's anchored to inside the selection and thus part of it
    const SwPageFrame *pPage      = pStartFrame->FindPageFrame();
    const SwPageFrame *pEndPage   = pEndFrame->FindPageFrame();
 
    // for link rectangles: just remove all the fly portions - this prevents
    // splitting of portions vertically (causes spurious extra PDF annotations)
    if (eMode == RectsMode::NoAnchoredFlys)
    {
        for (SwContentFrame * pFrame = pStartFrame; ; pFrame = pFrame->GetFollow())
        {
            assert(pFrame->IsTextFrame());
            SwTextGridItem const*const pGrid(GetGridItem(pFrame->FindPageFrame()));
            SwTextPaintInfo info(static_cast<SwTextFrame*>(pFrame), pFrame->FindPageFrame()->getFrameArea());
            SwTextPainter painter(static_cast<SwTextFrame*>(pFrame), &info);
            // because nothing outside the start/end has been added, it doesn't
            // matter to match exactly the start/end, subtracting outside is no-op
            if (pFrame == pStartFrame)
            {
                painter.CharToLine(static_cast<SwTextFrame*>(pFrame)->MapModelToViewPos(*pStartPos));
            }
            do
            {
                info.SetPos(painter.GetTopLeft());
                bool const bAdjustBaseLine(
                    painter.GetLineInfo().HasSpecialAlign(pFrame->IsVertical())
                    || nullptr != pGrid || painter.GetCurr()->GetHangingBaseline());
                SwTwips nAscent, nHeight;
                painter.CalcAscentAndHeight(nAscent, nHeight);
                SwTwips const nOldY(info.Y());
                for (SwLinePortion const* pLP = painter.GetCurr()->GetFirstPortion();
                        pLP; pLP = pLP->GetNextPortion())
                {
                    if (pLP->IsFlyPortion())
                    {
                        info.Y(info.Y() + (bAdjustBaseLine
                                ? painter.AdjustBaseLine(*painter.GetCurr(), pLP)
                                : nAscent));
                        SwRect flyPortion;
                        info.CalcRect(*pLP, &flyPortion);
                        Sub(aRegion, flyPortion);
                        info.Y(nOldY);
                    }
                    pLP->Move(info);
                }
            }
            while (painter.Next());
            if (pFrame == pEndFrame)
            {
                break;
            }
        }
    }
    else while (pPage)
    {
        if ( pPage->GetSortedObjs() )
        {
            const SwSortedObjs &rObjs = *pPage->GetSortedObjs();
            for (SwAnchoredObject* pAnchoredObj : rObjs)
            {
                const SwFlyFrame* pFly = pAnchoredObj->DynCastFlyFrame();
                SdrObject const*const pObj(pAnchoredObj->GetDrawObj());
                SwFormatSurround const& rSur(pAnchoredObj->GetFrameFormat()->GetSurround());
                SwFormatAnchor const& rAnchor(pAnchoredObj->GetFrameFormat()->GetAnchor());
                const SwPosition* anchoredAt = rAnchor.GetContentAnchor();
                bool inSelection = (
                            anchoredAt != nullptr
                        && (   (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR
                                && IsDestroyFrameAnchoredAtChar(*anchoredAt, *pStartPos, *pEndPos))
                            || (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA
                                && IsSelectFrameAnchoredAtPara(*anchoredAt, *pStartPos, *pEndPos))));
                if (inSelection)
                {
                    Add(aRegion, pAnchoredObj->GetObjRect());
                }
                else if (pFly && !pFly->IsAnLower(pStartFrame) &&
                    (rSur.GetSurround() != css::text::WrapTextMode_THROUGH &&
                    !rSur.IsContour()) )
                {
                    if ( aSortObjs.Contains( *pAnchoredObj ) )
                        continue;
 
                    bool bSub = true;
                    const sal_uInt32 nPos = pObj->GetOrdNum();
                    for ( size_t k = 0; bSub && k < aSortObjs.size(); ++k )
                    {
                        assert( dynamic_cast< const SwFlyFrame *>( aSortObjs[k] ) &&
                            "<SwRootFrame::CalcFrameRects(..)> - object in <aSortObjs> of unexpected type" );
                        const SwFlyFrame* pTmp = static_cast<SwFlyFrame*>(aSortObjs[k]);
                        do
                        {
                            if ( nPos < pTmp->GetVirtDrawObj()->GetOrdNumDirect() )
                            {
                                bSub = false;
                            }
                            else
                            {
                                pTmp = pTmp->GetAnchorFrame()->FindFlyFrame();
                            }
                        } while ( bSub && pTmp );
                    }
                    if ( bSub )
                        Sub( aRegion, pFly->getFrameArea() );
                }
            }
        }
        if ( pPage == pEndPage )
            break;
        else
            pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
    }
 
    // Because it looks better, we cut out the DropCaps.
    SwRect aDropRect;
    if ( pStartFrame->IsTextFrame() )
    {
        if ( static_cast<const SwTextFrame*>(pStartFrame)->GetDropRect( aDropRect ) )
            Sub( aRegion, aDropRect );
    }
    if ( pEndFrame != pStartFrame && pEndFrame->IsTextFrame() )
    {
        if ( static_cast<const SwTextFrame*>(pEndFrame)->GetDropRect( aDropRect ) )
            Sub( aRegion, aDropRect );
    }
 
    rRects.assign( aRegion.begin(), aRegion.end() );
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

V595 The 'pTable' pointer was utilized before it was verified against nullptr. Check lines: 792, 799.

V654 The condition 'pCnt' of loop is always true.

V1004 The 'pSh' pointer was used unsafely after it was verified against nullptr. Check lines: 2071, 2075.