/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */
 
#include <config_wasm_strip.h>
 
#include <bodyfrm.hxx>
#include <pagefrm.hxx>
#include <rootfrm.hxx>
#include <IDocumentFieldsAccess.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <viewimp.hxx>
#include <fesh.hxx>
#include <swtable.hxx>
#include <deletelistener.hxx>
#include <dflyobj.hxx>
#include <anchoreddrawobject.hxx>
#include <fmtanchr.hxx>
#include <viewopt.hxx>
#include <hints.hxx>
#include <dbg_lay.hxx>
#include <ftnidx.hxx>
#include <svl/itemiter.hxx>
#include <editeng/keepitem.hxx>
#include <editeng/ulspitem.hxx>
#include <editeng/brushitem.hxx>
#include <editeng/boxitem.hxx>
#include <basegfx/range/b1drange.hxx>
#include <fmtlsplt.hxx>
#include <fmtrowsplt.hxx>
#include <fmtsrnd.hxx>
#include <fmtornt.hxx>
#include <fmtpdsc.hxx>
#include <fmtfsize.hxx>
#include <swtblfmt.hxx>
#include <tabfrm.hxx>
#include <rowfrm.hxx>
#include <cellfrm.hxx>
#include <flyfrms.hxx>
#include <txtfrm.hxx>
#include <ftnfrm.hxx>
#include <notxtfrm.hxx>
#include <htmltbl.hxx>
#include <sectfrm.hxx>
#include <fmtfollowtextflow.hxx>
#include <sortedobjs.hxx>
#include <objectformatter.hxx>
#include <layouter.hxx>
#include <calbck.hxx>
#include <DocumentSettingManager.hxx>
#include <sal/log.hxx>
#include <osl/diagnose.h>
#include <frmatr.hxx>
#include <frmtool.hxx>
#include <ndtxt.hxx>
#include <frameformats.hxx>
 
using namespace ::com::sun::star;
 
SwTabFrame::SwTabFrame( SwTable &rTab, SwFrame* pSib )
    : SwLayoutFrame( rTab.GetFrameFormat(), pSib )
    , SwFlowFrame( static_cast<SwFrame&>(*this) )
    , m_pTable( &rTab )
    , m_bComplete(false)
    , m_bCalcLowers(false)
    , m_bLowersFormatted(false)
    , m_bLockBackMove(false)
    , m_bWantBackMove(false)
    , m_bResizeHTMLTable(false)
    , m_bONECalcLowers(false)
    , m_bHasFollowFlowLine(false)
    , m_bIsRebuildLastLine(false)
    , m_bRestrictTableGrowth(false)
    , m_bRemoveFollowFlowLinePending(false)
    , m_bConsiderObjsForMinCellHeight(true)
    , m_bObjsDoesFit(true)
    , m_bInRecalcLowerRow(false)
{
    mbFixSize = false;     //Don't fall for import filter again.
    mnFrameType = SwFrameType::Tab;
 
    //Create the lines and insert them.
    const SwTableLines &rLines = rTab.GetTabLines();
    SwFrame *pTmpPrev = nullptr;
    bool bHiddenRedlines = getRootFrame()->IsHideRedlines() &&
        !GetFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable().empty();
    SwRedlineTable::size_type nRedlinePos = 0;
    for ( size_t i = 0; i < rLines.size(); ++i )
    {
        // skip lines deleted with track changes
        if ( bHiddenRedlines && rLines[i]->IsDeleted(nRedlinePos) )
            continue;
 
        SwRowFrame *pNew = new SwRowFrame( *rLines[i], this );
        if( pNew->Lower() )
        {
            pNew->InsertBehind( this, pTmpPrev );
            pTmpPrev = pNew;
        }
        else
            SwFrame::DestroyFrame(pNew);
    }
    OSL_ENSURE( Lower() && Lower()->IsRowFrame(), "SwTabFrame::SwTabFrame: No rows." );
}
 
SwTabFrame::SwTabFrame( SwTabFrame &rTab )
    : SwLayoutFrame( rTab.GetFormat(), &rTab )
    , SwFlowFrame( static_cast<SwFrame&>(*this) )
    , m_pTable( rTab.GetTable() )
    , m_bComplete(false)
    , m_bCalcLowers(false)
    , m_bLowersFormatted(false)
    , m_bLockBackMove(false)
    , m_bWantBackMove(false)
    , m_bResizeHTMLTable(false)
    , m_bONECalcLowers(false)
    , m_bHasFollowFlowLine(false)
    , m_bIsRebuildLastLine(false)
    , m_bRestrictTableGrowth(false)
    , m_bRemoveFollowFlowLinePending(false)
    , m_bConsiderObjsForMinCellHeight(true)
    , m_bObjsDoesFit(true)
    , m_bInRecalcLowerRow(false)
{
    mbFixSize = false;     //Don't fall for import filter again.
    mnFrameType = SwFrameType::Tab;
 
    SetFollow( rTab.GetFollow() );
    rTab.SetFollow( this );
}
 
void SwTabFrame::DestroyImpl()
{
    // There is some terrible code in fetab.cxx, that
    // caches pointers to SwTabFrames.
    ::ClearFEShellTabCols(*GetFormat()->GetDoc(), this);
 
    SwLayoutFrame::DestroyImpl();
}
 
SwTabFrame::~SwTabFrame()
{
}
 
void SwTabFrame::JoinAndDelFollows()
{
    SwTabFrame *pFoll = GetFollow();
    if ( pFoll->HasFollow() )
        pFoll->JoinAndDelFollows();
    pFoll->Cut();
    SetFollow( pFoll->GetFollow() );
    SwFrame::DestroyFrame(pFoll);
}
 
void SwTabFrame::RegistFlys()
{
    OSL_ENSURE( Lower() && Lower()->IsRowFrame(), "No rows." );
 
    SwPageFrame *pPage = FindPageFrame();
    if ( pPage )
    {
        SwRowFrame *pRow = static_cast<SwRowFrame*>(Lower());
        do
        {
            pRow->RegistFlys( pPage );
            pRow = static_cast<SwRowFrame*>(pRow->GetNext());
        } while ( pRow );
    }
}
 
static void SwInvalidateAll( SwFrame *pFrame, tools::Long nBottom );
static void lcl_RecalcRow( SwRowFrame& rRow, tools::Long nBottom );
static bool lcl_ArrangeLowers( SwLayoutFrame *pLay, tools::Long lYStart, bool bInva );
// #i26945# - add parameter <_bOnlyRowsAndCells> to control
// that only row and cell frames are formatted.
static bool lcl_InnerCalcLayout( SwFrame *pFrame,
                                      tools::Long nBottom,
                                      bool _bOnlyRowsAndCells = false );
// OD 2004-02-18 #106629# - correct type of 1st parameter
// #i26945# - add parameter <_bConsiderObjs> in order to
// control, if floating screen objects have to be considered for the minimal
// cell height.
static SwTwips lcl_CalcMinRowHeight( const SwRowFrame *pRow,
                                     const bool _bConsiderObjs );
static sal_uInt16 lcl_GetTopSpace( const SwRowFrame& rRow );
 
static SwTwips lcl_CalcTopAndBottomMargin( const SwLayoutFrame&, const SwBorderAttrs& );
 
static SwTwips lcl_calcHeightOfRowBeforeThisFrame(const SwRowFrame& rRow);
 
static SwTwips lcl_GetHeightOfRows( const SwFrame* pStart, tools::Long nCount )
{
    if ( !nCount || !pStart)
        return 0;
 
    SwRectFnSet aRectFnSet(pStart);
    SwTwips nTop = aRectFnSet.GetTop(pStart->getFrameArea());
    while (pStart->GetNext() && nCount > 1)
    {
        pStart = pStart->GetNext();
        --nCount;
    }
 
    return -aRectFnSet.BottomDist(pStart->getFrameArea(), nTop);
}
 
// Local helper function to insert a new follow flow line
static SwRowFrame* lcl_InsertNewFollowFlowLine( SwTabFrame& rTab, const SwFrame& rTmpRow, bool bRowSpanLine )
{
    OSL_ENSURE( rTmpRow.IsRowFrame(), "No row frame to copy for FollowFlowLine" );
    const SwRowFrame& rRow = static_cast<const SwRowFrame&>(rTmpRow);
 
    rTab.SetFollowFlowLine( true );
    SwRowFrame *pFollowFlowLine = new SwRowFrame(*rRow.GetTabLine(), &rTab, false );
    pFollowFlowLine->SetRowSpanLine( bRowSpanLine );
    SwFrame* pFirstRow = rTab.GetFollow()->GetFirstNonHeadlineRow();
    pFollowFlowLine->InsertBefore( rTab.GetFollow(), pFirstRow );
    return pFollowFlowLine;
}
 
// #i26945# - local helper function to invalidate all lower
// objects. By parameter <_bMoveObjsOutOfRange> it can be controlled, if
// additionally the objects are moved 'out of range'.
static void lcl_InvalidateLowerObjs( SwLayoutFrame& _rLayoutFrame,
                              const bool _bMoveObjsOutOfRange = false,
                              SwPageFrame* _pPageFrame = nullptr )
{
    // determine page frame, if needed
    if ( !_pPageFrame )
    {
        _pPageFrame = _rLayoutFrame.FindPageFrame();
        OSL_ENSURE( _pPageFrame,
                "<lcl_InvalidateLowerObjs(..)> - missing page frame -> no move of lower objects out of range" );
        if ( !_pPageFrame )
        {
            return;
        }
    }
 
    // loop on lower frames
    SwFrame* pLowerFrame = _rLayoutFrame.Lower();
    while ( pLowerFrame )
    {
        if ( pLowerFrame->IsLayoutFrame() )
        {
            ::lcl_InvalidateLowerObjs( *static_cast<SwLayoutFrame*>(pLowerFrame),
                                       _bMoveObjsOutOfRange, _pPageFrame );
        }
        if ( pLowerFrame->GetDrawObjs() )
        {
            for (size_t i = 0, nCount = pLowerFrame->GetDrawObjs()->size(); i < nCount; ++i)
            {
                SwAnchoredObject* pAnchoredObj = (*pLowerFrame->GetDrawObjs())[i];
 
                // invalidate position of anchored object
                pAnchoredObj->SetTmpConsiderWrapInfluence( false );
                pAnchoredObj->SetConsiderForTextWrap( false );
                pAnchoredObj->UnlockPosition();
                pAnchoredObj->InvalidateObjPos();
 
                SwFlyFrame *pFly = pAnchoredObj->DynCastFlyFrame();
 
                // move anchored object 'out of range'
                if ( _bMoveObjsOutOfRange )
                {
                    // indicate, that positioning is progress to avoid
                    // modification of the anchored object resp. it's attributes
                    // due to the movement
                    SwObjPositioningInProgress aObjPosInProgress( *pAnchoredObj );
                    pAnchoredObj->SetObjLeft( _pPageFrame->getFrameArea().Right() );
                    // #115759# - reset character rectangle,
                    // top of line and relative position in order to assure,
                    // that anchored object is correctly positioned.
                    pAnchoredObj->ClearCharRectAndTopOfLine();
                    pAnchoredObj->SetCurrRelPos( Point( 0, 0 ) );
                    const SwFrameFormat* pObjFormat = pAnchoredObj->GetFrameFormat();
                    if (pObjFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR)
                    {
                        pAnchoredObj->AnchorFrame()
                                ->Prepare( PrepareHint::FlyFrameAttributesChanged,
                                           pObjFormat );
                    }
                    if ( pFly != nullptr )
                    {
                        pFly->GetVirtDrawObj()->SetBoundAndSnapRectsDirty();
                        pFly->GetVirtDrawObj()->SetChanged();
                    }
                }
 
                // If anchored object is a fly frame, invalidate its lower objects
                if ( pFly != nullptr )
                {
                    ::lcl_InvalidateLowerObjs( *pFly, _bMoveObjsOutOfRange, _pPageFrame );
                }
            }
        }
        pLowerFrame = pLowerFrame->GetNext();
    }
}
 
// Local helper function to shrink all lowers of pRow to 0 height
static void lcl_ShrinkCellsAndAllContent( SwRowFrame& rRow )
{
    SwCellFrame* pCurrMasterCell = static_cast<SwCellFrame*>(rRow.Lower());
    SwRectFnSet aRectFnSet(pCurrMasterCell);
 
    bool bAllCellsCollapsed = true;
    while ( pCurrMasterCell )
    {
        // NEW TABLES
        SwCellFrame& rToAdjust = pCurrMasterCell->GetTabBox()->getRowSpan() < 1 ?
                               const_cast<SwCellFrame&>(pCurrMasterCell->FindStartEndOfRowSpanCell( true )) :
                               *pCurrMasterCell;
 
        // #i26945#
        // all lowers should have the correct position
        lcl_ArrangeLowers( &rToAdjust,
                           aRectFnSet.GetPrtTop(rToAdjust),
                           false );
        // TODO: Optimize number of frames which are set to 0 height
        // we have to start with the last lower frame, otherwise
        // the shrink will not shrink the current cell
        SwFrame* pTmp = rToAdjust.GetLastLower();
        bool bAllLowersCollapsed = true;
 
        if ( pTmp && pTmp->IsRowFrame() )
        {
            SwRowFrame* pTmpRow = static_cast<SwRowFrame*>(pTmp);
            lcl_ShrinkCellsAndAllContent( *pTmpRow );
        }
        else
        {
            // TODO: Optimize number of frames which are set to 0 height
            while ( pTmp )
            {
                // the frames have to be shrunk
                if ( pTmp->IsTabFrame() )
                {
                    SwRowFrame* pTmpRow = static_cast<SwRowFrame*>(static_cast<SwTabFrame*>(pTmp)->Lower());
                    bool bAllRowsCollapsed = true;
 
                    while ( pTmpRow )
                    {
                        lcl_ShrinkCellsAndAllContent( *pTmpRow );
 
                        if (aRectFnSet.GetHeight(pTmpRow->getFrameArea()) > 0)
                            bAllRowsCollapsed = false;
 
                        pTmpRow = static_cast<SwRowFrame*>(pTmpRow->GetNext());
                    }
 
                    if (bAllRowsCollapsed)
                    {
                        // All rows of this table have 0 height -> set height of the table itself as well.
                        SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pTmp);
                        aRectFnSet.SetHeight(aFrm, 0);
 
                        SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pTmp);
                        aRectFnSet.SetTop(aPrt, 0);
                        aRectFnSet.SetHeight(aPrt, 0);
                    }
                    else
                        bAllLowersCollapsed = false;
                }
                else
                {
                    pTmp->Shrink(aRectFnSet.GetHeight(pTmp->getFrameArea()));
                    SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pTmp);
                    aRectFnSet.SetTop(aPrt, 0);
                    aRectFnSet.SetHeight(aPrt, 0);
 
                    if (aRectFnSet.GetHeight(pTmp->getFrameArea()) > 0)
                    {
                        bAllLowersCollapsed = false;
                    }
                }
 
                pTmp = pTmp->GetPrev();
            }
 
            // all lowers should have the correct position
            lcl_ArrangeLowers( &rToAdjust,
                               aRectFnSet.GetPrtTop(rToAdjust),
                               false );
        }
 
        if (bAllLowersCollapsed)
        {
            // All lower frame of this cell have 0 height -> set height of the cell itself as well.
            SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pCurrMasterCell);
            aRectFnSet.SetHeight(aFrm, 0);
 
            SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pCurrMasterCell);
            aRectFnSet.SetTop(aPrt, 0);
            aRectFnSet.SetHeight(aPrt, 0);
        }
        else
            bAllCellsCollapsed = false;
 
        pCurrMasterCell = static_cast<SwCellFrame*>(pCurrMasterCell->GetNext());
    }
 
    if (bAllCellsCollapsed)
    {
        // All cells have 0 height -> set height of row as well.
        SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(rRow);
        aRectFnSet.SetHeight(aFrm, 0);
 
        SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(rRow);
        aRectFnSet.SetTop(aPrt, 0);
        aRectFnSet.SetHeight(aPrt, 0);
    }
}
 
// Local helper function to move the content from rSourceLine to rDestLine
// The content is inserted behind the last content in the corresponding
// cell in rDestLine.
static void lcl_MoveRowContent( SwRowFrame& rSourceLine, SwRowFrame& rDestLine )
{
    SwCellFrame* pCurrDestCell = static_cast<SwCellFrame*>(rDestLine.Lower());
    SwCellFrame* pCurrSourceCell = static_cast<SwCellFrame*>(rSourceLine.Lower());
 
    // Move content of follow cells into master cells
    while ( pCurrSourceCell )
    {
        if ( pCurrSourceCell->Lower() && pCurrSourceCell->Lower()->IsRowFrame() )
        {
            SwRowFrame* pTmpSourceRow = static_cast<SwRowFrame*>(pCurrSourceCell->Lower());
            while ( pTmpSourceRow )
            {
                // #125926# Attention! It is possible,
                // that pTmpSourceRow->IsFollowFlowRow() but pTmpDestRow
                // cannot be found. In this case, we have to move the complete
                // row.
                SwRowFrame* pTmpDestRow = static_cast<SwRowFrame*>(pCurrDestCell->Lower());
 
                if ( pTmpSourceRow->IsFollowFlowRow() && pTmpDestRow )
                {
                    // move content from follow flow row to pTmpDestRow:
                    while ( pTmpDestRow->GetNext() )
                        pTmpDestRow = static_cast<SwRowFrame*>(pTmpDestRow->GetNext());
 
                    assert(pTmpDestRow->GetFollowRow() == pTmpSourceRow);
 
                    lcl_MoveRowContent( *pTmpSourceRow, *pTmpDestRow );
                    pTmpDestRow->SetFollowRow( pTmpSourceRow->GetFollowRow() );
                    pTmpSourceRow->RemoveFromLayout();
                    SwFrame::DestroyFrame(pTmpSourceRow);
                }
                else
                {
                    // move complete row:
                    pTmpSourceRow->RemoveFromLayout();
                    pTmpSourceRow->InsertBefore( pCurrDestCell, nullptr );
                }
 
                pTmpSourceRow = static_cast<SwRowFrame*>(pCurrSourceCell->Lower());
            }
        }
        else
        {
            SwFrame *pTmp = ::SaveContent( pCurrSourceCell );
            if ( pTmp )
            {
                // NEW TABLES
                SwCellFrame* pDestCell = pCurrDestCell;
                if ( pDestCell->GetTabBox()->getRowSpan() < 1 )
                    pDestCell = & const_cast<SwCellFrame&>(pDestCell->FindStartEndOfRowSpanCell( true ));
 
                // Find last content
                SwFrame* pFrame = pDestCell->GetLastLower();
                ::RestoreContent( pTmp, pDestCell, pFrame );
            }
        }
        pCurrDestCell = static_cast<SwCellFrame*>(pCurrDestCell->GetNext());
        pCurrSourceCell = static_cast<SwCellFrame*>(pCurrSourceCell->GetNext());
    }
}
 
// Local helper function to move all footnotes in rRowFrame from
// the footnote boss of rSource to the footnote boss of rDest.
static void lcl_MoveFootnotes( SwTabFrame& rSource, SwTabFrame& rDest, SwLayoutFrame& rRowFrame )
{
    if ( !rSource.GetFormat()->GetDoc()->GetFootnoteIdxs().empty() )
    {
        SwFootnoteBossFrame* pOldBoss = rSource.FindFootnoteBossFrame( true );
        SwFootnoteBossFrame* pNewBoss = rDest.FindFootnoteBossFrame( true );
        rRowFrame.MoveLowerFootnotes( nullptr, pOldBoss, pNewBoss, true );
    }
}
 
// Local helper function to handle nested table cells before the split process
static void lcl_PreprocessRowsInCells( SwTabFrame& rTab, SwRowFrame& rLastLine,
                                SwRowFrame& rFollowFlowLine, SwTwips nRemain )
{
    SwCellFrame* pCurrLastLineCell = static_cast<SwCellFrame*>(rLastLine.Lower());
    SwCellFrame* pCurrFollowFlowLineCell = static_cast<SwCellFrame*>(rFollowFlowLine.Lower());
 
    SwRectFnSet aRectFnSet(pCurrLastLineCell);
 
    // Move content of follow cells into master cells
    while ( pCurrLastLineCell )
    {
        if ( pCurrLastLineCell->Lower() && pCurrLastLineCell->Lower()->IsRowFrame() )
        {
            SwTwips nTmpCut = nRemain;
            SwRowFrame* pTmpLastLineRow = static_cast<SwRowFrame*>(pCurrLastLineCell->Lower());
 
            // #i26945#
            SwTwips nCurrentHeight =
                    lcl_CalcMinRowHeight( pTmpLastLineRow,
                                          rTab.IsConsiderObjsForMinCellHeight() );
            while ( pTmpLastLineRow->GetNext() && nTmpCut > nCurrentHeight )
            {
                nTmpCut -= nCurrentHeight;
                pTmpLastLineRow = static_cast<SwRowFrame*>(pTmpLastLineRow->GetNext());
                // #i26945#
                nCurrentHeight =
                    lcl_CalcMinRowHeight( pTmpLastLineRow,
                                          rTab.IsConsiderObjsForMinCellHeight() );
            }
 
            // pTmpLastLineRow does not fit to the line or it is the last line
            // Check if we can move pTmpLastLineRow to the follow table,
            // or if we have to split the line:
            bool bTableLayoutTooComplex = false;
            tools::Long nMinHeight = 0;
 
            // We have to take into account:
            // 1. The fixed height of the row
            // 2. The borders of the cells inside the row
            // 3. The minimum height of the row
            if ( pTmpLastLineRow->HasFixSize() )
                nMinHeight = aRectFnSet.GetHeight(pTmpLastLineRow->getFrameArea());
            else
            {
                {
                    const SwFormatFrameSize &rSz = pTmpLastLineRow->GetFormat()->GetFrameSize();
                    if ( rSz.GetHeightSizeType() == SwFrameSize::Minimum )
                        nMinHeight = rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*pTmpLastLineRow);
                }
 
                SwFrame* pCell = pTmpLastLineRow->Lower();
                while ( pCell )
                {
                    if ( static_cast<SwCellFrame*>(pCell)->Lower() &&
                         static_cast<SwCellFrame*>(pCell)->Lower()->IsRowFrame() )
                    {
                        bTableLayoutTooComplex = true;
                        break;
                    }
 
                    SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCell );
                    const SwBorderAttrs &rAttrs = *aAccess.Get();
                    nMinHeight = std::max( nMinHeight, tools::Long(lcl_CalcTopAndBottomMargin( *static_cast<SwLayoutFrame*>(pCell), rAttrs )) );
                    pCell = pCell->GetNext();
                }
            }
 
            // 1. Case:
            // The line completely fits into the master table.
            // Nevertheless, we build a follow (otherwise painting problems
            // with empty cell).
 
            // 2. Case:
            // The line has to be split, the minimum height still fits into
            // the master table, and the table structure is not too complex.
            if ( nTmpCut > nCurrentHeight ||
                 ( pTmpLastLineRow->IsRowSplitAllowed() &&
                  !bTableLayoutTooComplex && nMinHeight < nTmpCut ) )
            {
                // The line has to be split:
                SwRowFrame* pNewRow = new SwRowFrame( *pTmpLastLineRow->GetTabLine(), &rTab, false );
                pNewRow->SetFollowFlowRow( true );
                pNewRow->SetFollowRow( pTmpLastLineRow->GetFollowRow() );
                pTmpLastLineRow->SetFollowRow( pNewRow );
                pNewRow->InsertBehind( pCurrFollowFlowLineCell, nullptr );
                pTmpLastLineRow = static_cast<SwRowFrame*>(pTmpLastLineRow->GetNext());
            }
 
            // The following lines have to be moved:
            while ( pTmpLastLineRow )
            {
                SwRowFrame* pTmp = static_cast<SwRowFrame*>(pTmpLastLineRow->GetNext());
                lcl_MoveFootnotes( rTab, *rTab.GetFollow(), *pTmpLastLineRow );
                pTmpLastLineRow->RemoveFromLayout();
                pTmpLastLineRow->InsertBefore( pCurrFollowFlowLineCell, nullptr );
                pTmpLastLineRow->Shrink( aRectFnSet.GetHeight(pTmpLastLineRow->getFrameArea()) );
                pCurrFollowFlowLineCell->Grow( aRectFnSet.GetHeight(pTmpLastLineRow->getFrameArea()) );
                pTmpLastLineRow = pTmp;
            }
        }
 
        pCurrLastLineCell = static_cast<SwCellFrame*>(pCurrLastLineCell->GetNext());
        pCurrFollowFlowLineCell = static_cast<SwCellFrame*>(pCurrFollowFlowLineCell->GetNext());
    }
}
 
// Local helper function to handle nested table cells after the split process
static void lcl_PostprocessRowsInCells( SwTabFrame& rTab, SwRowFrame& rLastLine )
{
    SwCellFrame* pCurrMasterCell = static_cast<SwCellFrame*>(rLastLine.Lower());
    while ( pCurrMasterCell )
    {
        if ( pCurrMasterCell->Lower() &&
             pCurrMasterCell->Lower()->IsRowFrame() )
        {
            SwRowFrame* pRowFrame = static_cast<SwRowFrame*>(pCurrMasterCell->GetLastLower());
 
            if ( nullptr != pRowFrame->GetPrev() && !pRowFrame->ContainsContent() )
            {
                OSL_ENSURE( pRowFrame->GetFollowRow(), "Deleting row frame without follow" );
 
                // The footnotes have to be moved:
                lcl_MoveFootnotes( rTab, *rTab.GetFollow(), *pRowFrame );
                pRowFrame->Cut();
                SwRowFrame* pFollowRow = pRowFrame->GetFollowRow();
                pRowFrame->Paste( pFollowRow->GetUpper(), pFollowRow );
                pRowFrame->SetFollowRow( pFollowRow->GetFollowRow() );
                lcl_MoveRowContent( *pFollowRow, *pRowFrame );
                pFollowRow->Cut();
                SwFrame::DestroyFrame(pFollowRow);
                ::SwInvalidateAll( pCurrMasterCell, LONG_MAX );
            }
        }
 
        pCurrMasterCell = static_cast<SwCellFrame*>(pCurrMasterCell->GetNext());
    }
}
 
// Local helper function to re-calculate the split line.
inline void TableSplitRecalcLock( SwFlowFrame *pTab ) { pTab->LockJoin(); }
inline void TableSplitRecalcUnlock( SwFlowFrame *pTab ) { pTab->UnlockJoin(); }
 
static bool lcl_RecalcSplitLine( SwRowFrame& rLastLine, SwRowFrame& rFollowLine,
                          SwTwips nRemainingSpaceForLastRow, SwTwips nAlreadyFree,
                          bool & rIsFootnoteGrowth)
{
    bool bRet = true;
 
    vcl::RenderContext* pRenderContext = rLastLine.getRootFrame()->GetCurrShell()->GetOut();
    SwTabFrame& rTab = static_cast<SwTabFrame&>(*rLastLine.GetUpper());
    SwRectFnSet aRectFnSet(rTab.GetUpper());
    SwTwips nCurLastLineHeight = aRectFnSet.GetHeight(rLastLine.getFrameArea());
 
    SwTwips nFootnoteHeight(0);
    if (SwFootnoteBossFrame const*const pBoss = rTab.FindFootnoteBossFrame())
    {
        if (SwFootnoteContFrame const*const pCont = pBoss->FindFootnoteCont())
        {
            for (SwFootnoteFrame const* pFootnote = static_cast<SwFootnoteFrame const*>(pCont->Lower());
                 pFootnote != nullptr;
                 pFootnote = static_cast<SwFootnoteFrame const*>(pFootnote->GetNext()))
            {
                SwContentFrame const*const pAnchor = pFootnote->GetRef();
                SwTabFrame const* pTab = pAnchor->FindTabFrame();
                if (pTab)
                {
                    while (pTab->GetUpper()->IsInTab())
                    {
                        pTab = pTab->GetUpper()->FindTabFrame();
                    }
                    // TODO currently do this only for top-level tables?
                    // otherwise would need to check rTab's follow and any upper table's follow?
                    if (pTab == &rTab)
                    {
                        nFootnoteHeight += aRectFnSet.GetHeight(pFootnote->getFrameArea());
                    }
                }
            }
        }
    }
 
    // If there are nested cells in rLastLine, the recalculation of the last
    // line needs some preprocessing.
    lcl_PreprocessRowsInCells( rTab, rLastLine, rFollowLine, nRemainingSpaceForLastRow );
 
    // Here the recalculation process starts:
    rTab.SetRebuildLastLine( true );
    // #i26945#
    rTab.SetDoesObjsFit( true );
 
    // #i26945# - invalidate and move floating screen
    // objects 'out of range'
    ::lcl_InvalidateLowerObjs( rLastLine, true );
 
    // manipulate row and cell sizes
 
    // #i26945# - Do *not* consider floating screen objects
    // for the minimal cell height.
    rTab.SetConsiderObjsForMinCellHeight( false );
    ::lcl_ShrinkCellsAndAllContent( rLastLine );
    rTab.SetConsiderObjsForMinCellHeight( true );
 
    // invalidate last line
    ::SwInvalidateAll( &rLastLine, LONG_MAX );
 
    // Shrink the table to account for the shrunk last row, as well as lower rows
    // that had been moved to follow table in SwTabFrame::Split.
    // It will grow later when last line will recalc its height.
    rTab.Shrink(nAlreadyFree + nCurLastLineHeight - nRemainingSpaceForLastRow + 1);
 
    // Lock this tab frame and its follow
    bool bUnlockMaster = false;
    SwFlowFrame * pFollow = nullptr;
    SwTabFrame* pMaster = rTab.IsFollow() ? rTab.FindMaster() : nullptr;
    if ( pMaster && !pMaster->IsJoinLocked() )
    {
        bUnlockMaster = true;
        ::TableSplitRecalcLock( pMaster );
    }
    if ( !rTab.GetFollow()->IsJoinLocked() )
    {
        pFollow = rTab.GetFollow();
        ::TableSplitRecalcLock( pFollow );
    }
 
    bool bInSplit = rLastLine.IsInSplit();
    rLastLine.SetInSplit();
 
    // Do the recalculation
    lcl_RecalcRow( rLastLine, LONG_MAX );
    // #115759# - force a format of the last line in order to
    // get the correct height.
    rLastLine.InvalidateSize();
    rLastLine.Calc(pRenderContext);
 
    rLastLine.SetInSplit(bInSplit);
 
    // Unlock this tab frame and its follow
    if ( pFollow )
        ::TableSplitRecalcUnlock( pFollow );
    if ( bUnlockMaster )
        ::TableSplitRecalcUnlock( pMaster );
 
    // If there are nested cells in rLastLine, the recalculation of the last
    // line needs some postprocessing.
    lcl_PostprocessRowsInCells( rTab, rLastLine );
 
    // Do a couple of checks on the current situation.
 
    // If we are not happy with the current situation we return false.
    // This will start a new try to split the table, this time we do not
    // try to split the table rows.
 
    // 1. Check if table fits to its upper.
    // #i26945# - include check, if objects fit
    const SwTwips nDistanceToUpperPrtBottom =
        aRectFnSet.BottomDist(rTab.getFrameArea(), aRectFnSet.GetPrtBottom(*rTab.GetUpper()));
    // also check the footnote boss - it *may* be smaller than the upper now!
    const SwTwips nDistanceToFootnoteBodyPrtBottom =
        aRectFnSet.BottomDist(rTab.getFrameArea(), aRectFnSet.GetPrtBottom(*rTab.FindFootnoteBossFrame()->FindBodyCont()));
    // tdf#125685 ignore footnotes that are anchored in follow-table of this
    // table - if split is successful they move to the next page/column anyway
    assert(rTab.GetFollow() == rFollowLine.GetUpper());
    SwTwips nFollowFootnotes(0);
    // actually there should always be a boss frame, except if "this" isn't
    // connected to a page yet; not sure if that can happen
    if (SwFootnoteBossFrame const*const pBoss = rTab.FindFootnoteBossFrame())
    {
        if (SwFootnoteContFrame const*const pCont = pBoss->FindFootnoteCont())
        {
            for (SwFootnoteFrame const* pFootnote = static_cast<SwFootnoteFrame const*>(pCont->Lower());
                 pFootnote != nullptr;
                 pFootnote = static_cast<SwFootnoteFrame const*>(pFootnote->GetNext()))
            {
                SwContentFrame const*const pAnchor = pFootnote->GetRef();
                SwTabFrame const* pTab = pAnchor->FindTabFrame();
                if (pTab)
                {
                    while (pTab->GetUpper()->IsInTab())
                    {
                        pTab = pTab->GetUpper()->FindTabFrame();
                    }
                    // TODO currently do this only for top-level tables?
                    // otherwise would need to check rTab's follow and any upper table's follow?
                    if (pTab == rTab.GetFollow())
                    {
                        nFollowFootnotes += aRectFnSet.GetHeight(pFootnote->getFrameArea());
                    }
                    if (pTab == &rTab)
                    {
                        nFootnoteHeight -= aRectFnSet.GetHeight(pFootnote->getFrameArea());
                    }
                }
            }
            if (nFootnoteHeight < 0)
            {   // tdf#156724 footnotes have grown, try to split again
                rIsFootnoteGrowth = true;
            }
        }
    }
    if (nDistanceToUpperPrtBottom + nFollowFootnotes < 0 || !rTab.DoesObjsFit())
        bRet = false;
 
    // apparently checking nFootnoteHeight here does *not* guarantee that it fits into the body
    if (bRet && rTab.IsInDocBody()
        && nDistanceToFootnoteBodyPrtBottom + nFollowFootnotes < 0)
    {
        assert(rTab.GetUpper() != rTab.FindFootnoteBossFrame()->FindBodyCont());
        SAL_INFO("sw.layout", "SwTabFrame Split failed because of footnote growth");
        bRet = false; // tdf#160897
    }
 
    // 2. Check if each cell in the last line has at least one content frame.
 
    // Note: a FollowFlowRow may contains empty cells!
    if ( bRet )
    {
        if ( !rLastLine.IsInFollowFlowRow() )
        {
            SwCellFrame* pCurrMasterCell = static_cast<SwCellFrame*>(rLastLine.Lower());
            while ( pCurrMasterCell )
            {
                if ( !pCurrMasterCell->ContainsContent() && pCurrMasterCell->GetTabBox()->getRowSpan() >= 1 )
                {
                    bRet = false;
                    break;
                }
                pCurrMasterCell = static_cast<SwCellFrame*>(pCurrMasterCell->GetNext());
            }
        }
    }
 
    // 3. Check if last line does not contain any content:
    if ( bRet )
    {
        if ( !rLastLine.ContainsContent() )
        {
            bRet = false;
        }
    }
 
    // 4. Check if follow flow line does not contain content:
    if ( bRet )
    {
        if ( !rFollowLine.IsRowSpanLine() && !rFollowLine.ContainsContent() )
        {
            bRet = false;
        }
    }
 
    if ( bRet )
    {
        // Everything looks fine. Splitting seems to be successful. We invalidate
        // rFollowLine to force a new formatting.
        ::SwInvalidateAll( &rFollowLine, LONG_MAX );
    }
    else
    {
        // Splitting the table row gave us an unexpected result.
        // Everything has to be prepared for a second try to split
        // the table, this time without splitting the row.
        ::SwInvalidateAll( &rLastLine, LONG_MAX );
    }
 
    rTab.SetRebuildLastLine( false );
    // #i26945#
    rTab.SetDoesObjsFit( true );
 
    return bRet;
}
 
