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

Quelle  tabfrm.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <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);
    }
    SwFrame* pLower = Lower();
    OSL_ENSURE( pLower && pLower->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()
{
    SwFrame* pLower = Lower();
    OSL_ENSURE( pLower && pLower->IsRowFrame(), "No rows." );

    SwPageFrame *pPage = FindPageFrame();
    if ( pPage )
    {
        SwRowFrame *pRow = static_cast<SwRowFrame*>(pLower);
        do
        {
            pRow->RegistFlys( pPage );
            pRow = static_cast<SwRowFrame*>(pRow->GetNext());
        } while ( pRow );
    }
}

static void InvalidateVertOrientCells(SwRowFrame & rRow)
{
    for (SwFrame * pLower{rRow.Lower()}; pLower != nullptr; pLower = pLower->GetNext())
    {
        SwCellFrame * pCell{static_cast<SwCellFrame *>(pLower)};
        if (pCell->GetLayoutRowSpan() != 1)
        {
            pCell = const_cast<SwCellFrame *>(&pCell->FindStartEndOfRowSpanCell(true));
        }
        if (pCell->GetFormat()->GetVertOrient().GetVertOrient() != text::VertOrientation::NONE)
        {   // tdf#159029 force repositioning of content in cell because it
            pCell->InvalidatePrt(); // was disabled by SetRebuildLastLine()
        }
    }
}

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 sal_uInt16 lcl_GetBottomLineDist(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;

    // There are moments when some rows have correct size, but wrong position: e.g., when a merged
    // cell is split, the 0-height follow of the previous row is inserted, and arrives here with a
    // correct height, but positioned at [0, 0]; the following rows have correct position (offset
    // to the new page's starting Y coordinate). This disallows to simplify the height calculation
    // to just difference between top row's top and bottom row's bottom.
    SwTwips nRet = 0;
    SwRectFnSet aRectFnSet(pStart);
    while (pStart && nCount > 0)
    {
        nRet += aRectFnSet.GetHeight(pStart->getFrameArea());
        pStart = pStart->GetNext();
        --nCount;
    }

    return nRet;
}

// Local helper function to insert a new follow flow line
static SwRowFrame* lcl_InsertNewFollowFlowLine( SwTabFrame& rTab, const SwFrame&&nbsp;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,
                " - 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 )
    {
        assert(pCurrDestCell);
        SwFrame* pLower = pCurrSourceCell->Lower();
        if ( pLower && pLower->IsRowFrame() )
        {
            SwRowFrame* pTmpSourceRow = static_cast<SwRowFrame*>(pLower);
            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 );
                }

                // RemoveFromLayout invalidates Lower() so it must be refetched
                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 )
                {
                    SwFrame* pCellLower = static_cast<SwCellFrame*>(pCell)->Lower();
                    if ( pCellLower && pCellLower->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 )
    {
        SwFrame* pLower = pCurrMasterCell->Lower();
        if ( pLower && pLower->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 );
        InvalidateVertOrientCells(rLastLine);
    }
    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(), *thisstatic_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);

    if (nRemainingSpaceForLastRow <= 0)
        return false// upper has no space for this table at all

    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(const 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;
                }
            }
        }
        const SwFrame* pLower = rCell.Lower();
        assert(pLower);
        if (rTab.IsCollapsingBorders() && pLower && !pLower->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_();
--> --------------------

--> maximum size reached

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

Messung V0.5
C=90 H=94 G=91

¤ Dauer der Verarbeitung: 0.10 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.