/* -*- 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 .
*/
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);
// There are moments when some rows have correct size, but wrong position: e.g., when a merged // cell is split, the 0-height follow of the previous row is inserted, and arrives here with a // correct height, but positioned at [0, 0]; the following rows have correct position (offset // to the new page's starting Y coordinate). This disallows to simplify the height calculation // to just difference between top row's top and bottom row's bottom.
SwTwips nRet = 0;
SwRectFnSet aRectFnSet(pStart); while (pStart && nCount > 0)
{
nRet += aRectFnSet.GetHeight(pStart->getFrameArea());
pStart = pStart->GetNext();
--nCount;
}
return nRet;
}
// Local helper function to insert a new follow flow line static SwRowFrame* lcl_InsertNewFollowFlowLine( SwTabFrame& rTab, const SwFrame& rTmpRow, bool bRowSpanLine )
{
OSL_ENSURE( rTmpRow.IsRowFrame(), "No row frame to copy for FollowFlowLine" ); const SwRowFrame& rRow = static_cast<const SwRowFrame&>(rTmpRow);
// #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'. staticvoid lcl_InvalidateLowerObjs( SwLayoutFrame& _rLayoutFrame, constbool _bMoveObjsOutOfRange = false,
SwPageFrame* _pPageFrame = nullptr )
{ // determine page frame, if needed if ( !_pPageFrame )
{
_pPageFrame = _rLayoutFrame.FindPageFrame();
OSL_ENSURE( _pPageFrame, " - missing page frame -> no move of lower objects out of range" ); if ( !_pPageFrame )
{ return;
}
}
// loop on lower frames
SwFrame* pLowerFrame = _rLayoutFrame.Lower(); while ( pLowerFrame )
{ if ( pLowerFrame->IsLayoutFrame() )
{
::lcl_InvalidateLowerObjs( *static_cast<SwLayoutFrame*>(pLowerFrame),
_bMoveObjsOutOfRange, _pPageFrame );
} if ( pLowerFrame->GetDrawObjs() )
{ for (size_t i = 0, nCount = pLowerFrame->GetDrawObjs()->size(); i < nCount; ++i)
{
SwAnchoredObject* pAnchoredObj = (*pLowerFrame->GetDrawObjs())[i];
// invalidate position of anchored object
pAnchoredObj->SetTmpConsiderWrapInfluence( false );
pAnchoredObj->SetConsiderForTextWrap( false );
pAnchoredObj->UnlockPosition();
pAnchoredObj->InvalidateObjPos();
// move anchored object 'out of range' if ( _bMoveObjsOutOfRange )
{ // indicate, that positioning is progress to avoid // modification of the anchored object resp. it's attributes // due to the movement
SwObjPositioningInProgress aObjPosInProgress( *pAnchoredObj );
pAnchoredObj->SetObjLeft( _pPageFrame->getFrameArea().Right() ); // #115759# - reset character rectangle, // top of line and relative position in order to assure, // that anchored object is correctly positioned.
pAnchoredObj->ClearCharRectAndTopOfLine();
pAnchoredObj->SetCurrRelPos( Point( 0, 0 ) ); const SwFrameFormat* pObjFormat = pAnchoredObj->GetFrameFormat(); if (pObjFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR)
{
pAnchoredObj->AnchorFrame()
->Prepare( PrepareHint::FlyFrameAttributesChanged,
pObjFormat );
} if ( pFly != nullptr )
{
pFly->GetVirtDrawObj()->SetBoundAndSnapRectsDirty();
pFly->GetVirtDrawObj()->SetChanged();
}
}
// If anchored object is a fly frame, invalidate its lower objects if ( pFly != nullptr )
{
::lcl_InvalidateLowerObjs( *pFly, _bMoveObjsOutOfRange, _pPageFrame );
}
}
}
pLowerFrame = pLowerFrame->GetNext();
}
}
// Local helper function to shrink all lowers of pRow to 0 height staticvoid lcl_ShrinkCellsAndAllContent( SwRowFrame& rRow )
{
SwCellFrame* pCurrMasterCell = static_cast<SwCellFrame*>(rRow.Lower());
SwRectFnSet aRectFnSet(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;
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);
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);
if (bAllCellsCollapsed)
{ // All cells have 0 height -> set height of row as well.
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(rRow);
aRectFnSet.SetHeight(aFrm, 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. staticvoid lcl_MoveRowContent( SwRowFrame& rSourceLine, SwRowFrame& rDestLine )
{
SwCellFrame* pCurrDestCell = static_cast<SwCellFrame*>(rDestLine.Lower());
SwCellFrame* pCurrSourceCell = static_cast<SwCellFrame*>(rSourceLine.Lower());
// Move content of follow cells into master cells while ( pCurrSourceCell )
{
assert(pCurrDestCell);
SwFrame* pLower = pCurrSourceCell->Lower(); if ( pLower && pLower->IsRowFrame() )
{
SwRowFrame* pTmpSourceRow = static_cast<SwRowFrame*>(pLower); while ( pTmpSourceRow )
{ // #125926# Attention! It is possible, // that pTmpSourceRow->IsFollowFlowRow() but pTmpDestRow // cannot be found. In this case, we have to move the complete // row.
SwRowFrame* pTmpDestRow = static_cast<SwRowFrame*>(pCurrDestCell->Lower());
if ( pTmpSourceRow->IsFollowFlowRow() && pTmpDestRow )
{ // move content from follow flow row to pTmpDestRow: while ( pTmpDestRow->GetNext() )
pTmpDestRow = static_cast<SwRowFrame*>(pTmpDestRow->GetNext());
// Local helper function to move all footnotes in rRowFrame from // the footnote boss of rSource to the footnote boss of rDest. staticvoid 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 staticvoid 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());
// 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);
}
// 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;
}
}
SwTwips nFootnoteHeight(0); if (SwFootnoteBossFrame const*const pBoss = rTab.FindFootnoteBossFrame())
{ if (SwFootnoteContFrame const*const pCont = pBoss->FindFootnoteCont())
{ for (SwFootnoteFrame const* pFootnote = static_cast<SwFootnoteFrame const*>(pCont->Lower());
pFootnote != nullptr;
pFootnote = static_cast<SwFootnoteFrame const*>(pFootnote->GetNext()))
{
SwContentFrame const*const pAnchor = pFootnote->GetRef();
SwTabFrame const* pTab = pAnchor->FindTabFrame(); if (pTab)
{ while (pTab->GetUpper()->IsInTab())
{
pTab = pTab->GetUpper()->FindTabFrame();
} // TODO currently do this only for top-level tables? // otherwise would need to check rTab's follow and any upper table's follow? if (pTab == &rTab)
{
nFootnoteHeight += aRectFnSet.GetHeight(pFootnote->getFrameArea());
}
}
}
}
}
// If there are nested cells in rLastLine, the recalculation of the last // line needs some preprocessing.
lcl_PreprocessRowsInCells( rTab, rLastLine, rFollowLine, nRemainingSpaceForLastRow );
// Here the recalculation process starts:
rTab.SetRebuildLastLine( true ); // #i26945#
rTab.SetDoesObjsFit( true );
// #i26945# - invalidate and move floating screen // objects 'out of range'
::lcl_InvalidateLowerObjs( rLastLine, true );
// manipulate row and cell sizes
// #i26945# - Do *not* consider floating screen objects // for the minimal cell height.
rTab.SetConsiderObjsForMinCellHeight( false );
::lcl_ShrinkCellsAndAllContent( rLastLine );
rTab.SetConsiderObjsForMinCellHeight( true );
// invalidate last line
::SwInvalidateAll( &rLastLine, LONG_MAX );
// Shrink the table to account for the shrunk last row, as well as lower rows // that had been moved to follow table in SwTabFrame::Split. // It will grow later when last line will recalc its height.
rTab.Shrink(nAlreadyFree + nCurLastLineHeight - nRemainingSpaceForLastRow + 1);
// Do the recalculation
lcl_RecalcRow( rLastLine, LONG_MAX ); // #115759# - force a format of the last line in order to // get the correct height.
rLastLine.InvalidateSize();
rLastLine.Calc(pRenderContext);
rLastLine.SetInSplit(bInSplit);
// Unlock this tab frame and its follow if ( pFollow )
::TableSplitRecalcUnlock( pFollow ); if ( bUnlockMaster )
::TableSplitRecalcUnlock( pMaster );
// If there are nested cells in rLastLine, the recalculation of the last // line needs some postprocessing.
lcl_PostprocessRowsInCells( rTab, rLastLine );
// Do a couple of checks on the current situation.
// If we are not happy with the current situation we return false. // This will start a new try to split the table, this time we do not // try to split the table rows.
// 1. Check if table fits to its upper. // #i26945# - include check, if objects fit const SwTwips nDistanceToUpperPrtBottom =
aRectFnSet.BottomDist(rTab.getFrameArea(), aRectFnSet.GetPrtBottom(*rTab.GetUpper())); // also check the footnote boss - it *may* be smaller than the upper now! const SwTwips nDistanceToFootnoteBodyPrtBottom =
aRectFnSet.BottomDist(rTab.getFrameArea(), aRectFnSet.GetPrtBottom(*rTab.FindFootnoteBossFrame()->FindBodyCont())); // tdf#125685 ignore footnotes that are anchored in follow-table of this // table - if split is successful they move to the next page/column anyway
assert(rTab.GetFollow() == rFollowLine.GetUpper());
SwTwips nFollowFootnotes(0); // actually there should always be a boss frame, except if "this" isn't // connected to a page yet; not sure if that can happen if (SwFootnoteBossFrame const*const pBoss = rTab.FindFootnoteBossFrame())
{ if (SwFootnoteContFrame const*const pCont = pBoss->FindFootnoteCont())
{ for (SwFootnoteFrame const* pFootnote = static_cast<SwFootnoteFrame const*>(pCont->Lower());
pFootnote != nullptr;
pFootnote = static_cast<SwFootnoteFrame const*>(pFootnote->GetNext()))
{
SwContentFrame const*const pAnchor = pFootnote->GetRef();
SwTabFrame const* pTab = pAnchor->FindTabFrame(); if (pTab)
{ while (pTab->GetUpper()->IsInTab())
{
pTab = pTab->GetUpper()->FindTabFrame();
} // TODO currently do this only for top-level tables? // otherwise would need to check rTab's follow and any upper table's follow? if (pTab == rTab.GetFollow())
{
nFollowFootnotes += aRectFnSet.GetHeight(pFootnote->getFrameArea());
} if (pTab == &rTab)
{
nFootnoteHeight -= aRectFnSet.GetHeight(pFootnote->getFrameArea());
}
}
} if (nFootnoteHeight < 0)
{ // tdf#156724 footnotes have grown, try to split again
rIsFootnoteGrowth = true;
}
}
} if (nDistanceToUpperPrtBottom + nFollowFootnotes < 0 || !rTab.DoesObjsFit())
bRet = false;
// apparently checking nFootnoteHeight here does *not* guarantee that it fits into the body if (bRet && rTab.IsInDocBody()
&& nDistanceToFootnoteBodyPrtBottom + nFollowFootnotes < 0)
{
assert(rTab.GetUpper() != rTab.FindFootnoteBossFrame()->FindBodyCont());
SAL_INFO("sw.layout", "SwTabFrame Split failed because of footnote growth");
bRet = false; // tdf#160897
}
// 2. Check if each cell in the last line has at least one content frame.
// Note: a FollowFlowRow may contains empty cells! if ( bRet )
{ if ( !rLastLine.IsInFollowFlowRow() )
{
SwCellFrame* pCurrMasterCell = static_cast<SwCellFrame*>(rLastLine.Lower()); while ( pCurrMasterCell )
{ if ( !pCurrMasterCell->ContainsContent() && pCurrMasterCell->GetTabBox()->getRowSpan() >= 1 )
{
bRet = false; break;
}
pCurrMasterCell = static_cast<SwCellFrame*>(pCurrMasterCell->GetNext());
}
}
}
// 3. Check if last line does not contain any content: if ( bRet )
{ if ( !rLastLine.ContainsContent() )
{
bRet = false;
}
}
// 4. Check if follow flow line does not contain content: if ( bRet )
{ if ( !rFollowLine.IsRowSpanLine() && !rFollowLine.ContainsContent() )
{
bRet = false;
}
}
if ( bRet )
{ // Everything looks fine. Splitting seems to be successful. We invalidate // rFollowLine to force a new formatting.
::SwInvalidateAll( &rFollowLine, LONG_MAX );
InvalidateVertOrientCells(rLastLine);
} else
{ // Splitting the table row gave us an unexpected result. // Everything has to be prepared for a second try to split // the table, this time without splitting the row.
::SwInvalidateAll( &rLastLine, LONG_MAX );
}
// 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;
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 ) returntrue; if (pFollowFlowLine->IsDeleteForbidden())
{
SAL_WARN("sw.layout", "Cannot remove in-use Follow Flow Line"); returnfalse;
}
// 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 );
// 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 );
// #i26945# - Floating screen objects are no longer searched. staticbool lcl_FindSectionsInRow( const SwRowFrame& rRow )
{ bool bRet = false; const SwCellFrame* pLower = static_cast<const SwCellFrame*>(rRow.Lower()); while ( pLower )
{ if ( pLower->IsVertical() != rRow.IsVertical() ) returntrue;
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 ) returntrue;
pTmpFrame = pTmpFrame->GetNext();
}
// #i26745# - format row and cell frames of table
{
Lower()->InvalidatePos_(); // #i43913# - correction // call method <lcl_InnerCalcLayout> with first lower.
lcl_InnerCalcLayout( Lower(), LONG_MAX, true );
}
//In order to be able to compare the positions of the cells with CutPos, //they have to be calculated consecutively starting from the table. //They can definitely be invalid because of position changes of the table.
SwRowFrame *pRow = static_cast<SwRowFrame*>(Lower()); if( !pRow ) return bRet;
const sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat();
sal_uInt16 nRowCount = 0; // pRow currently points to the first row
// Due to the comment above, about possibly invalid positions of cells due to position changes // of the table, calculate the rows height subtracting last row's bottom from first row's top, // and compare with the available height
SwTwips nRemainingSpaceForLastRow = aRectFnSet.YDiff(nCutPos, aRectFnSet.GetPrtTop(*this));
nRemainingSpaceForLastRow -= aRectFnSet.GetBottomMargin(*this);
if (nRemainingSpaceForLastRow <= 0) returnfalse; // upper has no space for this table at all
// Make pRow point to the line that does not fit anymore: while (pRow->GetNext())
{
SwTwips nNewRemaining = getRemainingAfter(pRow); if (nNewRemaining < 0) break; if( bTryToSplit || !pRow->IsRowSpanLine() ||
0 != aRectFnSet.GetHeight(pRow->getFrameArea()) )
++nRowCount;
nRemainingSpaceForLastRow = nNewRemaining;
pRow = static_cast<SwRowFrame*>(pRow->GetNext());
}
// bSplitRowAllowed: Row may be split according to its attributes. // bTryToSplit: Row will never be split if bTryToSplit = false. // This can either be passed as a parameter, indicating // that we are currently doing the second try to split the // table, or it will be set to false under certain // conditions that are not suitable for splitting // the row. bool bSplitRowAllowed = bTryToSplit; if (bSplitRowAllowed && !pRow->IsRowSplitAllowed())
{ // A row larger than the entire page ought to be allowed to split regardless of setting, // otherwise it has hidden content and that makes no sense
tools::Long nMaxHeight = FindPageFrame()->getFramePrintArea().Height(); for (auto pBody = FindBodyFrame(); pBody; pBody = pBody->GetUpper()->FindBodyFrame())
{ if (pBody->IsPageBodyFrame())
nMaxHeight = pBody->getFramePrintArea().Height();
} if (pRow->getFrameArea().Height() > nMaxHeight)
pRow->SetForceRowSplitAllowed( true ); else
bSplitRowAllowed = false;
} // #i29438# // #i26945# - Floating screen objects no longer forbid // a splitting of the table row. // Special DoNotSplit case 1: // Search for sections inside pRow: if (bSplitRowAllowed && lcl_FindSectionsInRow(*pRow))
{
bSplitRowAllowed = false;
}
SwFlyFrame* pFly = FindFlyFrame(); if (bSplitRowAllowed && pFly && pFly->IsFlySplitAllowed())
{ // The remaining size is less than the minimum row height, then don't even try to split the // row, just move it forward. const SwFormatFrameSize& rRowSize = pRow->GetFormat()->GetFrameSize(); if (rRowSize.GetHeightSizeType() == SwFrameSize::Minimum)
{
SwTwips nMinHeight = rRowSize.GetHeight(); if (nMinHeight > nRemainingSpaceForLastRow)
{
bSplitRowAllowed = false;
if (!pRow->GetPrev() && aRectFnSet.GetHeight(pRow->getFrameArea()) > nRemainingSpaceForLastRow)
{ // Split of pRow is not allowed, no previous row, the current row doesn't fit: // that's a failure, we'll have to move forward instead. returnfalse;
}
}
}
}
// #i29771# // To avoid loops, we do some checks before actually trying to split // the row. Maybe we should keep the next row in this table. // Note: This is only done if we are at the beginning of our upper bool bKeepNextRow = false; if ( nRowCount < nRepeat )
{ // First case: One of the repeated headline does not fit to the page anymore. // tdf#88496 Disable repeated headline (like for #i44910#) to avoid loops and // to fix interoperability problems (very long tables only with headline) // tdf#150149 except in multi-column sections, where it's possible to enlarge // the height of the section frame instead of using this fallback
OSL_ENSURE( !GetIndPrev(), "Table is supposed to be at beginning" ); if ( !IsInSct() )
{ // This would mean the layout modifies the doc model, so RowsToRepeat drops to 0 while // there are existing row frames with RepeatedHeadline == true. Avoid this at least // inside split flys, it would lead to a crash in SwTabFrame::MakeAll(). if (!pFly || !pFly->IsFlySplitAllowed())
{
m_pTable->SetRowsToRepeat(0);
} returnfalse;
} else
bKeepNextRow = true;
} elseif ( !GetIndPrev() && nRepeat == nRowCount )
{ // Second case: The first non-headline row does not fit to the page. // If it is not allowed to be split, or it contains a sub-row that // is not allowed to be split, we keep the row in this table: if (bSplitRowAllowed)
{ // Check if there are (first) rows inside this row, // which are not allowed to be split.
SwCellFrame* pLowerCell = static_cast<SwCellFrame*>(pRow->Lower()); while ( pLowerCell )
{ if ( pLowerCell->Lower() && pLowerCell->Lower()->IsRowFrame() )
{ const SwRowFrame* pLowerRow = static_cast<SwRowFrame*>(pLowerCell->Lower()); if ( !pLowerRow->IsRowSplitAllowed() &&
aRectFnSet.GetHeight(pLowerRow->getFrameArea()) > nRemainingSpaceForLastRow )
{
bKeepNextRow = true; break;
}
}
pLowerCell = static_cast<SwCellFrame*>(pLowerCell->GetNext());
}
} else
bKeepNextRow = true;
}
// Better keep the next row in this table: if ( bKeepNextRow )
{
pRow = GetFirstNonHeadlineRow(); if ( pRow && pRow->IsRowSpanLine() && 0 == aRectFnSet.GetHeight(pRow->getFrameArea()) )
pRow = static_cast<SwRowFrame*>(pRow->GetNext()); if ( pRow )
{
pRow = static_cast<SwRowFrame*>(pRow->GetNext());
++nRowCount;
}
}
// No more row to split or to move to follow table: if ( !pRow ) return bRet;
// We try to split the row if // - the attributes of the row are set accordingly and // - we are allowed to do so // - it should not be kept with the next row
bSplitRowAllowed = bSplitRowAllowed && (!bTableRowKeep || !pRow->ShouldRowKeepWithNext());
// Adjust pRow according to the keep-with-next attribute: if ( !bSplitRowAllowed && bTableRowKeep )
{
SwRowFrame* pTmpRow = static_cast<SwRowFrame*>(pRow->GetPrev());
SwRowFrame* pOldRow = pRow; while ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() &&
nRowCount > nRepeat )
{ // Special case: pTmpRow wants to keep with pRow, but allows splitting, and some its // cells span several rows, including pRow. In this case, the "split" of the spanning // cells of the pTmpRow may still happen by moving pRow to the next page, even here // with !bSplitRowAllowed. if (pTmpRow->IsRowSplitAllowed())
{ bool bCellSpanCanSplit = false; for (auto pCellFrame = static_cast<const SwCellFrame*>(pTmpRow->GetLower());
pCellFrame;
pCellFrame = static_cast<const SwCellFrame*>(pCellFrame->GetNext()))
{ if (pCellFrame->GetTabBox()->getRowSpan() > 1) // Master cell
{
bCellSpanCanSplit = true; break;
}
} if (bCellSpanCanSplit) break;
}
pRow = pTmpRow;
--nRowCount;
pTmpRow = static_cast<SwRowFrame*>(pTmpRow->GetPrev());
}
// 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 ) returnfalse;
// 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()));
}
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;
}
//Optimization: There is no paste needed for the new Follow and the //optimized insert can be used (large numbers of rows luckily only occur in //such situations). if ( bNewFollow )
{
SwFrame* pInsertBehind = pFoll->GetLastLower();
while ( pRow )
{
SwFrame* pNxt = pRow->GetNext(); // The footnotes do not have to be moved, this is done in the // MoveFwd of the follow table!!!
pRow->RemoveFromLayout();
pRow->InsertBehind( pFoll, pInsertBehind );
pRow->InvalidateAll_();
pInsertBehind = pRow;
pRow = static_cast<SwRowFrame*>(pNxt);
}
} else
{
SwFrame* pPasteBefore = HasFollowFlowLine() ?
pFollowRow->GetNext() :
pFoll->GetFirstNonHeadlineRow();
while ( pRow )
{
SwFrame* pNxt = pRow->GetNext();
// The footnotes have to be moved:
lcl_MoveFootnotes( *this, *GetFollow(), *pRow );
if ( !pLastRow )
Shrink( nShrink ); else
{ // we rebuild the last line to assure that it will be fully formatted // we also don't shrink here, because we will be doing that in lcl_RecalcSplitLine
// recalculate the split line
bRet = lcl_RecalcSplitLine(*pLastRow, *pFollowRow, getRemainingAfter(pLastRow->GetPrev()), nShrink, rIsFootnoteGrowth);
// RecalcSplitLine did not work. In this case we conceal the split error: if (!bRet && !bSplitRowAllowed)
{
bRet = true;
}
// NEW TABLES // check if each cell in the row span line has a good height if ( bRet && pFollowRow->IsRowSpanLine() )
lcl_AdjustRowSpanCells( pFollowRow );
}
return bRet;
}
namespace
{ bool CanDeleteFollow(const SwTabFrame *pFoll)
{ if (pFoll->IsJoinLocked()) returnfalse;
if (pFoll->IsDeleteForbidden())
{
SAL_WARN("sw.layout", "Delete Forbidden"); returnfalse;
}
returntrue;
}
auto IsAllHiddenSection(SwSectionFrame const& rSection) -> bool
{ if (rSection.IsHiddenNow()) returntrue; for (SwFrame const* pFrame = rSection.Lower(); pFrame; pFrame = pFrame->GetNext())
{ if (pFrame->IsColumnFrame())
{ returnfalse; // adds some padding
} elseif (pFrame->IsSctFrame())
{
assert(false); // these aren't nested? if (!IsAllHiddenSection(*static_cast<SwSectionFrame const*>(pFrame)))
{ returnfalse;
}
} elseif (pFrame->IsTabFrame())
{ returnfalse; // presumably
} elseif (pFrame->IsTextFrame())
{ if (!pFrame->IsHiddenNow())
{ returnfalse;
}
}
} returntrue;
}
auto IsAllHiddenRow(SwRowFrame const& rRow, SwTabFrame const& rTab) -> bool;
// In case the row does not have a height, Grow(nHeight) did nothing. // If this is not invalidated, subsequent follows may never be joined. // Try to guess if the height of the row will be 0. If the document // was just loaded, it will be 0 in any case, but probably it's not a good // idea to join *all* follows for a newly loaded document, it would be // easier not to split the table in the first place; presumably it is split // because that improves performance. if (isAllHidden)
{
InvalidateSize_();
}
}
staticvoid 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();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.