// Sets the correct height for all spanned cells
static void lcl_AdjustRowSpanCells( SwRowFrame* pRow )
{
    SwRectFnSet aRectFnSet(pRow);
    SwCellFrame* pCellFrame = static_cast<SwCellFrame*>(pRow->GetLower());
    while ( pCellFrame )
    {
        const tools::Long nLayoutRowSpan = pCellFrame->GetLayoutRowSpan();
        if ( nLayoutRowSpan > 1 )
        {
            // calculate height of cell:
            const tools::Long nNewCellHeight = lcl_GetHeightOfRows( pRow, nLayoutRowSpan );
            const tools::Long nDiff = nNewCellHeight - aRectFnSet.GetHeight(pCellFrame->getFrameArea());
 
            if ( nDiff )
            {
                SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pCellFrame);
                aRectFnSet.AddBottom(aFrm, nDiff);
            }
        }
 
        pCellFrame = static_cast<SwCellFrame*>(pCellFrame->GetNext());
    }
}
 
// Returns the maximum layout row span of the row
// Looking for the next row that contains no covered cells:
static tools::Long lcl_GetMaximumLayoutRowSpan( const SwRowFrame& rRow )
{
    tools::Long nRet = 1;
 
    const SwRowFrame* pCurrentRowFrame = static_cast<const SwRowFrame*>(rRow.GetNext());
    bool bNextRow = false;
 
    while ( pCurrentRowFrame )
    {
        // if there is any covered cell, we proceed to the next row frame
        const SwCellFrame* pLower = static_cast<const SwCellFrame*>( pCurrentRowFrame->Lower());
        while ( pLower )
        {
            if ( pLower->GetTabBox()->getRowSpan() < 0 )
            {
                ++nRet;
                bNextRow = true;
                break;
            }
            pLower = static_cast<const SwCellFrame*>(pLower->GetNext());
        }
        pCurrentRowFrame = bNextRow ?
                         static_cast<const SwRowFrame*>(pCurrentRowFrame->GetNext() ) :
                         nullptr;
    }
 
    return nRet;
}
 
// Function to remove the FollowFlowLine of rTab.
// The content of the FollowFlowLine is moved to the associated line in the
// master table.
bool SwTabFrame::RemoveFollowFlowLine()
{
    // find FollowFlowLine
    SwTabFrame *pFoll = GetFollow();
    SwRowFrame* pFollowFlowLine = pFoll ? pFoll->GetFirstNonHeadlineRow() : nullptr;
 
    // find last row in master
    SwFrame* pLastLine = GetLastLower();
 
    OSL_ENSURE( HasFollowFlowLine() &&
            pFollowFlowLine &&
            pLastLine, "There should be a flowline in the follow" );
 
    // #140081# Make code robust.
    if ( !pFollowFlowLine || !pLastLine )
        return true;
    if (pFollowFlowLine->IsDeleteForbidden())
    {
        SAL_WARN("sw.layout", "Cannot remove in-use Follow Flow Line");
        return false;
    }
 
    // We have to reset the flag here, because lcl_MoveRowContent
    // calls a GrowFrame(), which has a different behavior if
    // this flag is set.
    SetFollowFlowLine( false );
 
    // Move content
    lcl_MoveRowContent( *pFollowFlowLine, *static_cast<SwRowFrame*>(pLastLine) );
 
    // NEW TABLES
    // If a row span follow flow line is removed, we want to move the whole span
    // to the master:
    tools::Long nRowsToMove = lcl_GetMaximumLayoutRowSpan( *pFollowFlowLine );
 
    if ( nRowsToMove > 1 )
    {
        SwRectFnSet aRectFnSet(this);
        SwFrame* pRow = pFollowFlowLine->GetNext();
        SwFrame* pInsertBehind = GetLastLower();
        SwTwips nGrow = 0;
 
        while ( pRow && nRowsToMove-- > 1 )
        {
            SwFrame* pNxt = pRow->GetNext();
            nGrow += aRectFnSet.GetHeight(pRow->getFrameArea());
 
            // The footnotes have to be moved:
            lcl_MoveFootnotes( *GetFollow(), *this, static_cast<SwRowFrame&>(*pRow) );
 
            pRow->RemoveFromLayout();
            pRow->InsertBehind( this, pInsertBehind );
            pRow->InvalidateAll_();
            pRow->CheckDirChange();
            pInsertBehind = pRow;
            pRow = pNxt;
        }
 
        SwFrame* pFirstRow = Lower();
        while ( pFirstRow )
        {
            lcl_AdjustRowSpanCells( static_cast<SwRowFrame*>(pFirstRow) );
            pFirstRow = pFirstRow->GetNext();
        }
 
        Grow( nGrow );
        GetFollow()->Shrink( nGrow );
    }
 
    bool bJoin = !pFollowFlowLine->GetNext();
    pFollowFlowLine->Cut();
    SwFrame::DestroyFrame(pFollowFlowLine);
 
    return bJoin;
}
 
