diff options
Diffstat (limited to 'sw/source/core/layout/tabfrm.cxx')
-rw-r--r-- | sw/source/core/layout/tabfrm.cxx | 6170 |
1 files changed, 6170 insertions, 0 deletions
diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx new file mode 100644 index 000000000..8e3a1b5dc --- /dev/null +++ b/sw/source/core/layout/tabfrm.cxx @@ -0,0 +1,6170 @@ +/* -*- 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 <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_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_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 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; + + 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& 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 ) ); + if ( pAnchoredObj->GetFrameFormat().GetAnchor().GetAnchorId() + == RndStdIds::FLY_AS_CHAR ) + { + pAnchoredObj->AnchorFrame() + ->Prepare( PrepareHint::FlyFrameAttributesChanged, + &(pAnchoredObj->GetFrameFormat()) ); + } + 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 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()); + + // 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())); + // 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 (nDistanceToUpperPrtBottom + nFollowFootnotes < 0 || !rTab.DoesObjsFit()) + bRet = false; + + // 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 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 + + SwTwips nRemainingSpaceForLastRow = + aRectFnSet.YDiff(nCutPos, aRectFnSet.GetTop(getFrameArea())); + nRemainingSpaceForLastRow -= aRectFnSet.GetTopMargin(*this); + + // Make pRow point to the line that does not fit anymore: + while( pRow->GetNext() && + nRemainingSpaceForLastRow >= ( aRectFnSet.GetHeight(pRow->getFrameArea()) + + (IsCollapsingBorders() ? + pRow->GetBottomLineSize() : + 0 ) ) ) + { + if( bTryToSplit || !pRow->IsRowSpanLine() || + 0 != aRectFnSet.GetHeight(pRow->getFrameArea()) ) + ++nRowCount; + nRemainingSpaceForLastRow -= aRectFnSet.GetHeight(pRow->getFrameArea()); + 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 = true; + if (!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 + if ( pRow->getFrameArea().Height() > FindPageFrame()->getFramePrintArea().Height() ) + 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 ( lcl_FindSectionsInRow( *pRow ) ) + { + bTryToSplit = 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() ) + { + 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 ( bTryToSplit && 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 && bTryToSplit && + ( !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 ) + { + 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 SwFrameFormats *pTable = GetFormat()->GetDoc()->GetSpzFrameFormats(); + if( !pTable->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( pTable, 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 ); + } + + SwTwips nShrink = 0; + + //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(); + nShrink += aRectFnSet.GetHeight(pRow->getFrameArea()); + // 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(); + nShrink += aRectFnSet.GetHeight(pRow->getFrameArea()); + + // 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, nRemainingSpaceForLastRow, nShrink ); + + // 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; + } +} + +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. + + while ( pRow ) + { + pNxt = pRow->GetNext(); + nHeight += aRectFnSet.GetHeight(pRow->getFrameArea()); + 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 ); +} + +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 + 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; + } + +#if OSL_DEBUG_LEVEL > 1 + OSL_FAIL( "LoopControl in SwContentFrame::CalcLowers" ); +#endif + } + } + 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 ) + { + // #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; + } +} + +// 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(); + + // All rows should keep together + const bool bDontSplit = !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. + const bool bTableRowKeep = !bDontSplit && GetFormat()->GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::TABLE_ROW_KEEP); + + // 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() ) + { + 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; + } + } + + int nUnSplitted = 5; // Just another loop control :-( + int nThrowAwayValidLayoutLimit = 5; // And 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 ) ); + } + + 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 ) ); + } + + 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 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 ( 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) + { + // 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) + { + lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), 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(); + 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()); + if( IsInSct() || GetUpper()->IsInTab() ) // TABLE IN TABLE) + 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(); + 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 ) ); + + // 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()); + RemoveFollowFlowLine(); + const bool bFinalLoopEndCondition(isFrameAreaDefinitionValid()); + + if (bInitialLoopEndCondition && !bFinalLoopEndCondition) + { + --nThrowAwayValidLayoutLimit; + } + } + + oAccess.reset(); + const bool bSplitError = !Split( nDeadLine, bTryToSplit, ( bTableRowKeep && !(bAllowSplitOfRow || bEmulateTableKeepSplitAllowed) ) ); + + // 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() ) + 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. + if ( bSplitError && bTryToSplit ) // no restart if we did not try to split: i72847, i79426 + { + lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), LONG_MAX); + setFrameAreaPositionValid(false); + bTryToSplit = false; + continue; + } + + 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) + { + // 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; + } + + // #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(); + } + + 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 ( 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 +{ + 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 ( pPage->GetSortedObjs() && bWrapAllowed ) + { + 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]; + if ( auto pFly = pAnchoredObj->DynCastFlyFrame() ) + { + 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(); + bool bConsiderFly = + // #i46807# - do not consider invalid + // Writer fly frames. + (pFly->isFrameAreaDefinitionValid() || bAddVerticalFlyOffsets) && + // 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 ) + { + 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() ) ) + bConsiderFly = false; + } + + if ( bConsiderFly ) + { + const SwFormatSurround &rSur = pFly->GetFormat()->GetSurround(); + const SwFormatHoriOrient &rHori= pFly->GetFormat()->GetHoriOrient(); + bool bShiftDown = css::text::WrapTextMode_NONE == rSur.GetSurround(); + if (!bShiftDown && bAddVerticalFlyOffsets) + { + if (rSur.GetSurround() == text::WrapTextMode_PARALLEL + && rHori.GetHoriOrient() == text::HoriOrientation::NONE) + { + // 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. + const SwRect aFlyRectWithoutSpaces = pFly->GetObjRect(); + 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 (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; + } + } + if ( (css::text::WrapTextMode_RIGHT == rSur.GetSurround() || + css::text::WrapTextMode_PARALLEL == rSur.GetSurround())&& + text::HoriOrientation::LEFT == rHori.GetHoriOrient() ) + { + 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 == rSur.GetSurround() || + css::text::WrapTextMode_PARALLEL == rSur.GetSurround())&& + text::HoriOrientation::RIGHT == rHori.GetHoriOrient() ) + { + 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, bool bTst, bool bInfo ) +{ + SwRectFnSet aRectFnSet(this); + SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea()); + if( nHeight > 0 && nDist > ( LONG_MAX - nHeight ) ) + nDist = LONG_MAX - nHeight; + + 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()); + SwFrame *pFrame = GetUpper()->Lower(); + while ( pFrame && GetFollow() != pFrame ) + { + nReal -= aRectFnSet.GetHeight(pFrame->getFrameArea()); + pFrame = pFrame->GetNext(); + } + + if ( nReal < nDist ) + { + tools::Long nTmp = GetUpper()->Grow( nDist - std::max<tools::Long>(nReal, 0), 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::SwClientNotify(const SwModify& rMod, const SfxHint& rHint) +{ + 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); + + 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::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_TBLHEADLINECHG: + if ( IsFollow() ) + { + // 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 ); + } + } + rInvFlags |= SwTabFrameInvFlags::InvalidatePrt; + break; + + 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 ); + SwDocPosUpdate aMsgHint( pPage->getFrameArea().Top() ); + GetFormat()->GetDoc()->getIDocumentFieldsAccess().UpdatePageFields( &aMsgHint ); + } + } + 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)); + } +} + +bool SwTabFrame::GetInfo( SfxPoolItem &rHint ) const +{ + if ( RES_VIRTPAGENUM_INFO == rHint.Which() && IsInDocBody() && !IsFollow() ) + { + SwVirtPageNumInfo &rInfo = static_cast<SwVirtPageNumInfo&>(rHint); + const SwPageFrame *pPage = FindPageFrame(); + if ( pPage ) + { + if ( pPage == rInfo.GetOrigPage() && !GetPrev() ) + { + // Should be the one (can temporarily be different, should we be + // concerned about this possibility?) + rInfo.SetInfo( pPage, this ); + return false; + } + if ( pPage->GetPhyPageNum() < rInfo.GetOrigPage()->GetPhyPageNum() && + (!rInfo.GetPage() || pPage->GetPhyPageNum() > rInfo.GetPage()->GetPhyPageNum())) + { + //This could be the one. + rInfo.SetInfo( pPage, this ); + } + } + } + return true; +} + +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" ); + const SwFrame* pRow = pRet->GetUpper(); + while ( pRow && !pRow->GetUpper()->IsTabFrame() ) + pRow = pRow->GetUpper(); + const SwContentFrame* pContentFrame = pRow ? static_cast<const SwLayoutFrame*>(pRow)->ContainsContent() : nullptr; + pRet = nullptr; + + while ( pContentFrame && static_cast<const SwLayoutFrame*>(pRow)->IsAnLower( pContentFrame ) ) + { + pRet = const_cast<SwContentFrame*>(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 if (!m_bLockBackMove) + bMoveAnyway = 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 (!m_bLockBackMove && 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. + return nTmpHeight <= nSpace; + } + } + 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; + // #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_(); + } + } + // 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; + for ( size_t i = 0; i < rBoxes.size(); ++i ) + { + 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(auto pNewFormatHint = dynamic_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(auto pMoveTableLineHint = dynamic_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); +} + +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& rFrameFormat = pAnchoredObj->GetFrameFormat(); + bool bFollowTextFlow = rFrameFormat.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 = + (rFrameFormat.GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) && + bIsFarAway && + bFollowTextFlow && bIsAnchoredToTmpFrm; + bool bWrapThrough = rFrameFormat.GetSurround().GetValue() == text::WrapTextMode_THROUGH; + bool bInBackground = !rFrameFormat.GetOpaque().GetValue(); + if (pFrame->IsInTab() && bFollowTextFlow && bWrapThrough && bInBackground) + { + // 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 = rFrameFormat.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 ) +{ + SwTwips nHeight = 0; + if ( !_pRow->IsRowSpanLine() ) + { + const SwFormatFrameSize &rSz = _pRow->GetFormat()->GetFrameSize(); + if ( _pRow->HasFixSize() ) + { + 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 ) + { + nHeight = rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*_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 = const_cast<SwCellFrame*>(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 = const_cast<SwCellFrame*>(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 = const_cast<SwCellFrame*>(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 = const_cast<SwCellFrame*>(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; + + 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 + SwTabFrame* pTabFrame = FindTabFrame(); + 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 ) + { + 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, bool bTst, bool bInfo ) +{ + 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, 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(); + } + + 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 = static_cast<SwTableLineFormat*>(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 ( rBox.GetSttIdx() ) + { + 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(); + if ( pPageFrame ) + pPageFrame->MoveFly( pFly, pPageOfAnchor ); + else + pPageOfAnchor->AppendFlyToPage( pFly ); + } + } + // 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(); + if ( pPageFrame ) + { + pPageFrame->RemoveDrawObjFromPage( *pAnchoredObj ); + } + pPageOfAnchor->AppendDrawObjToPage( *pAnchoredObj ); + } + } + // #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& rAnchoredObjFrameFormat = pAnchoredObj->GetFrameFormat(); + const SwFormatSurround &rSur = rAnchoredObjFrameFormat.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() || + !rAnchoredObjFrameFormat.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(auto pNewFormatHint = dynamic_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(auto pMoveTableBoxHint = dynamic_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; + const SfxPoolItem* pProtectItem = nullptr; + 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); + pProtectItem = rChgSet.GetItemIfSet(RES_PROTECT, false); + 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: + pProtectItem = pLegacy->m_pNew; + 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()); +} + +// #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(); +} + +/// 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 = USHRT_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 ( USHRT_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 && USHRT_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()); + } + + SwTwips nTmpHeight = 0; + + const SwRowFrame* pFirstRow = GetFirstNonHeadlineRow(); + OSL_ENSURE( !IsFollow() || pFirstRow, "FollowTable without Lower" ); + + // NEW TABLES + if ( pFirstRow && pFirstRow->IsRowSpanLine() && pFirstRow->GetNext() ) + pFirstRow = static_cast<const SwRowFrame*>(pFirstRow->GetNext()); + + // Calculate the height of the headlines: + const sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat(); + SwTwips nRepeatHeight = nRepeat ? lcl_GetHeightOfRows( GetLower(), nRepeat ) : 0; + + // Calculate the height of the keeping lines + // (headlines + following keeping lines): + SwTwips nKeepHeight = nRepeatHeight; + if ( GetFormat()->GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::TABLE_ROW_KEEP) ) + { + sal_uInt16 nKeepRows = nRepeat; + + // Check how many rows want to keep together + while ( pFirstRow && pFirstRow->ShouldRowKeepWithNext() ) + { + ++nKeepRows; + pFirstRow = static_cast<const SwRowFrame*>(pFirstRow->GetNext()); + } + + if ( nKeepRows > nRepeat ) + nKeepHeight = lcl_GetHeightOfRows( GetLower(), nKeepRows ); + } + + // 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() ) + { + nTmpHeight = nKeepHeight; + } + else + { + nTmpHeight = nKeepHeight - nRepeatHeight; + } + + // 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: */ |