summaryrefslogtreecommitdiffstats
path: root/sw/source/core/layout/tabfrm.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/layout/tabfrm.cxx')
-rw-r--r--sw/source/core/layout/tabfrm.cxx6170
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: */