// #i26945# - Floating screen objects are no longer searched.
static bool lcl_FindSectionsInRow( const SwRowFrame& rRow )
{
    bool bRet = false;
    const SwCellFrame* pLower = static_cast<const SwCellFrame*>(rRow.Lower());
    while ( pLower )
    {
        if ( pLower->IsVertical() != rRow.IsVertical() )
            return true;
 
        const SwFrame* pTmpFrame = pLower->Lower();
        while ( pTmpFrame )
        {
            if ( pTmpFrame->IsRowFrame() )
            {
                bRet = lcl_FindSectionsInRow( *static_cast<const SwRowFrame*>(pTmpFrame) );
            }
            else
            {
                // #i26945# - search only for sections
                if (pTmpFrame->IsSctFrame())
                {
                    bRet = true;
 
                    if (!rRow.IsInSct())
                    {
                        // This row is not in a section.
                        if (const SwFrame* pSectionLower = pTmpFrame->GetLower())
                        {
                            if (!pSectionLower->IsColumnFrame())
                            {
                                // Section has a single column only, try to
                                // split that.
                                bRet = false;
 
                                for (const SwFrame* pFrame = pSectionLower; pFrame; pFrame = pFrame->GetNext())
                                {
                                    if (pFrame->IsTabFrame())
                                    {
                                        // Section contains a table, no split in that case.
                                        bRet = true;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }
 
            if ( bRet )
                return true;
            pTmpFrame = pTmpFrame->GetNext();
        }
 
        pLower = static_cast<const SwCellFrame*>(pLower->GetNext());
    }
    return bRet;
}
 
bool SwTabFrame::Split(const SwTwips nCutPos, bool bTryToSplit,
        bool bTableRowKeep, bool & rIsFootnoteGrowth)
{
    bool bRet = true;
 
    SwRectFnSet aRectFnSet(this);
 
    // #i26745# - format row and cell frames of table
    {
        Lower()->InvalidatePos_();
        // #i43913# - correction
        // call method <lcl_InnerCalcLayout> with first lower.
        lcl_InnerCalcLayout( Lower(), LONG_MAX, true );
    }
 
    //In order to be able to compare the positions of the cells with CutPos,
    //they have to be calculated consecutively starting from the table.
    //They can definitely be invalid because of position changes of the table.
    SwRowFrame *pRow = static_cast<SwRowFrame*>(Lower());
    if( !pRow )
        return bRet;
 
    const sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat();
    sal_uInt16 nRowCount = 0;           // pRow currently points to the first row
 
    // Due to the comment above, about possibly invalid positions of cells due to position changes
    // of the table, calculate the rows height subtracting last row's bottom from first row's top,
    // and compare with the available height
    SwTwips nRemainingSpaceForLastRow = aRectFnSet.YDiff(nCutPos, aRectFnSet.GetPrtTop(*this));
    nRemainingSpaceForLastRow -= aRectFnSet.GetBottomMargin(*this);
    auto getRemainingAfter = [aRectFnSet, nAvailable = nRemainingSpaceForLastRow,
                              nFirstRowTop = aRectFnSet.GetTop(pRow->getFrameArea())](SwFrame* p)
    { return nAvailable + (p ? aRectFnSet.BottomDist(p->getFrameArea(), nFirstRowTop) : 0); };
 
    // Make pRow point to the line that does not fit anymore:
    while (pRow->GetNext())
    {
        SwTwips nNewRemaining = getRemainingAfter(pRow);
        if (nNewRemaining < 0)
            break;
        if( bTryToSplit || !pRow->IsRowSpanLine() ||
            0 != aRectFnSet.GetHeight(pRow->getFrameArea()) )
            ++nRowCount;
        nRemainingSpaceForLastRow = nNewRemaining;
        pRow = static_cast<SwRowFrame*>(pRow->GetNext());
    }
 
    // bSplitRowAllowed: Row may be split according to its attributes.
    // bTryToSplit:      Row will never be split if bTryToSplit = false.
    //                   This can either be passed as a parameter, indicating
    //                   that we are currently doing the second try to split the
    //                   table, or it will be set to false under certain
    //                   conditions that are not suitable for splitting
    //                   the row.
    bool bSplitRowAllowed = bTryToSplit;
    if (bSplitRowAllowed && !pRow->IsRowSplitAllowed())
    {
        // A row larger than the entire page ought to be allowed to split regardless of setting,
        // otherwise it has hidden content and that makes no sense
        tools::Long nMaxHeight = FindPageFrame()->getFramePrintArea().Height();
        for (auto pBody = FindBodyFrame(); pBody; pBody = pBody->GetUpper()->FindBodyFrame())
        {
            if (pBody->IsPageBodyFrame())
                nMaxHeight = pBody->getFramePrintArea().Height();
        }
        if (pRow->getFrameArea().Height() > nMaxHeight)
            pRow->SetForceRowSplitAllowed( true );
        else
            bSplitRowAllowed = false;
    }
    // #i29438#
    // #i26945# - Floating screen objects no longer forbid
    // a splitting of the table row.
    // Special DoNotSplit case 1:
    // Search for sections inside pRow:
    if (bSplitRowAllowed && lcl_FindSectionsInRow(*pRow))
    {
        bSplitRowAllowed = false;
    }
 
    SwFlyFrame* pFly = FindFlyFrame();
    if (bSplitRowAllowed && pFly && pFly->IsFlySplitAllowed())
    {
        // The remaining size is less than the minimum row height, then don't even try to split the
        // row, just move it forward.
        const SwFormatFrameSize& rRowSize = pRow->GetFormat()->GetFrameSize();
        if (rRowSize.GetHeightSizeType() == SwFrameSize::Minimum)
        {
            SwTwips nMinHeight = rRowSize.GetHeight();
            if (nMinHeight > nRemainingSpaceForLastRow)
            {
                bSplitRowAllowed = false;
 
                if (!pRow->GetPrev() && aRectFnSet.GetHeight(pRow->getFrameArea()) > nRemainingSpaceForLastRow)
                {
                    // Split of pRow is not allowed, no previous row, the current row doesn't fit:
                    // that's a failure, we'll have to move forward instead.
                    return false;
                }
            }
        }
    }
 
    // #i29771#
    // To avoid loops, we do some checks before actually trying to split
    // the row. Maybe we should keep the next row in this table.
    // Note: This is only done if we are at the beginning of our upper
    bool bKeepNextRow = false;
    if ( nRowCount < nRepeat )
    {
        // First case: One of the repeated headline does not fit to the page anymore.
        // tdf#88496 Disable repeated headline (like for #i44910#) to avoid loops and
        // to fix interoperability problems (very long tables only with headline)
        // tdf#150149 except in multi-column sections, where it's possible to enlarge
        // the height of the section frame instead of using this fallback
        OSL_ENSURE( !GetIndPrev(), "Table is supposed to be at beginning" );
        if ( !IsInSct() )
        {
            // This would mean the layout modifies the doc model, so RowsToRepeat drops to 0 while
            // there are existing row frames with RepeatedHeadline == true. Avoid this at least
            // inside split flys, it would lead to a crash in SwTabFrame::MakeAll().
            if (!pFly || !pFly->IsFlySplitAllowed())
            {
                m_pTable->SetRowsToRepeat(0);
            }
            return false;
        }
        else
            bKeepNextRow = true;
    }
    else if ( !GetIndPrev() && nRepeat == nRowCount )
    {
        // Second case: The first non-headline row does not fit to the page.
        // If it is not allowed to be split, or it contains a sub-row that
        // is not allowed to be split, we keep the row in this table:
        if (bSplitRowAllowed)
        {
            // Check if there are (first) rows inside this row,
            // which are not allowed to be split.
            SwCellFrame* pLowerCell = static_cast<SwCellFrame*>(pRow->Lower());
            while ( pLowerCell )
            {
                if ( pLowerCell->Lower() && pLowerCell->Lower()->IsRowFrame() )
                {
                    const SwRowFrame* pLowerRow = static_cast<SwRowFrame*>(pLowerCell->Lower());
                    if ( !pLowerRow->IsRowSplitAllowed() &&
                        aRectFnSet.GetHeight(pLowerRow->getFrameArea()) > nRemainingSpaceForLastRow )
                    {
                        bKeepNextRow = true;
                        break;
                    }
                }
                pLowerCell = static_cast<SwCellFrame*>(pLowerCell->GetNext());
            }
        }
        else
            bKeepNextRow = true;
    }
 
    // Better keep the next row in this table:
    if ( bKeepNextRow )
    {
        pRow = GetFirstNonHeadlineRow();
        if ( pRow && pRow->IsRowSpanLine() && 0 == aRectFnSet.GetHeight(pRow->getFrameArea()) )
            pRow = static_cast<SwRowFrame*>(pRow->GetNext());
        if ( pRow )
        {
            pRow = static_cast<SwRowFrame*>(pRow->GetNext());
            ++nRowCount;
        }
    }
 
    // No more row to split or to move to follow table:
    if ( !pRow )
        return bRet;
 
    // We try to split the row if
    // - the attributes of the row are set accordingly and
    // - we are allowed to do so
    // - it should not be kept with the next row
    bSplitRowAllowed = bSplitRowAllowed && (!bTableRowKeep || !pRow->ShouldRowKeepWithNext());
 
    // Adjust pRow according to the keep-with-next attribute:
    if ( !bSplitRowAllowed && bTableRowKeep )
    {
        SwRowFrame* pTmpRow = static_cast<SwRowFrame*>(pRow->GetPrev());
        SwRowFrame* pOldRow = pRow;
        while ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() &&
                nRowCount > nRepeat )
        {
            // Special case: pTmpRow wants to keep with pRow, but allows splitting, and some its
            // cells span several rows, including pRow. In this case, the "split" of the spanning
            // cells of the pTmpRow may still happen by moving pRow to the next page, even here
            // with !bSplitRowAllowed.
            if (pTmpRow->IsRowSplitAllowed())
            {
                bool bCellSpanCanSplit = false;
                for (auto pCellFrame = static_cast<const SwCellFrame*>(pTmpRow->GetLower());
                     pCellFrame;
                     pCellFrame = static_cast<const SwCellFrame*>(pCellFrame->GetNext()))
                {
                    if (pCellFrame->GetTabBox()->getRowSpan() > 1) // Master cell
                    {
                        bCellSpanCanSplit = true;
                        break;
                    }
                }
                if (bCellSpanCanSplit)
                    break;
            }
            pRow = pTmpRow;
            --nRowCount;
            pTmpRow = static_cast<SwRowFrame*>(pTmpRow->GetPrev());
        }
 
        // loop prevention
        if ( nRowCount == nRepeat && !GetIndPrev())
        {
            pRow = pOldRow;
        }
    }
 
    // If we do not intend to split pRow, we check if we are
    // allowed to move pRow to a follow. Otherwise we return
    // false, indicating an error
    if ( !bSplitRowAllowed )
    {
        SwRowFrame* pFirstNonHeadlineRow = GetFirstNonHeadlineRow();
        if ( pRow == pFirstNonHeadlineRow )
            return false;
 
        // #i91764#
        // Ignore row span lines
        SwRowFrame* pTmpRow = pFirstNonHeadlineRow;
        while ( pTmpRow && pTmpRow->IsRowSpanLine() )
        {
            pTmpRow = static_cast<SwRowFrame*>(pTmpRow->GetNext());
        }
        if ( !pTmpRow || pRow == pTmpRow )
        {
            return false;
        }
    }
 
    // Build follow table if not already done:
    bool bNewFollow;
    SwTabFrame *pFoll;
    if ( GetFollow() )
    {
        pFoll = GetFollow();
        bNewFollow = false;
    }
    else
    {
        bNewFollow = true;
        pFoll = new SwTabFrame( *this );
 
        // We give the follow table an initial width.
        {
            SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFoll);
            aRectFnSet.AddWidth(aFrm, aRectFnSet.GetWidth(getFrameArea()));
            aRectFnSet.SetLeft(aFrm, aRectFnSet.GetLeft(getFrameArea()));
        }
 
        {
            SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pFoll);
            aRectFnSet.AddWidth(aPrt, aRectFnSet.GetWidth(getFramePrintArea()));
        }
 
        // Insert the new follow table
        pFoll->InsertBehind( GetUpper(), this );
 
        // Repeat the headlines.
        auto& rLines = GetTable()->GetTabLines();
        for ( nRowCount = 0; nRowCount < nRepeat; ++nRowCount )
        {
            // Insert new headlines:
            SwRowFrame* pHeadline = new SwRowFrame(*rLines[nRowCount], this);
            {
                sw::FlyCreationSuppressor aSuppressor;
                pHeadline->SetRepeatedHeadline(true);
            }
            pHeadline->InsertBefore( pFoll, nullptr );
 
            SwPageFrame *pPage = pHeadline->FindPageFrame();
            const sw::SpzFrameFormats* pSpzs = GetFormat()->GetDoc()->GetSpzFrameFormats();
            if( !pSpzs->empty() )
            {
                SwNodeOffset nIndex;
                SwContentFrame* pFrame = pHeadline->ContainsContent();
                while( pFrame )
                {
                    // sw_redlinehide: the implementation of AppendObjs
                    // takes care of iterating merged SwTextFrame
                    nIndex = pFrame->IsTextFrame()
                        ? static_cast<SwTextFrame*>(pFrame)->GetTextNodeFirst()->GetIndex()
                        : static_cast<SwNoTextFrame*>(pFrame)->GetNode()->GetIndex();
                    AppendObjs(pSpzs, nIndex, pFrame, pPage, GetFormat()->GetDoc());
                    pFrame = pFrame->GetNextContentFrame();
                    if( !pHeadline->IsAnLower( pFrame ) )
                        break;
                }
            }
        }
    }
 
    SwRowFrame* pLastRow = nullptr;     // points to the last remaining line in master
    SwRowFrame* pFollowRow = nullptr;   // points to either the follow flow line or the
                                        // first regular line in the follow
 
    if ( bSplitRowAllowed )
    {
        // If the row that does not fit anymore is allowed
        // to be split, the next row has to be moved to the follow table.
        pLastRow = pRow;
        pRow = static_cast<SwRowFrame*>(pRow->GetNext());
 
        // new follow flow line for last row of master table
        pFollowRow = lcl_InsertNewFollowFlowLine( *this, *pLastRow, false );
    }
    else
    {
        pFollowRow = pRow;
 
        // NEW TABLES
        // check if we will break a row span by moving pFollowRow to the follow:
        // In this case we want to reformat the last line.
        const SwCellFrame* pCellFrame = static_cast<const SwCellFrame*>(pFollowRow->GetLower());
        while ( pCellFrame )
        {
            if ( pCellFrame->GetTabBox()->getRowSpan() < 1 )
            {
                pLastRow = static_cast<SwRowFrame*>(pRow->GetPrev());
                break;
            }
 
            pCellFrame = static_cast<const SwCellFrame*>(pCellFrame->GetNext());
        }
 
        // new follow flow line for last row of master table
        if ( pLastRow )
            pFollowRow = lcl_InsertNewFollowFlowLine( *this, *pLastRow, true );
    }
 
    const SwTwips nShrink = lcl_GetHeightOfRows(pRow, std::numeric_limits<tools::Long>::max());
 
    //Optimization: There is no paste needed for the new Follow and the
    //optimized insert can be used (large numbers of rows luckily only occur in
    //such situations).
    if ( bNewFollow )
    {
        SwFrame* pInsertBehind = pFoll->GetLastLower();
 
        while ( pRow )
        {
            SwFrame* pNxt = pRow->GetNext();
            // The footnotes do not have to be moved, this is done in the
            // MoveFwd of the follow table!!!
            pRow->RemoveFromLayout();
            pRow->InsertBehind( pFoll, pInsertBehind );
            pRow->InvalidateAll_();
            pInsertBehind = pRow;
            pRow = static_cast<SwRowFrame*>(pNxt);
        }
    }
    else
    {
        SwFrame* pPasteBefore = HasFollowFlowLine() ?
                              pFollowRow->GetNext() :
                              pFoll->GetFirstNonHeadlineRow();
 
        while ( pRow )
        {
            SwFrame* pNxt = pRow->GetNext();
 
            // The footnotes have to be moved:
            lcl_MoveFootnotes( *this, *GetFollow(), *pRow );
 
            pRow->RemoveFromLayout();
            pRow->Paste( pFoll, pPasteBefore );
 
            pRow->CheckDirChange();
            pRow = static_cast<SwRowFrame*>(pNxt);
        }
    }
 
    if ( !pLastRow )
        Shrink( nShrink );
    else
    {
        // we rebuild the last line to assure that it will be fully formatted
        // we also don't shrink here, because we will be doing that in lcl_RecalcSplitLine
 
        // recalculate the split line
        bRet = lcl_RecalcSplitLine(*pLastRow, *pFollowRow, getRemainingAfter(pLastRow->GetPrev()), nShrink, rIsFootnoteGrowth);
 
        // RecalcSplitLine did not work. In this case we conceal the split error:
        if (!bRet && !bSplitRowAllowed)
        {
            bRet = true;
        }
 
        // NEW TABLES
        // check if each cell in the row span line has a good height
        if ( bRet && pFollowRow->IsRowSpanLine() )
            lcl_AdjustRowSpanCells( pFollowRow );
    }
 
    return bRet;
}
 
namespace
{
    bool CanDeleteFollow(SwTabFrame *pFoll)
    {
        if (pFoll->IsJoinLocked())
            return false;
 
        if (pFoll->IsDeleteForbidden())
        {
            SAL_WARN("sw.layout", "Delete Forbidden");
            return false;
        }
 
        return true;
    }
 
    auto IsAllHiddenSection(SwSectionFrame const& rSection) -> bool
    {
        if (rSection.IsHiddenNow())
            return true;
        for (SwFrame const* pFrame = rSection.Lower(); pFrame; pFrame = pFrame->GetNext())
        {
            if (pFrame->IsColumnFrame())
            {
                return false; // adds some padding
            }
            else if (pFrame->IsSctFrame())
            {
                assert(false); // these aren't nested?
                if (!IsAllHiddenSection(*static_cast<SwSectionFrame const*>(pFrame)))
                {
                    return false;
                }
            }
            else if (pFrame->IsTabFrame())
            {
                return false; // presumably
            }
            else if (pFrame->IsTextFrame())
            {
                if (!pFrame->IsHiddenNow())
                {
                    return false;
                }
            }
        }
        return true;
    }
 
    auto IsAllHiddenRow(SwRowFrame const& rRow, SwTabFrame const& rTab) -> bool;
 
    auto IsAllHiddenCell(SwCellFrame const& rCell, SwRowFrame const& rRow, SwTabFrame const& rTab) -> bool
    {
        for (SwFrame const* pFrame = rCell.Lower(); pFrame; pFrame = pFrame->GetNext())
        {
            if (pFrame->IsRowFrame())
            {
                if (!IsAllHiddenRow(*static_cast<SwRowFrame const*>(pFrame), rTab))
                {
                    return false;
                }
            }
            else if (pFrame->IsSctFrame())
            {
                if (!IsAllHiddenSection(*static_cast<SwSectionFrame const*>(pFrame)))
                {
                    return false;
                }
            }
            else if (pFrame->IsTabFrame())
            {
                return false; // presumably
            }
            else if (pFrame->IsTextFrame())
            {
                if (!pFrame->IsHiddenNow())
                {
                    return false;
                }
            }
        }
        assert(rCell.Lower());
        if (rTab.IsCollapsingBorders() && rCell.Lower() && !rCell.Lower()->IsRowFrame())
        {
            if (rRow.GetTopMarginForLowers() != 0
                || rRow.GetBottomMarginForLowers() != 0)
            {
                return false;
            }
        }
        else
        {
            SwBorderAttrAccess border(SwFrame::GetCache(), &rCell);
            if (border.Get()->CalcTop() != 0 || border.Get()->CalcBottom() != 0)
            {
                return false;
            }
        }
        return true;
    }
 
    auto IsAllHiddenRow(SwRowFrame const& rRow, SwTabFrame const& rTab) -> bool
    {
        for (SwFrame const* pCell = rRow.Lower(); pCell; pCell = pCell->GetNext())
        {
            if (!IsAllHiddenCell(*static_cast<SwCellFrame const*>(pCell), rRow, rTab))
            {
                return false;
            }
        }
        return true;
    }
 
} // namespace
 
void SwTabFrame::Join()
{
    OSL_ENSURE( !HasFollowFlowLine(), "Joining follow flow line" );
 
    SwTabFrame *pFoll = GetFollow();
 
    if (!pFoll || !CanDeleteFollow(pFoll))
        return;
 
    SwRectFnSet aRectFnSet(this);
    pFoll->Cut();   //Cut out first to avoid unnecessary notifications.
 
    SwFrame *pRow = pFoll->GetFirstNonHeadlineRow(),
          *pNxt;
 
    SwFrame* pPrv = GetLastLower();
 
    SwTwips nHeight = 0;    //Total height of the inserted rows as return value.
    bool isAllHidden(true);
 
    while ( pRow )
    {
        pNxt = pRow->GetNext();
        nHeight += aRectFnSet.GetHeight(pRow->getFrameArea());
        if (nHeight != 0)
        {
            isAllHidden = false;
        }
        if (isAllHidden)
        {
            isAllHidden = IsAllHiddenRow(*static_cast<SwRowFrame *>(pRow), *this);
        }
        pRow->RemoveFromLayout();
        pRow->InvalidateAll_();
        pRow->InsertBehind( this, pPrv );
        pRow->CheckDirChange();
        pPrv = pRow;
        pRow = pNxt;
    }
 
    SetFollow( pFoll->GetFollow() );
    SetFollowFlowLine( pFoll->HasFollowFlowLine() );
    SwFrame::DestroyFrame(pFoll);
 
    Grow( nHeight );
 
    // In case the row does not have a height, Grow(nHeight) did nothing.
    // If this is not invalidated, subsequent follows may never be joined.
    // Try to guess if the height of the row will be 0.  If the document
    // was just loaded, it will be 0 in any case, but probably it's not a good
    // idea to join *all* follows for a newly loaded document, it would be
    // easier not to split the table in the first place; presumably it is split
    // because that improves performance.
    if (isAllHidden)
    {
        InvalidateSize_();
    }
}
 
static void SwInvalidatePositions( SwFrame *pFrame, tools::Long nBottom )
{
    // LONG_MAX == nBottom means we have to calculate all
    bool bAll = LONG_MAX == nBottom;
    SwRectFnSet aRectFnSet(pFrame);
    do
    {   pFrame->InvalidatePos_();
        pFrame->InvalidateSize_();
        if( pFrame->IsLayoutFrame() )
        {
            if ( static_cast<SwLayoutFrame*>(pFrame)->Lower() )
            {
                ::SwInvalidatePositions( static_cast<SwLayoutFrame*>(pFrame)->Lower(), nBottom);
                // #i26945#
                ::lcl_InvalidateLowerObjs( *static_cast<SwLayoutFrame*>(pFrame) );
            }
        }
        else
            pFrame->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
        pFrame = pFrame->GetNext();
    } while ( pFrame &&
              ( bAll ||
              aRectFnSet.YDiff( aRectFnSet.GetTop(pFrame->getFrameArea()), nBottom ) < 0 ) );
}
 
void SwInvalidateAll( SwFrame *pFrame, tools::Long nBottom )
{
    // LONG_MAX == nBottom means we have to calculate all
    bool bAll = LONG_MAX == nBottom;
    SwRectFnSet aRectFnSet(pFrame);
    do
    {
        pFrame->InvalidatePos_();
        pFrame->InvalidateSize_();
        pFrame->InvalidatePrt_();
        if( pFrame->IsLayoutFrame() )
        {
            // NEW TABLES
            SwLayoutFrame* pToInvalidate = static_cast<SwLayoutFrame*>(pFrame);
            if (pFrame->IsCellFrame())
            {
                SwCellFrame* pThisCell = static_cast<SwCellFrame*>(pFrame);
                if ( pThisCell->GetTabBox()->getRowSpan() < 1 )
                {
                    pToInvalidate = & const_cast<SwCellFrame&>(pThisCell->FindStartEndOfRowSpanCell( true ));
                    pToInvalidate->InvalidatePos_();
                    pToInvalidate->InvalidateSize_();
                    pToInvalidate->InvalidatePrt_();
                }
            }
            if ( pToInvalidate->Lower() )
                ::SwInvalidateAll( pToInvalidate->Lower(), nBottom);
        }
        else
            pFrame->Prepare();
 
        pFrame = pFrame->GetNext();
    } while ( pFrame &&
              ( bAll ||
              aRectFnSet.YDiff( aRectFnSet.GetTop(pFrame->getFrameArea()), nBottom ) < 0 ) );
}
 
// #i29550#
static void lcl_InvalidateAllLowersPrt( SwLayoutFrame* pLayFrame )
{
    pLayFrame->InvalidatePrt_();
    pLayFrame->InvalidateSize_();
    pLayFrame->SetCompletePaint();
 
    SwFrame* pFrame = pLayFrame->Lower();
 
    while ( pFrame )
    {
        if ( pFrame->IsLayoutFrame() )
            lcl_InvalidateAllLowersPrt( static_cast<SwLayoutFrame*>(pFrame) );
        else
        {
            pFrame->InvalidatePrt_();
            pFrame->InvalidateSize_();
            pFrame->SetCompletePaint();
        }
 
        pFrame = pFrame->GetNext();
    }
}
 
bool SwContentFrame::CalcLowers(SwLayoutFrame & rLay, SwLayoutFrame const& rDontLeave,
                                 tools::Long nBottom, bool bSkipRowSpanCells )
{
    vcl::RenderContext* pRenderContext = rLay.getRootFrame()->GetCurrShell()->GetOut();
    // LONG_MAX == nBottom means we have to calculate all
    bool bAll = LONG_MAX == nBottom;
    bool bRet = false;
    SwContentFrame *pCnt = rLay.ContainsContent();
    SwRectFnSet aRectFnSet(&rLay);
 
    // FME 2007-08-30 #i81146# new loop control
    int nLoopControlRuns = 0;
    const int nLoopControlMax = 10;
    const sw::BroadcastingModify* pLoopControlCond = nullptr;
 
    while (pCnt && rDontLeave.IsAnLower(pCnt))
    {
        // #115759# - check, if a format of content frame is
        // possible. Thus, 'copy' conditions, found at the beginning of
        // <SwContentFrame::MakeAll(..)>, and check these.
        const bool bFormatPossible = !pCnt->IsJoinLocked() &&
                                     ( !pCnt->IsTextFrame() ||
                                       !static_cast<SwTextFrame*>(pCnt)->IsLocked() ) &&
                                     ( pCnt->IsFollow() || !StackHack::IsLocked() );
 
        // NEW TABLES
        bool bSkipContent = false;
        if ( bSkipRowSpanCells && pCnt->IsInTab() )
        {
            const SwFrame* pCell = pCnt->GetUpper();
            while ( pCell && !pCell->IsCellFrame() )
                pCell = pCell->GetUpper();
            if ( pCell && 1 != static_cast<const SwCellFrame*>( pCell )->GetLayoutRowSpan() )
                bSkipContent = true;
        }
 
        if ( bFormatPossible && !bSkipContent )
        {
            bRet |= !pCnt->isFrameAreaDefinitionValid();
            // #i26945# - no extra invalidation of floating
            // screen objects needed.
            // Thus, delete call of method <SwFrame::InvalidateObjs( true )>
            pCnt->Calc(pRenderContext);
            // #i46941# - frame has to be valid
            // Note: frame could be invalid after calling its format, if it's locked.
            OSL_ENSURE( !pCnt->IsTextFrame() ||
                    pCnt->isFrameAreaDefinitionValid() ||
                    static_cast<SwTextFrame*>(pCnt)->IsJoinLocked(),
                    "<SwContentFrame::CalcLowers(..)> - text frame invalid and not locked." );
            if ( pCnt->IsTextFrame() && pCnt->isFrameAreaDefinitionValid() )
            {
                // #i23129#, #i36347# - pass correct page frame to
                // the object formatter
                SwFrameDeleteGuard aDeleteGuard(pCnt);
                if ( !SwObjectFormatter::FormatObjsAtFrame( *pCnt,
                                                          *(pCnt->FindPageFrame()) ) )
                {
                    SwTextNode const*const pTextNode(
                        static_cast<SwTextFrame*>(pCnt)->GetTextNodeFirst());
                    if (pTextNode == pLoopControlCond)
                        ++nLoopControlRuns;
                    else
                    {
                        nLoopControlRuns = 0;
                        pLoopControlCond = pTextNode;
                    }
 
                    if ( nLoopControlRuns < nLoopControlMax )
                    {
                        // restart format with first content
                        pCnt = rLay.ContainsContent();
                        continue;
                    }
 
                    SAL_WARN("sw.layout", "LoopControl in SwContentFrame::CalcLowers");
                }
            }
            if (!rDontLeave.IsAnLower(pCnt)) // moved backward?
            {
                pCnt = rLay.ContainsContent();
                continue; // avoid formatting new upper on different page
            }
            pCnt->GetUpper()->Calc(pRenderContext);
        }
        if( ! bAll && aRectFnSet.YDiff(aRectFnSet.GetTop(pCnt->getFrameArea()), nBottom) > 0 )
            break;
        pCnt = pCnt->GetNextContentFrame();
    }
    return bRet;
}
 
// #i26945# - add parameter <_bOnlyRowsAndCells> to control
// that only row and cell frames are formatted.
static bool lcl_InnerCalcLayout( SwFrame *pFrame,
                                      tools::Long nBottom,
                                      bool _bOnlyRowsAndCells )
{
    vcl::RenderContext* pRenderContext = pFrame->getRootFrame()->GetCurrShell() ? pFrame->getRootFrame()->GetCurrShell()->GetOut() : nullptr;
    // LONG_MAX == nBottom means we have to calculate all
    bool bAll = LONG_MAX == nBottom;
    bool bRet = false;
    const SwFrame* pOldUp = pFrame->GetUpper();
    SwRectFnSet aRectFnSet(pFrame);
    do
    {
        // #i26945# - parameter <_bOnlyRowsAndCells> controls,
        // if only row and cell frames are formatted.
        if ( pFrame->IsLayoutFrame() &&
             ( !_bOnlyRowsAndCells || pFrame->IsRowFrame() || pFrame->IsCellFrame() ) )
        {
            SwFrameDeleteGuard aDeleteGuard(pFrame);
 
            // #130744# An invalid locked table frame will
            // not be calculated => It will not become valid =>
            // Loop in lcl_RecalcRow(). Therefore we do not consider them for bRet.
            bRet |= !pFrame->isFrameAreaDefinitionValid() && ( !pFrame->IsTabFrame() || !static_cast<SwTabFrame*>(pFrame)->IsJoinLocked() );
            pFrame->Calc(pRenderContext);
            if( static_cast<SwLayoutFrame*>(pFrame)->Lower() )
                bRet |= lcl_InnerCalcLayout( static_cast<SwLayoutFrame*>(pFrame)->Lower(), nBottom);
 
            // NEW TABLES
            if (pFrame->IsCellFrame())
            {
                SwCellFrame* pThisCell = static_cast<SwCellFrame*>(pFrame);
                if ( pThisCell->GetTabBox()->getRowSpan() < 1 )
                {
                    SwCellFrame& rToCalc = const_cast<SwCellFrame&>(pThisCell->FindStartEndOfRowSpanCell( true ));
                    bRet |= !rToCalc.isFrameAreaDefinitionValid();
                    rToCalc.Calc(pRenderContext);
                    if ( rToCalc.Lower() )
                        bRet |= lcl_InnerCalcLayout( rToCalc.Lower(), nBottom);
                }
            }
        }
        pFrame = pFrame->GetNext();
    } while( pFrame &&
            ( bAll ||
              aRectFnSet.YDiff(aRectFnSet.GetTop(pFrame->getFrameArea()), nBottom) < 0 )
            && pFrame->GetUpper() == pOldUp );
    return bRet;
}
 
static void lcl_RecalcRow(SwRowFrame & rRow, tools::Long const nBottom)
{
    // FME 2007-08-30 #i81146# new loop control
    int nLoopControlRuns_1 = 0;
    sal_uInt16 nLoopControlStage_1 = 0;
    const int nLoopControlMax = 10;
 
    bool bCheck = true;
    do
    {
        // FME 2007-08-30 #i81146# new loop control
        int nLoopControlRuns_2 = 0;
        sal_uInt16 nLoopControlStage_2 = 0;
 
        while (lcl_InnerCalcLayout(&rRow, nBottom))
        {
            if ( ++nLoopControlRuns_2 > nLoopControlMax )
            {
                SAL_WARN_IF(nLoopControlStage_2 == 0, "sw.layout", "LoopControl_2 in lcl_RecalcRow: Stage 1!");
                SAL_WARN_IF(nLoopControlStage_2 == 1, "sw.layout", "LoopControl_2 in lcl_RecalcRow: Stage 2!!");
                SAL_WARN_IF(nLoopControlStage_2 >= 2, "sw.layout", "LoopControl_2 in lcl_RecalcRow: Stage 3!!!");
                rRow.ValidateThisAndAllLowers( nLoopControlStage_2++ );
                nLoopControlRuns_2 = 0;
                if( nLoopControlStage_2 > 2 )
                    break;
            }
 
            bCheck = true;
        }
 
        if( bCheck )
        {
            SwFrameDeleteGuard g(&rRow);
 
            // #115759# - force another format of the
            // lowers, if at least one of it was invalid.
            bCheck = SwContentFrame::CalcLowers(rRow, *rRow.GetUpper(), nBottom, true);
 
            // NEW TABLES
            // First we calculate the cells with row span of < 1, afterwards
            // all cells with row span of > 1:
            for ( int i = 0; i < 2; ++i )
            {
                SwCellFrame* pCellFrame = static_cast<SwCellFrame*>(rRow.Lower());
                while ( pCellFrame )
                {
                    const bool bCalc = 0 == i ?
                                       pCellFrame->GetLayoutRowSpan() < 1 :
                                       pCellFrame->GetLayoutRowSpan() > 1;
 
                    if ( bCalc )
                    {
                        SwCellFrame& rToRecalc = 0 == i ?
                                               const_cast<SwCellFrame&>(pCellFrame->FindStartEndOfRowSpanCell( true )) :
                                               *pCellFrame;
                        bCheck |= SwContentFrame::CalcLowers(rToRecalc, rToRecalc, nBottom, false);
                    }
 
                    pCellFrame = static_cast<SwCellFrame*>(pCellFrame->GetNext());
                }
            }
 
            if ( bCheck )
            {
                if ( ++nLoopControlRuns_1 > nLoopControlMax )
                {
                    SAL_WARN_IF(nLoopControlStage_1 == 0, "sw.layout", "LoopControl_1 in lcl_RecalcRow: Stage 1!");
                    SAL_WARN_IF(nLoopControlStage_1 == 1, "sw.layout", "LoopControl_1 in lcl_RecalcRow: Stage 2!!");
                    SAL_WARN_IF(nLoopControlStage_1 >= 2, "sw.layout", "LoopControl_1 in lcl_RecalcRow: Stage 3!!!");
                    rRow.ValidateThisAndAllLowers( nLoopControlStage_1++ );
                    nLoopControlRuns_1 = 0;
                    if( nLoopControlStage_1 > 2 )
                        break;
                }
 
                continue;
            }
        }
        break;
    } while( true );
}
 
static void lcl_RecalcTable( SwTabFrame& rTab,
                                  SwLayoutFrame *pFirstRow,
                                  SwLayNotify &rNotify )
{
    if ( rTab.Lower() )
    {
        if ( !pFirstRow )
        {
            pFirstRow = static_cast<SwLayoutFrame*>(rTab.Lower());
            rNotify.SetLowersComplete( true );
        }
        ::SwInvalidatePositions( pFirstRow, LONG_MAX );
        lcl_RecalcRow( *static_cast<SwRowFrame*>(pFirstRow), LONG_MAX );
    }
}
 
// This is a new function to check the first condition whether
// a tab frame may move backward. It replaces the formerly used
// GetIndPrev(), which did not work correctly for #i5947#
static bool lcl_NoPrev( const SwFrame& rFrame )
{
    // #i79774#
    // skip empty sections on investigation of direct previous frame.
    // use information, that at least one empty section is skipped in the following code.
    bool bSkippedDirectPrevEmptySection( false );
    if ( rFrame.GetPrev() )
    {
        const SwFrame* pPrev( rFrame.GetPrev() );
        while ( pPrev &&
                pPrev->IsSctFrame() &&
                !dynamic_cast<const SwSectionFrame&>(*pPrev).GetSection() )
        {
            pPrev = pPrev->GetPrev();
            bSkippedDirectPrevEmptySection = true;
        }
        if ( pPrev )
        {
            return false;
        }
    }
 
    if ( ( !bSkippedDirectPrevEmptySection && !rFrame.GetIndPrev() ) ||
         ( bSkippedDirectPrevEmptySection &&
           ( !rFrame.IsInSct() || !rFrame.GetIndPrev_() ) ) )
    {
        return true;
    }
 
    // I do not have a direct prev, but I have an indirect prev.
    // In section frames I have to check if I'm located inside
    // the first column:
    if ( rFrame.IsInSct() )
    {
        const SwFrame* pSct = rFrame.GetUpper();
        if ( pSct && pSct->IsColBodyFrame() &&
             pSct->GetUpper()->GetUpper()->IsSctFrame() )
        {
            const SwFrame* pPrevCol = rFrame.GetUpper()->GetUpper()->GetPrev();
            if ( pPrevCol )
                // I'm not inside the first column and do not have a direct
                // prev. I can try to go backward.
                return true;
        }
    }
 
    return false;
}
 
#define KEEPTAB ( !GetFollow() && !IsFollow() )
 
// - helper method to find next content frame of
// a table frame and format it to assure keep attribute.
// method return true, if a next content frame is formatted.
// Precondition: The given table frame hasn't a follow and isn't a follow.
SwFrame* sw_FormatNextContentForKeep( SwTabFrame* pTabFrame )
{
    vcl::RenderContext* pRenderContext = pTabFrame->getRootFrame()->GetCurrShell()->GetOut();
    // find next content, table or section
    SwFrame* pNxt = pTabFrame->FindNext();
 
    // skip empty sections
    while ( pNxt && pNxt->IsSctFrame() &&
            !static_cast<SwSectionFrame*>(pNxt)->GetSection() )
    {
        pNxt = pNxt->FindNext();
    }
 
    // if found next frame is a section, get its first content.
    if ( pNxt && pNxt->IsSctFrame() )
    {
        pNxt = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
    }
 
    // format found next frame.
    // if table frame is inside another table, method <SwFrame::MakeAll()> is
    // called to avoid that the superior table frame is formatted.
    if ( pNxt )
    {
        if ( pTabFrame->GetUpper()->IsInTab() )
            pNxt->MakeAll(pNxt->getRootFrame()->GetCurrShell()->GetOut());
        else
            pNxt->Calc(pRenderContext);
    }
 
    return pNxt;
}
 
namespace {
    bool AreAllRowsKeepWithNext( const SwRowFrame* pFirstRowFrame, const bool bCheckParents = true  )
    {
        bool bRet = pFirstRowFrame != nullptr &&
                    pFirstRowFrame->ShouldRowKeepWithNext( bCheckParents );
 
        while ( bRet && pFirstRowFrame->GetNext() != nullptr )
        {
            pFirstRowFrame = dynamic_cast<const SwRowFrame*>(pFirstRowFrame->GetNext());
            bRet = pFirstRowFrame != nullptr &&
                   pFirstRowFrame->ShouldRowKeepWithNext( bCheckParents );
        }
 
        return bRet;
    }
 
// Similar to SwObjPosOscillationControl in sw/source/core/layout/anchoreddrawobject.cxx
class PosSizeOscillationControl
{
public:
    bool OscillationDetected(const SwFrameAreaDefinition& rFrameArea);
 
private:
    // A partial copy of SwFrameAreaDefinition data
    struct FrameData
    {
        SwRect frameArea;
        SwRect framePrintArea;
        bool frameAreaPositionValid;
        bool frameAreaSizeValid;
        bool framePrintAreaValid;
 
        FrameData(const SwFrameAreaDefinition& src)
            : frameArea(src.getFrameArea())
            , framePrintArea(src.getFramePrintArea())
            , frameAreaPositionValid(src.isFrameAreaPositionValid())
            , frameAreaSizeValid(src.isFrameAreaSizeValid())
            , framePrintAreaValid(src.isFramePrintAreaValid())
        {
        }
 
        bool operator==(const SwFrameAreaDefinition& src) const
        {
            return frameArea == src.getFrameArea() && framePrintArea == src.getFramePrintArea()
                   && frameAreaPositionValid == src.isFrameAreaPositionValid()
                   && frameAreaSizeValid == src.isFrameAreaSizeValid()
                   && framePrintAreaValid == src.isFramePrintAreaValid();
        }
    };
    std::vector<FrameData> maFrameDatas;
};
 
bool PosSizeOscillationControl::OscillationDetected(const SwFrameAreaDefinition& rFrameArea)
{
    for (size_t i = 0; i < maFrameDatas.size(); ++i)
    {
        const auto& f = maFrameDatas[i];
        if (f == rFrameArea)
        {
            SAL_WARN("sw.layout",
                     "PosSize oscillation: frame " << i << " repeated; total frames " << maFrameDatas.size());
            return true;
        }
    }
 
    if (maFrameDatas.size() == 20) // stack is full -> oscillation
    {
        SAL_WARN("sw.layout", "PosSize oscillation: max frames");
        return true;
    }
 
    maFrameDatas.emplace_back(rFrameArea);
    return false;
}
}
 
// extern because static can't be friend
void FriendHackInvalidateRowFrame(SwFrameAreaDefinition & rRowFrame)
{
    // hilariously static_cast<SwTabFrame*>(GetLower()) would not require friend declaration, but it's UB...
    rRowFrame.setFrameAreaPositionValid(false);
}
 
static void InvalidateFramePositions(SwFrame * pFrame)
{
    while (pFrame)
    {
        if (pFrame->IsLayoutFrame())
        {
            InvalidateFramePositions(pFrame->GetLower());
        }
        else if (pFrame->IsTextFrame())
        {
            pFrame->Prepare(PrepareHint::FramePositionChanged);
        }
        pFrame = pFrame->GetNext();
    }
}
 
void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext)
{
    if ( IsJoinLocked() || StackHack::IsLocked() || StackHack::Count() > 50 )
        return;
 
    if ( HasFollow() )
    {
        SwTabFrame* pFollowFrame = GetFollow();
        OSL_ENSURE( !pFollowFrame->IsJoinLocked() || !pFollowFrame->IsRebuildLastLine(),
                "SwTabFrame::MakeAll for master while follow is in RebuildLastLine()" );
        if ( pFollowFrame->IsJoinLocked() && pFollowFrame->IsRebuildLastLine() )
            return;
    }
 
    PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )
 
    LockJoin(); //I don't want to be destroyed on the way.
    SwLayNotify aNotify( this );    //does the notification in the DTor
    // If pos is invalid, we have to call a SetInvaKeep at aNotify.
    // Otherwise the keep attribute would not work in front of a table.
    const bool bOldValidPos = isFrameAreaPositionValid();
 
    //If my neighbour is my Follow at the same time, I'll swallow it up.
    // OD 09.04.2003 #108698# - join all follows, which are placed on the
    // same page/column.
    // OD 29.04.2003 #109213# - join follow, only if join for the follow
    // is not locked. Otherwise, join will not be performed and this loop
    // will be endless.
    while ( GetNext() && GetNext() == GetFollow() &&
            CanDeleteFollow(GetFollow())
          )
    {
        if ( HasFollowFlowLine() )
            RemoveFollowFlowLine();
        Join();
    }
 
    // The bRemoveFollowFlowLinePending is set if the split attribute of the
    // last line is set:
    if ( IsRemoveFollowFlowLinePending() && HasFollowFlowLine() )
    {
        if ( RemoveFollowFlowLine() )
            Join();
        SetRemoveFollowFlowLinePending( false );
    }
 
    if (m_bResizeHTMLTable) //Optimized interplay with grow/shrink of the content
    {
        m_bResizeHTMLTable = false;
        SwHTMLTableLayout *pLayout = GetTable()->GetHTMLTableLayout();
        if ( pLayout )
            m_bCalcLowers = pLayout->Resize(
                            pLayout->GetBrowseWidthByTabFrame( *this ) );
    }
 
    // as long as bMakePage is true, a new page can be created (exactly once)
    bool bMakePage = true;
    // bMovedBwd gets set to true when the frame flows backwards
    bool bMovedBwd = false;
    // as long as bMovedFwd is false, the Frame may flow backwards (until
    // it has been moved forward once)
    bool bMovedFwd  = false;
    // gets set to true when the Frame is split
    bool bSplit = false;
    const bool bFootnotesInDoc = !GetFormat()->GetDoc()->GetFootnoteIdxs().empty();
    const bool bFly     = IsInFly();
 
    std::optional<SwBorderAttrAccess> oAccess(std::in_place, SwFrame::GetCache(), this);
    const SwBorderAttrs *pAttrs = oAccess->Get();
 
    bool const isHiddenNow(IsHiddenNow());
    // All rows should keep together
    bool bDontSplit = isHiddenNow
                || (!IsFollow() && !GetFormat()->GetLayoutSplit().GetValue());
 
    // The number of repeated headlines
    const sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat();
 
    // This flag indicates that we are allowed to try to split the
    // table rows.
    bool bTryToSplit = true;
 
    // Indicates that two individual rows may keep together, based on the keep
    // attribute set at the first paragraph in the first cell.
    bool bTableRowKeep = !bDontSplit && GetFormat()->GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::TABLE_ROW_KEEP);
    if (SwFlyFrame* pFly = FindFlyFrame())
    {
        if (pFly->IsFlySplitAllowed())
        {
            // Ignore the above text node -> row inheritance for floating tables.
            bTableRowKeep = false;
        }
        else if (!pFly->GetNextLink())
        {
            // If the fly is not allowed to split and is not chained, then it makes no sense to
            // split the table.
            bDontSplit = true;
        }
    }
 
    // The Magic Move: Used for the table row keep feature.
    // If only the last row of the table wants to keep (implicitly by setting
    // keep for the first paragraph in the first cell), and this table does
    // not have a next, the last line will be cut. Loop prevention: Only
    // one try.
    // WHAT IS THIS??? It "magically" hides last line (paragraph) in a table,
    // if first is set to keep with next???
    bool bLastRowHasToMoveToFollow = false;
    bool bLastRowMoveNoMoreTries = false;
 
    const bool bLargeTable = GetTable()->GetTabLines().size() > 64;  //arbitrary value, virtually guaranteed to be larger than one page.
    const bool bEmulateTableKeep = !bLargeTable && bTableRowKeep
        && !pAttrs->GetAttrSet().GetKeep().GetValue()
        && AreAllRowsKeepWithNext(GetFirstNonHeadlineRow(), /*bCheckParents=*/false);
    // The beloved keep attribute
    const bool bKeep = IsKeep(pAttrs->GetAttrSet().GetKeep(), GetBreakItem(), bEmulateTableKeep);
 
    // Join follow table, if this table is not allowed to split:
    if ( bDontSplit )
    {
        while ( GetFollow() && !GetFollow()->IsJoinLocked() )
        {
            if ( HasFollowFlowLine() )
                RemoveFollowFlowLine();
            Join();
        }
    }
 
    // Join follow table, if this does not have enough (repeated) lines:
    if ( nRepeat )
    {
        if( GetFollow() && !GetFollow()->IsJoinLocked() &&
            nullptr == GetFirstNonHeadlineRow() )
        {
            if ( HasFollowFlowLine() )
                RemoveFollowFlowLine();
            Join();
        }
    }
 
    // Join follow table, if last row of this table should keep:
    if ( bTableRowKeep && GetFollow() && !GetFollow()->IsJoinLocked() )
    {
        const SwRowFrame* pTmpRow = static_cast<const SwRowFrame*>(GetLastLower());
        if ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() )
        {
            // Special case: pTmpRow wants to keep with next, but allows splitting, and some its
            // cells span several rows - i.e., also the next row. In this case, the "split" of the
            // spanning cells of the pTmpRow may still happen by moving next row to the next page,
            // even here with bTableRowKeep.
            bool bCellSpanCanSplit = false;
            if (pTmpRow->IsRowSplitAllowed())
            {
                for (auto pCellFrame = static_cast<const SwCellFrame*>(pTmpRow->GetLower());
                     pCellFrame;
                     pCellFrame = static_cast<const SwCellFrame*>(pCellFrame->GetNext()))
                {
                    if (pCellFrame->GetTabBox()->getRowSpan() > 1) // Master cell
                    {
                        bCellSpanCanSplit = true;
                        break;
                    }
                }
            }
 
            if (!bCellSpanCanSplit)
            {
                if (HasFollowFlowLine())
                    RemoveFollowFlowLine();
                Join();
            }
        }
    }
 
    // a new one is moved forwards immediately
    if ( !getFrameArea().Top() && IsFollow() )
    {
        SwFrame *pPre = GetPrev();
        if ( pPre && pPre->IsTabFrame() && static_cast<SwTabFrame*>(pPre)->GetFollow() == this)
        {
            // don't make the effort to move fwd if its known
            // conditions that are known not to work
            if (IsInFootnote() && ForbiddenForFootnoteCntFwd())
                bMakePage = false;
            else if (!MoveFwd(bMakePage, false))
                bMakePage = false;
            bMovedFwd = true;
        }
    }
 
    if (isHiddenNow)
        MakeValidZeroHeight();
 
    int nUnSplitted = 5; // Just another loop control :-(
    int nThrowAwayValidLayoutLimit = 5; // And another one :-(
    PosSizeOscillationControl posSizeOscillationControl; // And yet another one.
    SwRectFnSet aRectFnSet(this);
    while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
    {
        const bool bMoveable = IsMoveable();
        if (bMoveable &&
            !(bMovedFwd && bEmulateTableKeep) )
            if ( CheckMoveFwd( bMakePage, bKeep && KEEPTAB, bEmulateTableKeep ) )
            {
                bMovedFwd = true;
                m_bCalcLowers = true;
                // #i99267#
                // reset <bSplit> after forward move to assure that follows
                // can be joined, if further space is available.
                bSplit = false;
            }
 
        Point aOldPos( aRectFnSet.GetPos(getFrameArea()) );
        MakePos();
 
        if ( aOldPos != aRectFnSet.GetPos(getFrameArea()) )
        {
            if ( aOldPos.Y() != aRectFnSet.GetTop(getFrameArea()) )
            {
                SwHTMLTableLayout *pLayout = GetTable()->GetHTMLTableLayout();
                if( pLayout )
                {
                    oAccess.reset();
                    m_bCalcLowers |= pLayout->Resize(
                        pLayout->GetBrowseWidthByTabFrame( *this ) );
                }
 
                if (!isHiddenNow)
                {
                    setFramePrintAreaValid(false);
                }
                aNotify.SetLowersComplete( false );
            }
            SwFrame *pPre;
            if ( bKeep || (nullptr != (pPre = FindPrev()) &&
                pPre->GetAttrSet()->GetKeep().GetValue()) )
            {
                m_bCalcLowers = true;
            }
            if (GetLower())
            {   // it's possible that the rows already have valid pos - but it is surely wrong if the table's pos changed!
                FriendHackInvalidateRowFrame(*GetLower());
                // invalidate text frames to get rid of their SwFlyPortions
                InvalidateFramePositions(GetLower());
            }
        }
 
        //We need to know the height of the first row, because the master needs
        //to be invalidated if it shrinks and then absorb the row if possible.
        tools::Long n1StLineHeight = 0;
        if ( IsFollow() )
        {
            SwFrame* pFrame = GetFirstNonHeadlineRow();
            if ( pFrame )
                n1StLineHeight = aRectFnSet.GetHeight(pFrame->getFrameArea());
        }
 
        if ( !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
        {
            const tools::Long nOldPrtWidth = aRectFnSet.GetWidth(getFramePrintArea());
            const tools::Long nOldFrameWidth = aRectFnSet.GetWidth(getFrameArea());
            const Point aOldPrtPos  = aRectFnSet.GetPos(getFramePrintArea());
 
            if (!oAccess)
            {
                oAccess.emplace(SwFrame::GetCache(), this);
                pAttrs = oAccess->Get();
            }
            Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs );
 
            SwHTMLTableLayout *pLayout = GetTable()->GetHTMLTableLayout();
            if ( pLayout &&
                (aRectFnSet.GetWidth(getFramePrintArea()) != nOldPrtWidth ||
                aRectFnSet.GetWidth(getFrameArea()) != nOldFrameWidth) )
            {
                oAccess.reset();
                m_bCalcLowers |= pLayout->Resize(
                    pLayout->GetBrowseWidthByTabFrame( *this ) );
            }
            if ( aOldPrtPos != aRectFnSet.GetPos(getFramePrintArea()) )
                aNotify.SetLowersComplete( false );
        }
 
        // If this is the first one in a chain, check if this can flow
        // backwards (if this is movable at all).
        // To prevent oscillations/loops, check that this has not just
        // flowed forwards.
        if ( !bMovedFwd && (bMoveable || bFly) && lcl_NoPrev( *this ) )
        {
            // for Follows notify Master.
            // only move Follow if it has to skip empty pages.
            if ( IsFollow() )
            {
                // Only if the height of the first line got smaller.
                SwFrame *pFrame = GetFirstNonHeadlineRow();
                if( pFrame && n1StLineHeight >aRectFnSet.GetHeight(pFrame->getFrameArea()) )
                {
                    SwTabFrame *pMaster = FindMaster();
                    bool bDummy;
                    if ( ShouldBwdMoved( pMaster->GetUpper(), bDummy ) )
                        pMaster->InvalidatePos();
                }
            }
            SwFootnoteBossFrame *pOldBoss = bFootnotesInDoc ? FindFootnoteBossFrame( true ) : nullptr;
            bool bReformat;
            std::optional<SfxDeleteListener> oDeleteListener;
            if (pOldBoss)
                oDeleteListener.emplace(*pOldBoss);
            SwFrameDeleteGuard g(this);
            if ( MoveBwd( bReformat ) )
            {
                SAL_WARN_IF(oDeleteListener && oDeleteListener->WasDeleted(), "sw.layout", "SwFootnoteBossFrame unexpectedly deleted");
 
                aRectFnSet.Refresh(this);
                bMovedBwd = true;
                aNotify.SetLowersComplete( false );
                if (bFootnotesInDoc && !oDeleteListener->WasDeleted())
                    MoveLowerFootnotes( nullptr, pOldBoss, nullptr, true );
                if ( bReformat || bKeep )
                {
                    tools::Long nOldTop = aRectFnSet.GetTop(getFrameArea());
                    MakePos();
                    if( nOldTop != aRectFnSet.GetTop(getFrameArea()) )
                    {
                        SwHTMLTableLayout *pHTMLLayout =
                            GetTable()->GetHTMLTableLayout();
                        if( pHTMLLayout )
                        {
                            oAccess.reset();
                            m_bCalcLowers |= pHTMLLayout->Resize(
                                pHTMLLayout->GetBrowseWidthByTabFrame( *this ) );
                        }
 
                        if (!isHiddenNow)
                        {
                            setFramePrintAreaValid(false);
                        }
 
                        if (!oAccess)
                        {
                            oAccess.emplace(SwFrame::GetCache(), this);
                            pAttrs = oAccess->Get();
                        }
                        Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs );
                    }
 
                    oAccess.reset();
 
                    lcl_RecalcTable( *this, nullptr, aNotify );
 
                    m_bLowersFormatted = true;
                    if ( bKeep && KEEPTAB )
                    {
 
                        // Consider case that table is inside another table,
                        // because it has to be avoided, that superior table
                        // is formatted.
                        // Thus, find next content, table or section
                        // and, if a section is found, get its first
                        // content.
                        if ( nullptr != sw_FormatNextContentForKeep( this ) && !GetNext() )
                        {
                            setFrameAreaPositionValid(false);
                        }
                    }
                }
            }
        }
 
        //Again an invalid value? - do it again...
        if ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
            continue;
 
        // check, if calculation of table frame is ready.
 
        // Local variable <nDistanceToUpperPrtBottom>
        //     Introduce local variable and init it with the distance from the
        //     table frame bottom to the bottom of the upper printing area.
        // Note: negative values denotes the situation that table frame doesn't fit in its upper.
        SwTwips nDistanceToUpperPrtBottom =
                aRectFnSet.BottomDist(getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
 
        /// In online layout try to grow upper of table frame, if table frame doesn't fit in its upper.
        const SwViewShell *pSh = getRootFrame()->GetCurrShell();
        const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
        if ( nDistanceToUpperPrtBottom < 0 && bBrowseMode )
        {
            if ( GetUpper()->Grow( -nDistanceToUpperPrtBottom ) )
            {
                // upper is grown --> recalculate <nDistanceToUpperPrtBottom>
                nDistanceToUpperPrtBottom = aRectFnSet.BottomDist(getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
            }
        }
 
        if (GetFollow() && GetUpper()->IsFlyFrame())
        {
            auto pUpper = static_cast<SwFlyFrame*>(GetUpper());
            if (pUpper->IsFlySplitAllowed())
            {
                // We have a follow tab frame that may be joined, and we're directly in a split fly.
                // See if the fly could grow.
                SwTwips nTest = GetUpper()->Grow(LONG_MAX, /*bTst=*/true);
                if (nTest >= aRectFnSet.GetHeight(GetFollow()->getFrameArea()))
                {
                    // We have space to join at least one follow tab frame.
                    SwTwips nRequest = 0;
                    for (SwTabFrame* pFollow = GetFollow(); pFollow; pFollow = pFollow->GetFollow())
                    {
                        nRequest += aRectFnSet.GetHeight(pFollow->getFrameArea());
                    }
                    // Try to grow the split fly to join all follows.
                    pUpper->Grow(nRequest);
                    // Determine what is space we actually got from the requested space.
                    nDistanceToUpperPrtBottom = aRectFnSet.BottomDist(getFrameArea(), aRectFnSet.GetPrtBottom(*pUpper));
                }
            }
        }
 
        // If there is still some space left in the upper, we check if we
        // can join some rows of the follow.
        // Setting bLastRowHasToMoveToFollow to true means we want to force
        // the table to be split! Only skip this if condition once.
        if( nDistanceToUpperPrtBottom >= 0 && !bLastRowHasToMoveToFollow )
        {
            // If there is space left in the upper printing area, join as for trial
            // at least one further row of an existing follow.
            if ( !bSplit && GetFollow() )
            {
                bool bDummy;
                if (!(HasFollowFlowLine()
                        && GetFollow()->GetFirstNonHeadlineRow()->IsDeleteForbidden())
                    && GetFollow()->ShouldBwdMoved(GetUpper(), bDummy))
                {
                    SwFrame *pTmp = GetUpper();
                    SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*pTmp);
                    if ( bBrowseMode )
                        nDeadLine += pTmp->Grow( LONG_MAX, true );
                    bool bFits = aRectFnSet.BottomDist(getFrameArea(), nDeadLine) > 0;
                    if (!bFits && aRectFnSet.GetHeight(GetFollow()->getFrameArea()) == 0)
                        // The follow should move backwards, so allow the case
                        // when the upper has no space, but the follow is
                        // empty.
                        bFits = aRectFnSet.BottomDist(getFrameArea(), nDeadLine) >= 0;
 
                    if (bFits)
                    {
                        // The follow table's wants to move backwards, see if the first row has a
                        // split fly anchored in it that would have more space than what we have:
                        SwRowFrame* pRow = GetFollow()->GetFirstNonHeadlineRow();
                        if (pRow)
                        {
                            SwPageFrame* pPage = GetFollow()->FindPageFrame();
                            SwSortedObjs* pPageObjs = pPage->GetSortedObjs();
                            if (pPageObjs)
                            {
                                bool bSplitFly = false;
                                for (size_t i = 0; i < pPageObjs->size(); ++i)
                                {
                                    SwAnchoredObject* pAnchoredObj = (*pPage->GetSortedObjs())[i];
                                    auto pFly = pAnchoredObj->DynCastFlyFrame();
                                    if (!pFly || !pFly->IsFlySplitAllowed())
                                    {
                                        continue;
                                    }
 
                                    SwFrame* pFlyAnchor = pFly->FindAnchorCharFrame();
                                    if (!pFlyAnchor || !pRow->IsAnLower(pFlyAnchor))
                                    {
                                        continue;
                                    }
 
                                    bSplitFly = true;
                                    break;
                                }
                                SwTwips nFollowFirstRowHeight = aRectFnSet.GetHeight(pRow->getFrameArea());
                                SwTwips nSpace = aRectFnSet.BottomDist(getFrameArea(), nDeadLine);
                                if (bSplitFly && nFollowFirstRowHeight > 0 && nSpace < nFollowFirstRowHeight)
                                {
                                    // The row has at least one split fly and the row would not fit
                                    // to our remaining space, when also taking flys into account,
                                    // so that's not a fit.
                                    bFits = false;
                                }
                            }
                        }
                    }
 
                    if (bFits)
                    {
                        // First, we remove an existing follow flow line.
                        if ( HasFollowFlowLine() )
                        {
                            SwFrame* pLastLine = GetLastLower();
                            RemoveFollowFlowLine();
                            // invalidate and rebuild last row
                            if ( pLastLine )
                            {
                                ::SwInvalidateAll( pLastLine, LONG_MAX );
                                SetRebuildLastLine( true );
                                lcl_RecalcRow(*static_cast<SwRowFrame*>(pLastLine), LONG_MAX);
                                SetRebuildLastLine( false );
                            }
 
                            SwFrame* pRow = GetFollow()->GetFirstNonHeadlineRow();
 
                            if ( !pRow || !pRow->GetNext() )
                                // The follow became empty and hence useless
                                Join();
 
                            continue;
                        }
 
                        // If there is no follow flow line, we move the first
                        // row in the follow table to the master table.
                        SwRowFrame *pRow = GetFollow()->GetFirstNonHeadlineRow();
 
                        // The follow became empty and hence useless
                        if ( !pRow )
                        {
                            Join();
                            continue;
                        }
 
                        const SwTwips nOld = aRectFnSet.GetHeight(getFrameArea());
                        tools::Long nRowsToMove = lcl_GetMaximumLayoutRowSpan( *pRow );
                        SwFrame* pRowToMove = pRow;
 
                        while ( pRowToMove && nRowsToMove-- > 0 )
                        {
                            const bool bMoveFootnotes = bFootnotesInDoc && !GetFollow()->IsJoinLocked();
 
                            SwFootnoteBossFrame *pOldBoss = nullptr;
                            if ( bMoveFootnotes )
                                pOldBoss = pRowToMove->FindFootnoteBossFrame( true );
 
                            SwFrame* pNextRow = pRowToMove->GetNext();
 
                            if ( !pNextRow )
                            {
                                // The follow became empty and hence useless
                                Join();
                            }
                            else
                            {
                                pRowToMove->Cut();
                                pRowToMove->Paste( this );
                            }
 
                            // Move the footnotes!
                            if ( bMoveFootnotes )
                                if ( static_cast<SwLayoutFrame*>(pRowToMove)->MoveLowerFootnotes( nullptr, pOldBoss, FindFootnoteBossFrame( true ), true ) )
                                    GetUpper()->Calc(pRenderContext);
 
                            pRowToMove = pNextRow;
                        }
 
                        if ( nOld != aRectFnSet.GetHeight(getFrameArea()) )
                            lcl_RecalcTable( *this, static_cast<SwLayoutFrame*>(pRow), aNotify );
 
                        continue;
                    }
                }
            }
            else if ( KEEPTAB )
            {
                bool bFormat = false;
                if ( bKeep )
                    bFormat = true;
                else if ( bTableRowKeep && !bLastRowMoveNoMoreTries )
                {
                    // We only want to give the last row one chance to move
                    // to the follow table. Set the flag as early as possible:
                    bLastRowMoveNoMoreTries = true;
 
                    // The last line of the table has to be cut off if:
                    // 1. The table does not want to keep with its next
                    // 2. The compatibility option is set and the table is allowed to split
                    // 3. We did not already cut off the last row
                    // 4. There is not break after attribute set at the table
                    // 5. There is no break before attribute set behind the table
                    // 6. There is no section change behind the table (see IsKeep)
                    // 7. The last table row wants to keep with its next.
                    const SwRowFrame* pLastRow = static_cast<const SwRowFrame*>(GetLastLower());
                    if (pLastRow)
                    {
                        if (!oAccess)
                        {
                            oAccess.emplace(SwFrame::GetCache(), this);
                            pAttrs = oAccess->Get();
                        }
                        if (IsKeep(pAttrs->GetAttrSet().GetKeep(), GetBreakItem(), true)
                            && pLastRow->ShouldRowKeepWithNext())
                        {
                            bFormat = true;
                        }
                    }
                }
 
                if ( bFormat )
                {
                    oAccess.reset();
 
                    // Consider case that table is inside another table, because
                    // it has to be avoided, that superior table is formatted.
                    // Thus, find next content, table or section and, if a section
                    // is found, get its first content.
                    const SwFrame* pTmpNxt = sw_FormatNextContentForKeep( this );
 
                    // The last row wants to keep with the frame behind the table.
                    // Check if the next frame is on a different page and valid.
                    // In this case we do a magic trick:
                    if ( !bKeep && !GetNext() && pTmpNxt && pTmpNxt->isFrameAreaDefinitionValid() )
                    {
                        setFrameAreaPositionValid(false);
                        bLastRowHasToMoveToFollow = true;
                    }
                }
            }
 
            if ( isFrameAreaDefinitionValid() )
            {
                if (m_bCalcLowers)
                {
                    lcl_RecalcTable( *this, nullptr, aNotify );
                    m_bLowersFormatted = true;
                    m_bCalcLowers = false;
                }
                else if (m_bONECalcLowers)
                {
                    // tdf#147526 is a case of a macro which results in a null Lower() result
                    if (SwRowFrame* pLower = static_cast<SwRowFrame*>(Lower()))
                        lcl_RecalcRow(*pLower, LONG_MAX);
                    m_bONECalcLowers = false;
                }
            }
            continue;
        }
 
        // I don't fit in the upper Frame anymore, therefore it's the
        // right moment to do some preferably constructive changes.
 
        // If I'm NOT allowed to leave the upper Frame, I've got a problem.
        // Following Arthur Dent, we do the only thing that you can do with
        // an unsolvable problem: We ignore it with all our power.
        if ( !bMoveable )
        {
            if (m_bCalcLowers && isFrameAreaDefinitionValid())
            {
                lcl_RecalcTable( *this, nullptr, aNotify );
                m_bLowersFormatted = true;
                m_bCalcLowers = false;
            }
            else if (m_bONECalcLowers)
            {
                lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), LONG_MAX);
                m_bONECalcLowers = false;
            }
 
            // It does not make sense to cut off the last line if we are
            // not moveable:
            bLastRowHasToMoveToFollow = false;
 
            continue;
        }
 
        if (m_bCalcLowers && isFrameAreaDefinitionValid())
        {
            lcl_RecalcTable( *this, nullptr, aNotify );
            m_bLowersFormatted = true;
            m_bCalcLowers = false;
            if( !isFrameAreaDefinitionValid() )
                continue;
        }
 
        // First try to split the table. Condition:
        // 1. We have at least one non headline row
        // 2. If this row wants to keep, we need an additional row
        // 3. The table is allowed to split or we do not have a pIndPrev:
        SwFrame* pIndPrev = GetIndPrev();
 
        SwFlyFrame* pFly = FindFlyFrame();
        if (!pIndPrev && pFly && pFly->IsFlySplitAllowed())
        {
            auto pFlyAtContent = static_cast<SwFlyAtContentFrame*>(pFly);
            SwFrame* pAnchor = pFlyAtContent->FindAnchorCharFrame();
            if (pAnchor)
            {
                // If the anchor of the split has a previous frame, we're allowed to move forward.
                pIndPrev = pAnchor->GetIndPrev();
            }
        }
 
        const SwRowFrame* pFirstNonHeadlineRow = GetFirstNonHeadlineRow();
        // #i120016# if this row wants to keep, allow split in case that all rows want to keep with next,
        // the table can not move forward as it is the first one and a split is in general allowed.
        const bool bAllowSplitOfRow = bTableRowKeep && !pIndPrev && AreAllRowsKeepWithNext(pFirstNonHeadlineRow);
        // tdf91083 MSCompat: this extends bAllowSplitOfRow (and perhaps should just replace it).
        // If the kept-together items cannot move to a new page, a table split is in general allowed.
        const bool bEmulateTableKeepSplitAllowed =  bEmulateTableKeep && !IsKeepFwdMoveAllowed(/*IgnoreMyOwnKeepValue=*/true);
 
        if ( pFirstNonHeadlineRow && nUnSplitted > 0 &&
             ( bEmulateTableKeepSplitAllowed || bAllowSplitOfRow ||
               ( ( !bTableRowKeep || pFirstNonHeadlineRow->GetNext() ||
                   !pFirstNonHeadlineRow->ShouldRowKeepWithNext()
                 ) && ( !bDontSplit || !pIndPrev )
           ) ) )
        {
            // #i29438#
            // Special DoNotSplit cases:
            // We better avoid splitting of a row frame if we are inside a columned
            // section which has a height of 0, because this is not growable and thus
            // all kinds of unexpected things could happen.
            if ( IsInSct() && FindSctFrame()->Lower()->IsColumnFrame() &&
                 0 == aRectFnSet.GetHeight(GetUpper()->getFrameArea())
               )
            {
                bTryToSplit = false;
            }
 
            // 1. Try: bTryToSplit = true  => Try to split the row.
            // 2. Try: bTryToSplit = false => Split the table between the rows.
            if ( pFirstNonHeadlineRow->GetNext() || bTryToSplit )
            {
                SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
                bool bFlySplit = false;
                if (GetUpper()->IsFlyFrame())
                {
                    // See if this is a split fly that can also grow.
                    auto pUpperFly = static_cast<SwFlyFrame*>(GetUpper());
                    bFlySplit = pUpperFly->IsFlySplitAllowed();
 
                    if (bFlySplit && bTryToSplit)
                    {
                        // This is a split fly that wants to split the row itself. See if it's also
                        // nested. If so, we'll want to know if the row split has rowspans.
                        SwTextFrame* pAnchorCharFrame = pUpperFly->FindAnchorCharFrame();
                        if (pAnchorCharFrame && pAnchorCharFrame->IsInFly())
                        {
                            // Find the row we'll split.
                            SwTwips nRemaining
                                = aRectFnSet.YDiff(nDeadLine, aRectFnSet.GetTop(getFrameArea()));
                            nRemaining -= aRectFnSet.GetTopMargin(*this);
                            const SwFrame* pRow = Lower();
                            for (; pRow->GetNext(); pRow = pRow->GetNext())
                            {
                                if (nRemaining < aRectFnSet.GetHeight(pRow->getFrameArea()))
                                {
                                    break;
                                }
 
                                nRemaining -= aRectFnSet.GetHeight(pRow->getFrameArea());
                            }
                            // See if any cells have rowspans.
                            for (const SwFrame* pLower = pRow->GetLower(); pLower;
                                 pLower = pLower->GetNext())
                            {
                                auto pCellFrame = static_cast<const SwCellFrame*>(pLower);
                                if (pCellFrame->GetTabBox()->getRowSpan() != 1)
                                {
                                    // The cell has a rowspan, don't split the row itself in this
                                    // case (but just move it forward, i.e. split between the rows).
                                    bTryToSplit = false;
                                    break;
                                }
                            }
                        }
                    }
                }
                if( IsInSct() || GetUpper()->IsInTab() || bFlySplit )
                    nDeadLine = aRectFnSet.YInc( nDeadLine,
                                        GetUpper()->Grow( LONG_MAX, true ) );
 
                {
                    SwFrameDeleteGuard g(Lower()); // tdf#134965 prevent RemoveFollowFlowLine()
                    SetInRecalcLowerRow( true );
                    ::lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), nDeadLine);
                    SetInRecalcLowerRow( false );
                }
                m_bLowersFormatted = true;
                aNotify.SetLowersComplete( true );
 
                // One more check if it's really necessary to split the table.
                // 1. The table either has to exceed the deadline or
                // 2. We explicitly want to cut off the last row.
                if( aRectFnSet.BottomDist( getFrameArea(), nDeadLine ) > 0 && !bLastRowHasToMoveToFollow )
                {
                    continue;
                }
 
                // Set to false again as early as possible.
                bLastRowHasToMoveToFollow = false;
 
                // #i52781#
                // YaSC - Yet another special case:
                // If our upper is inside a table cell which is not allowed
                // to split, we do not try to split:
                if ( GetUpper()->IsInTab() )
                {
                    const SwFrame* pTmpRow = GetUpper();
                    while ( pTmpRow && !pTmpRow->IsRowFrame() )
                       pTmpRow = pTmpRow->GetUpper();
                    if ( pTmpRow && !static_cast<const SwRowFrame*>(pTmpRow)->IsRowSplitAllowed() )
                        continue;
                }
 
                sal_uInt16 nMinNumOfLines = nRepeat;
 
                if ( bTableRowKeep )
                {
                    const SwRowFrame* pTmpRow = GetFirstNonHeadlineRow();
                    // Copying the "NEW TABLES" comment, apparently related to commit dcb682563b24bf487a40a9fe7710b4d500850a52
                    if (pTmpRow && pTmpRow->IsRowSpanLine())
                    {
                        ++nMinNumOfLines;
                        pTmpRow = static_cast<const SwRowFrame*>(pTmpRow->GetNext());
                    }
 
                    while ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() )
                    {
                        ++nMinNumOfLines;
                        pTmpRow = static_cast<const SwRowFrame*>(pTmpRow->GetNext());
                    }
                }
 
                if ( !bTryToSplit )
                    ++nMinNumOfLines;
 
                const SwTwips nBreakLine = aRectFnSet.YInc(
                        aRectFnSet.GetTop(getFrameArea()),
                        aRectFnSet.GetTopMargin(*this) +
                         lcl_GetHeightOfRows( GetLower(), nMinNumOfLines ) );
 
                bool bHadFollowFlowLineBeforeSplit = false;
                // Some more checks if we want to call the split algorithm or not:
                // The repeating lines / keeping lines still fit into the upper or
                // if we do not have an (in)direct Prev, we split anyway.
                if( aRectFnSet.YDiff(nDeadLine, nBreakLine) >=0
                    || !pIndPrev || bEmulateTableKeepSplitAllowed )
                {
                    aNotify.SetLowersComplete( false );
                    bSplit = true;
 
                    // An existing follow flow line has to be removed.
                    if ( HasFollowFlowLine() )
                    {
                        if (!nThrowAwayValidLayoutLimit)
                            continue;
                        const bool bInitialLoopEndCondition(isFrameAreaDefinitionValid());
                        bHadFollowFlowLineBeforeSplit = true;
                        RemoveFollowFlowLine();
                        const bool bFinalLoopEndCondition(isFrameAreaDefinitionValid());
 
                        if (bInitialLoopEndCondition && !bFinalLoopEndCondition)
                        {
                            --nThrowAwayValidLayoutLimit;
                        }
                    }
 
                    oAccess.reset();
                    bool isFootnoteGrowth(false);
                    bool bEffectiveTableRowKeep;
                    if (bTryToSplit == true)
                    {
                        bEffectiveTableRowKeep = bTableRowKeep && !(bAllowSplitOfRow || bEmulateTableKeepSplitAllowed);
                    }
                    else
                    {
                        // The second attempt; ignore all the flags allowing to split the row
                        bEffectiveTableRowKeep = bTableRowKeep;
                    }
                    const bool bSplitError = !Split(nDeadLine, bTryToSplit,
                        bEffectiveTableRowKeep,
                        isFootnoteGrowth);
 
                    // tdf#130639 don't start table on a new page after the fallback "switch off repeating header"
                    if (bSplitError && nRepeat > GetTable()->GetRowsToRepeat())
                    {
                        setFrameAreaPositionValid(false);
                        break;
                    }
 
                    if (!bTryToSplit && !bSplitError)
                    {
                        --nUnSplitted;
                    }
 
                    // #i29771# Two tries to split the table
                    // If an error occurred during splitting. We start a second
                    // try, this time without splitting of table rows.
                    if ( bSplitError && HasFollowFlowLine() )
                        RemoveFollowFlowLine();
 
                    // If splitting the table was successful or not,
                    // we do not want to have 'empty' follow tables.
                    if ( GetFollow() && !GetFollow()->GetFirstNonHeadlineRow() )
                    {
                        // For split flys, if we just removed the follow flow line before split,
                        // then avoid the join in the error + rowsplit case, so split can be called
                        // again, this time without a rowsplit.
                        if (!bFlySplit || !bHadFollowFlowLineBeforeSplit || !bSplitError || !bTryToSplit)
                        {
                            Join();
                        }
                    }
 
                    // We want to restore the situation before the failed
                    // split operation as good as possible. Therefore we
                    // do some more calculations. Note: Restricting this
                    // to nDeadLine may not be enough.
                    // tdf#161508 hack: treat oscillation likewise
                    if ((bSplitError && bTryToSplit) // no restart if we did not try to split: i72847, i79426
                        || posSizeOscillationControl.OscillationDetected(*this))
                    {
                        lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), LONG_MAX);
                        setFrameAreaPositionValid(false);
                        // tdf#156724 if the table added footnotes, try to split *again*
                        if (!isFootnoteGrowth)
                        {
                            bTryToSplit = false;
                        }
                        continue;
                    }
 
                    // If split failed, then next time try without
                    // allowing to split the table rows.
                    bTryToSplit = !bSplitError;
 
                    //To avoid oscillations the Follow must become valid now
                    if ( GetFollow() )
                    {
                        // #i80924#
                        // After a successful split assure that the first row
                        // is invalid. When graphics are present, this isn't hold.
                        // Note: defect i80924 could also be fixed, if it is
                        // assured, that <SwLayNotify::bLowersComplete> is only
                        // set, if all lower are valid *and* are correct laid out.
                        if ( !bSplitError && GetFollow()->GetLower() )
                        {
                            GetFollow()->GetLower()->InvalidatePos();
                        }
                        SwRectFnSet fnRectX(GetFollow());
 
                        static sal_uInt8 nStack = 0;
                        if ( !StackHack::IsLocked() && nStack < 4 )
                        {
                            ++nStack;
                            StackHack aHack;
                            oAccess.reset();
 
                            GetFollow()->MakeAll(pRenderContext);
 
                            GetFollow()->SetLowersFormatted(false);
                            // #i43913# - lock follow table
                            // to avoid its formatting during the format of
                            // its content.
                            const bool bOldJoinLock =  GetFollow()->IsJoinLocked();
                            GetFollow()->LockJoin();
                            ::lcl_RecalcRow(*static_cast<SwRowFrame*>(GetFollow()->Lower()),
                                             fnRectX.GetBottom(GetFollow()->GetUpper()->getFrameArea()) );
                            // #i43913#
                            // #i63632# Do not unlock the
                            // follow if it wasn't locked before.
                            if ( !bOldJoinLock )
                                GetFollow()->UnlockJoin();
 
                            if ( !GetFollow()->GetFollow() )
                            {
                                SwFrame* pNxt = static_cast<SwFrame*>(GetFollow())->FindNext();
                                if ( pNxt )
                                {
                                    // #i18103# - no formatting of found next
                                    // frame, if it's a follow section of the
                                    // 'ColLocked' section, the follow table is
                                    // in.
                                    bool bCalcNxt = true;
                                    if ( GetFollow()->IsInSct() && pNxt->IsSctFrame() )
                                    {
                                        SwSectionFrame* pSct = GetFollow()->FindSctFrame();
                                        if ( pSct->IsColLocked() &&
                                             pSct->GetFollow() == pNxt )
                                        {
                                            bCalcNxt = false;
                                        }
                                    }
                                    if ( bCalcNxt )
                                    {
                                        // tdf#119109 follow was just formatted,
                                        // don't do it again now
                                        FlowFrameJoinLockGuard g(GetFollow());
                                        pNxt->Calc(pRenderContext);
                                    }
                                }
                            }
 
                            --nStack;
                        }
                        else if ( GetFollow() == GetNext() )
                            GetFollow()->MoveFwd( true, false );
                    }
                    continue;
                }
            }
        }
 
        // Set to false again as early as possible.
        bLastRowHasToMoveToFollow = false;
 
        if( IsInSct() && bMovedFwd && bMakePage && GetUpper()->IsColBodyFrame() &&
            GetUpper()->GetUpper()->GetUpper()->IsSctFrame() &&
            ( GetUpper()->GetUpper()->GetPrev() || GetIndPrev() ) &&
            static_cast<SwSectionFrame*>(GetUpper()->GetUpper()->GetUpper())->MoveAllowed(this) )
        {
            bMovedFwd = false;
        }
 
        // #i29771# Reset bTryToSplit flag on change of upper
        const SwFrame* pOldUpper = GetUpper();
 
        //Let's see if we find some place anywhere...
        if (!bMovedFwd)
        {
            bool bMoveAlways = false;
            SwFrame* pUpper = GetUpper();
            if (pUpper && pUpper->IsFlyFrame())
            {
                auto pFlyFrame = static_cast<SwFlyFrame*>(pUpper);
                if (pFlyFrame->IsFlySplitAllowed())
                {
                    // If the anchor of the split has a previous frame, MoveFwd() is allowed to move
                    // forward.
                    bMoveAlways = true;
                }
            }
            // don't make the effort to move fwd if its known
            // conditions that are known not to work
            if (IsInFootnote() && ForbiddenForFootnoteCntFwd())
                bMakePage = false;
            else if (!MoveFwd(bMakePage, false, bMoveAlways))
                bMakePage = false;
        }
 
        // #i29771# Reset bSplitError flag on change of upper
        if ( GetUpper() != pOldUpper )
        {
            bTryToSplit = true;
            nUnSplitted = 5;
        }
 
        aRectFnSet.Refresh(this);
        m_bCalcLowers = true;
        bMovedFwd = true;
        aNotify.SetLowersComplete( false );
        if ( IsFollow() )
        {
            // To avoid oscillations, master should not remain invalid
            SwTabFrame *pTab = FindMaster();
            if ( pTab->GetUpper() )
                pTab->GetUpper()->Calc(pRenderContext);
            pTab->Calc(pRenderContext);
            pTab->SetLowersFormatted( false );
        }
 
        //If my neighbour is my Follow at the same time, I'll swallow it up.
        if ( ( GetNext() && GetNext() == GetFollow() ) || !GetLower() )
        {
            if ( HasFollowFlowLine() )
                RemoveFollowFlowLine();
            if ( GetFollow() )
                Join();
        }
        else if (!GetNext() && !HasFollowFlowLine() && GetFollow()
                 && (getFrameArea().Bottom() + GetFollow()->getFrameArea().Height())
                        < GetUpper()->getFrameArea().Bottom())
        {
            // We're the last lower of the upper, no split row and we have a follow.  That follow
            // fits our upper, still.  Prefer joining that follow in the next iteration, instead of
            // trying to split the current table.
            bSplit = false;
        }
 
        if ( bMovedBwd && GetUpper() )
        {
            //During flowing back the upper was animated to do a full repaint,
            //we can now skip this after the whole flowing back and forth.
            GetUpper()->ResetCompletePaint();
        }
 
        if (m_bCalcLowers && isFrameAreaDefinitionValid())
        {
            // #i44910# - format of lower frames unnecessary
            // and can cause layout loops, if table doesn't fit and isn't
            // allowed to split.
            SwTwips nDistToUpperPrtBottom =
                aRectFnSet.BottomDist( getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
 
            if (GetUpper()->IsFlyFrame())
            {
                SwFlyFrame* pFlyFrame = GetUpper()->FindFlyFrame();
                if (pFlyFrame->IsFlySplitAllowed())
                {
                    SwTextFrame* pAnchor = pFlyFrame->FindAnchorCharFrame();
                    if (pAnchor && pAnchor->HasFollow())
                    {
                        // The split fly's anchor has a follow frame, we can move there & try to
                        // split again.
                        bTryToSplit = true;
                    }
                }
            }
 
            if ( nDistToUpperPrtBottom >= 0 || bTryToSplit )
            {
                lcl_RecalcTable( *this, nullptr, aNotify );
                m_bLowersFormatted = true;
                m_bCalcLowers = false;
                if (!isFramePrintAreaValid())
                    m_pTable->SetRowsToRepeat(1);
            }
#if OSL_DEBUG_LEVEL > 0
            else
            {
                OSL_FAIL( "debug assertion: <SwTabFrame::MakeAll()> - format of table lowers suppressed by fix i44910" );
            }
#endif
        }
 
    } //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
 
    //If my direct predecessor is my master now, it can destroy me during the
    //next best opportunity.
    if ( IsFollow() )
    {
        SwFrame *pPre = GetPrev();
        if ( pPre && pPre->IsTabFrame() && static_cast<SwTabFrame*>(pPre)->GetFollow() == this)
            pPre->InvalidatePos();
    }
 
    m_bCalcLowers = m_bONECalcLowers = false;
    oAccess.reset();
    UnlockJoin();
    if ( bMovedFwd || bMovedBwd || !bOldValidPos )
        aNotify.SetInvaKeep();
}
 
static bool IsNextOnSamePage(SwPageFrame const& rPage,
        SwTabFrame const& rTabFrame, SwTextFrame const& rAnchorFrame)
{
    for (SwContentFrame const* pContentFrame = rTabFrame.FindNextCnt();
        pContentFrame && pContentFrame->FindPageFrame() == &rPage;
        pContentFrame = pContentFrame->FindNextCnt())
    {
        if (pContentFrame == &rAnchorFrame)
        {
            return true;
        }
    }
    return false;
}
 
/// Calculate the offsets arising because of FlyFrames
bool SwTabFrame::CalcFlyOffsets( SwTwips& rUpper,
                               tools::Long& rLeftOffset,
                               tools::Long& rRightOffset,
                               SwTwips *const pSpaceBelowBottom) const
{
    if (IsHiddenNow())
    {
        rUpper = 0;
        rLeftOffset = 0;
        rRightOffset = 0;
        if (pSpaceBelowBottom)
            *pSpaceBelowBottom = 0;
        return false;
    }
 
    bool bInvalidatePrtArea = false;
    const SwPageFrame *pPage = FindPageFrame();
    const SwFlyFrame* pMyFly = FindFlyFrame();
 
    // --> #108724# Page header/footer content doesn't have to wrap around
    //              floating screen objects
 
    const IDocumentSettingAccess& rIDSA = GetFormat()->getIDocumentSettingAccess();
    const bool bWrapAllowed = rIDSA.get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) ||
                                ( !IsInFootnote() && nullptr == FindFooterOrHeader() );
 
    if (!bWrapAllowed || !pPage->GetSortedObjs())
        return bInvalidatePrtArea;
 
    SwRectFnSet aRectFnSet(this);
    const bool bConsiderWrapOnObjPos
        = rIDSA.get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION);
    tools::Long nPrtPos = aRectFnSet.GetTop(getFrameArea());
    nPrtPos = aRectFnSet.YInc(nPrtPos, rUpper);
    SwRect aRect(getFrameArea());
    if (pSpaceBelowBottom)
    {
        // set to space below table frame
        aRectFnSet.SetTopAndHeight(aRect, aRectFnSet.GetBottom(aRect), *pSpaceBelowBottom);
    }
    else
    {
        tools::Long nYDiff = aRectFnSet.YDiff(aRectFnSet.GetTop(getFramePrintArea()), rUpper);
        if (nYDiff > 0)
            aRectFnSet.AddBottom(aRect, -nYDiff);
    }
 
    bool bAddVerticalFlyOffsets = rIDSA.get(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS);
 
    for (size_t i = 0; i < pPage->GetSortedObjs()->size(); ++i)
    {
        SwAnchoredObject* pAnchoredObj = (*pPage->GetSortedObjs())[i];
        auto pFly = pAnchoredObj->DynCastFlyFrame();
        if (!pFly)
            continue;
 
        const SwRect aFlyRect = pFly->GetObjRectWithSpaces();
        // #i26945# - correction of conditions,
        // if Writer fly frame has to be considered:
        // - no need to check, if top of Writer fly frame differs
        //   from FAR_AWAY, because it's also checked, if the Writer
        //   fly frame rectangle overlaps with <aRect>
        // - no check, if bottom of anchor frame is prior the top of
        //   the table, because Writer fly frames can be negative positioned.
        // - correct check, if the Writer fly frame is a lower of the
        //   table, because table lines/rows can split and an at-character
        //   anchored Writer fly frame could be positioned in the follow
        //   flow line.
        // - add condition, that an existing anchor character text frame
        //   has to be on the same page as the table.
        //   E.g., it could happen, that the fly frame is still registered
        //   at the page frame, the table is on, but it's anchor character
        //   text frame has already changed its page.
        const SwTextFrame* pAnchorCharFrame = pFly->FindAnchorCharFrame();
        const SwFormatHoriOrient& rHori= pFly->GetFormat()->GetHoriOrient();
        // TODO: why not just ignore HoriOrient?
        bool isHoriOrientShiftDown =
               rHori.GetHoriOrient() == text::HoriOrientation::NONE
            || rHori.GetHoriOrient() == text::HoriOrientation::LEFT
            || rHori.GetHoriOrient() == text::HoriOrientation::RIGHT;
        // Only consider invalid Writer fly frames if they'll be shifted down.
        bool bIgnoreFlyValidity = bAddVerticalFlyOffsets && isHoriOrientShiftDown;
        bool bConsiderFly =
            // #i46807# - do not consider invalid
            // Writer fly frames.
            (pFly->isFrameAreaDefinitionValid() || bIgnoreFlyValidity)
            // fly anchored at character or at paragraph
            && pFly->IsFlyAtContentFrame()
            // fly overlaps with corresponding table rectangle
            && aFlyRect.Overlaps(aRect)
            // fly isn't lower of table and
            // anchor character frame of fly isn't lower of table
            && (pSpaceBelowBottom // not if in ShouldBwdMoved
                || (!IsAnLower(pFly) && (!pAnchorCharFrame || !IsAnLower(pAnchorCharFrame))))
            // table isn't lower of fly
            && !pFly->IsAnLower(this)
            // fly is lower of fly, the table is in
            // #123274# - correction
            // assure that fly isn't a lower of a fly, the table isn't in.
            // E.g., a table in the body doesn't wrap around a graphic,
            // which is inside a frame.
            && (!pMyFly || pMyFly->IsAnLower(pFly))
            && pMyFly == pFly->GetAnchorFrameContainingAnchPos()->FindFlyFrame()
            // anchor frame not on following page
            && pPage->GetPhyPageNum() >= pFly->GetAnchorFrame()->FindPageFrame()->GetPhyPageNum()
            // anchor character text frame on same page
            && (!pAnchorCharFrame ||
                pAnchorCharFrame->FindPageFrame()->GetPhyPageNum() == pPage->GetPhyPageNum());
 
        if (!bConsiderFly)
            continue;
 
        const SwFrame* pFlyHeaderFooterFrame = pFly->GetAnchorFrame()->FindFooterOrHeader();
        const SwFrame* pThisHeaderFooterFrame = FindFooterOrHeader();
        if (pFlyHeaderFooterFrame != pThisHeaderFooterFrame
            // #148493# If bConsiderWrapOnObjPos is set,
            // we want to consider the fly if it is located in the header and
            // the table is located in the body:
            && (!bConsiderWrapOnObjPos || nullptr != pThisHeaderFooterFrame
                || !pFlyHeaderFooterFrame->IsHeaderFrame()))
        {
            continue;
        }
 
        text::WrapTextMode nSurround = pFly->GetFormat()->GetSurround().GetSurround();
        // If the frame format is a TextBox of a draw shape,
        // then use the surround of the original shape.
        {
            bool bWrapThrough = nSurround == text::WrapTextMode_THROUGH;
            SwTextBoxHelper::getShapeWrapThrough(pFly->GetFormat(), bWrapThrough);
            if (bWrapThrough)
                continue;
        }
        if (nSurround == text::WrapTextMode_THROUGH)
            nSurround = text::WrapTextMode_PARALLEL;
 
        bool bShiftDown = css::text::WrapTextMode_NONE == nSurround;
        bool bSplitFly = pFly->IsFlySplitAllowed();
        const SwRect aFlyRectWithoutSpaces = pFly->GetObjRect();
        if (!bShiftDown && bAddVerticalFlyOffsets)
        {
            if (nSurround == text::WrapTextMode_PARALLEL && isHoriOrientShiftDown)
            {
                // We know that wrapping was requested and the table frame overlaps with
                // the fly frame. Check if the print area overlaps with the fly frame as
                // well (in case the table does not use all the available width).
                basegfx::B1DRange aTabRange(
                    aRectFnSet.GetLeft(aRect) + aRectFnSet.GetLeft(getFramePrintArea()),
                    aRectFnSet.GetLeft(aRect) + aRectFnSet.GetLeft(getFramePrintArea())
                        + aRectFnSet.GetWidth(getFramePrintArea()));
 
                // Ignore spacing when determining the left/right edge of the fly, like
                // Word does.
                basegfx::B1DRange aFlyRange(aRectFnSet.GetLeft(aFlyRectWithoutSpaces),
                                            aRectFnSet.GetRight(aFlyRectWithoutSpaces));
 
                // If it does, shift the table down. Do this only in the compat case,
                // normally an SwFlyPortion is created instead that increases the height
                // of the first table row.
                bShiftDown = aTabRange.overlaps(aFlyRange);
 
                if (bSplitFly && pFly->GetAnchorFrame()->GetUpper() == GetUpper())
                {
                    // Split fly followed by an inline table. Check if we have enough space to shift
                    // to the right instead.
                    SwTwips nShiftedTabRight = aFlyRectWithoutSpaces.Right() + getFramePrintArea().Width();
                    SwTwips nRightShiftDeadline = pFly->GetAnchorFrame()->GetUpper()->getFrameArea().Right();
                    if (aRectFnSet.XDiff(nRightShiftDeadline, nShiftedTabRight) >= 0)
                    {
                        bShiftDown = false;
                    }
                }
            }
        }
 
        if (bShiftDown)
        {
            // possible cases:
            //        both in body
            //        both in same fly
            //        any comb. of body, footnote, header/footer
            // to keep it safe, check only in doc body vs page margin for now
            tools::Long nBottom = aRectFnSet.GetBottom(aFlyRect);
            // tdf#138039 don't grow beyond the page body
            // if the fly is anchored below the table; the fly
            // must move with its anchor frame to the next page
            SwRectFnSet fnPage(pPage);
            if (!IsInDocBody() // TODO
                || fnPage.YDiff(fnPage.GetBottom(aFlyRect), fnPage.GetPrtBottom(*pPage)) <= 0
                || !IsNextOnSamePage(
                       *pPage, *this,
                       *static_cast<SwTextFrame*>(pFly->GetAnchorFrameContainingAnchPos())))
            {
                if (aRectFnSet.YDiff(nPrtPos, nBottom) < 0)
                    nPrtPos = nBottom;
                // tdf#116501 subtract flys blocking space from below
                // TODO this may not work ideally for multiple flys
                if (pSpaceBelowBottom && aRectFnSet.YDiff(aRectFnSet.GetBottom(aRect), nBottom) < 0)
                {
                    if (aRectFnSet.YDiff(aRectFnSet.GetTop(aRect), aRectFnSet.GetTop(aFlyRect)) < 0)
                    {
                        aRectFnSet.SetBottom(aRect, aRectFnSet.GetTop(aFlyRect));
                    }
                    else
                    {
                        aRectFnSet.SetHeight(aRect, 0);
                    }
                }
                bInvalidatePrtArea = true;
            }
        }
 
        bool bFlyHoriOrientLeft = text::HoriOrientation::LEFT == rHori.GetHoriOrient();
 
        bool bToplevelSplitFly = false;
        if (bSplitFly)
        {
            // Floating table wrapped by table: avoid this in the nested case.
            bToplevelSplitFly = !pFly->GetAnchorFrame()->IsInTab();
        }
 
        if (bToplevelSplitFly && !bFlyHoriOrientLeft)
        {
            // Only shift to the right if we don't have enough space on the left.
            SwTwips nTabWidth = getFramePrintArea().Width();
            SwTwips nWidthDeadline = aFlyRectWithoutSpaces.Left()
                                     - pFly->GetAnchorFrame()->GetUpper()->getFrameArea().Left();
            if (nTabWidth > nWidthDeadline)
            {
                // If a split fly is oriented "from left", we already checked if it has enough space on
                // the right, so from-left and left means the same here.
                bFlyHoriOrientLeft = rHori.GetHoriOrient() == text::HoriOrientation::NONE;
            }
        }
        if ((css::text::WrapTextMode_RIGHT == nSurround
             || css::text::WrapTextMode_PARALLEL == nSurround)
            && bFlyHoriOrientLeft
            && !bShiftDown)
        {
            const tools::Long nWidth
                = aRectFnSet.XDiff(aRectFnSet.GetRight(aFlyRect),
                                   aRectFnSet.GetLeft(pFly->GetAnchorFrame()->getFrameArea()));
            rLeftOffset = std::max(rLeftOffset, nWidth);
            bInvalidatePrtArea = true;
        }
        if ((css::text::WrapTextMode_LEFT == nSurround
             || css::text::WrapTextMode_PARALLEL == nSurround)
            && text::HoriOrientation::RIGHT == rHori.GetHoriOrient()
            && !bShiftDown)
        {
            const tools::Long nWidth
                = aRectFnSet.XDiff(aRectFnSet.GetRight(pFly->GetAnchorFrame()->getFrameArea()),
                                   aRectFnSet.GetLeft(aFlyRect));
            rRightOffset = std::max(rRightOffset, nWidth);
            bInvalidatePrtArea = true;
        }
    }
    rUpper = aRectFnSet.YDiff( nPrtPos, aRectFnSet.GetTop(getFrameArea()) );
    if (pSpaceBelowBottom)
    {
        *pSpaceBelowBottom = aRectFnSet.GetHeight(aRect);
    }
 
    return bInvalidatePrtArea;
}
 
/// "Formats" the frame; Frame and PrtArea.
/// The fixed size is not adjusted here.
void SwTabFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
{
    OSL_ENSURE( pAttrs, "TabFrame::Format, pAttrs is 0." );
 
    SwRectFnSet aRectFnSet(this);
    if ( !isFrameAreaSizeValid() )
    {
        tools::Long nDiff = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()) -
                     aRectFnSet.GetWidth(getFrameArea());
        if( nDiff )
        {
            SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
            aRectFnSet.AddRight( aFrm, nDiff );
        }
    }
 
    //VarSize is always the height.
    //For the upper/lower margins the same rules apply as for ContentFrames (see
    //MakePrtArea() of those).
 
    SwTwips nUpper = CalcUpperSpace( pAttrs );
 
    // We want to dodge the flys. Two possibilities:
    // 1. There are flys with SurroundNone, dodge them completely
    // 2. There are flys which only wrap on the right or the left side and
    //    those are right or left aligned, those set the minimum for the margins
    tools::Long nTmpRight = -1000000,
         nLeftOffset  = 0;
    if (CalcFlyOffsets(nUpper, nLeftOffset, nTmpRight, nullptr))
    {
        setFramePrintAreaValid(false);
    }
 
    tools::Long nRightOffset = std::max( tools::Long(0), nTmpRight );
 
    SwTwips nLower = pAttrs->CalcBottomLine();
    // #i29550#
    if ( IsCollapsingBorders() )
        nLower += GetBottomLineSize();
 
    if ( !isFramePrintAreaValid() )
    {
        setFramePrintAreaValid(true);
 
        // The width of the PrintArea is given by the FrameFormat, the margins
        // have to be set accordingly.
        // Minimum margins are determined depending on borders and shadows.
        // The margins are set so that the PrintArea is aligned into the
        // Frame according to the adjustment.
        // If the adjustment is 0, the margins are set according to the border
        // attributes.
 
        const SwTwips nOldHeight = aRectFnSet.GetHeight(getFramePrintArea());
        const SwTwips nMax = aRectFnSet.GetWidth(getFrameArea());
 
        // OD 14.03.2003 #i9040# - adjust variable names.
        const SwTwips nLeftLine  = pAttrs->CalcLeftLine();
        const SwTwips nRightLine = pAttrs->CalcRightLine();
 
        // The width possibly is a percentage value. If the table is inside
        // something else, the value refers to the environment. If it's in the
        // body then in the BrowseView the value refers to the screen width.
        const SwFormatFrameSize &rSz = GetFormat()->GetFrameSize();
        // OD 14.03.2003 #i9040# - adjust variable name.
        const SwTwips nWishedTableWidth = CalcRel( rSz );
 
        bool bCheckBrowseWidth = false;
 
        // OD 14.03.2003 #i9040# - insert new variables for left/right spacing.
        SwTwips nLeftSpacing  = 0;
        SwTwips nRightSpacing = 0;
        switch ( GetFormat()->GetHoriOrient().GetHoriOrient() )
        {
            case text::HoriOrientation::LEFT:
                {
                    // left indent:
                    nLeftSpacing = nLeftLine + nLeftOffset;
                    // OD 06.03.2003 #i9040# - correct calculation of right indent:
                    // - Consider right indent given by right line attributes.
                    // - Consider negative right indent.
                    // wished right indent determined by wished table width and
                    // left offset given by surround fly frames on the left:
                    const SwTwips nWishRight = nMax - nWishedTableWidth - nLeftOffset;
                    if ( nRightOffset > 0 )
                    {
                        // surrounding fly frames on the right
                        // -> right indent is maximum of given right offset
                        //    and wished right offset.
                        nRightSpacing = nRightLine + std::max( SwTwips(nRightOffset), nWishRight );
                    }
                    else
                    {
                        // no surrounding fly frames on the right
                        // If intrinsic right indent (intrinsic means not considering
                        // determined left indent) is negative,
                        //      then hold this intrinsic indent,
                        //      otherwise non negative wished right indent is hold.
                        nRightSpacing = nRightLine +
                                        ( ( (nWishRight+nLeftOffset) < 0 ) ?
                                            (nWishRight+nLeftOffset) :
                                            std::max( SwTwips(0), nWishRight ) );
                    }
                }
                break;
            case text::HoriOrientation::RIGHT:
                {
                    // right indent:
                    nRightSpacing = nRightLine + nRightOffset;
                    // OD 06.03.2003 #i9040# - correct calculation of left indent:
                    // - Consider left indent given by left line attributes.
                    // - Consider negative left indent.
                    // wished left indent determined by wished table width and
                    // right offset given by surrounding fly frames on the right:
                    const SwTwips nWishLeft = nMax - nWishedTableWidth - nRightOffset;
                    if ( nLeftOffset > 0 )
                    {
                        // surrounding fly frames on the left
                        // -> right indent is maximum of given left offset
                        //    and wished left offset.
                        nLeftSpacing = nLeftLine + std::max( SwTwips(nLeftOffset), nWishLeft );
                    }
                    else
                    {
                        // no surrounding fly frames on the left
                        // If intrinsic left indent (intrinsic = not considering
                        // determined right indent) is negative,
                        //      then hold this intrinsic indent,
                        //      otherwise non negative wished left indent is hold.
                        nLeftSpacing = nLeftLine +
                                       ( ( (nWishLeft+nRightOffset) < 0 ) ?
                                           (nWishLeft+nRightOffset) :
                                           std::max( SwTwips(0), nWishLeft ) );
                    }
                }
                break;
            case text::HoriOrientation::CENTER:
                {
                    // OD 07.03.2003 #i9040# - consider left/right line attribute.
                    const SwTwips nCenterSpacing = ( nMax - nWishedTableWidth ) / 2;
                    nLeftSpacing = nLeftLine +
                                   ( (nLeftOffset > 0) ?
                                     std::max( nCenterSpacing, SwTwips(nLeftOffset) ) :
                                     nCenterSpacing );
                    nRightSpacing = nRightLine +
                                    ( (nRightOffset > 0) ?
                                      std::max( nCenterSpacing, SwTwips(nRightOffset) ) :
                                      nCenterSpacing );
                }
                break;
            case text::HoriOrientation::FULL:
                    //This things grows over the whole width.
                    //Only the free space needed for the border is taken into
                    //account. The attribute values of LRSpace are ignored
                    //intentionally.
                    bCheckBrowseWidth = true;
                    nLeftSpacing  = nLeftLine + nLeftOffset;
                    nRightSpacing = nRightLine + nRightOffset;
                break;
            case text::HoriOrientation::NONE:
                {
                    // The margins are defined by the LRSpace attribute.
                    nLeftSpacing = pAttrs->CalcLeft( this );
                    if( nLeftOffset )
                    {
                        // OD 07.03.2003 #i9040# - surround fly frames only, if
                        // they overlap with the table.
                        // Thus, take maximum of left spacing and left offset.
                        // OD 10.03.2003 #i9040# - consider left line attribute.
                        nLeftSpacing = std::max( nLeftSpacing, SwTwips( nLeftOffset + nLeftLine ) );
                    }
                    // OD 23.01.2003 #106895# - add 1st param to <SwBorderAttrs::CalcRight(..)>
                    nRightSpacing = pAttrs->CalcRight( this );
                    if( nRightOffset )
                    {
                        // OD 07.03.2003 #i9040# - surround fly frames only, if
                        // they overlap with the table.
                        // Thus, take maximum of right spacing and right offset.
                        // OD 10.03.2003 #i9040# - consider right line attribute.
                        nRightSpacing = std::max( nRightSpacing, SwTwips( nRightOffset + nRightLine ) );
                    }
                }
                break;
            case text::HoriOrientation::LEFT_AND_WIDTH:
                {
                    // count left border and width (Word specialty)
                    // OD 10.03.2003 #i9040# - no width alignment in online mode.
                    //bCheckBrowseWidth = true;
                    nLeftSpacing = pAttrs->CalcLeft( this );
                    if( nLeftOffset )
                    {
                        // OD 10.03.2003 #i9040# - surround fly frames only, if
                        // they overlap with the table.
                        // Thus, take maximum of right spacing and right offset.
                        // OD 10.03.2003 #i9040# - consider left line attribute.
                        nLeftSpacing = std::max( nLeftSpacing, SwTwips( pAttrs->CalcLeftLine() + nLeftOffset ) );
                    }
                    // OD 10.03.2003 #i9040# - consider right and left line attribute.
                    const SwTwips nWishRight =
                            nMax - (nLeftSpacing-pAttrs->CalcLeftLine()) - nWishedTableWidth;
                    nRightSpacing = nRightLine +
                                    ( (nRightOffset > 0) ?
                                      std::max( nWishRight, SwTwips(nRightOffset) ) :
                                      nWishRight );
                }
                break;
            default:
                OSL_FAIL( "Invalid orientation for table." );
        }
 
        // #i26250# - extend bottom printing area, if table
        // is last content inside a table cell.
        if ( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS) &&
             GetUpper()->IsInTab() && !GetIndNext() )
        {
            nLower += pAttrs->GetULSpace().GetLower();
        }
        aRectFnSet.SetYMargins( *this, nUpper, nLower );
        if( (nMax - MINLAY) < (nLeftSpacing + nRightSpacing) )
            aRectFnSet.SetXMargins( *this, 0, 0 );
        else
            aRectFnSet.SetXMargins( *this, nLeftSpacing, nRightSpacing );
 
        SwViewShell *pSh = getRootFrame()->GetCurrShell();
        if ( bCheckBrowseWidth &&
             pSh && pSh->GetViewOptions()->getBrowseMode() &&
             GetUpper()->IsPageBodyFrame() &&  // only PageBodyFrames and not ColBodyFrames
             pSh->VisArea().Width() )
        {
            //Don't go beyond the edge of the visible area.
            //The page width can be bigger because objects with
            //"over-size" are possible (RootFrame::ImplCalcBrowseWidth())
            tools::Long nWidth = pSh->GetBrowseWidth();
            nWidth -= getFramePrintArea().Left();
            nWidth -= pAttrs->CalcRightLine();
 
            SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
            aPrt.Width( std::min( nWidth, aPrt.Width() ) );
        }
 
        if ( nOldHeight != aRectFnSet.GetHeight(getFramePrintArea()) )
        {
            setFrameAreaSizeValid(false);
        }
    }
 
    if ( isFrameAreaSizeValid() )
        return;
 
    setFrameAreaSizeValid(true);
 
    // The size is defined by the content plus the margins.
    SwTwips nRemaining = 0, nDiff;
    SwFrame *pFrame = m_pLower;
    while ( pFrame )
    {
        nRemaining += aRectFnSet.GetHeight(pFrame->getFrameArea());
        pFrame = pFrame->GetNext();
    }
    // And now add the margins
    nRemaining += nUpper + nLower;
 
    nDiff = aRectFnSet.GetHeight(getFrameArea()) - nRemaining;
    if ( nDiff > 0 )
        Shrink( nDiff );
    else if ( nDiff < 0 )
        Grow( -nDiff );
}
 
SwTwips SwTabFrame::GrowFrame(SwTwips nDist, SwResizeLimitReason& reason, bool bTst, bool bInfo)
{
    SwRectFnSet aRectFnSet(this);
    SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
    if( nHeight > 0 && nDist > ( LONG_MAX - nHeight ) )
        nDist = LONG_MAX - nHeight;
 
    reason = SwResizeLimitReason::Unspecified;
 
    if ( bTst && !IsRestrictTableGrowth() )
        return nDist;
 
    if ( GetUpper() )
    {
        //The upper only grows as far as needed. nReal provides the distance
        //which is already available.
        SwTwips nReal = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea());
        for (SwFrame* pFrame = GetUpper()->Lower(); pFrame && GetFollow() != pFrame;
             pFrame = pFrame->GetNext())
            nReal -= aRectFnSet.GetHeight(pFrame->getFrameArea());
 
        if ( nReal < nDist )
        {
            tools::Long nTmp = GetUpper()->Grow(nDist - std::max(nReal, SwTwips(0)), reason, bTst, bInfo);
 
            if ( IsRestrictTableGrowth() )
            {
                nTmp = std::min( tools::Long(nDist), nReal + nTmp );
                nDist = nTmp < 0 ? 0 : nTmp;
            }
        }
 
        if ( !bTst )
        {
            {
                SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                aRectFnSet.AddBottom( aFrm, nDist );
            }
 
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
            SwRootFrame *pRootFrame = getRootFrame();
            if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
                pRootFrame->GetCurrShell() )
            {
                SwRect aOldFrame( getFrameArea() );
                pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( this, aOldFrame );
            }
#endif
        }
    }
 
    if ( !bTst && ( nDist || IsRestrictTableGrowth() ) )
    {
        SwPageFrame *pPage = FindPageFrame();
        if ( GetNext() )
        {
            GetNext()->InvalidatePos_();
            if ( GetNext()->IsContentFrame() )
                GetNext()->InvalidatePage( pPage );
        }
        // #i28701# - Due to the new object positioning the
        // frame on the next page/column can flow backward (e.g. it was moved
        // forward due to the positioning of its objects ). Thus, invalivate this
        // next frame, if document compatibility option 'Consider wrapping style
        // influence on object positioning' is ON.
        else if ( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
        {
            InvalidateNextPos();
        }
        InvalidateAll_();
        InvalidatePage( pPage );
        SetComplete();
 
        std::unique_ptr<SvxBrushItem> aBack = GetFormat()->makeBackgroundBrushItem();
        const SvxGraphicPosition ePos = aBack->GetGraphicPos();
        if ( GPOS_NONE != ePos && GPOS_TILED != ePos )
            SetCompletePaint();
    }
 
    return nDist;
}
 
void SwTabFrame::Invalidate(SwTabFrameInvFlags eInvFlags)
{
    if(eInvFlags == SwTabFrameInvFlags::NONE)
        return;
    SwPageFrame* pPage = FindPageFrame();
    InvalidatePage(pPage);
    if(eInvFlags & SwTabFrameInvFlags::InvalidatePrt)
        InvalidatePrt_();
    if(eInvFlags & SwTabFrameInvFlags::InvalidatePos)
        InvalidatePos_();
    SwFrame* pTmp = GetIndNext();
    if(nullptr != pTmp)
    {
        if(eInvFlags & SwTabFrameInvFlags::InvalidateIndNextPrt)
        {
            pTmp->InvalidatePrt_();
            if(pTmp->IsContentFrame())
                pTmp->InvalidatePage(pPage);
        }
        if(eInvFlags & SwTabFrameInvFlags::SetIndNextCompletePaint)
            pTmp->SetCompletePaint();
    }
    if(eInvFlags & SwTabFrameInvFlags::InvalidatePrevPrt && nullptr != (pTmp = GetPrev()))
    {
        pTmp->InvalidatePrt_();
        if(pTmp->IsContentFrame())
            pTmp->InvalidatePage(pPage);
    }
    if(eInvFlags & SwTabFrameInvFlags::InvalidateBrowseWidth)
    {
        if(pPage && pPage->GetUpper() && !IsFollow())
            static_cast<SwRootFrame*>(pPage->GetUpper())->InvalidateBrowseWidth();
    }
    if(eInvFlags & SwTabFrameInvFlags::InvalidateNextPos)
        InvalidateNextPos();
}
 
void SwTabFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
{
    if(rHint.GetId() == SfxHintId::SwTableHeadingChange)
    {
        HandleTableHeadlineChange();
        return;
    }
    else if(rHint.GetId() == SfxHintId::SwVirtPageNumHint)
    {
        auto& rVirtPageNumHint = const_cast<sw::VirtPageNumHint&>(static_cast<const sw::VirtPageNumHint&>(rHint));
        if(!IsInDocBody() || IsFollow() || rVirtPageNumHint.IsFound())
            return;
        if(const SwPageFrame* pPage = FindPageFrame())
            pPage->UpdateVirtPageNumInfo(rVirtPageNumHint, this);
        return;
    }
    else if (rHint.GetId() != SfxHintId::SwLegacyModify)
        return;
    auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
    SwTabFrameInvFlags eInvFlags = SwTabFrameInvFlags::NONE;
    bool bAttrSetChg = pLegacy->m_pNew && RES_ATTRSET_CHG == pLegacy->m_pNew->Which();
 
    if(bAttrSetChg)
    {
        auto& rOldSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pOld);
        auto& rNewSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew);
        SfxItemIter aOIter(*rOldSetChg.GetChgSet());
        SfxItemIter aNIter(*rNewSetChg.GetChgSet());
        const SfxPoolItem* pOItem = aOIter.GetCurItem();
        const SfxPoolItem* pNItem = aNIter.GetCurItem();
        SwAttrSetChg aOldSet(rOldSetChg);
        SwAttrSetChg aNewSet(rNewSetChg);
        do
        {
            UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
            pNItem = aNIter.NextItem();
            pOItem = aOIter.NextItem();
        } while(pNItem);
        if(aOldSet.Count() || aNewSet.Count())
            SwLayoutFrame::SwClientNotify(rMod, sw::LegacyModifyHint(&aOldSet, &aNewSet));
    }
    else
        UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
    Invalidate(eInvFlags);
}
 
void SwTabFrame::HandleTableHeadlineChange()
{
    if(!IsFollow())
        return;
    // Delete remaining headlines:
    SwRowFrame* pLowerRow = nullptr;
    while(nullptr != (pLowerRow = static_cast<SwRowFrame*>(Lower())) && pLowerRow->IsRepeatedHeadline())
    {
        pLowerRow->Cut();
        SwFrame::DestroyFrame(pLowerRow);
    }
 
    // insert new headlines
    const sal_uInt16 nNewRepeat = GetTable()->GetRowsToRepeat();
    auto& rLines = GetTable()->GetTabLines();
    for(sal_uInt16 nIdx = 0; nIdx < nNewRepeat; ++nIdx)
    {
        SwRowFrame* pHeadline = new SwRowFrame(*rLines[nIdx], this);
        {
            sw::FlyCreationSuppressor aSuppressor;
            pHeadline->SetRepeatedHeadline(true);
        }
        pHeadline->Paste(this, pLowerRow);
    }
    Invalidate(SwTabFrameInvFlags::InvalidatePrt);
}
 
void SwTabFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
                            SwTabFrameInvFlags &rInvFlags,
                            SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
{
    bool bClear = true;
    const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
    switch( nWhich )
    {
        case RES_FRM_SIZE:
        case RES_HORI_ORIENT:
            rInvFlags |= SwTabFrameInvFlags::InvalidatePrt | SwTabFrameInvFlags::InvalidateBrowseWidth;
            break;
 
        case RES_PAGEDESC:                      //Attribute changes (on/off)
            if ( IsInDocBody() )
            {
                rInvFlags |= SwTabFrameInvFlags::InvalidatePos;
                SwPageFrame *pPage = FindPageFrame();
                if (pPage)
                {
                    if ( !GetPrev() )
                        CheckPageDescs( pPage );
                    if (GetFormat()->GetPageDesc().GetNumOffset())
                        static_cast<SwRootFrame*>(pPage->GetUpper())->SetVirtPageNum( true );
                    GetFormat()->GetDoc()->getIDocumentFieldsAccess().UpdatePageFields(pPage->getFrameArea().Top());
                }
            }
            break;
 
        case RES_BREAK:
            rInvFlags |= SwTabFrameInvFlags::InvalidatePos | SwTabFrameInvFlags::InvalidateNextPos;
            break;
 
        case RES_LAYOUT_SPLIT:
            if ( !IsFollow() )
                rInvFlags |= SwTabFrameInvFlags::InvalidatePos;
            break;
        case RES_FRAMEDIR :
            SetDerivedR2L( false );
            CheckDirChange();
            break;
        case RES_COLLAPSING_BORDERS :
            rInvFlags |= SwTabFrameInvFlags::InvalidatePrt;
            lcl_InvalidateAllLowersPrt( this );
            break;
        case RES_UL_SPACE:
            rInvFlags |= SwTabFrameInvFlags::InvalidateIndNextPrt | SwTabFrameInvFlags::InvalidatePrevPrt | SwTabFrameInvFlags::SetIndNextCompletePaint;
            [[fallthrough]];
 
        default:
            bClear = false;
    }
    if ( !bClear )
        return;
 
    if ( pOldSet || pNewSet )
    {
        if ( pOldSet )
            pOldSet->ClearItem( nWhich );
        if ( pNewSet )
            pNewSet->ClearItem( nWhich );
    }
    else
    {
        SwModify aMod;
        SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
    }
}
 
SwFrame *SwTabFrame::FindLastContentOrTable()
{
    SwFrame *pRet = m_pLower;
 
    while ( pRet && !pRet->IsContentFrame() )
    {
        SwFrame *pOld = pRet;
 
        SwFrame *pTmp = pRet;             // To skip empty section frames
        while ( pRet->GetNext() )
        {
            pRet = pRet->GetNext();
            if( !pRet->IsSctFrame() || static_cast<SwSectionFrame*>(pRet)->GetSection() )
                pTmp = pRet;
        }
        pRet = pTmp;
 
        if ( pRet->GetLower() )
            pRet = pRet->GetLower();
        if ( pRet == pOld )
        {
            // Check all other columns if there is a column based section with
            // an empty last column at the end of the last cell - this is done
            // by SwSectionFrame::FindLastContent
            if( pRet->IsColBodyFrame() )
            {
#if OSL_DEBUG_LEVEL > 0
                SwSectionFrame* pSect = pRet->FindSctFrame();
                OSL_ENSURE( pSect, "Where does this column come from?");
                OSL_ENSURE( IsAnLower( pSect ), "Split cell?" );
#endif
                return pRet->FindSctFrame()->FindLastContent();
            }
 
            // pRet may be a cell frame without a lower (cell has been split).
            // We have to find the last content the hard way:
 
            OSL_ENSURE( pRet->IsCellFrame(), "SwTabFrame::FindLastContent failed" );
            SwFrame* pRow = pRet->GetUpper();
            while ( pRow && !pRow->GetUpper()->IsTabFrame() )
                pRow = pRow->GetUpper();
            SwContentFrame* pContentFrame = pRow ? static_cast<SwLayoutFrame*>(pRow)->ContainsContent() : nullptr;
            pRet = nullptr;
 
            while ( pContentFrame && static_cast<const SwLayoutFrame*>(pRow)->IsAnLower( pContentFrame ) )
            {
                pRet = pContentFrame;
                pContentFrame = pContentFrame->GetNextContentFrame();
            }
        }
    }
 
    // #112929# There actually is a situation, which results in pRet = 0:
    // Insert frame, insert table via text <-> table. This gives you a frame
    // containing a table without any other content frames. Split the table
    // and undo the splitting. This operation gives us a table frame without
    // a lower.
    if ( pRet )
    {
        while ( pRet->GetNext() )
            pRet = pRet->GetNext();
 
        if (pRet->IsSctFrame())
            pRet = static_cast<SwSectionFrame*>(pRet)->FindLastContent();
    }
 
    assert(pRet == nullptr || dynamic_cast<SwContentFrame*>(pRet) || dynamic_cast<SwTabFrame*>(pRet));
    return pRet;
}
 
SwContentFrame *SwTabFrame::FindLastContent()
{
    SwFrame * pRet(FindLastContentOrTable());
 
    while (pRet && pRet->IsTabFrame()) // possibly there's only tables here!
    {   // tdf#126138 skip table, don't look inside
        pRet = pRet->GetPrev();
    }
 
    assert(pRet == nullptr || dynamic_cast<SwContentFrame*>(pRet));
    return static_cast<SwContentFrame*>(pRet);
}
 
/// Return value defines if the frm needs to be relocated
bool SwTabFrame::ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool &rReformat )
{
    rReformat = false;
    if ( SwFlowFrame::IsMoveBwdJump() || !IsPrevObjMove() )
    {
        //Flowing back Frames is quite time consuming unfortunately.
        //Most often the location where the Frame wants to flow to has the same
        //FixSize as the Frame itself. In such a situation it's easy to check if
        //the Frame will find enough space for its VarSize, if this is not the
        //case, the relocation can be skipped.
        //Checking if the Frame will find enough space is done by the Frame itself,
        //this also takes the possibility of splitting the Frame into account.
        //If the FixSize is different or Flys are involved  (at the old or the
        //new position) the checks are pointless, the Frame then
        //needs to be relocated tentatively (if a bit of space is available).
 
        //The FixSize of the environments which contain tables is always the
        //width.
 
        SwPageFrame *pOldPage = FindPageFrame(),
                  *pNewPage = pNewUpper->FindPageFrame();
        bool bMoveAnyway = false;
        SwTwips nSpace = 0;
 
        SwRectFnSet aRectFnSet(this);
        if ( !SwFlowFrame::IsMoveBwdJump() )
        {
 
            tools::Long nOldWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
            SwRectFnSet fnRectX(pNewUpper);
            tools::Long nNewWidth = fnRectX.GetWidth(pNewUpper->getFramePrintArea());
            if( std::abs( nNewWidth - nOldWidth ) < 2 )
            {
                bMoveAnyway = BwdMoveNecessary( pOldPage, getFrameArea() ) > 1;
                if( !bMoveAnyway )
                {
                    SwRect aRect( pNewUpper->getFramePrintArea() );
                    aRect.Pos() += pNewUpper->getFrameArea().Pos();
                    const SwFrame *pPrevFrame = pNewUpper->Lower();
                    while ( pPrevFrame && pPrevFrame != this )
                    {
                        fnRectX.SetTop( aRect, fnRectX.GetBottom(pPrevFrame->getFrameArea()) );
                        pPrevFrame = pPrevFrame->GetNext();
                    }
                    bMoveAnyway = BwdMoveNecessary( pNewPage, aRect) > 1;
 
                    // #i54861# Due to changes made in PrepareMake,
                    // the tabfrm may not have a correct position. Therefore
                    // it is possible that pNewUpper->getFramePrintArea().Height == 0. In this
                    // case the above calculation of nSpace might give wrong
                    // results and we really do not want to MoveBackward into a
                    // 0 height frame. If nTmpSpace is already <= 0, we take this
                    // value:
                    const SwTwips nTmpSpace = fnRectX.GetHeight(aRect);
                    if ( fnRectX.GetHeight(pNewUpper->getFramePrintArea()) > 0 || nTmpSpace <= 0 )
                        nSpace = nTmpSpace;
 
                    const SwViewShell *pSh = getRootFrame()->GetCurrShell();
                    if( pSh && pSh->GetViewOptions()->getBrowseMode() )
                        nSpace += pNewUpper->Grow( LONG_MAX, true );
                    if (0 < nSpace && GetPrecede())
                    {
                        SwTwips nUpperDummy(0);
                        tools::Long nLeftOffsetDummy(0), nRightOffsetDummy(0);
                        // tdf#116501 check for no-wrap fly overlap
                        static_cast<const SwTabFrame*>(GetPrecede())->CalcFlyOffsets(
                            nUpperDummy, nLeftOffsetDummy, nRightOffsetDummy, &nSpace);
                    }
                }
            }
            else if (!m_bLockBackMove)
                bMoveAnyway = true;
            else
            {
                m_bWantBackMove = true;
            }
        }
        else if (!m_bLockBackMove)
            bMoveAnyway = true;
        else
        {
            m_bWantBackMove = true;
        }
 
        if ( bMoveAnyway )
        {
            rReformat = true;
            return true;
        }
 
        bool bFits = nSpace > 0;
        if (!bFits && aRectFnSet.GetHeight(getFrameArea()) == 0)
            // This frame fits into pNewUpper in case it has no space, but this
            // frame is empty.
            bFits = nSpace >= 0;
        if (bFits)
        {
            // #i26945# - check, if follow flow line
            // contains frame, which are moved forward due to its object
            // positioning.
            const SwRowFrame* pFirstRow = GetFirstNonHeadlineRow();
            if ( pFirstRow && pFirstRow->IsInFollowFlowRow() &&
                 SwLayouter::DoesRowContainMovedFwdFrame(
                                            *(pFirstRow->GetFormat()->GetDoc()),
                                            *pFirstRow ) )
            {
                return false;
            }
            SwTwips nTmpHeight = CalcHeightOfFirstContentLine();
 
            // For some mysterious reason, I changed the good old
            // 'return nHeight <= nSpace' to 'return nTmpHeight < nSpace'.
            // This obviously results in problems with table frames in
            // sections. Remember: Every twip is sacred.
            if (nTmpHeight <= nSpace)
            {
                if (m_bLockBackMove)
                {
                    m_bWantBackMove = true;
                }
                else
                {
                    return true;
                }
            }
        }
    }
    return false;
}
 
void SwTabFrame::Cut()
{
    OSL_ENSURE( GetUpper(), "Cut without Upper()." );
 
    SwPageFrame *pPage = FindPageFrame();
    InvalidatePage( pPage );
    SwFrame *pFrame = GetNext();
    if( pFrame )
    {
        // Possibly the old follow calculated a spacing to the predecessor
        // which is obsolete now when it becomes the first frame
        pFrame->InvalidatePrt_();
        pFrame->InvalidatePos_();
        if ( pFrame->IsContentFrame() )
            pFrame->InvalidatePage( pPage );
        if( IsInSct() && !GetPrev() )
        {
            SwSectionFrame* pSct = FindSctFrame();
            if( !pSct->IsFollow() )
            {
                pSct->InvalidatePrt_();
                pSct->InvalidatePage( pPage );
            }
        }
    }
    else
    {
        InvalidateNextPos();
        //Someone has to do the retouch: predecessor or upper
        pFrame = GetPrev();
        if ( nullptr != pFrame )
        {
            pFrame->SetRetouche();
            pFrame->Prepare( PrepareHint::WidowsOrphans );
            pFrame->InvalidatePos_();
            if ( pFrame->IsContentFrame() )
                pFrame->InvalidatePage( pPage );
        }
        //If I am (was) the only FlowFrame in my own upper, it has to do
        //the retouch. Moreover a new empty page might be created.
        else
        {   SwRootFrame *pRoot = static_cast<SwRootFrame*>(pPage->GetUpper());
            pRoot->SetSuperfluous();
            GetUpper()->SetCompletePaint();
            if( IsInSct() )
            {
                SwSectionFrame* pSct = FindSctFrame();
                if( !pSct->IsFollow() )
                {
                    pSct->InvalidatePrt_();
                    pSct->InvalidatePage( pPage );
                }
            }
        }
    }
 
    //First remove, then shrink the upper.
    SwLayoutFrame *pUp = GetUpper();
    SwRectFnSet aRectFnSet(this);
    RemoveFromLayout();
    if ( pUp )
    {
        OSL_ENSURE( !pUp->IsFootnoteFrame(), "Table in Footnote." );
        SwSectionFrame *pSct = nullptr;
        SwFlyFrame *pFly = nullptr;
        // #126020# - adjust check for empty section
        // #130797# - correct fix #126020#
        if ( !pUp->Lower() && pUp->IsInSct() &&
             !(pSct = pUp->FindSctFrame())->ContainsContent() &&
             !pSct->ContainsAny( true ) )
        {
            if ( pUp->GetUpper() )
            {
                pSct->DelEmpty( false );
                pSct->InvalidateSize_();
            }
        }
        else if (!pUp->Lower() && pUp->IsInFly() &&
                !(pFly = pUp->FindFlyFrame())->ContainsContent() &&
                !pFly->ContainsAny())
        {
            bool bSplitFly = pFly->IsFlySplitAllowed();
            if (!bSplitFly && pFly->IsFlyAtContentFrame())
            {
                // If the fly is not allowed to split, it's still possible that it was allowed to
                // split. That is definitely the case when the fly is a follow.
                auto pFlyAtContent = static_cast<SwFlyAtContentFrame*>(pFly);
                bSplitFly = pFlyAtContent->IsFollow();
            }
            if (pUp == pFly && bSplitFly)
            {
                auto pFlyAtContent = static_cast<SwFlyAtContentFrame*>(pFly);
                pFlyAtContent->DelEmpty();
            }
        }
        // table-in-footnote: delete empty footnote frames (like SwContentFrame::Cut)
        else if (!pUp->Lower() && pUp->IsFootnoteFrame() && !pUp->IsColLocked())
        {
            if (pUp->GetNext() && !pUp->GetPrev())
            {
                if (SwFrame *const pTmp = static_cast<SwLayoutFrame*>(pUp->GetNext())->ContainsAny())
                {
                    pTmp->InvalidatePrt_();
                }
            }
            if (!pUp->IsDeleteForbidden())
            {
                pUp->Cut();
                SwFrame::DestroyFrame(pUp);
            }
        }
        else if( aRectFnSet.GetHeight(getFrameArea()) )
        {
            // OD 26.08.2003 #i18103# - *no* 'ColUnlock' of section -
            // undo changes of fix for #104992#
            pUp->Shrink( getFrameArea().Height() );
        }
    }
 
 
    if ( pPage && !IsFollow() && pPage->GetUpper() )
        static_cast<SwRootFrame*>(pPage->GetUpper())->InvalidateBrowseWidth();
}
 
void SwTabFrame::Paste( SwFrame* pParent, SwFrame* pSibling )
{
    OSL_ENSURE( pParent, "No parent for pasting." );
    OSL_ENSURE( pParent->IsLayoutFrame(), "Parent is ContentFrame." );
    OSL_ENSURE( pParent != this, "I'm the parent myself." );
    OSL_ENSURE( pSibling != this, "I'm my own neighbour." );
    OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(),
            "I'm still registered somewhere." );
 
    //Insert in the tree.
    InsertBefore( static_cast<SwLayoutFrame*>(pParent), pSibling );
 
    InvalidateAll_();
    SwPageFrame *pPage = FindPageFrame();
    InvalidatePage( pPage );
 
    if ( GetNext() )
    {
        GetNext()->InvalidatePos_();
        GetNext()->InvalidatePrt_();
        if ( GetNext()->IsContentFrame() )
            GetNext()->InvalidatePage( pPage );
    }
 
    SwRectFnSet aRectFnSet(this);
    if( aRectFnSet.GetHeight(getFrameArea()) )
        pParent->Grow( aRectFnSet.GetHeight(getFrameArea()) );
 
    if( aRectFnSet.GetWidth(getFrameArea()) != aRectFnSet.GetWidth(pParent->getFramePrintArea()) )
        Prepare( PrepareHint::FixSizeChanged );
    if ( GetPrev() )
    {
        if ( !IsFollow() )
        {
            GetPrev()->InvalidateSize();
            if ( GetPrev()->IsContentFrame() )
                GetPrev()->InvalidatePage( pPage );
        }
    }
    else if ( GetNext() )
        // Take the spacing into account when dealing with ContentFrames.
        // There are two situations (both always happen at the same time):
        // a) The Content becomes the first in a chain
        // b) The new follower was previously the first in a chain
        GetNext()->InvalidatePrt_();
 
    if ( !pPage || IsFollow() )
        return;
 
    if ( pPage->GetUpper() )
        static_cast<SwRootFrame*>(pPage->GetUpper())->InvalidateBrowseWidth();
 
    if ( !GetPrev() )//At least needed for HTML with a table at the beginning.
    {
        const SwPageDesc *pDesc = GetFormat()->GetPageDesc().GetPageDesc();
        if ( (pDesc && pDesc != pPage->GetPageDesc()) ||
             (!pDesc && pPage->GetPageDesc() != &GetFormat()->GetDoc()->GetPageDesc(0)) )
            CheckPageDescs( pPage );
    }
}
 
bool SwTabFrame::Prepare( const PrepareHint eHint, const void *, bool )
{
    if( PrepareHint::BossChanged == eHint )
        CheckDirChange();
    return false;
}
 
SwRowFrame::SwRowFrame(const SwTableLine &rLine, SwFrame* pSib, bool bInsertContent)
    : SwLayoutFrame( rLine.GetFrameFormat(), pSib )
    , m_pTabLine( &rLine )
    , m_pFollowRow( nullptr )
    // #i29550#
    , mnTopMarginForLowers( 0 )
    , mnBottomMarginForLowers( 0 )
    , mnBottomLineSize( 0 )
    // --> split table rows
    , m_bIsFollowFlowRow( false )
    // <-- split table rows
    , m_bIsRepeatedHeadline( false )
    , m_bIsRowSpanLine( false )
    , m_bForceRowSplitAllowed( false )
    , m_bIsInSplit( false )
{
    mnFrameType = SwFrameType::Row;
 
    //Create the boxes and insert them.
    const SwTableBoxes &rBoxes = rLine.GetTabBoxes();
    SwFrame *pTmpPrev = nullptr;
 
    bool bHiddenRedlines = getRootFrame()->IsHideRedlines() &&
        !GetFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable().empty();
    for ( size_t i = 0; i < rBoxes.size(); ++i )
    {
        // skip cells deleted with track changes
        if ( bHiddenRedlines && RedlineType::Delete == rBoxes[i]->GetRedlineType() )
            continue;
 
        SwCellFrame *pNew = new SwCellFrame( *rBoxes[i], this, bInsertContent );
        pNew->InsertBehind( this, pTmpPrev );
        pTmpPrev = pNew;
    }
}
 
void SwRowFrame::DestroyImpl()
{
    sw::BroadcastingModify* pMod = GetFormat();
    if( pMod )
    {
        pMod->Remove(*this);
        if( !pMod->HasWriterListeners() )
            delete pMod;
    }
 
    SwLayoutFrame::DestroyImpl();
}
 
SwRowFrame::~SwRowFrame()
{
}
 
void SwRowFrame::RegistFlys( SwPageFrame *pPage )
{
    ::RegistFlys( pPage ? pPage : FindPageFrame(), this );
}
 
void SwRowFrame::OnFrameSize(const SfxPoolItem& rSize)
{
    SwTabFrame* pTab = FindTabFrame();
    if(pTab)
    {
        const bool bInFirstNonHeadlineRow = pTab->IsFollow() && this == pTab->GetFirstNonHeadlineRow();
        // #i35063#
        // Invalidation required is pRow is last row
        if(bInFirstNonHeadlineRow)
            pTab = pTab->FindMaster();
        if(bInFirstNonHeadlineRow || !GetNext())
            pTab->InvalidatePos();
    }
    const sw::BroadcastingModify aMod;
    SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(nullptr, &rSize));
}
 
void SwRowFrame::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
{
    if(rHint.GetId() == SfxHintId::SwTableLineFormatChanged)
    {
        auto pNewFormatHint = static_cast<const sw::TableLineFormatChanged*>(&rHint);
        if(GetTabLine() != &pNewFormatHint->m_rTabLine)
            return;
        RegisterToFormat(const_cast<SwTableLineFormat&>(pNewFormatHint->m_rNewFormat));
        InvalidateSize();
        InvalidatePrt_();
        SetCompletePaint();
        ReinitializeFrameSizeAttrFlags();
 
        // #i35063#
        // consider 'split row allowed' attribute
        SwTabFrame* pTab = FindTabFrame();
        bool bInFollowFlowRow = false;
        const bool bInFirstNonHeadlineRow = pTab->IsFollow() && this == pTab->GetFirstNonHeadlineRow();
        if(bInFirstNonHeadlineRow ||
             !GetNext() ||
             (bInFollowFlowRow = IsInFollowFlowRow()) ||
             nullptr != IsInSplitTableRow() )
        {
            if(bInFirstNonHeadlineRow || bInFollowFlowRow)
                pTab = pTab->FindMaster();
 
            pTab->SetRemoveFollowFlowLinePending(true);
            pTab->InvalidatePos();
        }
    }
    else if(rHint.GetId() == SfxHintId::SwMoveTableLine)
    {
        auto pMoveTableLineHint = static_cast<const sw::MoveTableLineHint*>(&rHint);
        if(GetTabLine() != &pMoveTableLineHint->m_rTableLine)
            return;
        const_cast<SwFrameFormat*>(&pMoveTableLineHint->m_rNewFormat)->Add(*this);
        InvalidateAll();
        ReinitializeFrameSizeAttrFlags();
        return;
    }
    if (rHint.GetId() != SfxHintId::SwLegacyModify)
        return;
    auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
    if(!pLegacy->m_pNew)
    {
        // possibly not needed?
        SwLayoutFrame::SwClientNotify(rModify, rHint);
        return;
    }
    switch(pLegacy->m_pNew->Which())
    {
        case RES_ATTRSET_CHG:
        {
            const SwAttrSet* pChgSet = static_cast<const SwAttrSetChg*>(pLegacy->m_pNew)->GetChgSet();
            const SfxPoolItem* pItem = nullptr;
            pChgSet->GetItemState(RES_FRM_SIZE, false, &pItem);
            if(!pItem)
                pChgSet->GetItemState(RES_ROW_SPLIT, false, &pItem);
            if(pItem)
                OnFrameSize(*pItem);
            else
                SwLayoutFrame::SwClientNotify(rModify, rHint); // possibly not needed?
            return;
       }
       case RES_FRM_SIZE:
       case RES_ROW_SPLIT:
            OnFrameSize(*static_cast<const SwFormatFrameSize*>(pLegacy->m_pNew));
            return;
    }
}
 
void SwRowFrame::MakeAll(vcl::RenderContext* pRenderContext)
{
    if ( !GetNext() )
    {
        setFrameAreaSizeValid(false);
    }
 
    SwLayoutFrame::MakeAll(pRenderContext);
}
 
void SwRowFrame::dumpAsXml(xmlTextWriterPtr writer) const
{
    (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("row"));
    dumpAsXmlAttributes(writer);
 
    (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
    dumpInfosAsXml(writer);
    (void)xmlTextWriterEndElement(writer);
    dumpChildrenAsXml(writer);
 
    (void)xmlTextWriterEndElement(writer);
}
 
tools::Long CalcHeightWithFlys( const SwFrame *pFrame )
{
    SwRectFnSet aRectFnSet(pFrame);
    tools::Long nHeight = 0;
    const SwFrame* pTmp = pFrame->IsSctFrame() ?
            static_cast<const SwSectionFrame*>(pFrame)->ContainsContent() : pFrame;
    while( pTmp )
    {
        // #i26945# - consider follow text frames
        const SwSortedObjs* pObjs( nullptr );
        bool bIsFollow( false );
        if ( pTmp->IsTextFrame() && static_cast<const SwTextFrame*>(pTmp)->IsFollow() )
        {
            const SwFrame* pMaster;
            // #i46450# Master does not necessarily have
            // to exist if this function is called from JoinFrame() ->
            // Cut() -> Shrink()
            const SwTextFrame* pTmpFrame = static_cast<const SwTextFrame*>(pTmp);
            if ( pTmpFrame->GetPrev() && pTmpFrame->GetPrev()->IsTextFrame() &&
                 static_cast<const SwTextFrame*>(pTmpFrame->GetPrev())->GetFollow() &&
                 static_cast<const SwTextFrame*>(pTmpFrame->GetPrev())->GetFollow() != pTmp )
                 pMaster = nullptr;
            else
                 pMaster = pTmpFrame->FindMaster();
 
            if ( pMaster )
            {
                pObjs = static_cast<const SwTextFrame*>(pTmp)->FindMaster()->GetDrawObjs();
                bIsFollow = true;
            }
        }
        else
        {
            pObjs = pTmp->GetDrawObjs();
        }
        if ( pObjs )
        {
            for (SwAnchoredObject* pAnchoredObj : *pObjs)
            {
                // #i26945# - if <pTmp> is follow, the
                // anchor character frame has to be <pTmp>.
                if ( bIsFollow &&
                     pAnchoredObj->FindAnchorCharFrame() != pTmp )
                {
                    continue;
                }
                // #i26945# - consider also drawing objects
                {
                    // OD 30.09.2003 #i18732# - only objects, which follow
                    // the text flow have to be considered.
                    const SwFrameFormat* pFrameFormat = pAnchoredObj->GetFrameFormat();
                    bool bFollowTextFlow = pFrameFormat->GetFollowTextFlow().GetValue();
                    bool bIsFarAway = pAnchoredObj->GetObjRect().Top() != FAR_AWAY;
                    const SwPageFrame* pPageFrm = pTmp->FindPageFrame();
                    bool bIsAnchoredToTmpFrm = false;
                    if ( pPageFrm && pPageFrm->IsPageFrame() && pAnchoredObj->GetPageFrame())
                        bIsAnchoredToTmpFrm = pAnchoredObj->GetPageFrame() == pPageFrm ||
                        (pPageFrm->GetFormatPage().GetPhyPageNum() == pAnchoredObj->GetPageFrame()->GetFormatPage().GetPhyPageNum() + 1);
                    const bool bConsiderObj =
                        (pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) &&
                        bIsFarAway &&
                        bFollowTextFlow && bIsAnchoredToTmpFrm;
                    bool bWrapThrough = pFrameFormat->GetSurround().GetValue() == text::WrapTextMode_THROUGH;
                    bool bInBackground = !pFrameFormat->GetOpaque().GetValue();
                    // Legacy render requires in-background setting, the new mode does not.
                    bool bConsiderFollowTextFlow = bInBackground
                                                   || !pFrameFormat->getIDocumentSettingAccess().get(
                                                       DocumentSettingId::USE_FORMER_TEXT_WRAPPING);
                    if (pFrame->IsInTab() && bFollowTextFlow && bWrapThrough && bConsiderFollowTextFlow)
                    {
                        // Ignore wrap-through objects when determining the cell height.
                        // Normally FollowTextFlow requires a resize of the cell, but not in case of
                        // wrap-through.
                        continue;
                    }
 
                    if ( bConsiderObj )
                    {
                        const SwFormatFrameSize &rSz = pFrameFormat->GetFrameSize();
                        if( !rSz.GetHeightPercent() )
                        {
                            const SwTwips nDistOfFlyBottomToAnchorTop =
                                aRectFnSet.GetHeight(pAnchoredObj->GetObjRect()) +
                                    ( aRectFnSet.IsVert() ?
                                      pAnchoredObj->GetCurrRelPos().X() :
                                      pAnchoredObj->GetCurrRelPos().Y() );
 
                            const SwTwips nFrameDiff =
                                aRectFnSet.YDiff(
                                    aRectFnSet.GetTop(pTmp->getFrameArea()),
                                    aRectFnSet.GetTop(pFrame->getFrameArea()) );
 
                            nHeight = std::max( nHeight, nDistOfFlyBottomToAnchorTop + nFrameDiff -
                                            aRectFnSet.GetHeight(pFrame->getFrameArea()) );
 
                            // #i56115# The first height calculation
                            // gives wrong results if pFrame->getFramePrintArea().Y() > 0. We do
                            // a second calculation based on the actual rectangles of
                            // pFrame and pAnchoredObj, and use the maximum of the results.
                            // I do not want to remove the first calculation because
                            // if clipping has been applied, using the GetCurrRelPos
                            // might be the better option to calculate nHeight.
                            const SwTwips nDistOfFlyBottomToAnchorTop2 = aRectFnSet.YDiff(
                                                                            aRectFnSet.GetBottom(pAnchoredObj->GetObjRect()),
                                                                            aRectFnSet.GetBottom(pFrame->getFrameArea()) );
 
                            nHeight = std::max( nHeight, tools::Long(nDistOfFlyBottomToAnchorTop2 ));
                        }
                    }
                }
            }
        }
        if( !pFrame->IsSctFrame() )
            break;
        pTmp = pTmp->FindNextCnt();
        if( !static_cast<const SwSectionFrame*>(pFrame)->IsAnLower( pTmp ) )
            break;
    }
    return nHeight;
}
 
static SwTwips lcl_CalcTopAndBottomMargin( const SwLayoutFrame& rCell, const SwBorderAttrs& rAttrs )
{
    const SwTabFrame* pTab = rCell.FindTabFrame();
    SwTwips nTopSpace = 0;
    SwTwips nBottomSpace = 0;
 
    // #i29550#
    if ( pTab->IsCollapsingBorders() && rCell.Lower() && !rCell.Lower()->IsRowFrame() )
    {
        nTopSpace    = static_cast<const SwRowFrame*>(rCell.GetUpper())->GetTopMarginForLowers();
        nBottomSpace = static_cast<const SwRowFrame*>(rCell.GetUpper())->GetBottomMarginForLowers();
    }
    else
    {
        if ( pTab->IsVertical() != rCell.IsVertical() )
        {
            nTopSpace    = rAttrs.CalcLeft( &rCell );
            nBottomSpace = rAttrs.CalcRight( &rCell );
        }
        else
        {
            nTopSpace    = rAttrs.CalcTop();
            nBottomSpace = rAttrs.CalcBottom();
        }
    }
 
    return nTopSpace + nBottomSpace;
}
 
// #i26945# - add parameter <_bConsiderObjs> in order to
// control, if floating screen objects have to be considered for the minimal
// cell height.
static SwTwips lcl_CalcMinCellHeight( const SwLayoutFrame *_pCell,
                                      const bool _bConsiderObjs,
                                      const SwBorderAttrs *pAttrs = nullptr )
{
    SwRectFnSet aRectFnSet(_pCell);
    SwTwips nHeight = 0;
    const SwFrame* pLow = _pCell->Lower();
    if ( pLow )
    {
        tools::Long nFlyAdd = 0;
        while ( pLow )
        {
            if ( pLow->IsRowFrame() )
            {
                // #i26945#
                nHeight += ::lcl_CalcMinRowHeight( static_cast<const SwRowFrame*>(pLow),
                                                   _bConsiderObjs );
            }
            else
            {
                tools::Long nLowHeight = aRectFnSet.GetHeight(pLow->getFrameArea());
                nHeight += nLowHeight;
                // #i26945#
                if ( _bConsiderObjs )
                {
                    nFlyAdd = std::max( tools::Long(0), nFlyAdd - nLowHeight );
                    nFlyAdd = std::max( nFlyAdd, ::CalcHeightWithFlys( pLow ) );
                }
            }
 
            pLow = pLow->GetNext();
        }
        if ( nFlyAdd )
            nHeight += nFlyAdd;
    }
    // The border/margin needs to be considered too, unfortunately it can't be
    // calculated using PrintArea and FrameArea because any or all of those
    // may be invalid.
    if ( _pCell->Lower() )
    {
        if ( pAttrs )
            nHeight += lcl_CalcTopAndBottomMargin( *_pCell, *pAttrs );
        else
        {
            SwBorderAttrAccess aAccess( SwFrame::GetCache(), _pCell );
            const SwBorderAttrs &rAttrs = *aAccess.Get();
            nHeight += lcl_CalcTopAndBottomMargin( *_pCell, rAttrs );
        }
    }
    return nHeight;
}
 
// #i26945# - add parameter <_bConsiderObjs> in order to control,
// if floating screen objects have to be considered for the minimal cell height
static SwTwips lcl_CalcMinRowHeight( const SwRowFrame* _pRow,
                                     const bool _bConsiderObjs )
{
    //calc min height including width of horizontal border
    const bool bMinRowHeightInclBorder =
        _pRow->GetFormat()->GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::MIN_ROW_HEIGHT_INCL_BORDER);
    SwTwips nHeight = 0;
    if ( !_pRow->IsRowSpanLine() )
    {
        const SwFormatFrameSize &rSz = _pRow->GetFormat()->GetFrameSize();
        if (_pRow->HasFixSize() && !_pRow->IsForceRowSplitAllowed())
        {
            OSL_ENSURE(SwFrameSize::Fixed == rSz.GetHeightSizeType(), "pRow claims to have fixed size");
            return rSz.GetHeight();
        }
        // If this row frame is being split, then row's minimal height shouldn't restrict
        // this frame's minimal height, because the rest will go to follow frame.
        else if ( !_pRow->IsInSplit() && rSz.GetHeightSizeType() == SwFrameSize::Minimum )
        {
            bool bSplitFly = false;
            if (_pRow->IsInFly())
            {
                // See if we're in a split fly that is anchored on a page that has enough space to
                // host this row with its minimum row height.
                const SwFlyFrame* pFly = _pRow->FindFlyFrame();
                if (pFly->IsFlySplitAllowed())
                {
                    SwFrame* pAnchor = const_cast<SwFlyFrame*>(pFly)->FindAnchorCharFrame();
                    if (pAnchor)
                    {
                        if (pAnchor->FindPageFrame()->getFramePrintArea().Height() > rSz.GetHeight())
                        {
                            bSplitFly = true;
                        }
                    }
                }
            }
 
            if (bSplitFly)
            {
                // Split fly: enforce minimum row height for the master and follows.
                nHeight = rSz.GetHeight();
            }
            else
            {
                nHeight = rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*_pRow);
            }
            if (bMinRowHeightInclBorder)
            {
                //get horizontal border(s)
                nHeight += lcl_GetTopSpace(*_pRow);
            }
        }
    }
 
    SwRectFnSet aRectFnSet(_pRow);
    const SwCellFrame* pLow = static_cast<const SwCellFrame*>(_pRow->Lower());
    while ( pLow )
    {
        SwTwips nTmp = 0;
        const tools::Long nRowSpan = pLow->GetLayoutRowSpan();
        // --> NEW TABLES
        // Consider height of
        // 1. current cell if RowSpan == 1
        // 2. current cell if cell is "follow" cell of a cell with RowSpan == -1
        // 3. master cell if RowSpan == -1
        if ( 1 == nRowSpan )
        {
            nTmp = ::lcl_CalcMinCellHeight( pLow, _bConsiderObjs );
        }
        else if ( -1 == nRowSpan )
        {
            // Height of the last cell of a row span is height of master cell
            // minus the height of the other rows which are covered by the master
            // cell:
            const SwCellFrame& rMaster = pLow->FindStartEndOfRowSpanCell( true );
            nTmp = ::lcl_CalcMinCellHeight( &rMaster, _bConsiderObjs );
            const SwFrame* pMasterRow = rMaster.GetUpper();
            while ( pMasterRow && pMasterRow != _pRow )
            {
                nTmp -= aRectFnSet.GetHeight(pMasterRow->getFrameArea());
                pMasterRow = pMasterRow->GetNext();
            }
        }
        // <-- NEW TABLES
 
        // Do not consider rotated cells:
        if ( pLow->IsVertical() == aRectFnSet.IsVert() && nTmp > nHeight )
            nHeight = nTmp;
 
        pLow = static_cast<const SwCellFrame*>(pLow->GetNext());
    }
 
    return nHeight;
}
 
// #i29550#
 
// Calculate the maximum of (TopLineSize + TopLineDist) over all lowers:
static sal_uInt16 lcl_GetTopSpace( const SwRowFrame& rRow )
{
    sal_uInt16 nTopSpace = 0;
    for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
          pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
    {
        sal_uInt16 nTmpTopSpace = 0;
        if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
            nTmpTopSpace = lcl_GetTopSpace( *static_cast<const SwRowFrame*>(pCurrLower->Lower()) );
        else
        {
            const SwAttrSet& rSet = pCurrLower->GetFormat()->GetAttrSet();
            const SvxBoxItem& rBoxItem = rSet.GetBox();
            nTmpTopSpace = rBoxItem.CalcLineSpace( SvxBoxItemLine::TOP, true );
        }
        nTopSpace  = std::max( nTopSpace, nTmpTopSpace );
    }
    return nTopSpace;
}
 
// Calculate the maximum of TopLineDist over all lowers:
static sal_uInt16 lcl_GetTopLineDist( const SwRowFrame& rRow )
{
    sal_uInt16 nTopLineDist = 0;
    for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
          pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
    {
        sal_uInt16 nTmpTopLineDist = 0;
        if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
            nTmpTopLineDist = lcl_GetTopLineDist( *static_cast<const SwRowFrame*>(pCurrLower->Lower()) );
        else
        {
            const SwAttrSet& rSet = pCurrLower->GetFormat()->GetAttrSet();
            const SvxBoxItem& rBoxItem = rSet.GetBox();
            nTmpTopLineDist = rBoxItem.GetDistance( SvxBoxItemLine::TOP );
        }
        nTopLineDist = std::max( nTopLineDist, nTmpTopLineDist );
    }
    return nTopLineDist;
}
 
// Calculate the maximum of BottomLineSize over all lowers:
static sal_uInt16 lcl_GetBottomLineSize( const SwRowFrame& rRow )
{
    sal_uInt16 nBottomLineSize = 0;
    for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
          pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
    {
        sal_uInt16 nTmpBottomLineSize = 0;
        if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
        {
            const SwFrame* pRow = pCurrLower->GetLastLower();
            nTmpBottomLineSize = lcl_GetBottomLineSize( *static_cast<const SwRowFrame*>(pRow) );
        }
        else
        {
            const SwAttrSet& rSet = pCurrLower->GetFormat()->GetAttrSet();
            const SvxBoxItem& rBoxItem = rSet.GetBox();
            nTmpBottomLineSize = rBoxItem.CalcLineSpace( SvxBoxItemLine::BOTTOM, true ) -
                                 rBoxItem.GetDistance( SvxBoxItemLine::BOTTOM );
        }
        nBottomLineSize = std::max( nBottomLineSize, nTmpBottomLineSize );
    }
    return nBottomLineSize;
}
 
// Calculate the maximum of BottomLineDist over all lowers:
static sal_uInt16 lcl_GetBottomLineDist( const SwRowFrame& rRow )
{
    sal_uInt16 nBottomLineDist = 0;
    for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
          pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
    {
        sal_uInt16 nTmpBottomLineDist = 0;
        if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
        {
            const SwFrame* pRow = pCurrLower->GetLastLower();
            nTmpBottomLineDist = lcl_GetBottomLineDist( *static_cast<const SwRowFrame*>(pRow) );
        }
        else
        {
            const SwAttrSet& rSet = pCurrLower->GetFormat()->GetAttrSet();
            const SvxBoxItem& rBoxItem = rSet.GetBox();
            nTmpBottomLineDist = rBoxItem.GetDistance( SvxBoxItemLine::BOTTOM );
        }
        nBottomLineDist = std::max( nBottomLineDist, nTmpBottomLineDist );
    }
    return nBottomLineDist;
}
 
// tdf#104425: calculate the height of all row frames,
// for which this frame is a follow.
// When a row has fixed/minimum height, it may span over
// several pages. The minimal height on this page should
// take into account the sum of all the heights of previous
// frames that constitute the table row on previous pages.
// Otherwise, trying to split a too high row frame will
// result in loop trying to create that too high row
// on each following page
static SwTwips lcl_calcHeightOfRowBeforeThisFrame(const SwRowFrame& rRow)
{
    // We don't need to account for previous instances of repeated headlines
    if (rRow.IsRepeatedHeadline())
        return 0;
    SwRectFnSet aRectFnSet(&rRow);
    const SwTableLine* pLine = rRow.GetTabLine();
    const SwTabFrame* pTab = rRow.FindTabFrame();
    if (!pLine || !pTab || !pTab->IsFollow())
        return 0;
    SwTwips nResult = 0;
    SwIterator<SwRowFrame, SwFormat> aIter(*pLine->GetFrameFormat());
    for (const SwRowFrame* pCurRow = aIter.First(); pCurRow; pCurRow = aIter.Next())
    {
        if (pCurRow != &rRow && pCurRow->GetTabLine() == pLine)
        {
            // We've found another row frame that is part of the same table row
            const SwTabFrame* pCurTab = pCurRow->FindTabFrame();
            // A row frame may not belong to a table frame, when it is being cut, e.g., in
            // lcl_PostprocessRowsInCells().
            // Its SwRowFrame::Cut() has been called; it in turn called SwLayoutFrame::Cut(),
            // which nullified row's upper in RemoveFromLayout(), and then called Shrink()
            // for its former upper.
            // Regardless of whether it will be pasted back, or destroyed, currently it's not
            // part of layout, and its height does not count
            if (pCurTab && pCurTab->IsAnFollow(pTab))
            {
                // The found row frame belongs to a table frame that precedes
                // (above) this one in chain. So, include it in the sum
                nResult += aRectFnSet.GetHeight(pCurRow->getFrameArea());
            }
        }
    }
    return nResult;
}
 
void SwRowFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
{
    SwRectFnSet aRectFnSet(this);
    OSL_ENSURE( pAttrs, "SwRowFrame::Format without Attrs." );
 
    const bool bFix = mbFixSize;
    SwTabFrame* pTabFrame = FindTabFrame();
 
    if ( !isFramePrintAreaValid() )
    {
        // RowFrames don't have borders/margins therefore the PrintArea always
        // matches the FrameArea.
        setFramePrintAreaValid(true);
 
        {
            SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
            aPrt.Left( 0 );
            aPrt.Top( 0 );
            aPrt.Width ( getFrameArea().Width() );
            aPrt.Height( getFrameArea().Height() );
        }
 
        // #i29550#
        // Here we calculate the top-printing area for the lower cell frames
        if ( pTabFrame->IsCollapsingBorders() )
        {
            const sal_uInt16 nTopSpace        = lcl_GetTopSpace(       *this );
            const sal_uInt16 nTopLineDist     = lcl_GetTopLineDist(    *this );
            const sal_uInt16 nBottomLineSize  = lcl_GetBottomLineSize( *this );
            const sal_uInt16 nBottomLineDist  = lcl_GetBottomLineDist( *this );
 
            const SwRowFrame* pPreviousRow = nullptr;
 
            // #i32456#
            // In order to calculate the top printing area for the lower cell
            // frames, we have to find the 'previous' row frame and compare
            // the bottom values of the 'previous' row with the 'top' values
            // of this row. The best way to find the 'previous' row is to
            // use the table structure:
            const SwTable* pTable = pTabFrame->GetTable();
            const SwTableLine* pPrevTabLine = nullptr;
            const SwRowFrame* pTmpRow = this;
 
            while ( pTmpRow && !pPrevTabLine )
            {
                size_t nIdx = 0;
                const SwTableLines& rLines = pTmpRow->GetTabLine()->GetUpper() ?
                                             pTmpRow->GetTabLine()->GetUpper()->GetTabLines() :
                                             pTable->GetTabLines();
 
                while ( rLines[ nIdx ] != pTmpRow->GetTabLine() )
                    ++nIdx;
 
                if ( nIdx > 0 )
                {
                    // pTmpRow has a 'previous' row in the table structure:
                    pPrevTabLine = rLines[ nIdx - 1 ];
                }
                else
                {
                    // pTmpRow is a first row in the table structure.
                    // We go up in the table structure:
                    pTmpRow = pTmpRow->GetUpper()->GetUpper() &&
                              pTmpRow->GetUpper()->GetUpper()->IsRowFrame() ?
                              static_cast<const SwRowFrame*>( pTmpRow->GetUpper()->GetUpper() ) :
                              nullptr;
                }
            }
 
            // If we found a 'previous' row, we look for the appropriate row frame:
            if ( pPrevTabLine )
            {
                SwIterator<SwRowFrame,SwFormat> aIter( *pPrevTabLine->GetFrameFormat() );
                for ( SwRowFrame* pRow = aIter.First(); pRow; pRow = aIter.Next() )
                {
                    // #115759# - do *not* take repeated
                    // headlines, because during split of table it can be
                    // invalid and thus can't provide correct border values.
                    if ( pRow->GetTabLine() == pPrevTabLine &&
                         !pRow->IsRepeatedHeadline() )
                    {
                        pPreviousRow = pRow;
                        break;
                    }
                }
            }
 
            sal_uInt16 nTopPrtMargin = nTopSpace;
            if ( pPreviousRow )
            {
                const sal_uInt16 nTmpPrtMargin = pPreviousRow->GetBottomLineSize() + nTopLineDist;
                if ( nTmpPrtMargin > nTopPrtMargin )
                    nTopPrtMargin = nTmpPrtMargin;
            }
 
            // table has to be notified if it has to change its lower
            // margin due to changes of nBottomLineSize:
            if ( !GetNext() && nBottomLineSize != GetBottomLineSize() )
                 pTabFrame->InvalidatePrt_();
 
            // If there are rows nested inside this row, the nested rows
            // may not have been calculated yet. Therefore the
            // ::lcl_CalcMinRowHeight( this ) operation later in this
            // function cannot consider the correct border values. We
            // have to trigger the invalidation of the outer row frame
            // manually:
            // Note: If any further invalidations should be necessary, we
            // should consider moving the invalidation stuff to the
            // appropriate SwNotify object.
            if ( GetUpper()->GetUpper()->IsRowFrame() &&
                 ( nBottomLineDist != GetBottomMarginForLowers() ||
                   nTopPrtMargin   != GetTopMarginForLowers() ) )
                GetUpper()->GetUpper()->InvalidateSize_();
 
            SetBottomMarginForLowers( nBottomLineDist );    //  3.
            SetBottomLineSize( nBottomLineSize );           //  4.
            SetTopMarginForLowers( nTopPrtMargin );         //  5.
 
        }
    }
 
    while ( !isFrameAreaSizeValid() )
    {
        setFrameAreaSizeValid(true);
 
#if OSL_DEBUG_LEVEL > 0
        if ( HasFixSize() )
        {
            const SwFormatFrameSize &rFrameSize = GetFormat()->GetFrameSize();
            OSL_ENSURE( rFrameSize.GetSize().Height() > 0, "Has it" );
        }
#endif
        const SwTwips nDiff = aRectFnSet.GetHeight(getFrameArea()) -
                              ( HasFixSize() && !IsRowSpanLine()
                                ? pAttrs->GetSize().Height()
                                // #i26945#
                                : ::lcl_CalcMinRowHeight( this,
                                    FindTabFrame()->IsConsiderObjsForMinCellHeight() ) );
        if ( nDiff )
        {
            mbFixSize = false;
            if ( nDiff > 0 )
                Shrink( nDiff, false, true );
            else if ( nDiff < 0 )
                Grow( -nDiff );
            mbFixSize = bFix;
        }
    }
 
    // last row will fill the space in its upper.
    if ( GetNext() )
        return;
 
    //The last fills the remaining space in the upper.
    SwTwips nDiff = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea());
    SwFrame *pSibling = GetUpper()->Lower();
    do
    {   nDiff -= aRectFnSet.GetHeight(pSibling->getFrameArea());
        pSibling = pSibling->GetNext();
    } while ( pSibling );
    if (nDiff > 0 && pTabFrame->IsCollapsingBorders())
    {
        // In SwTabFrame::Format, this value will be added to the table's bottom margin
        nDiff -= GetBottomLineSize();
    }
    if ( nDiff > 0 )
    {
        mbFixSize = false;
        Grow( nDiff );
        mbFixSize = bFix;
        setFrameAreaSizeValid(true);
    }
}
 
void SwRowFrame::AdjustCells( const SwTwips nHeight, const bool bHeight )
{
    SwFrame *pFrame = Lower();
    if ( bHeight )
    {
        SwRectFnSet aRectFnSet(this);
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
        SwRect aOldFrame;
#endif
 
        while ( pFrame )
        {
            SwFrame* pNotify = nullptr;
 
            SwCellFrame* pCellFrame = static_cast<SwCellFrame*>(pFrame);
 
            // NEW TABLES
            // Which cells need to be adjusted if the current row changes
            // its height?
 
            // Current frame is a covered frame:
            // Set new height for covered cell and adjust master cell:
            if ( pCellFrame->GetTabBox()->getRowSpan() < 1 )
            {
                // Set height of current (covered) cell to new line height.
                const tools::Long nDiff = nHeight - aRectFnSet.GetHeight(pCellFrame->getFrameArea());
                if ( nDiff )
                {
                    {
                        SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pCellFrame);
                        aRectFnSet.AddBottom( aFrm, nDiff );
                    }
 
                    pCellFrame->InvalidatePrt_();
                }
            }
 
            SwCellFrame* pToAdjust = nullptr;
            SwFrame* pToAdjustRow = nullptr;
 
            // If current frame is covered frame, we still want to adjust the
            // height of the cell starting the row span
            if ( pCellFrame->GetLayoutRowSpan() < 1 )
            {
                pToAdjust = const_cast< SwCellFrame*>(&pCellFrame->FindStartEndOfRowSpanCell( true ));
                pToAdjustRow = pToAdjust->GetUpper();
            }
            else
            {
                pToAdjust = pCellFrame;
                pToAdjustRow = this;
            }
 
            // Set height of master cell to height of all lines spanned by this line.
            tools::Long nRowSpan = pToAdjust->GetLayoutRowSpan();
            SwTwips nSumRowHeight = 0;
            while ( pToAdjustRow )
            {
                // Use new height for the current row:
                nSumRowHeight += pToAdjustRow == this ?
                                 nHeight :
                                 aRectFnSet.GetHeight(pToAdjustRow->getFrameArea());
 
                if ( nRowSpan-- == 1 )
                    break;
 
                pToAdjustRow = pToAdjustRow->GetNext();
            }
 
            if ( pToAdjustRow && pToAdjustRow != this )
                pToAdjustRow->InvalidateSize_();
 
            const tools::Long nDiff = nSumRowHeight - aRectFnSet.GetHeight(pToAdjust->getFrameArea());
            if ( nDiff )
            {
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
                aOldFrame = pToAdjust->getFrameArea();
#endif
                SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pToAdjust);
                aRectFnSet.AddBottom( aFrm, nDiff );
                pNotify = pToAdjust;
            }
 
            if ( pNotify )
            {
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
                SwRootFrame *pRootFrame = getRootFrame();
                if( pRootFrame && pRootFrame->IsAnyShellAccessible() && pRootFrame->GetCurrShell() )
                    pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( pNotify, aOldFrame );
#endif
 
                pNotify->InvalidatePrt_();
            }
 
            pFrame = pFrame->GetNext();
        }
    }
    else
    {   while ( pFrame )
        {
            pFrame->InvalidateAll_();
            pFrame = pFrame->GetNext();
        }
    }
    InvalidatePage();
}
 
void SwRowFrame::Cut()
{
    SwTabFrame *pTab = FindTabFrame();
    if ( pTab && pTab->IsFollow() && this == pTab->GetFirstNonHeadlineRow() )
    {
        pTab->FindMaster()->InvalidatePos();
    }
 
    SwLayoutFrame::Cut();
}
 
SwTwips SwRowFrame::GrowFrame(SwTwips nDist, SwResizeLimitReason& reason, bool bTst, bool bInfo)
{
    const auto nOrigDist = nDist;
    SwTwips nReal = 0;
 
    SwTabFrame* pTab = FindTabFrame();
    SwRectFnSet aRectFnSet(pTab);
 
    bool bRestrictTableGrowth;
    bool bHasFollowFlowLine = pTab->HasFollowFlowLine();
 
    if ( GetUpper()->IsTabFrame() )
    {
        const SwRowFrame* pFollowFlowRow = IsInSplitTableRow();
        bRestrictTableGrowth = pFollowFlowRow && !pFollowFlowRow->IsRowSpanLine();
    }
    else
    {
        OSL_ENSURE( GetUpper()->IsCellFrame(), "RowFrame->GetUpper neither table nor cell" );
        bRestrictTableGrowth = GetFollowRow() && bHasFollowFlowLine;
        OSL_ENSURE( !bRestrictTableGrowth || !GetNext(),
                "GetFollowRow for row frame that has a Next" );
 
        // There may still be some space left in my direct upper:
        const SwTwips nAdditionalSpace =
                aRectFnSet.BottomDist( getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()->GetUpper()) );
        if ( bRestrictTableGrowth && nAdditionalSpace > 0 )
        {
            nReal = std::min( nAdditionalSpace, nDist );
            nDist -= nReal;
            if ( !bTst )
            {
                SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                aRectFnSet.AddBottom( aFrm, nReal );
            }
        }
    }
 
    if ( bRestrictTableGrowth )
        pTab->SetRestrictTableGrowth( true );
    else
    {
        // Ok, this looks like a hack, indeed, it is a hack.
        // If the current row frame is inside another cell frame,
        // and the current row frame has no follow, it should not
        // be allowed to grow. In fact, setting bRestrictTableGrowth
        // to 'false' does not work, because the surrounding RowFrame
        // would set this to 'true'.
        pTab->SetFollowFlowLine( false );
    }
 
    nReal += SwLayoutFrame::GrowFrame(nDist, reason, bTst, bInfo);
 
    pTab->SetRestrictTableGrowth( false );
    pTab->SetFollowFlowLine( bHasFollowFlowLine );
 
    //Update the height of the cells to the newest value.
    if ( !bTst )
    {
        SwRectFnSet fnRectX(this);
        AdjustCells( fnRectX.GetHeight(getFramePrintArea()) + nReal, true );
        if ( nReal )
            SetCompletePaint();
    }
    if (reason == SwResizeLimitReason::Unspecified && nReal < nOrigDist && IsInSplit())
        reason = SwResizeLimitReason::FlowToFollow;
    return nReal;
}
 
SwTwips SwRowFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool bInfo )
{
    SwRectFnSet aRectFnSet(this);
    if( HasFixSize() )
    {
        AdjustCells( aRectFnSet.GetHeight(getFramePrintArea()), true );
        return 0;
    }
 
    // bInfo may be set to true by SwRowFrame::Format; we need to handle this
    // here accordingly
    const bool bShrinkAnyway = bInfo;
 
    //Only shrink as much as the content of the biggest cell allows.
    SwTwips nRealDist = nDist;
    SwFormat* pMod = GetFormat();
    if (pMod)
    {
        const SwFormatFrameSize &rSz = pMod->GetFrameSize();
        SwTwips nMinHeight = 0;
        if (rSz.GetHeightSizeType() == SwFrameSize::Minimum)
            nMinHeight = std::max(rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*this),
                                  tools::Long(0));
 
        // Only necessary to calculate minimal row height if height
        // of pRow is at least nMinHeight. Otherwise nMinHeight is the
        // minimum height.
        if( nMinHeight < aRectFnSet.GetHeight(getFrameArea()) )
        {
            // #i26945#
            OSL_ENSURE( FindTabFrame(), "<SwRowFrame::ShrinkFrame(..)> - no table frame -> crash." );
            const bool bConsiderObjs( FindTabFrame()->IsConsiderObjsForMinCellHeight() );
            nMinHeight = lcl_CalcMinRowHeight( this, bConsiderObjs );
        }
 
        if ( (aRectFnSet.GetHeight(getFrameArea()) - nRealDist) < nMinHeight )
            nRealDist = aRectFnSet.GetHeight(getFrameArea()) - nMinHeight;
    }
    if ( nRealDist < 0 )
        nRealDist = 0;
 
    SwTwips nReal = nRealDist;
    if ( nReal )
    {
        if ( !bTst )
        {
            SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
            SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
            aRectFnSet.SetHeight( aFrm, nHeight - nReal );
 
            if( IsVertical() && !IsVertLR() )
            {
                aFrm.Pos().AdjustX(nReal );
            }
        }
 
        SwLayoutFrame* pFrame = GetUpper();
        SwTwips nTmp = pFrame ? pFrame->Shrink(nReal, bTst) : 0;
        if ( !bShrinkAnyway && !GetNext() && nTmp != nReal )
        {
            //The last one gets the leftover in the upper and therefore takes
            //care (otherwise: endless loop)
            if ( !bTst )
            {
                nReal -= nTmp;
                SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
                SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                aRectFnSet.SetHeight( aFrm, nHeight + nReal );
 
                if( IsVertical() && !IsVertLR() )
                {
                    aFrm.Pos().AdjustX( -nReal );
                }
            }
            nReal = nTmp;
        }
    }
 
    // Invalidate appropriately and update the height to the newest value.
    if ( !bTst )
    {
        if ( nReal )
        {
            if ( GetNext() )
                GetNext()->InvalidatePos_();
            InvalidateAll_();
            SetCompletePaint();
 
            SwTabFrame *pTab = FindTabFrame();
            if ( !pTab->IsRebuildLastLine()
                 && pTab->IsFollow()
                 && this == pTab->GetFirstNonHeadlineRow()
                 && !pTab->IsInRecalcLowerRow() )
            {
                SwTabFrame* pMasterTab = pTab->FindMaster();
                pMasterTab->InvalidatePos();
            }
        }
        AdjustCells( aRectFnSet.GetHeight(getFramePrintArea()) - nReal, true );
    }
    return nReal;
}
 
bool SwRowFrame::IsRowSplitAllowed() const
{
    // Fixed size rows are never allowed to split:
    if ( HasFixSize() )
    {
        OSL_ENSURE( SwFrameSize::Fixed == GetFormat()->GetFrameSize().GetHeightSizeType(), "pRow claims to have fixed size" );
        return false;
    }
 
    // Repeated headlines are never allowed to split:
    const SwTabFrame* pTabFrame = FindTabFrame();
    if ( pTabFrame->GetTable()->GetRowsToRepeat() > 0 &&
         pTabFrame->IsInHeadline( *this ) )
        return false;
 
    if ( IsForceRowSplitAllowed() )
        return true;
 
    const SwTableLineFormat* pFrameFormat = GetTabLine()->GetFrameFormat();
    const SwFormatRowSplit& rLP = pFrameFormat->GetRowSplit();
    return rLP.GetValue();
}
 
bool SwRowFrame::ShouldRowKeepWithNext( const bool bCheckParents ) const
{
    // No KeepWithNext if nested in another table
    if ( GetUpper()->GetUpper()->IsCellFrame() )
        return false;
 
    const SwCellFrame* pCell = static_cast<const SwCellFrame*>(Lower());
    const SwFrame* pText = pCell->Lower();
 
    return pText && pText->IsTextFrame() &&
           static_cast<const SwTextFrame*>(pText)->GetTextNodeForParaProps()->GetSwAttrSet().GetKeep(bCheckParents).GetValue();
}
 
SwCellFrame::SwCellFrame(const SwTableBox &rBox, SwFrame* pSib, bool bInsertContent)
    : SwLayoutFrame( rBox.GetFrameFormat(), pSib )
    , m_pTabBox( &rBox )
{
    mnFrameType = SwFrameType::Cell;
 
    if ( !bInsertContent )
        return;
 
    //If a StartIdx is available, ContentFrames are added in the cell, otherwise
    //Rows have to be present and those are added.
    if ( SwNodeOffset nIndex = rBox.GetSttIdx() )
    {
        ::InsertCnt_( this, rBox.GetFrameFormat()->GetDoc(), ++nIndex );
    }
    else
    {
        const SwTableLines &rLines = rBox.GetTabLines();
        SwFrame *pTmpPrev = nullptr;
        for ( size_t i = 0; i < rLines.size(); ++i )
        {
            SwRowFrame *pNew = new SwRowFrame( *rLines[i], this, bInsertContent );
            pNew->InsertBehind( this, pTmpPrev );
            pTmpPrev = pNew;
        }
    }
}
 
void SwCellFrame::DestroyImpl()
{
    sw::BroadcastingModify* pMod = GetFormat();
    if( pMod )
    {
        // At this stage the lower frames aren't destroyed already,
        // therefore we have to do a recursive dispose.
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
        SwRootFrame *pRootFrame = getRootFrame();
        if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
            pRootFrame->GetCurrShell() )
        {
            pRootFrame->GetCurrShell()->Imp()->DisposeAccessibleFrame( this, true );
        }
#endif
 
        pMod->Remove(*this);
        if( !pMod->HasWriterListeners() )
            delete pMod;
    }
 
    SwLayoutFrame::DestroyImpl();
}
 
SwCellFrame::~SwCellFrame()
{
}
 
static bool lcl_ArrangeLowers( SwLayoutFrame *pLay, tools::Long lYStart, bool bInva )
{
    bool bRet = false;
    SwFrame *pFrame = pLay->Lower();
    SwRectFnSet aRectFnSet(pLay);
    while ( pFrame )
    {
        tools::Long nFrameTop = aRectFnSet.GetTop(pFrame->getFrameArea());
        if( nFrameTop != lYStart )
        {
            bRet = true;
            const tools::Long lDiff = aRectFnSet.YDiff( lYStart, nFrameTop );
            const tools::Long lDiffX = lYStart - nFrameTop;
 
            {
                SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFrame);
                aRectFnSet.SubTop( aFrm, -lDiff );
                aRectFnSet.AddBottom( aFrm, lDiff );
            }
 
            pFrame->SetCompletePaint();
 
            if ( !pFrame->GetNext() )
                pFrame->SetRetouche();
            if( bInva )
                pFrame->Prepare( PrepareHint::FramePositionChanged );
            if ( pFrame->IsLayoutFrame() && static_cast<SwLayoutFrame*>(pFrame)->Lower() )
                lcl_ArrangeLowers( static_cast<SwLayoutFrame*>(pFrame),
                    aRectFnSet.GetTop(static_cast<SwLayoutFrame*>(pFrame)->Lower()->getFrameArea())
                    + lDiffX, bInva );
            if ( pFrame->GetDrawObjs() )
            {
                for ( size_t i = 0; i < pFrame->GetDrawObjs()->size(); ++i )
                {
                    SwAnchoredObject* pAnchoredObj = (*pFrame->GetDrawObjs())[i];
                    // #i26945# - check, if anchored object
                    // is lower of layout frame by checking, if the anchor
                    // frame, which contains the anchor position, is a lower
                    // of the layout frame.
                    if ( !pLay->IsAnLower( pAnchoredObj->GetAnchorFrameContainingAnchPos() ) )
                    {
                        continue;
                    }
                    // #i52904# - distinguish between anchored
                    // objects, whose vertical position depends on its anchor
                    // frame and whose vertical position is independent
                    // from its anchor frame.
                    bool bVertPosDepOnAnchor( true );
                    {
                        SwFormatVertOrient aVert( pAnchoredObj->GetFrameFormat()->GetVertOrient() );
                        switch ( aVert.GetRelationOrient() )
                        {
                            case text::RelOrientation::PAGE_FRAME:
                            case text::RelOrientation::PAGE_PRINT_AREA:
                                bVertPosDepOnAnchor = false;
                                break;
                            default: break;
                        }
                    }
                    if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
                    {
 
                        // OD 2004-05-18 #i28701# - no direct move of objects,
                        // which are anchored to-paragraph/to-character, if
                        // the wrapping style influence has to be considered
                        // on the object positioning.
                        // #i52904# - no direct move of objects,
                        // whose vertical position doesn't depend on anchor frame.
                        const bool bDirectMove =
                                FAR_AWAY != pFly->getFrameArea().Top() &&
                                bVertPosDepOnAnchor &&
                                !pFly->ConsiderObjWrapInfluenceOnObjPos();
                        if ( bDirectMove )
                        {
                            {
                                SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFly);
                                aRectFnSet.SubTop( aFrm, -lDiff );
                                aRectFnSet.AddBottom( aFrm, lDiff );
                            }
 
                            pFly->GetVirtDrawObj()->SetBoundAndSnapRectsDirty();
                            // --> OD 2004-08-17 - also notify view of <SdrObject>
                            // instance, which represents the Writer fly frame in
                            // the drawing layer
                            pFly->GetVirtDrawObj()->SetChanged();
                            // #i58280#
                            pFly->InvalidateObjRectWithSpaces();
                        }
 
                        if ( pFly->IsFlyInContentFrame() )
                        {
                            static_cast<SwFlyInContentFrame*>(pFly)->AddRefOfst( lDiff );
                            // #115759# - reset current relative
                            // position to get re-positioned, if not directly moved.
                            if ( !bDirectMove )
                            {
                                pAnchoredObj->SetCurrRelPos( Point( 0, 0 ) );
                            }
                        }
                        else if( pFly->IsAutoPos() )
                        {
                            pFly->AddLastCharY( lDiff );
                            // OD 2004-05-18 #i28701# - follow-up of #i22341#
                            // <mnLastTopOfLine> has also been adjusted.
                            pFly->AddLastTopOfLineY( lDiff );
                        }
                        // #i26945# - re-registration at
                        // page frame of anchor frame, if table frame isn't
                        // a follow table and table frame isn't in its
                        // rebuild of last line.
                        const SwTabFrame* pTabFrame = pLay->FindTabFrame();
                        // - save: check, if table frame is found.
                        if ( pTabFrame &&
                             !( pTabFrame->IsFollow() &&
                                pTabFrame->FindMaster()->IsRebuildLastLine() ) &&
                             pFly->IsFlyFreeFrame() )
                        {
                            SwPageFrame* pPageFrame = pFly->GetPageFrame();
                            SwPageFrame* pPageOfAnchor = pFrame->FindPageFrame();
                            if ( pPageFrame != pPageOfAnchor )
                            {
                                pFly->InvalidatePos();
                                pFly->RegisterAtPage(*pPageOfAnchor);
                            }
                        }
                        // OD 2004-05-11 #i28701# - Because of the introduction
                        // of new positionings and alignments (e.g. aligned at
                        // page area, but anchored at-character), the position
                        // of the Writer fly frame has to be invalidated.
                        pFly->InvalidatePos();
 
                        // #i26945# - follow-up of #i3317#
                        // No arrangement of lowers, if Writer fly frame isn't
                        // moved
                        if ( bDirectMove &&
                             ::lcl_ArrangeLowers( pFly,
                                                  aRectFnSet.GetPrtTop(*pFly),
                                                  bInva ) )
                        {
                            pFly->SetCompletePaint();
                        }
                    }
                    else if ( dynamic_cast< const SwAnchoredDrawObject *>( pAnchoredObj ) !=  nullptr )
                    {
                        // #i26945#
                        const SwTabFrame* pTabFrame = pLay->FindTabFrame();
                        if ( pTabFrame &&
                             !( pTabFrame->IsFollow() &&
                                pTabFrame->FindMaster()->IsRebuildLastLine() ) &&
                            (pAnchoredObj->GetFrameFormat()->GetAnchor().GetAnchorId()
                                                            != RndStdIds::FLY_AS_CHAR))
                        {
                            SwPageFrame* pPageFrame = pAnchoredObj->GetPageFrame();
                            SwPageFrame* pPageOfAnchor = pFrame->FindPageFrame();
                            if ( pPageFrame != pPageOfAnchor )
                            {
                                pAnchoredObj->InvalidateObjPos();
                                pAnchoredObj->RegisterAtPage(*pPageOfAnchor);
                            }
                        }
                        // #i28701# - adjust last character
                        // rectangle and last top of line.
                        pAnchoredObj->AddLastCharY( lDiff );
                        pAnchoredObj->AddLastTopOfLineY( lDiff );
                        // #i52904# - re-introduce direct move
                        // of drawing objects
                        const bool bDirectMove =
                                static_cast<const SwDrawFrameFormat*>(pAnchoredObj->GetFrameFormat())->IsPosAttrSet() &&
                                bVertPosDepOnAnchor &&
                                !pAnchoredObj->ConsiderObjWrapInfluenceOnObjPos();
                        if ( bDirectMove )
                        {
                            SwObjPositioningInProgress aObjPosInProgress( *pAnchoredObj );
                            if ( aRectFnSet.IsVert() )
                            {
                                pAnchoredObj->DrawObj()->Move( Size( lDiff, 0 ) );
                            }
                            else
                            {
                                pAnchoredObj->DrawObj()->Move( Size( 0, lDiff ) );
                            }
                            // #i58280#
                            pAnchoredObj->InvalidateObjRectWithSpaces();
                        }
                        pAnchoredObj->InvalidateObjPos();
                    }
                    else
                    {
                        OSL_FAIL( "<lcl_ArrangeLowers(..)> - unknown type of anchored object!" );
                    }
                }
            }
        }
        // Columns and cells are ordered horizontal, not vertical
        if( !pFrame->IsColumnFrame() && !pFrame->IsCellFrame() )
            lYStart = aRectFnSet.YInc( lYStart,
                                        aRectFnSet.GetHeight(pFrame->getFrameArea()) );
 
        // Nowadays, the content inside a cell can flow into the follow table.
        // Thus, the cell may only grow up to the end of the environment.
        // So the content may have grown, but the cell could not grow.
        // Therefore we have to trigger a formatting for the frames, which do
        // not fit into the cell anymore:
        SwTwips nDistanceToUpperPrtBottom =
            aRectFnSet.BottomDist( pFrame->getFrameArea(), aRectFnSet.GetPrtBottom(*pLay) );
        // #i56146# - Revise fix of issue #i26945#
        // do *not* consider content inside fly frames, if it's an undersized paragraph.
        // #i26945# - consider content inside fly frames
        if ( nDistanceToUpperPrtBottom < 0 &&
             ( ( pFrame->IsInFly() &&
                 ( !pFrame->IsTextFrame() ||
                   !static_cast<SwTextFrame*>(pFrame)->IsUndersized() ) ) ||
               pFrame->IsInSplitTableRow() ) )
        {
            pFrame->InvalidatePos();
        }
 
        pFrame = pFrame->GetNext();
    }
    return bRet;
}
 
void SwCellFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
{
    OSL_ENSURE( pAttrs, "CellFrame::Format, pAttrs is 0." );
    const SwTabFrame* pTab = FindTabFrame();
    SwRectFnSet aRectFnSet(pTab);
 
    if ( !isFramePrintAreaValid() )
    {
        setFramePrintAreaValid(true);
 
        //Adjust position.
        if ( Lower() )
        {
            SwTwips nTopSpace, nBottomSpace, nLeftSpace, nRightSpace;
            // #i29550#
            if ( pTab->IsCollapsingBorders() && !Lower()->IsRowFrame()  )
            {
                const SvxBoxItem& rBoxItem = pAttrs->GetBox();
                nLeftSpace   = rBoxItem.GetDistance( SvxBoxItemLine::LEFT );
                nRightSpace  = rBoxItem.GetDistance( SvxBoxItemLine::RIGHT );
                nTopSpace    =  static_cast<SwRowFrame*>(GetUpper())->GetTopMarginForLowers();
                nBottomSpace =  static_cast<SwRowFrame*>(GetUpper())->GetBottomMarginForLowers();
            }
            else
            {
                // OD 23.01.2003 #106895# - add 1st param to <SwBorderAttrs::CalcRight(..)>
                nLeftSpace   = pAttrs->CalcLeft( this );
                nRightSpace  = pAttrs->CalcRight( this );
                nTopSpace    = pAttrs->CalcTop();
                nBottomSpace = pAttrs->CalcBottom();
            }
            aRectFnSet.SetXMargins( *this, nLeftSpace, nRightSpace );
            aRectFnSet.SetYMargins( *this, nTopSpace, nBottomSpace );
        }
    }
    // #i26945#
    tools::Long nRemaining = GetTabBox()->getRowSpan() >= 1 ?
                      ::lcl_CalcMinCellHeight( this, pTab->IsConsiderObjsForMinCellHeight(), pAttrs ) :
                      0;
    if ( !isFrameAreaSizeValid() )
    {
        setFrameAreaSizeValid(true);
 
        //The VarSize of the CellFrames is always the width.
        //The width is not variable though, it is defined by the format.
        //This predefined value however does not necessary match the actual
        //width. The width is calculated based on the attribute, the value in
        //the attribute matches the desired value of the TabFrame. Changes which
        //were done there are taken into account here proportionately.
        //If the cell doesn't have a neighbour anymore, it does not take the
        //attribute into account and takes the rest of the upper instead.
        SwTwips nWidth;
        if ( GetNext() )
        {
            const SwTwips nWish = pTab->GetFormat()->GetFrameSize().GetWidth();
            nWidth = pAttrs->GetSize().Width();
 
            OSL_ENSURE( nWish, "Table without width?" );
            OSL_ENSURE( nWidth <= nWish, "Width of cell larger than table." );
            OSL_ENSURE( nWidth > 0, "Box without width" );
 
            const tools::Long nPrtWidth = aRectFnSet.GetWidth(pTab->getFramePrintArea());
            if ( nWish != nPrtWidth )
            {
                // Avoid rounding problems, at least for the new table model
                if ( pTab->GetTable()->IsNewModel() )
                {
                    // 1. sum of widths of cells up to this cell (in model)
                    const SwTableLine* pTabLine = GetTabBox()->GetUpper();
                    const SwTableBoxes& rBoxes = pTabLine->GetTabBoxes();
                    const SwTableBox* pTmpBox = nullptr;
 
                    SwTwips nSumWidth = 0;
                    size_t i = 0;
                    do
                    {
                        pTmpBox = rBoxes[ i++ ];
                        nSumWidth += pTmpBox->GetFrameFormat()->GetFrameSize().GetWidth();
                    }
                    while ( pTmpBox != GetTabBox() );
 
                    // 2. calculate actual width of cells up to this one
                    double nTmpWidth = nSumWidth;
                    nTmpWidth *= nPrtWidth;
                    nTmpWidth /= nWish;
                    nWidth = static_cast<SwTwips>(nTmpWidth);
 
                    // 3. calculate frame widths of cells up to this one:
                    const SwFrame* pTmpCell = static_cast<const SwLayoutFrame*>(GetUpper())->Lower();
                    SwTwips nSumFrameWidths = 0;
                    while ( pTmpCell != this )
                    {
                        nSumFrameWidths += aRectFnSet.GetWidth(pTmpCell->getFrameArea());
                        pTmpCell = pTmpCell->GetNext();
                    }
 
                    nWidth = nWidth - nSumFrameWidths;
                }
                else
                {
                    // #i12092# use double instead of long,
                    // otherwise this could lead to overflows
                    double nTmpWidth = nWidth;
                    nTmpWidth *= nPrtWidth;
                    nTmpWidth /= nWish;
                    nWidth = static_cast<SwTwips>(nTmpWidth);
                }
            }
        }
        else
        {
            OSL_ENSURE( pAttrs->GetSize().Width() > 0, "Box without width" );
            nWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
            SwFrame *pPre = GetUpper()->Lower();
            while ( pPre != this )
            {
                nWidth -= aRectFnSet.GetWidth(pPre->getFrameArea());
                pPre = pPre->GetNext();
            }
        }
 
        const tools::Long nDiff = nWidth - aRectFnSet.GetWidth(getFrameArea());
 
        {
            SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
 
            if( IsNeighbourFrame() && IsRightToLeft() )
            {
                aRectFnSet.SubLeft( aFrm, nDiff );
            }
            else
            {
                aRectFnSet.AddRight( aFrm, nDiff );
            }
        }
 
        {
            SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
            aRectFnSet.AddRight( aPrt, nDiff );
        }
 
        //Adjust the height, it's defined through the content and the margins.
        const tools::Long nDiffHeight = nRemaining - aRectFnSet.GetHeight(getFrameArea());
        if ( nDiffHeight )
        {
            if ( nDiffHeight > 0 )
            {
                //Validate again if no growth happened. Invalidation is done
                //through AdjustCells of the row.
                if ( !Grow( nDiffHeight ) )
                {
                    setFrameAreaSizeValid(true);
                    setFramePrintAreaValid(true);
                }
            }
            else
            {
                // Only keep invalidated if shrinking was actually done; the
                // attempt can be ignored because all horizontally adjoined
                // cells have to be the same height.
                if ( !Shrink( -nDiffHeight ) )
                {
                    setFrameAreaSizeValid(true);
                    setFramePrintAreaValid(true);
                }
            }
        }
    }
    const SwFormatVertOrient &rOri = pAttrs->GetAttrSet().GetVertOrient();
 
    if ( !Lower() )
        return;
 
    // From now on, all operations are related to the table cell.
    aRectFnSet.Refresh(this);
 
    SwPageFrame* pPg = nullptr;
    if ( !FindTabFrame()->IsRebuildLastLine() && text::VertOrientation::NONE != rOri.GetVertOrient() &&
    // #158225# no vertical alignment of covered cells
         !IsCoveredCell() &&
         (pPg = FindPageFrame())!=nullptr )
    {
        if ( !Lower()->IsContentFrame() && !Lower()->IsSctFrame() && !Lower()->IsTabFrame() )
        {
            // OSL_ENSURE(for HTML-import!
            OSL_ENSURE( false, "VAlign to cell without content" );
            return;
        }
        bool bVertDir = true;
        // #i43913# - no vertical alignment, if wrapping
        // style influence is considered on object positioning and
        // an object is anchored inside the cell.
        const bool bConsiderWrapOnObjPos( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) );
        // No alignment if fly with wrap overlaps the cell.
        if ( pPg->GetSortedObjs() )
        {
            SwRect aRect( getFramePrintArea() ); aRect += getFrameArea().Pos();
            for (SwAnchoredObject* pAnchoredObj : *pPg->GetSortedObjs())
            {
                SwRect aTmp( pAnchoredObj->GetObjRect() );
                const SwFrame* pAnch = pAnchoredObj->GetAnchorFrame();
                if ( (bConsiderWrapOnObjPos && IsAnLower( pAnch )) || (!bConsiderWrapOnObjPos && aTmp.Overlaps( aRect )) )
                {
                    const SwFrameFormat* pAnchoredObjFrameFormat = pAnchoredObj->GetFrameFormat();
                    const SwFormatSurround &rSur = pAnchoredObjFrameFormat->GetSurround();
 
                    if ( bConsiderWrapOnObjPos || css::text::WrapTextMode_THROUGH != rSur.GetSurround() )
                    {
                        // frames, which the cell is a lower of, aren't relevant
                        if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
                        {
                            if ( pFly->IsAnLower( this ) )
                                continue;
                        }
 
                        // #i43913#
                        // #i52904# - no vertical alignment,
                        // if object, anchored inside cell, has temporarily
                        // consider its wrapping style on object positioning.
                        // #i58806# - no vertical alignment
                        // if object does not follow the text flow.
                        if ( bConsiderWrapOnObjPos ||
                             !IsAnLower( pAnch ) ||
                             pAnchoredObj->IsTmpConsiderWrapInfluence() ||
                             !pAnchoredObjFrameFormat->GetFollowTextFlow().GetValue() )
                        {
                            bVertDir = false;
                            break;
                        }
                    }
                }
            }
        }
 
        tools::Long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
        if( ( bVertDir && ( nRemaining -= lcl_CalcTopAndBottomMargin( *this, *pAttrs ) ) < nPrtHeight ) ||
            aRectFnSet.GetTop(Lower()->getFrameArea()) != aRectFnSet.GetPrtTop(*this) )
        {
            tools::Long nDiff = aRectFnSet.GetHeight(getFramePrintArea()) - nRemaining;
            if ( nDiff >= 0 )
            {
                tools::Long lTopOfst = 0;
                if ( bVertDir )
                {
                    switch ( rOri.GetVertOrient() )
                    {
                        case text::VertOrientation::CENTER:   lTopOfst = nDiff / 2; break;
                        case text::VertOrientation::BOTTOM:   lTopOfst = nDiff;     break;
                        default: break;
                    }
                }
                tools::Long nTmp = aRectFnSet.YInc(
                                    aRectFnSet.GetPrtTop(*this), lTopOfst );
                if ( lcl_ArrangeLowers( this, nTmp, !bVertDir ) )
                    SetCompletePaint();
            }
        }
    }
    else
    {
        //Was an old alignment taken into account?
        if ( Lower()->IsContentFrame() )
        {
            const tools::Long lYStart = aRectFnSet.GetPrtTop(*this);
            lcl_ArrangeLowers( this, lYStart, true );
        }
    }
 
    // Handle rotated portions of lowers: it's possible that we have changed amount of vertical
    // space since the last format, and this affects how many rotated portions we need. So throw
    // away the current portions to build them using the new line width.
    for (SwFrame* pFrame = Lower(); pFrame; pFrame = pFrame->GetNext())
    {
        if (!pFrame->IsTextFrame())
        {
            continue;
        }
 
        auto pTextFrame = static_cast<SwTextFrame*>(pFrame);
        if (!pTextFrame->GetHasRotatedPortions())
        {
            continue;
        }
 
        pTextFrame->Prepare();
    }
}
 
void SwCellFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
{
    if(rHint.GetId() == SfxHintId::SwTableBoxFormatChanged)
    {
        auto pNewFormatHint = static_cast<const sw::TableBoxFormatChanged*>(&rHint);
        if(GetTabBox() != &pNewFormatHint->m_rTableBox)
            return;
        RegisterToFormat(const_cast<SwTableBoxFormat&>(pNewFormatHint->m_rNewFormat));
        InvalidateSize();
        InvalidatePrt_();
        SetCompletePaint();
        SetDerivedVert(false);
        CheckDirChange();
 
        // #i47489#
        // make sure that the row will be formatted, in order
        // to have the correct Get(Top|Bottom)MarginForLowers values
        // set at the row.
        const SwTabFrame* pTab = FindTabFrame();
        if(pTab && pTab->IsCollapsingBorders())
        {
            SwFrame* pRow = GetUpper();
            pRow->InvalidateSize_();
            pRow->InvalidatePrt_();
        }
    }
    else if(rHint.GetId() == SfxHintId::SwMoveTableBox)
    {
        auto pMoveTableBoxHint = static_cast<const sw::MoveTableBoxHint*>(&rHint);
        if(GetTabBox() != &pMoveTableBoxHint->m_rTableBox)
            return;
        const_cast<SwFrameFormat*>(&pMoveTableBoxHint->m_rNewFormat)->Add(*this);
        InvalidateAll();
        ReinitializeFrameSizeAttrFlags();
        SetDerivedVert(false);
        CheckDirChange();
        return;
    }
    else if (rHint.GetId() == SfxHintId::SwLegacyModify)
    {
        auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
        const SfxPoolItem* pVertOrientItem = nullptr;
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
        const SfxPoolItem* pProtectItem = nullptr;
#endif
        const SfxPoolItem* pFrameDirItem = nullptr;
        const SfxPoolItem* pBoxItem = nullptr;
        const auto nWhich = pLegacy->m_pNew ? pLegacy->m_pNew->Which() : 0;
        switch(nWhich)
        {
            case RES_ATTRSET_CHG:
            {
                auto& rChgSet = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew)->GetChgSet();
                pVertOrientItem = rChgSet.GetItemIfSet(RES_VERT_ORIENT, false);
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
                pProtectItem = rChgSet.GetItemIfSet(RES_PROTECT, false);
#endif
                pFrameDirItem = rChgSet.GetItemIfSet(RES_FRAMEDIR, false);
                pBoxItem = rChgSet.GetItemIfSet(RES_BOX, false);
                break;
            }
            case RES_VERT_ORIENT:
                pVertOrientItem = pLegacy->m_pNew;
                break;
            case RES_PROTECT:
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
                pProtectItem = pLegacy->m_pNew;
#endif
                break;
            case RES_FRAMEDIR:
                pFrameDirItem = pLegacy->m_pNew;
                break;
            case RES_BOX:
                pBoxItem = pLegacy->m_pNew;
                break;
        }
        if(pVertOrientItem)
        {
            bool bInva = true;
            const auto eVertOrient = static_cast<const SwFormatVertOrient*>(pVertOrientItem)->GetVertOrient();
            if(text::VertOrientation::NONE == eVertOrient && Lower() && Lower()->IsContentFrame())
            {
                SwRectFnSet aRectFnSet(this);
                const tools::Long lYStart = aRectFnSet.GetPrtTop(*this);
                bInva = lcl_ArrangeLowers(this, lYStart, false);
            }
            if (bInva)
            {
                SetCompletePaint();
                InvalidatePrt();
            }
        }
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
        if(pProtectItem)
        {
            SwViewShell* pSh = getRootFrame()->GetCurrShell();
            if(pSh && pSh->GetLayout()->IsAnyShellAccessible())
                pSh->Imp()->InvalidateAccessibleEditableState(true, this);
        }
#endif
        if(pFrameDirItem)
        {
            SetDerivedVert(false);
            CheckDirChange();
        }
        // #i29550#
        if(pBoxItem)
        {
            SwFrame* pTmpUpper = GetUpper();
            while(pTmpUpper->GetUpper() && !pTmpUpper->GetUpper()->IsTabFrame())
                pTmpUpper = pTmpUpper->GetUpper();
 
            SwTabFrame* pTabFrame = static_cast<SwTabFrame*>(pTmpUpper->GetUpper());
            if(pTabFrame->IsCollapsingBorders())
            {
                // Invalidate lowers of this and next row:
                lcl_InvalidateAllLowersPrt(static_cast<SwRowFrame*>(pTmpUpper));
                pTmpUpper = pTmpUpper->GetNext();
                if(pTmpUpper)
                    lcl_InvalidateAllLowersPrt(static_cast<SwRowFrame*>(pTmpUpper));
                else
                    pTabFrame->InvalidatePrt();
            }
        }
        SwLayoutFrame::SwClientNotify(rMod, rHint);
    }
}
 
tools::Long SwCellFrame::GetLayoutRowSpan() const
{
    const SwTableBox *pTabBox = GetTabBox();
    tools::Long nRet = pTabBox ? pTabBox->getRowSpan() : 0;
    if ( nRet < 1 )
    {
        const SwFrame* pRow = GetUpper();
        const SwTabFrame* pTab = pRow ? static_cast<const SwTabFrame*>(pRow->GetUpper()) : nullptr;
 
        if ( pTab && pTab->IsFollow() && pRow == pTab->GetFirstNonHeadlineRow() )
            nRet = -nRet;
    }
    return  nRet;
}
 
const SwCellFrame* SwCellFrame::GetCoveredCellInRow(const SwRowFrame& rRow) const
{
    if (GetLayoutRowSpan() <= 1)
    {
        // Not merged vertically.
        return nullptr;
    }
 
    for (const SwFrame* pCell = rRow.GetLower(); pCell; pCell = pCell->GetNext())
    {
        if (!pCell->IsCellFrame())
        {
            continue;
        }
 
        auto pCellFrame = static_cast<const SwCellFrame*>(pCell);
        if (!pCellFrame->IsCoveredCell())
        {
            continue;
        }
 
        if (pCellFrame->getFrameArea().Left() != getFrameArea().Left())
        {
            continue;
        }
 
        if (pCellFrame->getFrameArea().Width() != getFrameArea().Width())
        {
            continue;
        }
 
        // pCellFrame is covered, there are only covered cell frames between "this" and pCellFrame
        // and the horizontal position/size matches "this".
        return pCellFrame;
    }
 
    return nullptr;
}
 
std::vector<const SwCellFrame*> SwCellFrame::GetCoveredCells() const
{
    std::vector<const SwCellFrame*> aRet;
    if (GetLayoutRowSpan() <= 1)
    {
        return aRet;
    }
 
    if (!GetUpper()->IsRowFrame())
    {
        return aRet;
    }
 
    auto pFirstRowFrame = static_cast<const SwRowFrame*>(GetUpper());
    if (!pFirstRowFrame->GetNext())
    {
        return aRet;
    }
 
    if (!pFirstRowFrame->GetNext()->IsRowFrame())
    {
        return aRet;
    }
 
    for (const SwFrame* pRow = pFirstRowFrame->GetNext(); pRow; pRow = pRow->GetNext())
    {
        if (!pRow->IsRowFrame())
        {
            continue;
        }
 
        auto pRowFrame = static_cast<const SwRowFrame*>(pRow);
        const SwCellFrame* pCovered = GetCoveredCellInRow(*pRowFrame);
        if (!pCovered)
        {
            continue;
        }
 
        // Found a cell in a next row that is covered by "this".
        aRet.push_back(pCovered);
    }
 
    return aRet;
}
 
void SwCellFrame::dumpAsXmlAttributes(xmlTextWriterPtr pWriter) const
{
    SwFrame::dumpAsXmlAttributes(pWriter);
    if (SwCellFrame* pFollow = GetFollowCell())
        (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("follow"), "%" SAL_PRIuUINT32, pFollow->GetFrameId());
 
    if (SwCellFrame* pPrevious = GetPreviousCell())
        (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("precede"), "%" SAL_PRIuUINT32, pPrevious->GetFrameId());
}
 
void SwCellFrame::dumpAsXml(xmlTextWriterPtr writer) const
{
    (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("cell"));
    dumpAsXmlAttributes(writer);
    (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "rowspan" ), "%ld", GetLayoutRowSpan() );
 
    (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
    dumpInfosAsXml(writer);
    (void)xmlTextWriterEndElement(writer);
    dumpChildrenAsXml(writer);
 
    (void)xmlTextWriterEndElement(writer);
}
 
// #i103961#
void SwCellFrame::Cut()
{
    // notification for accessibility
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
    {
        SwRootFrame *pRootFrame = getRootFrame();
        if( pRootFrame && pRootFrame->IsAnyShellAccessible() )
        {
            SwViewShell* pVSh = pRootFrame->GetCurrShell();
            if ( pVSh && pVSh->Imp() )
            {
                pVSh->Imp()->DisposeAccessibleFrame( this );
            }
        }
    }
#endif
 
    SwLayoutFrame::Cut();
}
 
// Helper functions for repeated headlines:
 
bool SwTabFrame::IsInHeadline( const SwFrame& rFrame ) const
{
    OSL_ENSURE( IsAnLower( &rFrame ) && rFrame.IsInTab(),
             "SwTabFrame::IsInHeadline called for frame not lower of table" );
 
    const SwFrame* pTmp = &rFrame;
    while ( !pTmp->GetUpper()->IsTabFrame() )
        pTmp = pTmp->GetUpper();
 
    return GetTable()->IsHeadline( *static_cast<const SwRowFrame*>(pTmp)->GetTabLine() );
}
 
/*
 * If this is a master table, we can may assume, that there are at least
 * nRepeat lines in the table.
 * If this is a follow table, there are intermediate states for the table
 * layout, e.g., during deletion of rows, which makes it necessary to find
 * the first non-headline row by evaluating the headline flag at the row frame.
 */
SwRowFrame* SwTabFrame::GetFirstNonHeadlineRow() const
{
    SwRowFrame* pRet = const_cast<SwRowFrame*>(static_cast<const SwRowFrame*>(Lower()));
    if ( pRet )
    {
        if ( IsFollow() )
        {
            while ( pRet && pRet->IsRepeatedHeadline() )
                pRet = static_cast<SwRowFrame*>(pRet->GetNext());
        }
        else
        {
            sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat();
            while ( pRet && nRepeat > 0 )
            {
                pRet = static_cast<SwRowFrame*>(pRet->GetNext());
                --nRepeat;
            }
        }
    }
 
    return pRet;
}
 
bool SwTable::IsHeadline( const SwTableLine& rLine ) const
{
    for ( sal_uInt16 i = 0; i < GetRowsToRepeat(); ++i )
        if ( GetTabLines()[ i ] == &rLine )
            return true;
 
    return false;
}
 
bool SwTabFrame::IsLayoutSplitAllowed() const
{
    return GetFormat()->GetLayoutSplit().GetValue();
}
 
// #i29550#
 
sal_uInt16 SwTabFrame::GetBottomLineSize() const
{
    OSL_ENSURE( IsCollapsingBorders(),
            "BottomLineSize only required for collapsing borders" );
 
    OSL_ENSURE( Lower(), "Warning! Trying to prevent a crash" );
 
    const SwFrame* pTmp = GetLastLower();
 
    // #124755# Try to make code robust
    if ( !pTmp ) return 0;
 
    return static_cast<const SwRowFrame*>(pTmp)->GetBottomLineSize();
}
 
bool SwTabFrame::IsCollapsingBorders() const
{
    return GetFormat()->GetAttrSet().Get( RES_COLLAPSING_BORDERS ).GetValue();
}
 
void SwTabFrame::dumpAsXml(xmlTextWriterPtr writer) const
{
    (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("tab"));
    SwFrame::dumpAsXmlAttributes( writer );
 
    (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("has-follow-flow-line"),
                                      BAD_CAST(OString::boolean(m_bHasFollowFlowLine).getStr()));
 
    if ( HasFollow() )
        (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "follow" ), "%" SAL_PRIuUINT32, GetFollow()->GetFrameId() );
 
    if (m_pPrecede != nullptr)
        (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "precede" ), "%" SAL_PRIuUINT32, static_cast<SwTabFrame*>( m_pPrecede )->GetFrameId() );
 
    (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
    dumpInfosAsXml(writer);
    (void)xmlTextWriterEndElement(writer);
    dumpChildrenAsXml(writer);
 
    (void)xmlTextWriterEndElement(writer);
}
 
/// Local helper function to calculate height of first text row
static SwTwips lcl_CalcHeightOfFirstContentLine( const SwRowFrame& rSourceLine )
{
    // Find corresponding split line in master table
    const SwTabFrame* pTab = rSourceLine.FindTabFrame();
    SwRectFnSet aRectFnSet(pTab);
    const SwCellFrame* pCurrSourceCell = static_cast<const SwCellFrame*>(rSourceLine.Lower());
 
    // 1. Case: rSourceLine is a follow flow line.
    // In this case we have to return the minimum of the heights
    // of the first lines in rSourceLine.
 
    // 2. Case: rSourceLine is not a follow flow line.
    // In this case we have to return the maximum of the heights
    // of the first lines in rSourceLine.
    bool bIsInFollowFlowLine = rSourceLine.IsInFollowFlowRow();
    SwTwips nHeight = bIsInFollowFlowLine ? LONG_MAX : 0;
 
    while ( pCurrSourceCell )
    {
        // NEW TABLES
        // Skip cells which are not responsible for the height of
        // the follow flow line:
        if ( bIsInFollowFlowLine && pCurrSourceCell->GetLayoutRowSpan() > 1 )
        {
            pCurrSourceCell = static_cast<const SwCellFrame*>(pCurrSourceCell->GetNext());
            continue;
        }
 
        const SwFrame *pTmp = pCurrSourceCell->Lower();
        if ( pTmp )
        {
            SwTwips nTmpHeight = std::numeric_limits<SwTwips>::max();
            // #i32456# Consider lower row frames
            if ( pTmp->IsRowFrame() )
            {
                const SwRowFrame* pTmpSourceRow = static_cast<const SwRowFrame*>(pCurrSourceCell->Lower());
                nTmpHeight = lcl_CalcHeightOfFirstContentLine( *pTmpSourceRow );
            }
            else if (pTmp->IsTabFrame() || (pTmp->IsSctFrame() && pTmp->GetLower() && pTmp->GetLower()->IsTabFrame()))
            {
                SwTabFrame const*const pTabFrame(pTmp->IsTabFrame()
                        ? static_cast<SwTabFrame const*>(pTmp)
                        : static_cast<SwTabFrame const*>(pTmp->GetLower()));
                nTmpHeight = pTabFrame->CalcHeightOfFirstContentLine();
            }
            else if (pTmp->IsTextFrame() || (pTmp->IsSctFrame() && pTmp->GetLower() && pTmp->GetLower()->IsTextFrame()))
            {
                // Section frames don't influence the size/position of text
                // frames, so 'text frame' and 'text frame in section frame' is
                // the same case.
                SwTextFrame* pTextFrame = nullptr;
                if (pTmp->IsTextFrame())
                    pTextFrame = const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pTmp));
                else
                    pTextFrame = const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pTmp->GetLower()));
                pTextFrame->GetFormatted();
                nTmpHeight = pTextFrame->FirstLineHeight();
            }
 
            if (std::numeric_limits<SwTwips>::max() != nTmpHeight)
            {
                const SwCellFrame* pPrevCell = pCurrSourceCell->GetPreviousCell();
                if ( pPrevCell )
                {
                    // If we are in a split row, there may be some space
                    // left in the cell frame of the master row.
                    // We look for the minimum of all first line heights;
                    SwTwips nReal = aRectFnSet.GetHeight(pPrevCell->getFramePrintArea());
                    const SwFrame* pFrame = pPrevCell->Lower();
                    const SwFrame* pLast = pFrame;
                    while ( pFrame )
                    {
                        nReal -= aRectFnSet.GetHeight(pFrame->getFrameArea());
                        pLast = pFrame;
                        pFrame = pFrame->GetNext();
                    }
 
                    // #i26831#, #i26520#
                    // The additional lower space of the current last.
                    // #115759# - do *not* consider the
                    // additional lower space for 'master' text frames
                    if ( pLast && pLast->IsFlowFrame() &&
                         ( !pLast->IsTextFrame() ||
                           !static_cast<const SwTextFrame*>(pLast)->GetFollow() ) )
                    {
                        nReal += SwFlowFrame::CastFlowFrame(pLast)->CalcAddLowerSpaceAsLastInTableCell();
                    }
                    // Don't forget the upper space and lower space,
                    // #115759# - do *not* consider the upper
                    // and the lower space for follow text frames.
                    if ( pTmp->IsFlowFrame() &&
                         ( !pTmp->IsTextFrame() ||
                           !static_cast<const SwTextFrame*>(pTmp)->IsFollow() ) )
                    {
                        nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)->CalcUpperSpace( nullptr, pLast);
                        nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)->CalcLowerSpace();
                    }
                    // #115759# - consider additional lower
                    // space of <pTmp>, if contains only one line.
                    // In this case it would be the new last text frame, which
                    // would have no follow and thus would add this space.
                    if ( pTmp->IsTextFrame() &&
                         const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pTmp))
                            ->GetLineCount(TextFrameIndex(COMPLETE_STRING)) == 1)
                    {
                        nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)
                                        ->CalcAddLowerSpaceAsLastInTableCell();
                    }
                    if ( nReal > 0 )
                        nTmpHeight -= nReal;
                }
                else
                {
                    // pFirstRow is not a FollowFlowRow. In this case,
                    // we look for the maximum of all first line heights:
                    SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCurrSourceCell );
                    const SwBorderAttrs &rAttrs = *aAccess.Get();
                    nTmpHeight += rAttrs.CalcTop() + rAttrs.CalcBottom();
                    // #i26250#
                    // Don't forget the upper space and lower space,
                    if ( pTmp->IsFlowFrame() )
                    {
                        nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)->CalcUpperSpace();
                        nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)->CalcLowerSpace();
                    }
                }
            }
 
            if ( bIsInFollowFlowLine )
            {
                // minimum
                if ( nTmpHeight < nHeight )
                    nHeight = nTmpHeight;
            }
            else
            {
                // maximum
                if (nTmpHeight > nHeight && std::numeric_limits<SwTwips>::max() != nTmpHeight)
                    nHeight = nTmpHeight;
            }
        }
 
        pCurrSourceCell = static_cast<const SwCellFrame*>(pCurrSourceCell->GetNext());
    }
 
    return ( LONG_MAX == nHeight ) ? 0 : nHeight;
}
 
/// Function to calculate height of first text row
SwTwips SwTabFrame::CalcHeightOfFirstContentLine() const
{
    SwRectFnSet aRectFnSet(this);
 
    const bool bDontSplit = !IsFollow() && !GetFormat()->GetLayoutSplit().GetValue();
 
    if ( bDontSplit )
    {
        // Table is not allowed to split: Take the whole height, that's all
        return aRectFnSet.GetHeight(getFrameArea());
    }
 
    const SwRowFrame* pFirstRow = GetFirstNonHeadlineRow();
    OSL_ENSURE( !IsFollow() || pFirstRow, "FollowTable without Lower" );
 
    // NEW TABLES
    bool bHasRowSpanLine = pFirstRow && pFirstRow->IsRowSpanLine() && pFirstRow->GetNext();
    if (bHasRowSpanLine)
        pFirstRow = static_cast<const SwRowFrame*>(pFirstRow->GetNext());
    const SwRowFrame* pFirstKeepTogetherRow = pFirstRow;
 
    // Check how many rows want to keep together
    sal_uInt16 nKeepRows = 0;
    if ( GetFormat()->GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::TABLE_ROW_KEEP) )
    {
        while ( pFirstRow && pFirstRow->ShouldRowKeepWithNext() )
        {
            ++nKeepRows;
            pFirstRow = static_cast<const SwRowFrame*>(pFirstRow->GetNext());
        }
    }
 
    SwTwips nTmpHeight;
 
    // For master tables, the height of the headlines + the height of the
    // keeping lines (if any) has to be considered. For follow tables, we
    // only consider the height of the keeping rows without the repeated lines:
    if ( !IsFollow() )
    {
        nKeepRows += GetTable()->GetRowsToRepeat();
        if (bHasRowSpanLine)
            ++nKeepRows;
        nTmpHeight = lcl_GetHeightOfRows(GetLower(), nKeepRows);
    }
    else
    {
        nTmpHeight = lcl_GetHeightOfRows(pFirstKeepTogetherRow, nKeepRows);
    }
 
    // pFirstRow row is the first non-heading row.
    // nTmpHeight is the height of the heading row if we are a follow.
    if ( pFirstRow )
    {
        const bool bSplittable = pFirstRow->IsRowSplitAllowed();
        const SwTwips nFirstLineHeight = aRectFnSet.GetHeight(pFirstRow->getFrameArea());
 
        if ( !bSplittable )
        {
            // pFirstRow is not splittable, but it is still possible that the line height of pFirstRow
            // actually is determined by a lower cell with rowspan = -1. In this case we should not
            // just return the height of the first line. Basically we need to get the height of the
            // line as it would be on the last page. Since this is quite complicated to calculate,
            // we only calculate the height of the first line.
            SwFormatFrameSize const& rFrameSize(pFirstRow->GetAttrSet()->GetFrameSize());
            if ( pFirstRow->GetPrev() &&
                 static_cast<const SwRowFrame*>(pFirstRow->GetPrev())->IsRowSpanLine()
                && rFrameSize.GetHeightSizeType() != SwFrameSize::Fixed)
            {
                // Calculate maximum height of all cells with rowspan = 1:
                SwTwips nMaxHeight = rFrameSize.GetHeightSizeType() == SwFrameSize::Minimum
                    ? rFrameSize.GetHeight()
                    : 0;
                const SwCellFrame* pLower2 = static_cast<const SwCellFrame*>(pFirstRow->Lower());
                while ( pLower2 )
                {
                    if ( 1 == pLower2->GetTabBox()->getRowSpan() )
                    {
                        const SwTwips nCellHeight = lcl_CalcMinCellHeight( pLower2, true );
                        nMaxHeight = std::max( nCellHeight, nMaxHeight );
                    }
                    pLower2 = static_cast<const SwCellFrame*>(pLower2->GetNext());
                }
                nTmpHeight += nMaxHeight;
            }
            else
            {
                nTmpHeight += nFirstLineHeight;
            }
        }
 
        // Optimization: lcl_CalcHeightOfFirstContentLine actually can trigger
        // a formatting of the row frame (via the GetFormatted()). We don't
        // want this formatting if the row does not have a height.
        else if ( 0 != nFirstLineHeight )
        {
            const bool bOldJoinLock = IsJoinLocked();
            const_cast<SwTabFrame*>(this)->LockJoin();
            const SwTwips nHeightOfFirstContentLine = lcl_CalcHeightOfFirstContentLine( *pFirstRow );
 
            // Consider minimum row height:
            const SwFormatFrameSize &rSz = pFirstRow->GetFormat()->GetFrameSize();
 
            SwTwips nMinRowHeight = 0;
            if (rSz.GetHeightSizeType() == SwFrameSize::Minimum)
            {
                nMinRowHeight = std::max(rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*pFirstRow),
                                         tools::Long(0));
            }
 
            nTmpHeight += std::max( nHeightOfFirstContentLine, nMinRowHeight );
 
            if ( !bOldJoinLock )
                const_cast<SwTabFrame*>(this)->UnlockJoin();
        }
    }
 
    return nTmpHeight;
}
 
// Some more functions for covered/covering cells. This way inclusion of
// SwCellFrame can be avoided
 
bool SwFrame::IsLeaveUpperAllowed() const
{
    return false;
}
 
bool SwCellFrame::IsLeaveUpperAllowed() const
{
    return GetLayoutRowSpan() > 1;
}
 
bool SwFrame::IsCoveredCell() const
{
    return false;
}
 
bool SwCellFrame::IsCoveredCell() const
{
    return GetLayoutRowSpan() < 1;
}
 
bool SwFrame::IsInCoveredCell() const
{
    bool bRet = false;
 
    const SwFrame* pThis = this;
    while ( pThis && !pThis->IsCellFrame() )
        pThis = pThis->GetUpper();
 
    if ( pThis )
        bRet = pThis->IsCoveredCell();
 
    return bRet;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V773 Visibility scope of the 'pHeadline' pointer was exited without releasing the memory. A memory leak is possible.

V773 Visibility scope of the 'pHeadline' pointer was exited without releasing the memory. A memory leak is possible.

V1053 Calling the 'GetFormat' virtual function in the constructor may lead to unexpected result at runtime.

V1004 The pointer was used unsafely after it was verified against nullptr. Check lines: 4173, 4176.

V1007 The value from the potentially uninitialized optional 'oDeleteListener' is used. Probably it is a mistake.

V1019 Compound assignment expression is used inside condition.