docthemes: Save themes def. to a file when added to ColorSets
[LibreOffice.git] / sw / source / core / layout / tabfrm.cxx
blob56f381729bb89fb9f44dcc2185cc404eb0ba60b5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_wasm_strip.h>
22 #include <bodyfrm.hxx>
23 #include <pagefrm.hxx>
24 #include <rootfrm.hxx>
25 #include <IDocumentFieldsAccess.hxx>
26 #include <IDocumentRedlineAccess.hxx>
27 #include <viewimp.hxx>
28 #include <fesh.hxx>
29 #include <swtable.hxx>
30 #include <deletelistener.hxx>
31 #include <dflyobj.hxx>
32 #include <anchoreddrawobject.hxx>
33 #include <fmtanchr.hxx>
34 #include <viewopt.hxx>
35 #include <hints.hxx>
36 #include <dbg_lay.hxx>
37 #include <ftnidx.hxx>
38 #include <svl/itemiter.hxx>
39 #include <editeng/keepitem.hxx>
40 #include <editeng/ulspitem.hxx>
41 #include <editeng/brushitem.hxx>
42 #include <editeng/boxitem.hxx>
43 #include <basegfx/range/b1drange.hxx>
44 #include <fmtlsplt.hxx>
45 #include <fmtrowsplt.hxx>
46 #include <fmtsrnd.hxx>
47 #include <fmtornt.hxx>
48 #include <fmtpdsc.hxx>
49 #include <fmtfsize.hxx>
50 #include <swtblfmt.hxx>
51 #include <tabfrm.hxx>
52 #include <rowfrm.hxx>
53 #include <cellfrm.hxx>
54 #include <flyfrms.hxx>
55 #include <txtfrm.hxx>
56 #include <ftnfrm.hxx>
57 #include <notxtfrm.hxx>
58 #include <htmltbl.hxx>
59 #include <sectfrm.hxx>
60 #include <fmtfollowtextflow.hxx>
61 #include <sortedobjs.hxx>
62 #include <objectformatter.hxx>
63 #include <layouter.hxx>
64 #include <calbck.hxx>
65 #include <DocumentSettingManager.hxx>
66 #include <sal/log.hxx>
67 #include <osl/diagnose.h>
68 #include <frmatr.hxx>
69 #include <frmtool.hxx>
70 #include <ndtxt.hxx>
71 #include <frameformats.hxx>
73 using namespace ::com::sun::star;
75 SwTabFrame::SwTabFrame( SwTable &rTab, SwFrame* pSib )
76 : SwLayoutFrame( rTab.GetFrameFormat(), pSib )
77 , SwFlowFrame( static_cast<SwFrame&>(*this) )
78 , m_pTable( &rTab )
79 , m_bComplete(false)
80 , m_bCalcLowers(false)
81 , m_bLowersFormatted(false)
82 , m_bLockBackMove(false)
83 , m_bWantBackMove(false)
84 , m_bResizeHTMLTable(false)
85 , m_bONECalcLowers(false)
86 , m_bHasFollowFlowLine(false)
87 , m_bIsRebuildLastLine(false)
88 , m_bRestrictTableGrowth(false)
89 , m_bRemoveFollowFlowLinePending(false)
90 , m_bConsiderObjsForMinCellHeight(true)
91 , m_bObjsDoesFit(true)
92 , m_bInRecalcLowerRow(false)
94 mbFixSize = false; //Don't fall for import filter again.
95 mnFrameType = SwFrameType::Tab;
97 //Create the lines and insert them.
98 const SwTableLines &rLines = rTab.GetTabLines();
99 SwFrame *pTmpPrev = nullptr;
100 bool bHiddenRedlines = getRootFrame()->IsHideRedlines() &&
101 !GetFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable().empty();
102 SwRedlineTable::size_type nRedlinePos = 0;
103 for ( size_t i = 0; i < rLines.size(); ++i )
105 // skip lines deleted with track changes
106 if ( bHiddenRedlines && rLines[i]->IsDeleted(nRedlinePos) )
107 continue;
109 SwRowFrame *pNew = new SwRowFrame( *rLines[i], this );
110 if( pNew->Lower() )
112 pNew->InsertBehind( this, pTmpPrev );
113 pTmpPrev = pNew;
115 else
116 SwFrame::DestroyFrame(pNew);
118 SwFrame* pLower = Lower();
119 OSL_ENSURE( pLower && pLower->IsRowFrame(), "SwTabFrame::SwTabFrame: No rows." );
122 SwTabFrame::SwTabFrame( SwTabFrame &rTab )
123 : SwLayoutFrame( rTab.GetFormat(), &rTab )
124 , SwFlowFrame( static_cast<SwFrame&>(*this) )
125 , m_pTable( rTab.GetTable() )
126 , m_bComplete(false)
127 , m_bCalcLowers(false)
128 , m_bLowersFormatted(false)
129 , m_bLockBackMove(false)
130 , m_bWantBackMove(false)
131 , m_bResizeHTMLTable(false)
132 , m_bONECalcLowers(false)
133 , m_bHasFollowFlowLine(false)
134 , m_bIsRebuildLastLine(false)
135 , m_bRestrictTableGrowth(false)
136 , m_bRemoveFollowFlowLinePending(false)
137 , m_bConsiderObjsForMinCellHeight(true)
138 , m_bObjsDoesFit(true)
139 , m_bInRecalcLowerRow(false)
141 mbFixSize = false; //Don't fall for import filter again.
142 mnFrameType = SwFrameType::Tab;
144 SetFollow( rTab.GetFollow() );
145 rTab.SetFollow( this );
148 void SwTabFrame::DestroyImpl()
150 // There is some terrible code in fetab.cxx, that
151 // caches pointers to SwTabFrames.
152 ::ClearFEShellTabCols(*GetFormat()->GetDoc(), this);
154 SwLayoutFrame::DestroyImpl();
157 SwTabFrame::~SwTabFrame()
161 void SwTabFrame::JoinAndDelFollows()
163 SwTabFrame *pFoll = GetFollow();
164 if ( pFoll->HasFollow() )
165 pFoll->JoinAndDelFollows();
166 pFoll->Cut();
167 SetFollow( pFoll->GetFollow() );
168 SwFrame::DestroyFrame(pFoll);
171 void SwTabFrame::RegistFlys()
173 SwFrame* pLower = Lower();
174 OSL_ENSURE( pLower && pLower->IsRowFrame(), "No rows." );
176 SwPageFrame *pPage = FindPageFrame();
177 if ( pPage )
179 SwRowFrame *pRow = static_cast<SwRowFrame*>(pLower);
182 pRow->RegistFlys( pPage );
183 pRow = static_cast<SwRowFrame*>(pRow->GetNext());
184 } while ( pRow );
188 static void SwInvalidateAll( SwFrame *pFrame, tools::Long nBottom );
189 static void lcl_RecalcRow( SwRowFrame& rRow, tools::Long nBottom );
190 static bool lcl_ArrangeLowers( SwLayoutFrame *pLay, tools::Long lYStart, bool bInva );
191 // #i26945# - add parameter <_bOnlyRowsAndCells> to control
192 // that only row and cell frames are formatted.
193 static bool lcl_InnerCalcLayout( SwFrame *pFrame,
194 tools::Long nBottom,
195 bool _bOnlyRowsAndCells = false );
196 // OD 2004-02-18 #106629# - correct type of 1st parameter
197 // #i26945# - add parameter <_bConsiderObjs> in order to
198 // control, if floating screen objects have to be considered for the minimal
199 // cell height.
200 static SwTwips lcl_CalcMinRowHeight( const SwRowFrame *pRow,
201 const bool _bConsiderObjs );
202 static sal_uInt16 lcl_GetTopSpace( const SwRowFrame& rRow );
204 static SwTwips lcl_CalcTopAndBottomMargin( const SwLayoutFrame&, const SwBorderAttrs& );
206 static SwTwips lcl_calcHeightOfRowBeforeThisFrame(const SwRowFrame& rRow);
208 static SwTwips lcl_GetHeightOfRows( const SwFrame* pStart, tools::Long nCount )
210 if ( !nCount || !pStart)
211 return 0;
213 SwRectFnSet aRectFnSet(pStart);
214 SwTwips nTop = aRectFnSet.GetTop(pStart->getFrameArea());
215 while (pStart->GetNext() && nCount > 1)
217 pStart = pStart->GetNext();
218 --nCount;
221 return -aRectFnSet.BottomDist(pStart->getFrameArea(), nTop);
224 // Local helper function to insert a new follow flow line
225 static SwRowFrame* lcl_InsertNewFollowFlowLine( SwTabFrame& rTab, const SwFrame& rTmpRow, bool bRowSpanLine )
227 OSL_ENSURE( rTmpRow.IsRowFrame(), "No row frame to copy for FollowFlowLine" );
228 const SwRowFrame& rRow = static_cast<const SwRowFrame&>(rTmpRow);
230 rTab.SetFollowFlowLine( true );
231 SwRowFrame *pFollowFlowLine = new SwRowFrame(*rRow.GetTabLine(), &rTab, false );
232 pFollowFlowLine->SetRowSpanLine( bRowSpanLine );
233 SwFrame* pFirstRow = rTab.GetFollow()->GetFirstNonHeadlineRow();
234 pFollowFlowLine->InsertBefore( rTab.GetFollow(), pFirstRow );
235 return pFollowFlowLine;
238 // #i26945# - local helper function to invalidate all lower
239 // objects. By parameter <_bMoveObjsOutOfRange> it can be controlled, if
240 // additionally the objects are moved 'out of range'.
241 static void lcl_InvalidateLowerObjs( SwLayoutFrame& _rLayoutFrame,
242 const bool _bMoveObjsOutOfRange = false,
243 SwPageFrame* _pPageFrame = nullptr )
245 // determine page frame, if needed
246 if ( !_pPageFrame )
248 _pPageFrame = _rLayoutFrame.FindPageFrame();
249 OSL_ENSURE( _pPageFrame,
250 "<lcl_InvalidateLowerObjs(..)> - missing page frame -> no move of lower objects out of range" );
251 if ( !_pPageFrame )
253 return;
257 // loop on lower frames
258 SwFrame* pLowerFrame = _rLayoutFrame.Lower();
259 while ( pLowerFrame )
261 if ( pLowerFrame->IsLayoutFrame() )
263 ::lcl_InvalidateLowerObjs( *static_cast<SwLayoutFrame*>(pLowerFrame),
264 _bMoveObjsOutOfRange, _pPageFrame );
266 if ( pLowerFrame->GetDrawObjs() )
268 for (size_t i = 0, nCount = pLowerFrame->GetDrawObjs()->size(); i < nCount; ++i)
270 SwAnchoredObject* pAnchoredObj = (*pLowerFrame->GetDrawObjs())[i];
272 // invalidate position of anchored object
273 pAnchoredObj->SetTmpConsiderWrapInfluence( false );
274 pAnchoredObj->SetConsiderForTextWrap( false );
275 pAnchoredObj->UnlockPosition();
276 pAnchoredObj->InvalidateObjPos();
278 SwFlyFrame *pFly = pAnchoredObj->DynCastFlyFrame();
280 // move anchored object 'out of range'
281 if ( _bMoveObjsOutOfRange )
283 // indicate, that positioning is progress to avoid
284 // modification of the anchored object resp. it's attributes
285 // due to the movement
286 SwObjPositioningInProgress aObjPosInProgress( *pAnchoredObj );
287 pAnchoredObj->SetObjLeft( _pPageFrame->getFrameArea().Right() );
288 // #115759# - reset character rectangle,
289 // top of line and relative position in order to assure,
290 // that anchored object is correctly positioned.
291 pAnchoredObj->ClearCharRectAndTopOfLine();
292 pAnchoredObj->SetCurrRelPos( Point( 0, 0 ) );
293 const SwFrameFormat* pObjFormat = pAnchoredObj->GetFrameFormat();
294 if (pObjFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR)
296 pAnchoredObj->AnchorFrame()
297 ->Prepare( PrepareHint::FlyFrameAttributesChanged,
298 pObjFormat );
300 if ( pFly != nullptr )
302 pFly->GetVirtDrawObj()->SetBoundAndSnapRectsDirty();
303 pFly->GetVirtDrawObj()->SetChanged();
307 // If anchored object is a fly frame, invalidate its lower objects
308 if ( pFly != nullptr )
310 ::lcl_InvalidateLowerObjs( *pFly, _bMoveObjsOutOfRange, _pPageFrame );
314 pLowerFrame = pLowerFrame->GetNext();
318 // Local helper function to shrink all lowers of pRow to 0 height
319 static void lcl_ShrinkCellsAndAllContent( SwRowFrame& rRow )
321 SwCellFrame* pCurrMasterCell = static_cast<SwCellFrame*>(rRow.Lower());
322 SwRectFnSet aRectFnSet(pCurrMasterCell);
324 bool bAllCellsCollapsed = true;
325 while ( pCurrMasterCell )
327 // NEW TABLES
328 SwCellFrame& rToAdjust = pCurrMasterCell->GetTabBox()->getRowSpan() < 1 ?
329 const_cast<SwCellFrame&>(pCurrMasterCell->FindStartEndOfRowSpanCell( true )) :
330 *pCurrMasterCell;
332 // #i26945#
333 // all lowers should have the correct position
334 lcl_ArrangeLowers( &rToAdjust,
335 aRectFnSet.GetPrtTop(rToAdjust),
336 false );
337 // TODO: Optimize number of frames which are set to 0 height
338 // we have to start with the last lower frame, otherwise
339 // the shrink will not shrink the current cell
340 SwFrame* pTmp = rToAdjust.GetLastLower();
341 bool bAllLowersCollapsed = true;
343 if ( pTmp && pTmp->IsRowFrame() )
345 SwRowFrame* pTmpRow = static_cast<SwRowFrame*>(pTmp);
346 lcl_ShrinkCellsAndAllContent( *pTmpRow );
348 else
350 // TODO: Optimize number of frames which are set to 0 height
351 while ( pTmp )
353 // the frames have to be shrunk
354 if ( pTmp->IsTabFrame() )
356 SwRowFrame* pTmpRow = static_cast<SwRowFrame*>(static_cast<SwTabFrame*>(pTmp)->Lower());
357 bool bAllRowsCollapsed = true;
359 while ( pTmpRow )
361 lcl_ShrinkCellsAndAllContent( *pTmpRow );
363 if (aRectFnSet.GetHeight(pTmpRow->getFrameArea()) > 0)
364 bAllRowsCollapsed = false;
366 pTmpRow = static_cast<SwRowFrame*>(pTmpRow->GetNext());
369 if (bAllRowsCollapsed)
371 // All rows of this table have 0 height -> set height of the table itself as well.
372 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pTmp);
373 aRectFnSet.SetHeight(aFrm, 0);
375 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pTmp);
376 aRectFnSet.SetTop(aPrt, 0);
377 aRectFnSet.SetHeight(aPrt, 0);
379 else
380 bAllLowersCollapsed = false;
382 else
384 pTmp->Shrink(aRectFnSet.GetHeight(pTmp->getFrameArea()));
385 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pTmp);
386 aRectFnSet.SetTop(aPrt, 0);
387 aRectFnSet.SetHeight(aPrt, 0);
389 if (aRectFnSet.GetHeight(pTmp->getFrameArea()) > 0)
391 bAllLowersCollapsed = false;
395 pTmp = pTmp->GetPrev();
398 // all lowers should have the correct position
399 lcl_ArrangeLowers( &rToAdjust,
400 aRectFnSet.GetPrtTop(rToAdjust),
401 false );
404 if (bAllLowersCollapsed)
406 // All lower frame of this cell have 0 height -> set height of the cell itself as well.
407 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pCurrMasterCell);
408 aRectFnSet.SetHeight(aFrm, 0);
410 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pCurrMasterCell);
411 aRectFnSet.SetTop(aPrt, 0);
412 aRectFnSet.SetHeight(aPrt, 0);
414 else
415 bAllCellsCollapsed = false;
417 pCurrMasterCell = static_cast<SwCellFrame*>(pCurrMasterCell->GetNext());
420 if (bAllCellsCollapsed)
422 // All cells have 0 height -> set height of row as well.
423 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(rRow);
424 aRectFnSet.SetHeight(aFrm, 0);
426 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(rRow);
427 aRectFnSet.SetTop(aPrt, 0);
428 aRectFnSet.SetHeight(aPrt, 0);
432 // Local helper function to move the content from rSourceLine to rDestLine
433 // The content is inserted behind the last content in the corresponding
434 // cell in rDestLine.
435 static void lcl_MoveRowContent( SwRowFrame& rSourceLine, SwRowFrame& rDestLine )
437 SwCellFrame* pCurrDestCell = static_cast<SwCellFrame*>(rDestLine.Lower());
438 SwCellFrame* pCurrSourceCell = static_cast<SwCellFrame*>(rSourceLine.Lower());
440 // Move content of follow cells into master cells
441 while ( pCurrSourceCell )
443 assert(pCurrDestCell);
444 SwFrame* pLower = pCurrSourceCell->Lower();
445 if ( pLower && pLower->IsRowFrame() )
447 SwRowFrame* pTmpSourceRow = static_cast<SwRowFrame*>(pLower);
448 while ( pTmpSourceRow )
450 // #125926# Attention! It is possible,
451 // that pTmpSourceRow->IsFollowFlowRow() but pTmpDestRow
452 // cannot be found. In this case, we have to move the complete
453 // row.
454 SwRowFrame* pTmpDestRow = static_cast<SwRowFrame*>(pCurrDestCell->Lower());
456 if ( pTmpSourceRow->IsFollowFlowRow() && pTmpDestRow )
458 // move content from follow flow row to pTmpDestRow:
459 while ( pTmpDestRow->GetNext() )
460 pTmpDestRow = static_cast<SwRowFrame*>(pTmpDestRow->GetNext());
462 assert(pTmpDestRow->GetFollowRow() == pTmpSourceRow);
464 lcl_MoveRowContent( *pTmpSourceRow, *pTmpDestRow );
465 pTmpDestRow->SetFollowRow( pTmpSourceRow->GetFollowRow() );
466 pTmpSourceRow->RemoveFromLayout();
467 SwFrame::DestroyFrame(pTmpSourceRow);
469 else
471 // move complete row:
472 pTmpSourceRow->RemoveFromLayout();
473 pTmpSourceRow->InsertBefore( pCurrDestCell, nullptr );
476 // RemoveFromLayout invalidates Lower() so it must be refetched
477 pTmpSourceRow = static_cast<SwRowFrame*>(pCurrSourceCell->Lower());
480 else
482 SwFrame *pTmp = ::SaveContent( pCurrSourceCell );
483 if ( pTmp )
485 // NEW TABLES
486 SwCellFrame* pDestCell = pCurrDestCell;
487 if ( pDestCell->GetTabBox()->getRowSpan() < 1 )
488 pDestCell = & const_cast<SwCellFrame&>(pDestCell->FindStartEndOfRowSpanCell( true ));
490 // Find last content
491 SwFrame* pFrame = pDestCell->GetLastLower();
492 ::RestoreContent( pTmp, pDestCell, pFrame );
495 pCurrDestCell = static_cast<SwCellFrame*>(pCurrDestCell->GetNext());
496 pCurrSourceCell = static_cast<SwCellFrame*>(pCurrSourceCell->GetNext());
500 // Local helper function to move all footnotes in rRowFrame from
501 // the footnote boss of rSource to the footnote boss of rDest.
502 static void lcl_MoveFootnotes( SwTabFrame& rSource, SwTabFrame& rDest, SwLayoutFrame& rRowFrame )
504 if ( !rSource.GetFormat()->GetDoc()->GetFootnoteIdxs().empty() )
506 SwFootnoteBossFrame* pOldBoss = rSource.FindFootnoteBossFrame( true );
507 SwFootnoteBossFrame* pNewBoss = rDest.FindFootnoteBossFrame( true );
508 rRowFrame.MoveLowerFootnotes( nullptr, pOldBoss, pNewBoss, true );
512 // Local helper function to handle nested table cells before the split process
513 static void lcl_PreprocessRowsInCells( SwTabFrame& rTab, SwRowFrame& rLastLine,
514 SwRowFrame& rFollowFlowLine, SwTwips nRemain )
516 SwCellFrame* pCurrLastLineCell = static_cast<SwCellFrame*>(rLastLine.Lower());
517 SwCellFrame* pCurrFollowFlowLineCell = static_cast<SwCellFrame*>(rFollowFlowLine.Lower());
519 SwRectFnSet aRectFnSet(pCurrLastLineCell);
521 // Move content of follow cells into master cells
522 while ( pCurrLastLineCell )
524 if ( pCurrLastLineCell->Lower() && pCurrLastLineCell->Lower()->IsRowFrame() )
526 SwTwips nTmpCut = nRemain;
527 SwRowFrame* pTmpLastLineRow = static_cast<SwRowFrame*>(pCurrLastLineCell->Lower());
529 // #i26945#
530 SwTwips nCurrentHeight =
531 lcl_CalcMinRowHeight( pTmpLastLineRow,
532 rTab.IsConsiderObjsForMinCellHeight() );
533 while ( pTmpLastLineRow->GetNext() && nTmpCut > nCurrentHeight )
535 nTmpCut -= nCurrentHeight;
536 pTmpLastLineRow = static_cast<SwRowFrame*>(pTmpLastLineRow->GetNext());
537 // #i26945#
538 nCurrentHeight =
539 lcl_CalcMinRowHeight( pTmpLastLineRow,
540 rTab.IsConsiderObjsForMinCellHeight() );
543 // pTmpLastLineRow does not fit to the line or it is the last line
544 // Check if we can move pTmpLastLineRow to the follow table,
545 // or if we have to split the line:
546 bool bTableLayoutTooComplex = false;
547 tools::Long nMinHeight = 0;
549 // We have to take into account:
550 // 1. The fixed height of the row
551 // 2. The borders of the cells inside the row
552 // 3. The minimum height of the row
553 if ( pTmpLastLineRow->HasFixSize() )
554 nMinHeight = aRectFnSet.GetHeight(pTmpLastLineRow->getFrameArea());
555 else
558 const SwFormatFrameSize &rSz = pTmpLastLineRow->GetFormat()->GetFrameSize();
559 if ( rSz.GetHeightSizeType() == SwFrameSize::Minimum )
560 nMinHeight = rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*pTmpLastLineRow);
563 SwFrame* pCell = pTmpLastLineRow->Lower();
564 while ( pCell )
566 SwFrame* pCellLower = static_cast<SwCellFrame*>(pCell)->Lower();
567 if ( pCellLower && pCellLower->IsRowFrame() )
569 bTableLayoutTooComplex = true;
570 break;
573 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCell );
574 const SwBorderAttrs &rAttrs = *aAccess.Get();
575 nMinHeight = std::max( nMinHeight, tools::Long(lcl_CalcTopAndBottomMargin( *static_cast<SwLayoutFrame*>(pCell), rAttrs )) );
576 pCell = pCell->GetNext();
580 // 1. Case:
581 // The line completely fits into the master table.
582 // Nevertheless, we build a follow (otherwise painting problems
583 // with empty cell).
585 // 2. Case:
586 // The line has to be split, the minimum height still fits into
587 // the master table, and the table structure is not too complex.
588 if ( nTmpCut > nCurrentHeight ||
589 ( pTmpLastLineRow->IsRowSplitAllowed() &&
590 !bTableLayoutTooComplex && nMinHeight < nTmpCut ) )
592 // The line has to be split:
593 SwRowFrame* pNewRow = new SwRowFrame( *pTmpLastLineRow->GetTabLine(), &rTab, false );
594 pNewRow->SetFollowFlowRow( true );
595 pNewRow->SetFollowRow( pTmpLastLineRow->GetFollowRow() );
596 pTmpLastLineRow->SetFollowRow( pNewRow );
597 pNewRow->InsertBehind( pCurrFollowFlowLineCell, nullptr );
598 pTmpLastLineRow = static_cast<SwRowFrame*>(pTmpLastLineRow->GetNext());
601 // The following lines have to be moved:
602 while ( pTmpLastLineRow )
604 SwRowFrame* pTmp = static_cast<SwRowFrame*>(pTmpLastLineRow->GetNext());
605 lcl_MoveFootnotes( rTab, *rTab.GetFollow(), *pTmpLastLineRow );
606 pTmpLastLineRow->RemoveFromLayout();
607 pTmpLastLineRow->InsertBefore( pCurrFollowFlowLineCell, nullptr );
608 pTmpLastLineRow->Shrink( aRectFnSet.GetHeight(pTmpLastLineRow->getFrameArea()) );
609 pCurrFollowFlowLineCell->Grow( aRectFnSet.GetHeight(pTmpLastLineRow->getFrameArea()) );
610 pTmpLastLineRow = pTmp;
614 pCurrLastLineCell = static_cast<SwCellFrame*>(pCurrLastLineCell->GetNext());
615 pCurrFollowFlowLineCell = static_cast<SwCellFrame*>(pCurrFollowFlowLineCell->GetNext());
619 // Local helper function to handle nested table cells after the split process
620 static void lcl_PostprocessRowsInCells( SwTabFrame& rTab, SwRowFrame& rLastLine )
622 SwCellFrame* pCurrMasterCell = static_cast<SwCellFrame*>(rLastLine.Lower());
623 while ( pCurrMasterCell )
625 SwFrame* pLower = pCurrMasterCell->Lower();
626 if ( pLower && pLower->IsRowFrame() )
628 SwRowFrame* pRowFrame = static_cast<SwRowFrame*>(pCurrMasterCell->GetLastLower());
630 if ( nullptr != pRowFrame->GetPrev() && !pRowFrame->ContainsContent() )
632 OSL_ENSURE( pRowFrame->GetFollowRow(), "Deleting row frame without follow" );
634 // The footnotes have to be moved:
635 lcl_MoveFootnotes( rTab, *rTab.GetFollow(), *pRowFrame );
636 pRowFrame->Cut();
637 SwRowFrame* pFollowRow = pRowFrame->GetFollowRow();
638 pRowFrame->Paste( pFollowRow->GetUpper(), pFollowRow );
639 pRowFrame->SetFollowRow( pFollowRow->GetFollowRow() );
640 lcl_MoveRowContent( *pFollowRow, *pRowFrame );
641 pFollowRow->Cut();
642 SwFrame::DestroyFrame(pFollowRow);
643 ::SwInvalidateAll( pCurrMasterCell, LONG_MAX );
647 pCurrMasterCell = static_cast<SwCellFrame*>(pCurrMasterCell->GetNext());
651 // Local helper function to re-calculate the split line.
652 inline void TableSplitRecalcLock( SwFlowFrame *pTab ) { pTab->LockJoin(); }
653 inline void TableSplitRecalcUnlock( SwFlowFrame *pTab ) { pTab->UnlockJoin(); }
655 static bool lcl_RecalcSplitLine( SwRowFrame& rLastLine, SwRowFrame& rFollowLine,
656 SwTwips nRemainingSpaceForLastRow, SwTwips nAlreadyFree,
657 bool & rIsFootnoteGrowth)
659 bool bRet = true;
661 vcl::RenderContext* pRenderContext = rLastLine.getRootFrame()->GetCurrShell()->GetOut();
662 SwTabFrame& rTab = static_cast<SwTabFrame&>(*rLastLine.GetUpper());
663 SwRectFnSet aRectFnSet(rTab.GetUpper());
664 SwTwips nCurLastLineHeight = aRectFnSet.GetHeight(rLastLine.getFrameArea());
666 SwTwips nFootnoteHeight(0);
667 if (SwFootnoteBossFrame const*const pBoss = rTab.FindFootnoteBossFrame())
669 if (SwFootnoteContFrame const*const pCont = pBoss->FindFootnoteCont())
671 for (SwFootnoteFrame const* pFootnote = static_cast<SwFootnoteFrame const*>(pCont->Lower());
672 pFootnote != nullptr;
673 pFootnote = static_cast<SwFootnoteFrame const*>(pFootnote->GetNext()))
675 SwContentFrame const*const pAnchor = pFootnote->GetRef();
676 SwTabFrame const* pTab = pAnchor->FindTabFrame();
677 if (pTab)
679 while (pTab->GetUpper()->IsInTab())
681 pTab = pTab->GetUpper()->FindTabFrame();
683 // TODO currently do this only for top-level tables?
684 // otherwise would need to check rTab's follow and any upper table's follow?
685 if (pTab == &rTab)
687 nFootnoteHeight += aRectFnSet.GetHeight(pFootnote->getFrameArea());
694 // If there are nested cells in rLastLine, the recalculation of the last
695 // line needs some preprocessing.
696 lcl_PreprocessRowsInCells( rTab, rLastLine, rFollowLine, nRemainingSpaceForLastRow );
698 // Here the recalculation process starts:
699 rTab.SetRebuildLastLine( true );
700 // #i26945#
701 rTab.SetDoesObjsFit( true );
703 // #i26945# - invalidate and move floating screen
704 // objects 'out of range'
705 ::lcl_InvalidateLowerObjs( rLastLine, true );
707 // manipulate row and cell sizes
709 // #i26945# - Do *not* consider floating screen objects
710 // for the minimal cell height.
711 rTab.SetConsiderObjsForMinCellHeight( false );
712 ::lcl_ShrinkCellsAndAllContent( rLastLine );
713 rTab.SetConsiderObjsForMinCellHeight( true );
715 // invalidate last line
716 ::SwInvalidateAll( &rLastLine, LONG_MAX );
718 // Shrink the table to account for the shrunk last row, as well as lower rows
719 // that had been moved to follow table in SwTabFrame::Split.
720 // It will grow later when last line will recalc its height.
721 rTab.Shrink(nAlreadyFree + nCurLastLineHeight - nRemainingSpaceForLastRow + 1);
723 // Lock this tab frame and its follow
724 bool bUnlockMaster = false;
725 SwFlowFrame * pFollow = nullptr;
726 SwTabFrame* pMaster = rTab.IsFollow() ? rTab.FindMaster() : nullptr;
727 if ( pMaster && !pMaster->IsJoinLocked() )
729 bUnlockMaster = true;
730 ::TableSplitRecalcLock( pMaster );
732 if ( !rTab.GetFollow()->IsJoinLocked() )
734 pFollow = rTab.GetFollow();
735 ::TableSplitRecalcLock( pFollow );
738 bool bInSplit = rLastLine.IsInSplit();
739 rLastLine.SetInSplit();
741 // Do the recalculation
742 lcl_RecalcRow( rLastLine, LONG_MAX );
743 // #115759# - force a format of the last line in order to
744 // get the correct height.
745 rLastLine.InvalidateSize();
746 rLastLine.Calc(pRenderContext);
748 rLastLine.SetInSplit(bInSplit);
750 // Unlock this tab frame and its follow
751 if ( pFollow )
752 ::TableSplitRecalcUnlock( pFollow );
753 if ( bUnlockMaster )
754 ::TableSplitRecalcUnlock( pMaster );
756 // If there are nested cells in rLastLine, the recalculation of the last
757 // line needs some postprocessing.
758 lcl_PostprocessRowsInCells( rTab, rLastLine );
760 // Do a couple of checks on the current situation.
762 // If we are not happy with the current situation we return false.
763 // This will start a new try to split the table, this time we do not
764 // try to split the table rows.
766 // 1. Check if table fits to its upper.
767 // #i26945# - include check, if objects fit
768 const SwTwips nDistanceToUpperPrtBottom =
769 aRectFnSet.BottomDist(rTab.getFrameArea(), aRectFnSet.GetPrtBottom(*rTab.GetUpper()));
770 // also check the footnote boss - it *may* be smaller than the upper now!
771 const SwTwips nDistanceToFootnoteBodyPrtBottom =
772 aRectFnSet.BottomDist(rTab.getFrameArea(), aRectFnSet.GetPrtBottom(*rTab.FindFootnoteBossFrame()->FindBodyCont()));
773 // tdf#125685 ignore footnotes that are anchored in follow-table of this
774 // table - if split is successful they move to the next page/column anyway
775 assert(rTab.GetFollow() == rFollowLine.GetUpper());
776 SwTwips nFollowFootnotes(0);
777 // actually there should always be a boss frame, except if "this" isn't
778 // connected to a page yet; not sure if that can happen
779 if (SwFootnoteBossFrame const*const pBoss = rTab.FindFootnoteBossFrame())
781 if (SwFootnoteContFrame const*const pCont = pBoss->FindFootnoteCont())
783 for (SwFootnoteFrame const* pFootnote = static_cast<SwFootnoteFrame const*>(pCont->Lower());
784 pFootnote != nullptr;
785 pFootnote = static_cast<SwFootnoteFrame const*>(pFootnote->GetNext()))
787 SwContentFrame const*const pAnchor = pFootnote->GetRef();
788 SwTabFrame const* pTab = pAnchor->FindTabFrame();
789 if (pTab)
791 while (pTab->GetUpper()->IsInTab())
793 pTab = pTab->GetUpper()->FindTabFrame();
795 // TODO currently do this only for top-level tables?
796 // otherwise would need to check rTab's follow and any upper table's follow?
797 if (pTab == rTab.GetFollow())
799 nFollowFootnotes += aRectFnSet.GetHeight(pFootnote->getFrameArea());
801 if (pTab == &rTab)
803 nFootnoteHeight -= aRectFnSet.GetHeight(pFootnote->getFrameArea());
807 if (nFootnoteHeight < 0)
808 { // tdf#156724 footnotes have grown, try to split again
809 rIsFootnoteGrowth = true;
813 if (nDistanceToUpperPrtBottom + nFollowFootnotes < 0 || !rTab.DoesObjsFit())
814 bRet = false;
816 // apparently checking nFootnoteHeight here does *not* guarantee that it fits into the body
817 if (bRet && rTab.IsInDocBody()
818 && nDistanceToFootnoteBodyPrtBottom + nFollowFootnotes < 0)
820 assert(rTab.GetUpper() != rTab.FindFootnoteBossFrame()->FindBodyCont());
821 SAL_INFO("sw.layout", "SwTabFrame Split failed because of footnote growth");
822 bRet = false; // tdf#160897
825 // 2. Check if each cell in the last line has at least one content frame.
827 // Note: a FollowFlowRow may contains empty cells!
828 if ( bRet )
830 if ( !rLastLine.IsInFollowFlowRow() )
832 SwCellFrame* pCurrMasterCell = static_cast<SwCellFrame*>(rLastLine.Lower());
833 while ( pCurrMasterCell )
835 if ( !pCurrMasterCell->ContainsContent() && pCurrMasterCell->GetTabBox()->getRowSpan() >= 1 )
837 bRet = false;
838 break;
840 pCurrMasterCell = static_cast<SwCellFrame*>(pCurrMasterCell->GetNext());
845 // 3. Check if last line does not contain any content:
846 if ( bRet )
848 if ( !rLastLine.ContainsContent() )
850 bRet = false;
854 // 4. Check if follow flow line does not contain content:
855 if ( bRet )
857 if ( !rFollowLine.IsRowSpanLine() && !rFollowLine.ContainsContent() )
859 bRet = false;
863 if ( bRet )
865 // Everything looks fine. Splitting seems to be successful. We invalidate
866 // rFollowLine to force a new formatting.
867 ::SwInvalidateAll( &rFollowLine, LONG_MAX );
869 else
871 // Splitting the table row gave us an unexpected result.
872 // Everything has to be prepared for a second try to split
873 // the table, this time without splitting the row.
874 ::SwInvalidateAll( &rLastLine, LONG_MAX );
877 rTab.SetRebuildLastLine( false );
878 // #i26945#
879 rTab.SetDoesObjsFit( true );
881 return bRet;
884 // Sets the correct height for all spanned cells
885 static void lcl_AdjustRowSpanCells( SwRowFrame* pRow )
887 SwRectFnSet aRectFnSet(pRow);
888 SwCellFrame* pCellFrame = static_cast<SwCellFrame*>(pRow->GetLower());
889 while ( pCellFrame )
891 const tools::Long nLayoutRowSpan = pCellFrame->GetLayoutRowSpan();
892 if ( nLayoutRowSpan > 1 )
894 // calculate height of cell:
895 const tools::Long nNewCellHeight = lcl_GetHeightOfRows( pRow, nLayoutRowSpan );
896 const tools::Long nDiff = nNewCellHeight - aRectFnSet.GetHeight(pCellFrame->getFrameArea());
898 if ( nDiff )
900 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pCellFrame);
901 aRectFnSet.AddBottom(aFrm, nDiff);
905 pCellFrame = static_cast<SwCellFrame*>(pCellFrame->GetNext());
909 // Returns the maximum layout row span of the row
910 // Looking for the next row that contains no covered cells:
911 static tools::Long lcl_GetMaximumLayoutRowSpan( const SwRowFrame& rRow )
913 tools::Long nRet = 1;
915 const SwRowFrame* pCurrentRowFrame = static_cast<const SwRowFrame*>(rRow.GetNext());
916 bool bNextRow = false;
918 while ( pCurrentRowFrame )
920 // if there is any covered cell, we proceed to the next row frame
921 const SwCellFrame* pLower = static_cast<const SwCellFrame*>( pCurrentRowFrame->Lower());
922 while ( pLower )
924 if ( pLower->GetTabBox()->getRowSpan() < 0 )
926 ++nRet;
927 bNextRow = true;
928 break;
930 pLower = static_cast<const SwCellFrame*>(pLower->GetNext());
932 pCurrentRowFrame = bNextRow ?
933 static_cast<const SwRowFrame*>(pCurrentRowFrame->GetNext() ) :
934 nullptr;
937 return nRet;
940 // Function to remove the FollowFlowLine of rTab.
941 // The content of the FollowFlowLine is moved to the associated line in the
942 // master table.
943 bool SwTabFrame::RemoveFollowFlowLine()
945 // find FollowFlowLine
946 SwTabFrame *pFoll = GetFollow();
947 SwRowFrame* pFollowFlowLine = pFoll ? pFoll->GetFirstNonHeadlineRow() : nullptr;
949 // find last row in master
950 SwFrame* pLastLine = GetLastLower();
952 OSL_ENSURE( HasFollowFlowLine() &&
953 pFollowFlowLine &&
954 pLastLine, "There should be a flowline in the follow" );
956 // #140081# Make code robust.
957 if ( !pFollowFlowLine || !pLastLine )
958 return true;
959 if (pFollowFlowLine->IsDeleteForbidden())
961 SAL_WARN("sw.layout", "Cannot remove in-use Follow Flow Line");
962 return false;
965 // We have to reset the flag here, because lcl_MoveRowContent
966 // calls a GrowFrame(), which has a different behavior if
967 // this flag is set.
968 SetFollowFlowLine( false );
970 // Move content
971 lcl_MoveRowContent( *pFollowFlowLine, *static_cast<SwRowFrame*>(pLastLine) );
973 // NEW TABLES
974 // If a row span follow flow line is removed, we want to move the whole span
975 // to the master:
976 tools::Long nRowsToMove = lcl_GetMaximumLayoutRowSpan( *pFollowFlowLine );
978 if ( nRowsToMove > 1 )
980 SwRectFnSet aRectFnSet(this);
981 SwFrame* pRow = pFollowFlowLine->GetNext();
982 SwFrame* pInsertBehind = GetLastLower();
983 SwTwips nGrow = 0;
985 while ( pRow && nRowsToMove-- > 1 )
987 SwFrame* pNxt = pRow->GetNext();
988 nGrow += aRectFnSet.GetHeight(pRow->getFrameArea());
990 // The footnotes have to be moved:
991 lcl_MoveFootnotes( *GetFollow(), *this, static_cast<SwRowFrame&>(*pRow) );
993 pRow->RemoveFromLayout();
994 pRow->InsertBehind( this, pInsertBehind );
995 pRow->InvalidateAll_();
996 pRow->CheckDirChange();
997 pInsertBehind = pRow;
998 pRow = pNxt;
1001 SwFrame* pFirstRow = Lower();
1002 while ( pFirstRow )
1004 lcl_AdjustRowSpanCells( static_cast<SwRowFrame*>(pFirstRow) );
1005 pFirstRow = pFirstRow->GetNext();
1008 Grow( nGrow );
1009 GetFollow()->Shrink( nGrow );
1012 bool bJoin = !pFollowFlowLine->GetNext();
1013 pFollowFlowLine->Cut();
1014 SwFrame::DestroyFrame(pFollowFlowLine);
1016 return bJoin;
1019 // #i26945# - Floating screen objects are no longer searched.
1020 static bool lcl_FindSectionsInRow( const SwRowFrame& rRow )
1022 bool bRet = false;
1023 const SwCellFrame* pLower = static_cast<const SwCellFrame*>(rRow.Lower());
1024 while ( pLower )
1026 if ( pLower->IsVertical() != rRow.IsVertical() )
1027 return true;
1029 const SwFrame* pTmpFrame = pLower->Lower();
1030 while ( pTmpFrame )
1032 if ( pTmpFrame->IsRowFrame() )
1034 bRet = lcl_FindSectionsInRow( *static_cast<const SwRowFrame*>(pTmpFrame) );
1036 else
1038 // #i26945# - search only for sections
1039 if (pTmpFrame->IsSctFrame())
1041 bRet = true;
1043 if (!rRow.IsInSct())
1045 // This row is not in a section.
1046 if (const SwFrame* pSectionLower = pTmpFrame->GetLower())
1048 if (!pSectionLower->IsColumnFrame())
1050 // Section has a single column only, try to
1051 // split that.
1052 bRet = false;
1054 for (const SwFrame* pFrame = pSectionLower; pFrame; pFrame = pFrame->GetNext())
1056 if (pFrame->IsTabFrame())
1058 // Section contains a table, no split in that case.
1059 bRet = true;
1060 break;
1069 if ( bRet )
1070 return true;
1071 pTmpFrame = pTmpFrame->GetNext();
1074 pLower = static_cast<const SwCellFrame*>(pLower->GetNext());
1076 return bRet;
1079 bool SwTabFrame::Split(const SwTwips nCutPos, bool bTryToSplit,
1080 bool bTableRowKeep, bool & rIsFootnoteGrowth)
1082 bool bRet = true;
1084 SwRectFnSet aRectFnSet(this);
1086 // #i26745# - format row and cell frames of table
1088 Lower()->InvalidatePos_();
1089 // #i43913# - correction
1090 // call method <lcl_InnerCalcLayout> with first lower.
1091 lcl_InnerCalcLayout( Lower(), LONG_MAX, true );
1094 //In order to be able to compare the positions of the cells with CutPos,
1095 //they have to be calculated consecutively starting from the table.
1096 //They can definitely be invalid because of position changes of the table.
1097 SwRowFrame *pRow = static_cast<SwRowFrame*>(Lower());
1098 if( !pRow )
1099 return bRet;
1101 const sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat();
1102 sal_uInt16 nRowCount = 0; // pRow currently points to the first row
1104 // Due to the comment above, about possibly invalid positions of cells due to position changes
1105 // of the table, calculate the rows height subtracting last row's bottom from first row's top,
1106 // and compare with the available height
1107 SwTwips nRemainingSpaceForLastRow = aRectFnSet.YDiff(nCutPos, aRectFnSet.GetPrtTop(*this));
1108 nRemainingSpaceForLastRow -= aRectFnSet.GetBottomMargin(*this);
1109 auto getRemainingAfter = [aRectFnSet, nAvailable = nRemainingSpaceForLastRow,
1110 nFirstRowTop = aRectFnSet.GetTop(pRow->getFrameArea())](SwFrame* p)
1111 { return nAvailable + (p ? aRectFnSet.BottomDist(p->getFrameArea(), nFirstRowTop) : 0); };
1113 // Make pRow point to the line that does not fit anymore:
1114 while (pRow->GetNext())
1116 SwTwips nNewRemaining = getRemainingAfter(pRow);
1117 if (nNewRemaining < 0)
1118 break;
1119 if( bTryToSplit || !pRow->IsRowSpanLine() ||
1120 0 != aRectFnSet.GetHeight(pRow->getFrameArea()) )
1121 ++nRowCount;
1122 nRemainingSpaceForLastRow = nNewRemaining;
1123 pRow = static_cast<SwRowFrame*>(pRow->GetNext());
1126 // bSplitRowAllowed: Row may be split according to its attributes.
1127 // bTryToSplit: Row will never be split if bTryToSplit = false.
1128 // This can either be passed as a parameter, indicating
1129 // that we are currently doing the second try to split the
1130 // table, or it will be set to false under certain
1131 // conditions that are not suitable for splitting
1132 // the row.
1133 bool bSplitRowAllowed = bTryToSplit;
1134 if (bSplitRowAllowed && !pRow->IsRowSplitAllowed())
1136 // A row larger than the entire page ought to be allowed to split regardless of setting,
1137 // otherwise it has hidden content and that makes no sense
1138 tools::Long nMaxHeight = FindPageFrame()->getFramePrintArea().Height();
1139 for (auto pBody = FindBodyFrame(); pBody; pBody = pBody->GetUpper()->FindBodyFrame())
1141 if (pBody->IsPageBodyFrame())
1142 nMaxHeight = pBody->getFramePrintArea().Height();
1144 if (pRow->getFrameArea().Height() > nMaxHeight)
1145 pRow->SetForceRowSplitAllowed( true );
1146 else
1147 bSplitRowAllowed = false;
1149 // #i29438#
1150 // #i26945# - Floating screen objects no longer forbid
1151 // a splitting of the table row.
1152 // Special DoNotSplit case 1:
1153 // Search for sections inside pRow:
1154 if (bSplitRowAllowed && lcl_FindSectionsInRow(*pRow))
1156 bSplitRowAllowed = false;
1159 SwFlyFrame* pFly = FindFlyFrame();
1160 if (bSplitRowAllowed && pFly && pFly->IsFlySplitAllowed())
1162 // The remaining size is less than the minimum row height, then don't even try to split the
1163 // row, just move it forward.
1164 const SwFormatFrameSize& rRowSize = pRow->GetFormat()->GetFrameSize();
1165 if (rRowSize.GetHeightSizeType() == SwFrameSize::Minimum)
1167 SwTwips nMinHeight = rRowSize.GetHeight();
1168 if (nMinHeight > nRemainingSpaceForLastRow)
1170 bSplitRowAllowed = false;
1172 if (!pRow->GetPrev() && aRectFnSet.GetHeight(pRow->getFrameArea()) > nRemainingSpaceForLastRow)
1174 // Split of pRow is not allowed, no previous row, the current row doesn't fit:
1175 // that's a failure, we'll have to move forward instead.
1176 return false;
1182 // #i29771#
1183 // To avoid loops, we do some checks before actually trying to split
1184 // the row. Maybe we should keep the next row in this table.
1185 // Note: This is only done if we are at the beginning of our upper
1186 bool bKeepNextRow = false;
1187 if ( nRowCount < nRepeat )
1189 // First case: One of the repeated headline does not fit to the page anymore.
1190 // tdf#88496 Disable repeated headline (like for #i44910#) to avoid loops and
1191 // to fix interoperability problems (very long tables only with headline)
1192 // tdf#150149 except in multi-column sections, where it's possible to enlarge
1193 // the height of the section frame instead of using this fallback
1194 OSL_ENSURE( !GetIndPrev(), "Table is supposed to be at beginning" );
1195 if ( !IsInSct() )
1197 // This would mean the layout modifies the doc model, so RowsToRepeat drops to 0 while
1198 // there are existing row frames with RepeatedHeadline == true. Avoid this at least
1199 // inside split flys, it would lead to a crash in SwTabFrame::MakeAll().
1200 if (!pFly || !pFly->IsFlySplitAllowed())
1202 m_pTable->SetRowsToRepeat(0);
1204 return false;
1206 else
1207 bKeepNextRow = true;
1209 else if ( !GetIndPrev() && nRepeat == nRowCount )
1211 // Second case: The first non-headline row does not fit to the page.
1212 // If it is not allowed to be split, or it contains a sub-row that
1213 // is not allowed to be split, we keep the row in this table:
1214 if (bSplitRowAllowed)
1216 // Check if there are (first) rows inside this row,
1217 // which are not allowed to be split.
1218 SwCellFrame* pLowerCell = static_cast<SwCellFrame*>(pRow->Lower());
1219 while ( pLowerCell )
1221 if ( pLowerCell->Lower() && pLowerCell->Lower()->IsRowFrame() )
1223 const SwRowFrame* pLowerRow = static_cast<SwRowFrame*>(pLowerCell->Lower());
1224 if ( !pLowerRow->IsRowSplitAllowed() &&
1225 aRectFnSet.GetHeight(pLowerRow->getFrameArea()) > nRemainingSpaceForLastRow )
1227 bKeepNextRow = true;
1228 break;
1231 pLowerCell = static_cast<SwCellFrame*>(pLowerCell->GetNext());
1234 else
1235 bKeepNextRow = true;
1238 // Better keep the next row in this table:
1239 if ( bKeepNextRow )
1241 pRow = GetFirstNonHeadlineRow();
1242 if ( pRow && pRow->IsRowSpanLine() && 0 == aRectFnSet.GetHeight(pRow->getFrameArea()) )
1243 pRow = static_cast<SwRowFrame*>(pRow->GetNext());
1244 if ( pRow )
1246 pRow = static_cast<SwRowFrame*>(pRow->GetNext());
1247 ++nRowCount;
1251 // No more row to split or to move to follow table:
1252 if ( !pRow )
1253 return bRet;
1255 // We try to split the row if
1256 // - the attributes of the row are set accordingly and
1257 // - we are allowed to do so
1258 // - it should not be kept with the next row
1259 bSplitRowAllowed = bSplitRowAllowed && (!bTableRowKeep || !pRow->ShouldRowKeepWithNext());
1261 // Adjust pRow according to the keep-with-next attribute:
1262 if ( !bSplitRowAllowed && bTableRowKeep )
1264 SwRowFrame* pTmpRow = static_cast<SwRowFrame*>(pRow->GetPrev());
1265 SwRowFrame* pOldRow = pRow;
1266 while ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() &&
1267 nRowCount > nRepeat )
1269 // Special case: pTmpRow wants to keep with pRow, but allows splitting, and some its
1270 // cells span several rows, including pRow. In this case, the "split" of the spanning
1271 // cells of the pTmpRow may still happen by moving pRow to the next page, even here
1272 // with !bSplitRowAllowed.
1273 if (pTmpRow->IsRowSplitAllowed())
1275 bool bCellSpanCanSplit = false;
1276 for (auto pCellFrame = static_cast<const SwCellFrame*>(pTmpRow->GetLower());
1277 pCellFrame;
1278 pCellFrame = static_cast<const SwCellFrame*>(pCellFrame->GetNext()))
1280 if (pCellFrame->GetTabBox()->getRowSpan() > 1) // Master cell
1282 bCellSpanCanSplit = true;
1283 break;
1286 if (bCellSpanCanSplit)
1287 break;
1289 pRow = pTmpRow;
1290 --nRowCount;
1291 pTmpRow = static_cast<SwRowFrame*>(pTmpRow->GetPrev());
1294 // loop prevention
1295 if ( nRowCount == nRepeat && !GetIndPrev())
1297 pRow = pOldRow;
1301 // If we do not intend to split pRow, we check if we are
1302 // allowed to move pRow to a follow. Otherwise we return
1303 // false, indicating an error
1304 if ( !bSplitRowAllowed )
1306 SwRowFrame* pFirstNonHeadlineRow = GetFirstNonHeadlineRow();
1307 if ( pRow == pFirstNonHeadlineRow )
1308 return false;
1310 // #i91764#
1311 // Ignore row span lines
1312 SwRowFrame* pTmpRow = pFirstNonHeadlineRow;
1313 while ( pTmpRow && pTmpRow->IsRowSpanLine() )
1315 pTmpRow = static_cast<SwRowFrame*>(pTmpRow->GetNext());
1317 if ( !pTmpRow || pRow == pTmpRow )
1319 return false;
1323 // Build follow table if not already done:
1324 bool bNewFollow;
1325 SwTabFrame *pFoll;
1326 if ( GetFollow() )
1328 pFoll = GetFollow();
1329 bNewFollow = false;
1331 else
1333 bNewFollow = true;
1334 pFoll = new SwTabFrame( *this );
1336 // We give the follow table an initial width.
1338 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFoll);
1339 aRectFnSet.AddWidth(aFrm, aRectFnSet.GetWidth(getFrameArea()));
1340 aRectFnSet.SetLeft(aFrm, aRectFnSet.GetLeft(getFrameArea()));
1344 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pFoll);
1345 aRectFnSet.AddWidth(aPrt, aRectFnSet.GetWidth(getFramePrintArea()));
1348 // Insert the new follow table
1349 pFoll->InsertBehind( GetUpper(), this );
1351 // Repeat the headlines.
1352 auto& rLines = GetTable()->GetTabLines();
1353 for ( nRowCount = 0; nRowCount < nRepeat; ++nRowCount )
1355 // Insert new headlines:
1356 SwRowFrame* pHeadline = new SwRowFrame(*rLines[nRowCount], this);
1358 sw::FlyCreationSuppressor aSuppressor;
1359 pHeadline->SetRepeatedHeadline(true);
1361 pHeadline->InsertBefore( pFoll, nullptr );
1363 SwPageFrame *pPage = pHeadline->FindPageFrame();
1364 const sw::SpzFrameFormats* pSpzs = GetFormat()->GetDoc()->GetSpzFrameFormats();
1365 if( !pSpzs->empty() )
1367 SwNodeOffset nIndex;
1368 SwContentFrame* pFrame = pHeadline->ContainsContent();
1369 while( pFrame )
1371 // sw_redlinehide: the implementation of AppendObjs
1372 // takes care of iterating merged SwTextFrame
1373 nIndex = pFrame->IsTextFrame()
1374 ? static_cast<SwTextFrame*>(pFrame)->GetTextNodeFirst()->GetIndex()
1375 : static_cast<SwNoTextFrame*>(pFrame)->GetNode()->GetIndex();
1376 AppendObjs(pSpzs, nIndex, pFrame, pPage, GetFormat()->GetDoc());
1377 pFrame = pFrame->GetNextContentFrame();
1378 if( !pHeadline->IsAnLower( pFrame ) )
1379 break;
1385 SwRowFrame* pLastRow = nullptr; // points to the last remaining line in master
1386 SwRowFrame* pFollowRow = nullptr; // points to either the follow flow line or the
1387 // first regular line in the follow
1389 if ( bSplitRowAllowed )
1391 // If the row that does not fit anymore is allowed
1392 // to be split, the next row has to be moved to the follow table.
1393 pLastRow = pRow;
1394 pRow = static_cast<SwRowFrame*>(pRow->GetNext());
1396 // new follow flow line for last row of master table
1397 pFollowRow = lcl_InsertNewFollowFlowLine( *this, *pLastRow, false );
1399 else
1401 pFollowRow = pRow;
1403 // NEW TABLES
1404 // check if we will break a row span by moving pFollowRow to the follow:
1405 // In this case we want to reformat the last line.
1406 const SwCellFrame* pCellFrame = static_cast<const SwCellFrame*>(pFollowRow->GetLower());
1407 while ( pCellFrame )
1409 if ( pCellFrame->GetTabBox()->getRowSpan() < 1 )
1411 pLastRow = static_cast<SwRowFrame*>(pRow->GetPrev());
1412 break;
1415 pCellFrame = static_cast<const SwCellFrame*>(pCellFrame->GetNext());
1418 // new follow flow line for last row of master table
1419 if ( pLastRow )
1420 pFollowRow = lcl_InsertNewFollowFlowLine( *this, *pLastRow, true );
1423 const SwTwips nShrink = lcl_GetHeightOfRows(pRow, std::numeric_limits<tools::Long>::max());
1425 //Optimization: There is no paste needed for the new Follow and the
1426 //optimized insert can be used (large numbers of rows luckily only occur in
1427 //such situations).
1428 if ( bNewFollow )
1430 SwFrame* pInsertBehind = pFoll->GetLastLower();
1432 while ( pRow )
1434 SwFrame* pNxt = pRow->GetNext();
1435 // The footnotes do not have to be moved, this is done in the
1436 // MoveFwd of the follow table!!!
1437 pRow->RemoveFromLayout();
1438 pRow->InsertBehind( pFoll, pInsertBehind );
1439 pRow->InvalidateAll_();
1440 pInsertBehind = pRow;
1441 pRow = static_cast<SwRowFrame*>(pNxt);
1444 else
1446 SwFrame* pPasteBefore = HasFollowFlowLine() ?
1447 pFollowRow->GetNext() :
1448 pFoll->GetFirstNonHeadlineRow();
1450 while ( pRow )
1452 SwFrame* pNxt = pRow->GetNext();
1454 // The footnotes have to be moved:
1455 lcl_MoveFootnotes( *this, *GetFollow(), *pRow );
1457 pRow->RemoveFromLayout();
1458 pRow->Paste( pFoll, pPasteBefore );
1460 pRow->CheckDirChange();
1461 pRow = static_cast<SwRowFrame*>(pNxt);
1465 if ( !pLastRow )
1466 Shrink( nShrink );
1467 else
1469 // we rebuild the last line to assure that it will be fully formatted
1470 // we also don't shrink here, because we will be doing that in lcl_RecalcSplitLine
1472 // recalculate the split line
1473 bRet = lcl_RecalcSplitLine(*pLastRow, *pFollowRow, getRemainingAfter(pLastRow->GetPrev()), nShrink, rIsFootnoteGrowth);
1475 // RecalcSplitLine did not work. In this case we conceal the split error:
1476 if (!bRet && !bSplitRowAllowed)
1478 bRet = true;
1481 // NEW TABLES
1482 // check if each cell in the row span line has a good height
1483 if ( bRet && pFollowRow->IsRowSpanLine() )
1484 lcl_AdjustRowSpanCells( pFollowRow );
1487 return bRet;
1490 namespace
1492 bool CanDeleteFollow(SwTabFrame *pFoll)
1494 if (pFoll->IsJoinLocked())
1495 return false;
1497 if (pFoll->IsDeleteForbidden())
1499 SAL_WARN("sw.layout", "Delete Forbidden");
1500 return false;
1503 return true;
1506 auto IsAllHiddenSection(SwSectionFrame const& rSection) -> bool
1508 if (rSection.IsHiddenNow())
1509 return true;
1510 for (SwFrame const* pFrame = rSection.Lower(); pFrame; pFrame = pFrame->GetNext())
1512 if (pFrame->IsColumnFrame())
1514 return false; // adds some padding
1516 else if (pFrame->IsSctFrame())
1518 assert(false); // these aren't nested?
1519 if (!IsAllHiddenSection(*static_cast<SwSectionFrame const*>(pFrame)))
1521 return false;
1524 else if (pFrame->IsTabFrame())
1526 return false; // presumably
1528 else if (pFrame->IsTextFrame())
1530 if (!pFrame->IsHiddenNow())
1532 return false;
1536 return true;
1539 auto IsAllHiddenRow(SwRowFrame const& rRow, SwTabFrame const& rTab) -> bool;
1541 auto IsAllHiddenCell(SwCellFrame const& rCell, SwRowFrame const& rRow, SwTabFrame const& rTab) -> bool
1543 for (SwFrame const* pFrame = rCell.Lower(); pFrame; pFrame = pFrame->GetNext())
1545 if (pFrame->IsRowFrame())
1547 if (!IsAllHiddenRow(*static_cast<SwRowFrame const*>(pFrame), rTab))
1549 return false;
1552 else if (pFrame->IsSctFrame())
1554 if (!IsAllHiddenSection(*static_cast<SwSectionFrame const*>(pFrame)))
1556 return false;
1559 else if (pFrame->IsTabFrame())
1561 return false; // presumably
1563 else if (pFrame->IsTextFrame())
1565 if (!pFrame->IsHiddenNow())
1567 return false;
1571 const SwFrame* pLower = rCell.Lower();
1572 assert(pLower);
1573 if (rTab.IsCollapsingBorders() && pLower && !pLower->IsRowFrame())
1575 if (rRow.GetTopMarginForLowers() != 0
1576 || rRow.GetBottomMarginForLowers() != 0)
1578 return false;
1581 else
1583 SwBorderAttrAccess border(SwFrame::GetCache(), &rCell);
1584 if (border.Get()->CalcTop() != 0 || border.Get()->CalcBottom() != 0)
1586 return false;
1589 return true;
1592 auto IsAllHiddenRow(SwRowFrame const& rRow, SwTabFrame const& rTab) -> bool
1594 for (SwFrame const* pCell = rRow.Lower(); pCell; pCell = pCell->GetNext())
1596 if (!IsAllHiddenCell(*static_cast<SwCellFrame const*>(pCell), rRow, rTab))
1598 return false;
1601 return true;
1604 } // namespace
1606 void SwTabFrame::Join()
1608 OSL_ENSURE( !HasFollowFlowLine(), "Joining follow flow line" );
1610 SwTabFrame *pFoll = GetFollow();
1612 if (!pFoll || !CanDeleteFollow(pFoll))
1613 return;
1615 SwRectFnSet aRectFnSet(this);
1616 pFoll->Cut(); //Cut out first to avoid unnecessary notifications.
1618 SwFrame *pRow = pFoll->GetFirstNonHeadlineRow(),
1619 *pNxt;
1621 SwFrame* pPrv = GetLastLower();
1623 SwTwips nHeight = 0; //Total height of the inserted rows as return value.
1624 bool isAllHidden(true);
1626 while ( pRow )
1628 pNxt = pRow->GetNext();
1629 nHeight += aRectFnSet.GetHeight(pRow->getFrameArea());
1630 if (nHeight != 0)
1632 isAllHidden = false;
1634 if (isAllHidden)
1636 isAllHidden = IsAllHiddenRow(*static_cast<SwRowFrame *>(pRow), *this);
1638 pRow->RemoveFromLayout();
1639 pRow->InvalidateAll_();
1640 pRow->InsertBehind( this, pPrv );
1641 pRow->CheckDirChange();
1642 pPrv = pRow;
1643 pRow = pNxt;
1646 SetFollow( pFoll->GetFollow() );
1647 SetFollowFlowLine( pFoll->HasFollowFlowLine() );
1648 SwFrame::DestroyFrame(pFoll);
1650 Grow( nHeight );
1652 // In case the row does not have a height, Grow(nHeight) did nothing.
1653 // If this is not invalidated, subsequent follows may never be joined.
1654 // Try to guess if the height of the row will be 0. If the document
1655 // was just loaded, it will be 0 in any case, but probably it's not a good
1656 // idea to join *all* follows for a newly loaded document, it would be
1657 // easier not to split the table in the first place; presumably it is split
1658 // because that improves performance.
1659 if (isAllHidden)
1661 InvalidateSize_();
1665 static void SwInvalidatePositions( SwFrame *pFrame, tools::Long nBottom )
1667 // LONG_MAX == nBottom means we have to calculate all
1668 bool bAll = LONG_MAX == nBottom;
1669 SwRectFnSet aRectFnSet(pFrame);
1671 { pFrame->InvalidatePos_();
1672 pFrame->InvalidateSize_();
1673 if( pFrame->IsLayoutFrame() )
1675 if ( static_cast<SwLayoutFrame*>(pFrame)->Lower() )
1677 ::SwInvalidatePositions( static_cast<SwLayoutFrame*>(pFrame)->Lower(), nBottom);
1678 // #i26945#
1679 ::lcl_InvalidateLowerObjs( *static_cast<SwLayoutFrame*>(pFrame) );
1682 else
1683 pFrame->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
1684 pFrame = pFrame->GetNext();
1685 } while ( pFrame &&
1686 ( bAll ||
1687 aRectFnSet.YDiff( aRectFnSet.GetTop(pFrame->getFrameArea()), nBottom ) < 0 ) );
1690 void SwInvalidateAll( SwFrame *pFrame, tools::Long nBottom )
1692 // LONG_MAX == nBottom means we have to calculate all
1693 bool bAll = LONG_MAX == nBottom;
1694 SwRectFnSet aRectFnSet(pFrame);
1697 pFrame->InvalidatePos_();
1698 pFrame->InvalidateSize_();
1699 pFrame->InvalidatePrt_();
1700 if( pFrame->IsLayoutFrame() )
1702 // NEW TABLES
1703 SwLayoutFrame* pToInvalidate = static_cast<SwLayoutFrame*>(pFrame);
1704 if (pFrame->IsCellFrame())
1706 SwCellFrame* pThisCell = static_cast<SwCellFrame*>(pFrame);
1707 if ( pThisCell->GetTabBox()->getRowSpan() < 1 )
1709 pToInvalidate = & const_cast<SwCellFrame&>(pThisCell->FindStartEndOfRowSpanCell( true ));
1710 pToInvalidate->InvalidatePos_();
1711 pToInvalidate->InvalidateSize_();
1712 pToInvalidate->InvalidatePrt_();
1715 if ( pToInvalidate->Lower() )
1716 ::SwInvalidateAll( pToInvalidate->Lower(), nBottom);
1718 else
1719 pFrame->Prepare();
1721 pFrame = pFrame->GetNext();
1722 } while ( pFrame &&
1723 ( bAll ||
1724 aRectFnSet.YDiff( aRectFnSet.GetTop(pFrame->getFrameArea()), nBottom ) < 0 ) );
1727 // #i29550#
1728 static void lcl_InvalidateAllLowersPrt( SwLayoutFrame* pLayFrame )
1730 pLayFrame->InvalidatePrt_();
1731 pLayFrame->InvalidateSize_();
1732 pLayFrame->SetCompletePaint();
1734 SwFrame* pFrame = pLayFrame->Lower();
1736 while ( pFrame )
1738 if ( pFrame->IsLayoutFrame() )
1739 lcl_InvalidateAllLowersPrt( static_cast<SwLayoutFrame*>(pFrame) );
1740 else
1742 pFrame->InvalidatePrt_();
1743 pFrame->InvalidateSize_();
1744 pFrame->SetCompletePaint();
1747 pFrame = pFrame->GetNext();
1751 bool SwContentFrame::CalcLowers(SwLayoutFrame & rLay, SwLayoutFrame const& rDontLeave,
1752 tools::Long nBottom, bool bSkipRowSpanCells )
1754 vcl::RenderContext* pRenderContext = rLay.getRootFrame()->GetCurrShell()->GetOut();
1755 // LONG_MAX == nBottom means we have to calculate all
1756 bool bAll = LONG_MAX == nBottom;
1757 bool bRet = false;
1758 SwContentFrame *pCnt = rLay.ContainsContent();
1759 SwRectFnSet aRectFnSet(&rLay);
1761 // FME 2007-08-30 #i81146# new loop control
1762 int nLoopControlRuns = 0;
1763 const int nLoopControlMax = 10;
1764 const sw::BroadcastingModify* pLoopControlCond = nullptr;
1766 while (pCnt && rDontLeave.IsAnLower(pCnt))
1768 // #115759# - check, if a format of content frame is
1769 // possible. Thus, 'copy' conditions, found at the beginning of
1770 // <SwContentFrame::MakeAll(..)>, and check these.
1771 const bool bFormatPossible = !pCnt->IsJoinLocked() &&
1772 ( !pCnt->IsTextFrame() ||
1773 !static_cast<SwTextFrame*>(pCnt)->IsLocked() ) &&
1774 ( pCnt->IsFollow() || !StackHack::IsLocked() );
1776 // NEW TABLES
1777 bool bSkipContent = false;
1778 if ( bSkipRowSpanCells && pCnt->IsInTab() )
1780 const SwFrame* pCell = pCnt->GetUpper();
1781 while ( pCell && !pCell->IsCellFrame() )
1782 pCell = pCell->GetUpper();
1783 if ( pCell && 1 != static_cast<const SwCellFrame*>( pCell )->GetLayoutRowSpan() )
1784 bSkipContent = true;
1787 if ( bFormatPossible && !bSkipContent )
1789 bRet |= !pCnt->isFrameAreaDefinitionValid();
1790 // #i26945# - no extra invalidation of floating
1791 // screen objects needed.
1792 // Thus, delete call of method <SwFrame::InvalidateObjs( true )>
1793 pCnt->Calc(pRenderContext);
1794 // #i46941# - frame has to be valid
1795 // Note: frame could be invalid after calling its format, if it's locked.
1796 OSL_ENSURE( !pCnt->IsTextFrame() ||
1797 pCnt->isFrameAreaDefinitionValid() ||
1798 static_cast<SwTextFrame*>(pCnt)->IsJoinLocked(),
1799 "<SwContentFrame::CalcLowers(..)> - text frame invalid and not locked." );
1800 if ( pCnt->IsTextFrame() && pCnt->isFrameAreaDefinitionValid() )
1802 // #i23129#, #i36347# - pass correct page frame to
1803 // the object formatter
1804 SwFrameDeleteGuard aDeleteGuard(pCnt);
1805 if ( !SwObjectFormatter::FormatObjsAtFrame( *pCnt,
1806 *(pCnt->FindPageFrame()) ) )
1808 SwTextNode const*const pTextNode(
1809 static_cast<SwTextFrame*>(pCnt)->GetTextNodeFirst());
1810 if (pTextNode == pLoopControlCond)
1811 ++nLoopControlRuns;
1812 else
1814 nLoopControlRuns = 0;
1815 pLoopControlCond = pTextNode;
1818 if ( nLoopControlRuns < nLoopControlMax )
1820 // restart format with first content
1821 pCnt = rLay.ContainsContent();
1822 continue;
1825 SAL_WARN("sw.layout", "LoopControl in SwContentFrame::CalcLowers");
1828 if (!rDontLeave.IsAnLower(pCnt)) // moved backward?
1830 pCnt = rLay.ContainsContent();
1831 continue; // avoid formatting new upper on different page
1833 pCnt->GetUpper()->Calc(pRenderContext);
1835 if( ! bAll && aRectFnSet.YDiff(aRectFnSet.GetTop(pCnt->getFrameArea()), nBottom) > 0 )
1836 break;
1837 pCnt = pCnt->GetNextContentFrame();
1839 return bRet;
1842 // #i26945# - add parameter <_bOnlyRowsAndCells> to control
1843 // that only row and cell frames are formatted.
1844 static bool lcl_InnerCalcLayout( SwFrame *pFrame,
1845 tools::Long nBottom,
1846 bool _bOnlyRowsAndCells )
1848 vcl::RenderContext* pRenderContext = pFrame->getRootFrame()->GetCurrShell() ? pFrame->getRootFrame()->GetCurrShell()->GetOut() : nullptr;
1849 // LONG_MAX == nBottom means we have to calculate all
1850 bool bAll = LONG_MAX == nBottom;
1851 bool bRet = false;
1852 const SwFrame* pOldUp = pFrame->GetUpper();
1853 SwRectFnSet aRectFnSet(pFrame);
1856 // #i26945# - parameter <_bOnlyRowsAndCells> controls,
1857 // if only row and cell frames are formatted.
1858 if ( pFrame->IsLayoutFrame() &&
1859 ( !_bOnlyRowsAndCells || pFrame->IsRowFrame() || pFrame->IsCellFrame() ) )
1861 SwFrameDeleteGuard aDeleteGuard(pFrame);
1863 // #130744# An invalid locked table frame will
1864 // not be calculated => It will not become valid =>
1865 // Loop in lcl_RecalcRow(). Therefore we do not consider them for bRet.
1866 bRet |= !pFrame->isFrameAreaDefinitionValid() && ( !pFrame->IsTabFrame() || !static_cast<SwTabFrame*>(pFrame)->IsJoinLocked() );
1867 pFrame->Calc(pRenderContext);
1868 if( static_cast<SwLayoutFrame*>(pFrame)->Lower() )
1869 bRet |= lcl_InnerCalcLayout( static_cast<SwLayoutFrame*>(pFrame)->Lower(), nBottom);
1871 // NEW TABLES
1872 if (pFrame->IsCellFrame())
1874 SwCellFrame* pThisCell = static_cast<SwCellFrame*>(pFrame);
1875 if ( pThisCell->GetTabBox()->getRowSpan() < 1 )
1877 SwCellFrame& rToCalc = const_cast<SwCellFrame&>(pThisCell->FindStartEndOfRowSpanCell( true ));
1878 bRet |= !rToCalc.isFrameAreaDefinitionValid();
1879 rToCalc.Calc(pRenderContext);
1880 if ( rToCalc.Lower() )
1881 bRet |= lcl_InnerCalcLayout( rToCalc.Lower(), nBottom);
1885 pFrame = pFrame->GetNext();
1886 } while( pFrame &&
1887 ( bAll ||
1888 aRectFnSet.YDiff(aRectFnSet.GetTop(pFrame->getFrameArea()), nBottom) < 0 )
1889 && pFrame->GetUpper() == pOldUp );
1890 return bRet;
1893 static void lcl_RecalcRow(SwRowFrame & rRow, tools::Long const nBottom)
1895 // FME 2007-08-30 #i81146# new loop control
1896 int nLoopControlRuns_1 = 0;
1897 sal_uInt16 nLoopControlStage_1 = 0;
1898 const int nLoopControlMax = 10;
1900 bool bCheck = true;
1903 // FME 2007-08-30 #i81146# new loop control
1904 int nLoopControlRuns_2 = 0;
1905 sal_uInt16 nLoopControlStage_2 = 0;
1907 while (lcl_InnerCalcLayout(&rRow, nBottom))
1909 if ( ++nLoopControlRuns_2 > nLoopControlMax )
1911 SAL_WARN_IF(nLoopControlStage_2 == 0, "sw.layout", "LoopControl_2 in lcl_RecalcRow: Stage 1!");
1912 SAL_WARN_IF(nLoopControlStage_2 == 1, "sw.layout", "LoopControl_2 in lcl_RecalcRow: Stage 2!!");
1913 SAL_WARN_IF(nLoopControlStage_2 >= 2, "sw.layout", "LoopControl_2 in lcl_RecalcRow: Stage 3!!!");
1914 rRow.ValidateThisAndAllLowers( nLoopControlStage_2++ );
1915 nLoopControlRuns_2 = 0;
1916 if( nLoopControlStage_2 > 2 )
1917 break;
1920 bCheck = true;
1923 if( bCheck )
1925 SwFrameDeleteGuard g(&rRow);
1927 // #115759# - force another format of the
1928 // lowers, if at least one of it was invalid.
1929 bCheck = SwContentFrame::CalcLowers(rRow, *rRow.GetUpper(), nBottom, true);
1931 // NEW TABLES
1932 // First we calculate the cells with row span of < 1, afterwards
1933 // all cells with row span of > 1:
1934 for ( int i = 0; i < 2; ++i )
1936 SwCellFrame* pCellFrame = static_cast<SwCellFrame*>(rRow.Lower());
1937 while ( pCellFrame )
1939 const bool bCalc = 0 == i ?
1940 pCellFrame->GetLayoutRowSpan() < 1 :
1941 pCellFrame->GetLayoutRowSpan() > 1;
1943 if ( bCalc )
1945 SwCellFrame& rToRecalc = 0 == i ?
1946 const_cast<SwCellFrame&>(pCellFrame->FindStartEndOfRowSpanCell( true )) :
1947 *pCellFrame;
1948 bCheck |= SwContentFrame::CalcLowers(rToRecalc, rToRecalc, nBottom, false);
1951 pCellFrame = static_cast<SwCellFrame*>(pCellFrame->GetNext());
1955 if ( bCheck )
1957 if ( ++nLoopControlRuns_1 > nLoopControlMax )
1959 SAL_WARN_IF(nLoopControlStage_1 == 0, "sw.layout", "LoopControl_1 in lcl_RecalcRow: Stage 1!");
1960 SAL_WARN_IF(nLoopControlStage_1 == 1, "sw.layout", "LoopControl_1 in lcl_RecalcRow: Stage 2!!");
1961 SAL_WARN_IF(nLoopControlStage_1 >= 2, "sw.layout", "LoopControl_1 in lcl_RecalcRow: Stage 3!!!");
1962 rRow.ValidateThisAndAllLowers( nLoopControlStage_1++ );
1963 nLoopControlRuns_1 = 0;
1964 if( nLoopControlStage_1 > 2 )
1965 break;
1968 continue;
1971 break;
1972 } while( true );
1975 static void lcl_RecalcTable( SwTabFrame& rTab,
1976 SwLayoutFrame *pFirstRow,
1977 SwLayNotify &rNotify )
1979 SwFrame* pLower = rTab.Lower();
1980 if ( pLower )
1982 if ( !pFirstRow )
1984 pFirstRow = static_cast<SwLayoutFrame*>(pLower);
1985 rNotify.SetLowersComplete( true );
1987 ::SwInvalidatePositions( pFirstRow, LONG_MAX );
1988 lcl_RecalcRow( *static_cast<SwRowFrame*>(pFirstRow), LONG_MAX );
1992 // This is a new function to check the first condition whether
1993 // a tab frame may move backward. It replaces the formerly used
1994 // GetIndPrev(), which did not work correctly for #i5947#
1995 static bool lcl_NoPrev( const SwFrame& rFrame )
1997 // #i79774#
1998 // skip empty sections on investigation of direct previous frame.
1999 // use information, that at least one empty section is skipped in the following code.
2000 bool bSkippedDirectPrevEmptySection( false );
2001 if ( rFrame.GetPrev() )
2003 const SwFrame* pPrev( rFrame.GetPrev() );
2004 while ( pPrev &&
2005 pPrev->IsSctFrame() &&
2006 !dynamic_cast<const SwSectionFrame&>(*pPrev).GetSection() )
2008 pPrev = pPrev->GetPrev();
2009 bSkippedDirectPrevEmptySection = true;
2011 if ( pPrev )
2013 return false;
2017 if ( ( !bSkippedDirectPrevEmptySection && !rFrame.GetIndPrev() ) ||
2018 ( bSkippedDirectPrevEmptySection &&
2019 ( !rFrame.IsInSct() || !rFrame.GetIndPrev_() ) ) )
2021 return true;
2024 // I do not have a direct prev, but I have an indirect prev.
2025 // In section frames I have to check if I'm located inside
2026 // the first column:
2027 if ( rFrame.IsInSct() )
2029 const SwFrame* pSct = rFrame.GetUpper();
2030 if ( pSct && pSct->IsColBodyFrame() &&
2031 pSct->GetUpper()->GetUpper()->IsSctFrame() )
2033 const SwFrame* pPrevCol = rFrame.GetUpper()->GetUpper()->GetPrev();
2034 if ( pPrevCol )
2035 // I'm not inside the first column and do not have a direct
2036 // prev. I can try to go backward.
2037 return true;
2041 return false;
2044 #define KEEPTAB ( !GetFollow() && !IsFollow() )
2046 // - helper method to find next content frame of
2047 // a table frame and format it to assure keep attribute.
2048 // method return true, if a next content frame is formatted.
2049 // Precondition: The given table frame hasn't a follow and isn't a follow.
2050 SwFrame* sw_FormatNextContentForKeep( SwTabFrame* pTabFrame )
2052 vcl::RenderContext* pRenderContext = pTabFrame->getRootFrame()->GetCurrShell()->GetOut();
2053 // find next content, table or section
2054 SwFrame* pNxt = pTabFrame->FindNext();
2056 // skip empty sections
2057 while ( pNxt && pNxt->IsSctFrame() &&
2058 !static_cast<SwSectionFrame*>(pNxt)->GetSection() )
2060 pNxt = pNxt->FindNext();
2063 // if found next frame is a section, get its first content.
2064 if ( pNxt && pNxt->IsSctFrame() )
2066 pNxt = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
2069 // format found next frame.
2070 // if table frame is inside another table, method <SwFrame::MakeAll()> is
2071 // called to avoid that the superior table frame is formatted.
2072 if ( pNxt )
2074 if ( pTabFrame->GetUpper()->IsInTab() )
2075 pNxt->MakeAll(pNxt->getRootFrame()->GetCurrShell()->GetOut());
2076 else
2077 pNxt->Calc(pRenderContext);
2080 return pNxt;
2083 namespace {
2084 bool AreAllRowsKeepWithNext( const SwRowFrame* pFirstRowFrame, const bool bCheckParents = true )
2086 bool bRet = pFirstRowFrame != nullptr &&
2087 pFirstRowFrame->ShouldRowKeepWithNext( bCheckParents );
2089 while ( bRet && pFirstRowFrame->GetNext() != nullptr )
2091 pFirstRowFrame = dynamic_cast<const SwRowFrame*>(pFirstRowFrame->GetNext());
2092 bRet = pFirstRowFrame != nullptr &&
2093 pFirstRowFrame->ShouldRowKeepWithNext( bCheckParents );
2096 return bRet;
2099 // Similar to SwObjPosOscillationControl in sw/source/core/layout/anchoreddrawobject.cxx
2100 class PosSizeOscillationControl
2102 public:
2103 bool OscillationDetected(const SwFrameAreaDefinition& rFrameArea);
2105 private:
2106 // A partial copy of SwFrameAreaDefinition data
2107 struct FrameData
2109 SwRect frameArea;
2110 SwRect framePrintArea;
2111 bool frameAreaPositionValid;
2112 bool frameAreaSizeValid;
2113 bool framePrintAreaValid;
2115 FrameData(const SwFrameAreaDefinition& src)
2116 : frameArea(src.getFrameArea())
2117 , framePrintArea(src.getFramePrintArea())
2118 , frameAreaPositionValid(src.isFrameAreaPositionValid())
2119 , frameAreaSizeValid(src.isFrameAreaSizeValid())
2120 , framePrintAreaValid(src.isFramePrintAreaValid())
2124 bool operator==(const SwFrameAreaDefinition& src) const
2126 return frameArea == src.getFrameArea() && framePrintArea == src.getFramePrintArea()
2127 && frameAreaPositionValid == src.isFrameAreaPositionValid()
2128 && frameAreaSizeValid == src.isFrameAreaSizeValid()
2129 && framePrintAreaValid == src.isFramePrintAreaValid();
2132 std::vector<FrameData> maFrameDatas;
2135 bool PosSizeOscillationControl::OscillationDetected(const SwFrameAreaDefinition& rFrameArea)
2137 for (size_t i = 0; i < maFrameDatas.size(); ++i)
2139 const auto& f = maFrameDatas[i];
2140 if (f == rFrameArea)
2142 SAL_WARN("sw.layout",
2143 "PosSize oscillation: frame " << i << " repeated; total frames " << maFrameDatas.size());
2144 return true;
2148 if (maFrameDatas.size() == 20) // stack is full -> oscillation
2150 SAL_WARN("sw.layout", "PosSize oscillation: max frames");
2151 return true;
2154 maFrameDatas.emplace_back(rFrameArea);
2155 return false;
2159 // extern because static can't be friend
2160 void FriendHackInvalidateRowFrame(SwFrameAreaDefinition & rRowFrame)
2162 // hilariously static_cast<SwTabFrame*>(GetLower()) would not require friend declaration, but it's UB...
2163 rRowFrame.setFrameAreaPositionValid(false);
2166 static void InvalidateFramePositions(SwFrame * pFrame)
2168 while (pFrame)
2170 if (pFrame->IsLayoutFrame())
2172 InvalidateFramePositions(pFrame->GetLower());
2174 else if (pFrame->IsTextFrame())
2176 pFrame->Prepare(PrepareHint::FramePositionChanged);
2178 pFrame = pFrame->GetNext();
2182 void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext)
2184 if ( IsJoinLocked() || StackHack::IsLocked() || StackHack::Count() > 50 )
2185 return;
2187 if ( HasFollow() )
2189 SwTabFrame* pFollowFrame = GetFollow();
2190 OSL_ENSURE( !pFollowFrame->IsJoinLocked() || !pFollowFrame->IsRebuildLastLine(),
2191 "SwTabFrame::MakeAll for master while follow is in RebuildLastLine()" );
2192 if ( pFollowFrame->IsJoinLocked() && pFollowFrame->IsRebuildLastLine() )
2193 return;
2196 PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )
2198 LockJoin(); //I don't want to be destroyed on the way.
2199 SwLayNotify aNotify( this ); //does the notification in the DTor
2200 // If pos is invalid, we have to call a SetInvaKeep at aNotify.
2201 // Otherwise the keep attribute would not work in front of a table.
2202 const bool bOldValidPos = isFrameAreaPositionValid();
2204 //If my neighbour is my Follow at the same time, I'll swallow it up.
2205 // OD 09.04.2003 #108698# - join all follows, which are placed on the
2206 // same page/column.
2207 // OD 29.04.2003 #109213# - join follow, only if join for the follow
2208 // is not locked. Otherwise, join will not be performed and this loop
2209 // will be endless.
2210 while ( GetNext() && GetNext() == GetFollow() &&
2211 CanDeleteFollow(GetFollow())
2214 if ( HasFollowFlowLine() )
2215 RemoveFollowFlowLine();
2216 Join();
2219 // The bRemoveFollowFlowLinePending is set if the split attribute of the
2220 // last line is set:
2221 if ( IsRemoveFollowFlowLinePending() && HasFollowFlowLine() )
2223 if ( RemoveFollowFlowLine() )
2224 Join();
2225 SetRemoveFollowFlowLinePending( false );
2228 if (m_bResizeHTMLTable) //Optimized interplay with grow/shrink of the content
2230 m_bResizeHTMLTable = false;
2231 SwHTMLTableLayout *pLayout = GetTable()->GetHTMLTableLayout();
2232 if ( pLayout )
2233 m_bCalcLowers = pLayout->Resize(
2234 pLayout->GetBrowseWidthByTabFrame( *this ) );
2237 // as long as bMakePage is true, a new page can be created (exactly once)
2238 bool bMakePage = true;
2239 // bMovedBwd gets set to true when the frame flows backwards
2240 bool bMovedBwd = false;
2241 // as long as bMovedFwd is false, the Frame may flow backwards (until
2242 // it has been moved forward once)
2243 bool bMovedFwd = false;
2244 // gets set to true when the Frame is split
2245 bool bSplit = false;
2246 const bool bFootnotesInDoc = !GetFormat()->GetDoc()->GetFootnoteIdxs().empty();
2247 const bool bFly = IsInFly();
2249 std::optional<SwBorderAttrAccess> oAccess(std::in_place, SwFrame::GetCache(), this);
2250 const SwBorderAttrs *pAttrs = oAccess->Get();
2252 bool const isHiddenNow(IsHiddenNow());
2253 // All rows should keep together
2254 bool bDontSplit = isHiddenNow
2255 || (!IsFollow() && !GetFormat()->GetLayoutSplit().GetValue());
2257 // The number of repeated headlines
2258 const sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat();
2260 // This flag indicates that we are allowed to try to split the
2261 // table rows.
2262 bool bTryToSplit = true;
2264 // Indicates that two individual rows may keep together, based on the keep
2265 // attribute set at the first paragraph in the first cell.
2266 bool bTableRowKeep = !bDontSplit && GetFormat()->GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::TABLE_ROW_KEEP);
2267 if (SwFlyFrame* pFly = FindFlyFrame())
2269 if (pFly->IsFlySplitAllowed())
2271 // Ignore the above text node -> row inheritance for floating tables.
2272 bTableRowKeep = false;
2274 else if (!pFly->GetNextLink())
2276 // If the fly is not allowed to split and is not chained, then it makes no sense to
2277 // split the table.
2278 bDontSplit = true;
2282 // The Magic Move: Used for the table row keep feature.
2283 // If only the last row of the table wants to keep (implicitly by setting
2284 // keep for the first paragraph in the first cell), and this table does
2285 // not have a next, the last line will be cut. Loop prevention: Only
2286 // one try.
2287 // WHAT IS THIS??? It "magically" hides last line (paragraph) in a table,
2288 // if first is set to keep with next???
2289 bool bLastRowHasToMoveToFollow = false;
2290 bool bLastRowMoveNoMoreTries = false;
2292 const bool bLargeTable = GetTable()->GetTabLines().size() > 64; //arbitrary value, virtually guaranteed to be larger than one page.
2293 const bool bEmulateTableKeep = !bLargeTable && bTableRowKeep
2294 && !pAttrs->GetAttrSet().GetKeep().GetValue()
2295 && AreAllRowsKeepWithNext(GetFirstNonHeadlineRow(), /*bCheckParents=*/false);
2296 // The beloved keep attribute
2297 const bool bKeep{!isHiddenNow && IsKeep(pAttrs->GetAttrSet().GetKeep(), GetBreakItem(), bEmulateTableKeep)};
2299 // Join follow table, if this table is not allowed to split:
2300 if ( bDontSplit )
2302 while ( GetFollow() && !GetFollow()->IsJoinLocked() )
2304 if ( HasFollowFlowLine() )
2305 RemoveFollowFlowLine();
2306 Join();
2310 // Join follow table, if this does not have enough (repeated) lines:
2311 if ( nRepeat )
2313 if( GetFollow() && !GetFollow()->IsJoinLocked() &&
2314 nullptr == GetFirstNonHeadlineRow() )
2316 if ( HasFollowFlowLine() )
2317 RemoveFollowFlowLine();
2318 Join();
2322 // Join follow table, if last row of this table should keep:
2323 if ( bTableRowKeep && GetFollow() && !GetFollow()->IsJoinLocked() )
2325 const SwRowFrame* pTmpRow = static_cast<const SwRowFrame*>(GetLastLower());
2326 if ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() )
2328 // Special case: pTmpRow wants to keep with next, but allows splitting, and some its
2329 // cells span several rows - i.e., also the next row. In this case, the "split" of the
2330 // spanning cells of the pTmpRow may still happen by moving next row to the next page,
2331 // even here with bTableRowKeep.
2332 bool bCellSpanCanSplit = false;
2333 if (pTmpRow->IsRowSplitAllowed())
2335 for (auto pCellFrame = static_cast<const SwCellFrame*>(pTmpRow->GetLower());
2336 pCellFrame;
2337 pCellFrame = static_cast<const SwCellFrame*>(pCellFrame->GetNext()))
2339 if (pCellFrame->GetTabBox()->getRowSpan() > 1) // Master cell
2341 bCellSpanCanSplit = true;
2342 break;
2347 if (!bCellSpanCanSplit)
2349 if (HasFollowFlowLine())
2350 RemoveFollowFlowLine();
2351 Join();
2356 // a new one is moved forwards immediately
2357 if ( !getFrameArea().Top() && IsFollow() )
2359 SwFrame *pPre = GetPrev();
2360 if ( pPre && pPre->IsTabFrame() && static_cast<SwTabFrame*>(pPre)->GetFollow() == this)
2362 // don't make the effort to move fwd if its known
2363 // conditions that are known not to work
2364 if (IsInFootnote() && ForbiddenForFootnoteCntFwd())
2365 bMakePage = false;
2366 else if (!MoveFwd(bMakePage, false))
2367 bMakePage = false;
2368 bMovedFwd = true;
2372 int nUnSplitted = 5; // Just another loop control :-(
2373 int nThrowAwayValidLayoutLimit = 5; // And another one :-(
2374 PosSizeOscillationControl posSizeOscillationControl; // And yet another one.
2375 SwRectFnSet aRectFnSet(this);
2376 while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
2378 const bool bMoveable = IsMoveable();
2379 if (bMoveable &&
2380 !(bMovedFwd && bEmulateTableKeep) )
2381 if ( CheckMoveFwd( bMakePage, bKeep && KEEPTAB, bEmulateTableKeep ) )
2383 bMovedFwd = true;
2384 m_bCalcLowers = true;
2385 // #i99267#
2386 // reset <bSplit> after forward move to assure that follows
2387 // can be joined, if further space is available.
2388 bSplit = false;
2391 Point aOldPos( aRectFnSet.GetPos(getFrameArea()) );
2392 MakePos();
2394 if (isHiddenNow)
2395 { // after MakePos()
2396 MakeValidZeroHeight();
2399 if ( aOldPos != aRectFnSet.GetPos(getFrameArea()) )
2401 if ( aOldPos.Y() != aRectFnSet.GetTop(getFrameArea()) )
2403 SwHTMLTableLayout *pLayout = GetTable()->GetHTMLTableLayout();
2404 if( pLayout )
2406 oAccess.reset();
2407 m_bCalcLowers |= pLayout->Resize(
2408 pLayout->GetBrowseWidthByTabFrame( *this ) );
2411 if (!isHiddenNow)
2413 setFramePrintAreaValid(false);
2415 aNotify.SetLowersComplete( false );
2417 SwFrame const*const pPre{bKeep ? nullptr : FindPrevIgnoreHidden()};
2418 if (bKeep || (nullptr != pPre && pPre->GetAttrSet()->GetKeep().GetValue()))
2420 m_bCalcLowers = true;
2422 if (GetLower())
2423 { // it's possible that the rows already have valid pos - but it is surely wrong if the table's pos changed!
2424 FriendHackInvalidateRowFrame(*GetLower());
2425 // invalidate text frames to get rid of their SwFlyPortions
2426 InvalidateFramePositions(GetLower());
2430 //We need to know the height of the first row, because the master needs
2431 //to be invalidated if it shrinks and then absorb the row if possible.
2432 tools::Long n1StLineHeight = 0;
2433 if ( IsFollow() )
2435 SwFrame* pFrame = GetFirstNonHeadlineRow();
2436 if ( pFrame )
2437 n1StLineHeight = aRectFnSet.GetHeight(pFrame->getFrameArea());
2440 if ( !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
2442 const tools::Long nOldPrtWidth = aRectFnSet.GetWidth(getFramePrintArea());
2443 const tools::Long nOldFrameWidth = aRectFnSet.GetWidth(getFrameArea());
2444 const Point aOldPrtPos = aRectFnSet.GetPos(getFramePrintArea());
2446 if (!oAccess)
2448 oAccess.emplace(SwFrame::GetCache(), this);
2449 pAttrs = oAccess->Get();
2451 Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs );
2453 SwHTMLTableLayout *pLayout = GetTable()->GetHTMLTableLayout();
2454 if ( pLayout &&
2455 (aRectFnSet.GetWidth(getFramePrintArea()) != nOldPrtWidth ||
2456 aRectFnSet.GetWidth(getFrameArea()) != nOldFrameWidth) )
2458 oAccess.reset();
2459 m_bCalcLowers |= pLayout->Resize(
2460 pLayout->GetBrowseWidthByTabFrame( *this ) );
2462 if ( aOldPrtPos != aRectFnSet.GetPos(getFramePrintArea()) )
2463 aNotify.SetLowersComplete( false );
2466 // If this is the first one in a chain, check if this can flow
2467 // backwards (if this is movable at all).
2468 // To prevent oscillations/loops, check that this has not just
2469 // flowed forwards.
2470 if ( !bMovedFwd && (bMoveable || bFly) && lcl_NoPrev( *this ) )
2472 // for Follows notify Master.
2473 // only move Follow if it has to skip empty pages.
2474 if ( IsFollow() )
2476 // Only if the height of the first line got smaller.
2477 SwFrame *pFrame = GetFirstNonHeadlineRow();
2478 if( pFrame && n1StLineHeight >aRectFnSet.GetHeight(pFrame->getFrameArea()) )
2480 SwTabFrame *pMaster = FindMaster();
2481 bool bDummy;
2482 if ( ShouldBwdMoved( pMaster->GetUpper(), bDummy ) )
2483 pMaster->InvalidatePos();
2486 SwFootnoteBossFrame *pOldBoss = bFootnotesInDoc ? FindFootnoteBossFrame( true ) : nullptr;
2487 bool bReformat;
2488 std::optional<SfxDeleteListener> oDeleteListener;
2489 if (pOldBoss)
2490 oDeleteListener.emplace(*pOldBoss);
2491 SwFrameDeleteGuard g(this);
2492 if ( MoveBwd( bReformat ) )
2494 SAL_WARN_IF(oDeleteListener && oDeleteListener->WasDeleted(), "sw.layout", "SwFootnoteBossFrame unexpectedly deleted");
2496 aRectFnSet.Refresh(this);
2497 bMovedBwd = true;
2498 aNotify.SetLowersComplete( false );
2499 if (bFootnotesInDoc && !oDeleteListener->WasDeleted())
2500 MoveLowerFootnotes( nullptr, pOldBoss, nullptr, true );
2501 if ( bReformat || bKeep )
2503 tools::Long nOldTop = aRectFnSet.GetTop(getFrameArea());
2504 MakePos();
2505 if( nOldTop != aRectFnSet.GetTop(getFrameArea()) )
2507 SwHTMLTableLayout *pHTMLLayout =
2508 GetTable()->GetHTMLTableLayout();
2509 if( pHTMLLayout )
2511 oAccess.reset();
2512 m_bCalcLowers |= pHTMLLayout->Resize(
2513 pHTMLLayout->GetBrowseWidthByTabFrame( *this ) );
2516 if (!isHiddenNow)
2518 setFramePrintAreaValid(false);
2521 if (!oAccess)
2523 oAccess.emplace(SwFrame::GetCache(), this);
2524 pAttrs = oAccess->Get();
2526 Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs );
2529 oAccess.reset();
2531 lcl_RecalcTable( *this, nullptr, aNotify );
2533 m_bLowersFormatted = true;
2534 if ( bKeep && KEEPTAB )
2537 // Consider case that table is inside another table,
2538 // because it has to be avoided, that superior table
2539 // is formatted.
2540 // Thus, find next content, table or section
2541 // and, if a section is found, get its first
2542 // content.
2543 if ( nullptr != sw_FormatNextContentForKeep( this ) && !GetNext() )
2545 setFrameAreaPositionValid(false);
2552 //Again an invalid value? - do it again...
2553 if ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
2554 continue;
2556 // check, if calculation of table frame is ready.
2558 // Local variable <nDistanceToUpperPrtBottom>
2559 // Introduce local variable and init it with the distance from the
2560 // table frame bottom to the bottom of the upper printing area.
2561 // Note: negative values denotes the situation that table frame doesn't fit in its upper.
2562 SwTwips nDistanceToUpperPrtBottom =
2563 aRectFnSet.BottomDist(getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
2565 /// In online layout try to grow upper of table frame, if table frame doesn't fit in its upper.
2566 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
2567 const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
2568 if ( nDistanceToUpperPrtBottom < 0 && bBrowseMode )
2570 if ( GetUpper()->Grow( -nDistanceToUpperPrtBottom ) )
2572 // upper is grown --> recalculate <nDistanceToUpperPrtBottom>
2573 nDistanceToUpperPrtBottom = aRectFnSet.BottomDist(getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
2577 if (GetFollow() && GetUpper()->IsFlyFrame())
2579 auto pUpper = static_cast<SwFlyFrame*>(GetUpper());
2580 if (pUpper->IsFlySplitAllowed())
2582 // We have a follow tab frame that may be joined, and we're directly in a split fly.
2583 // See if the fly could grow.
2584 SwTwips nTest = GetUpper()->Grow(LONG_MAX, /*bTst=*/true);
2585 if (nTest >= aRectFnSet.GetHeight(GetFollow()->getFrameArea()))
2587 // We have space to join at least one follow tab frame.
2588 SwTwips nRequest = 0;
2589 for (SwTabFrame* pFollow = GetFollow(); pFollow; pFollow = pFollow->GetFollow())
2591 nRequest += aRectFnSet.GetHeight(pFollow->getFrameArea());
2593 // Try to grow the split fly to join all follows.
2594 pUpper->Grow(nRequest);
2595 // Determine what is space we actually got from the requested space.
2596 nDistanceToUpperPrtBottom = aRectFnSet.BottomDist(getFrameArea(), aRectFnSet.GetPrtBottom(*pUpper));
2601 // If there is still some space left in the upper, we check if we
2602 // can join some rows of the follow.
2603 // Setting bLastRowHasToMoveToFollow to true means we want to force
2604 // the table to be split! Only skip this if condition once.
2605 if( nDistanceToUpperPrtBottom >= 0 && !bLastRowHasToMoveToFollow )
2607 // If there is space left in the upper printing area, join as for trial
2608 // at least one further row of an existing follow.
2609 if ( !bSplit && GetFollow() )
2611 bool bDummy;
2612 if (!(HasFollowFlowLine()
2613 && GetFollow()->GetFirstNonHeadlineRow()->IsDeleteForbidden())
2614 && GetFollow()->ShouldBwdMoved(GetUpper(), bDummy))
2616 SwFrame *pTmp = GetUpper();
2617 SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*pTmp);
2618 if ( bBrowseMode )
2619 nDeadLine += pTmp->Grow( LONG_MAX, true );
2620 bool bFits = aRectFnSet.BottomDist(getFrameArea(), nDeadLine) > 0;
2621 if (!bFits && aRectFnSet.GetHeight(GetFollow()->getFrameArea()) == 0)
2622 // The follow should move backwards, so allow the case
2623 // when the upper has no space, but the follow is
2624 // empty.
2625 bFits = aRectFnSet.BottomDist(getFrameArea(), nDeadLine) >= 0;
2627 if (bFits)
2629 // The follow table's wants to move backwards, see if the first row has a
2630 // split fly anchored in it that would have more space than what we have:
2631 SwRowFrame* pRow = GetFollow()->GetFirstNonHeadlineRow();
2632 if (pRow)
2634 SwPageFrame* pPage = GetFollow()->FindPageFrame();
2635 SwSortedObjs* pPageObjs = pPage->GetSortedObjs();
2636 if (pPageObjs)
2638 bool bSplitFly = false;
2639 for (size_t i = 0; i < pPageObjs->size(); ++i)
2641 SwAnchoredObject* pAnchoredObj = (*pPage->GetSortedObjs())[i];
2642 auto pFly = pAnchoredObj->DynCastFlyFrame();
2643 if (!pFly || !pFly->IsFlySplitAllowed())
2645 continue;
2648 SwFrame* pFlyAnchor = pFly->FindAnchorCharFrame();
2649 if (!pFlyAnchor || !pRow->IsAnLower(pFlyAnchor))
2651 continue;
2654 bSplitFly = true;
2655 break;
2657 SwTwips nFollowFirstRowHeight = aRectFnSet.GetHeight(pRow->getFrameArea());
2658 SwTwips nSpace = aRectFnSet.BottomDist(getFrameArea(), nDeadLine);
2659 if (bSplitFly && nFollowFirstRowHeight > 0 && nSpace < nFollowFirstRowHeight)
2661 // The row has at least one split fly and the row would not fit
2662 // to our remaining space, when also taking flys into account,
2663 // so that's not a fit.
2664 bFits = false;
2670 if (bFits)
2672 // First, we remove an existing follow flow line.
2673 if ( HasFollowFlowLine() )
2675 SwFrame* pLastLine = GetLastLower();
2676 RemoveFollowFlowLine();
2677 // invalidate and rebuild last row
2678 if ( pLastLine )
2680 ::SwInvalidateAll( pLastLine, LONG_MAX );
2681 SetRebuildLastLine( true );
2682 lcl_RecalcRow(*static_cast<SwRowFrame*>(pLastLine), LONG_MAX);
2683 SetRebuildLastLine( false );
2686 SwFrame* pRow = GetFollow()->GetFirstNonHeadlineRow();
2688 if ( !pRow || !pRow->GetNext() )
2689 // The follow became empty and hence useless
2690 Join();
2692 continue;
2695 // If there is no follow flow line, we move the first
2696 // row in the follow table to the master table.
2697 SwRowFrame *pRow = GetFollow()->GetFirstNonHeadlineRow();
2699 // The follow became empty and hence useless
2700 if ( !pRow )
2702 Join();
2703 continue;
2706 const SwTwips nOld = aRectFnSet.GetHeight(getFrameArea());
2707 tools::Long nRowsToMove = lcl_GetMaximumLayoutRowSpan( *pRow );
2708 SwFrame* pRowToMove = pRow;
2710 while ( pRowToMove && nRowsToMove-- > 0 )
2712 const bool bMoveFootnotes = bFootnotesInDoc && !GetFollow()->IsJoinLocked();
2714 SwFootnoteBossFrame *pOldBoss = nullptr;
2715 if ( bMoveFootnotes )
2716 pOldBoss = pRowToMove->FindFootnoteBossFrame( true );
2718 SwFrame* pNextRow = pRowToMove->GetNext();
2720 if ( !pNextRow )
2722 // The follow became empty and hence useless
2723 Join();
2725 else
2727 pRowToMove->Cut();
2728 pRowToMove->Paste( this );
2731 // Move the footnotes!
2732 if ( bMoveFootnotes )
2733 if ( static_cast<SwLayoutFrame*>(pRowToMove)->MoveLowerFootnotes( nullptr, pOldBoss, FindFootnoteBossFrame( true ), true ) )
2734 GetUpper()->Calc(pRenderContext);
2736 pRowToMove = pNextRow;
2739 if ( nOld != aRectFnSet.GetHeight(getFrameArea()) )
2740 lcl_RecalcTable( *this, static_cast<SwLayoutFrame*>(pRow), aNotify );
2742 continue;
2746 else if ( KEEPTAB )
2748 bool bFormat = false;
2749 if ( bKeep )
2750 bFormat = true;
2751 else if ( bTableRowKeep && !bLastRowMoveNoMoreTries )
2753 // We only want to give the last row one chance to move
2754 // to the follow table. Set the flag as early as possible:
2755 bLastRowMoveNoMoreTries = true;
2757 // The last line of the table has to be cut off if:
2758 // 1. The table does not want to keep with its next
2759 // 2. The compatibility option is set and the table is allowed to split
2760 // 3. We did not already cut off the last row
2761 // 4. There is not break after attribute set at the table
2762 // 5. There is no break before attribute set behind the table
2763 // 6. There is no section change behind the table (see IsKeep)
2764 // 7. The last table row wants to keep with its next.
2765 const SwRowFrame* pLastRow = static_cast<const SwRowFrame*>(GetLastLower());
2766 if (pLastRow)
2768 if (!oAccess)
2770 oAccess.emplace(SwFrame::GetCache(), this);
2771 pAttrs = oAccess->Get();
2773 if (!isHiddenNow
2774 && IsKeep(pAttrs->GetAttrSet().GetKeep(), GetBreakItem(), true)
2775 && pLastRow->ShouldRowKeepWithNext())
2777 bFormat = true;
2782 if ( bFormat )
2784 oAccess.reset();
2786 // Consider case that table is inside another table, because
2787 // it has to be avoided, that superior table is formatted.
2788 // Thus, find next content, table or section and, if a section
2789 // is found, get its first content.
2790 const SwFrame* pTmpNxt = sw_FormatNextContentForKeep( this );
2792 // The last row wants to keep with the frame behind the table.
2793 // Check if the next frame is on a different page and valid.
2794 // In this case we do a magic trick:
2795 if ( !bKeep && !GetNext() && pTmpNxt && pTmpNxt->isFrameAreaDefinitionValid() )
2797 setFrameAreaPositionValid(false);
2798 bLastRowHasToMoveToFollow = true;
2803 if ( isFrameAreaDefinitionValid() )
2805 if (m_bCalcLowers)
2807 lcl_RecalcTable( *this, nullptr, aNotify );
2808 m_bLowersFormatted = true;
2809 m_bCalcLowers = false;
2811 else if (m_bONECalcLowers)
2813 // tdf#147526 is a case of a macro which results in a null Lower() result
2814 if (SwRowFrame* pLower = static_cast<SwRowFrame*>(Lower()))
2815 lcl_RecalcRow(*pLower, LONG_MAX);
2816 m_bONECalcLowers = false;
2819 continue;
2822 // I don't fit in the upper Frame anymore, therefore it's the
2823 // right moment to do some preferably constructive changes.
2825 // If I'm NOT allowed to leave the upper Frame, I've got a problem.
2826 // Following Arthur Dent, we do the only thing that you can do with
2827 // an unsolvable problem: We ignore it with all our power.
2828 if ( !bMoveable )
2830 if (m_bCalcLowers && isFrameAreaDefinitionValid())
2832 lcl_RecalcTable( *this, nullptr, aNotify );
2833 m_bLowersFormatted = true;
2834 m_bCalcLowers = false;
2836 else if (m_bONECalcLowers)
2838 lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), LONG_MAX);
2839 m_bONECalcLowers = false;
2842 // It does not make sense to cut off the last line if we are
2843 // not moveable:
2844 bLastRowHasToMoveToFollow = false;
2846 continue;
2849 if (m_bCalcLowers && isFrameAreaDefinitionValid())
2851 lcl_RecalcTable( *this, nullptr, aNotify );
2852 m_bLowersFormatted = true;
2853 m_bCalcLowers = false;
2854 if( !isFrameAreaDefinitionValid() )
2855 continue;
2858 // First try to split the table. Condition:
2859 // 1. We have at least one non headline row
2860 // 2. If this row wants to keep, we need an additional row
2861 // 3. The table is allowed to split or we do not have a pIndPrev:
2862 SwFrame* pIndPrev = GetIndPrev();
2864 SwFlyFrame* pFly = FindFlyFrame();
2865 if (!pIndPrev && pFly && pFly->IsFlySplitAllowed())
2867 auto pFlyAtContent = static_cast<SwFlyAtContentFrame*>(pFly);
2868 SwFrame* pAnchor = pFlyAtContent->FindAnchorCharFrame();
2869 if (pAnchor)
2871 // If the anchor of the split has a previous frame, we're allowed to move forward.
2872 pIndPrev = pAnchor->GetIndPrev();
2876 const SwRowFrame* pFirstNonHeadlineRow = GetFirstNonHeadlineRow();
2877 // #i120016# if this row wants to keep, allow split in case that all rows want to keep with next,
2878 // the table can not move forward as it is the first one and a split is in general allowed.
2879 const bool bAllowSplitOfRow = bTableRowKeep && !pIndPrev && AreAllRowsKeepWithNext(pFirstNonHeadlineRow);
2880 // tdf91083 MSCompat: this extends bAllowSplitOfRow (and perhaps should just replace it).
2881 // If the kept-together items cannot move to a new page, a table split is in general allowed.
2882 const bool bEmulateTableKeepSplitAllowed = bEmulateTableKeep && !IsKeepFwdMoveAllowed(/*IgnoreMyOwnKeepValue=*/true);
2884 if ( pFirstNonHeadlineRow && nUnSplitted > 0 &&
2885 ( bEmulateTableKeepSplitAllowed || bAllowSplitOfRow ||
2886 ( ( !bTableRowKeep || pFirstNonHeadlineRow->GetNext() ||
2887 !pFirstNonHeadlineRow->ShouldRowKeepWithNext()
2888 ) && ( !bDontSplit || !pIndPrev )
2889 ) ) )
2891 // #i29438#
2892 // Special DoNotSplit cases:
2893 // We better avoid splitting of a row frame if we are inside a columned
2894 // section which has a height of 0, because this is not growable and thus
2895 // all kinds of unexpected things could happen.
2896 if ( IsInSct() && FindSctFrame()->Lower()->IsColumnFrame() &&
2897 0 == aRectFnSet.GetHeight(GetUpper()->getFrameArea())
2900 bTryToSplit = false;
2903 // 1. Try: bTryToSplit = true => Try to split the row.
2904 // 2. Try: bTryToSplit = false => Split the table between the rows.
2905 if ( pFirstNonHeadlineRow->GetNext() || bTryToSplit )
2907 SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
2908 bool bFlySplit = false;
2909 if (GetUpper()->IsFlyFrame())
2911 // See if this is a split fly that can also grow.
2912 auto pUpperFly = static_cast<SwFlyFrame*>(GetUpper());
2913 bFlySplit = pUpperFly->IsFlySplitAllowed();
2915 if (bFlySplit && bTryToSplit)
2917 // This is a split fly that wants to split the row itself. See if it's also
2918 // nested. If so, we'll want to know if the row split has rowspans.
2919 SwTextFrame* pAnchorCharFrame = pUpperFly->FindAnchorCharFrame();
2920 if (pAnchorCharFrame && pAnchorCharFrame->IsInFly())
2922 // Find the row we'll split.
2923 SwTwips nRemaining
2924 = aRectFnSet.YDiff(nDeadLine, aRectFnSet.GetTop(getFrameArea()));
2925 nRemaining -= aRectFnSet.GetTopMargin(*this);
2926 const SwFrame* pRow = Lower();
2927 for (; pRow->GetNext(); pRow = pRow->GetNext())
2929 if (nRemaining < aRectFnSet.GetHeight(pRow->getFrameArea()))
2931 break;
2934 nRemaining -= aRectFnSet.GetHeight(pRow->getFrameArea());
2936 // See if any cells have rowspans.
2937 for (const SwFrame* pLower = pRow->GetLower(); pLower;
2938 pLower = pLower->GetNext())
2940 auto pCellFrame = static_cast<const SwCellFrame*>(pLower);
2941 if (pCellFrame->GetTabBox()->getRowSpan() != 1)
2943 // The cell has a rowspan, don't split the row itself in this
2944 // case (but just move it forward, i.e. split between the rows).
2945 bTryToSplit = false;
2946 break;
2952 if( IsInSct() || GetUpper()->IsInTab() || bFlySplit )
2953 nDeadLine = aRectFnSet.YInc( nDeadLine,
2954 GetUpper()->Grow( LONG_MAX, true ) );
2957 SwFrameDeleteGuard g(Lower()); // tdf#134965 prevent RemoveFollowFlowLine()
2958 SetInRecalcLowerRow( true );
2959 ::lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), nDeadLine);
2960 SetInRecalcLowerRow( false );
2962 m_bLowersFormatted = true;
2963 aNotify.SetLowersComplete( true );
2965 // One more check if it's really necessary to split the table.
2966 // 1. The table either has to exceed the deadline or
2967 // 2. We explicitly want to cut off the last row.
2968 if( aRectFnSet.BottomDist( getFrameArea(), nDeadLine ) > 0 && !bLastRowHasToMoveToFollow )
2970 continue;
2973 // Set to false again as early as possible.
2974 bLastRowHasToMoveToFollow = false;
2976 // #i52781#
2977 // YaSC - Yet another special case:
2978 // If our upper is inside a table cell which is not allowed
2979 // to split, we do not try to split:
2980 if ( GetUpper()->IsInTab() )
2982 const SwFrame* pTmpRow = GetUpper();
2983 while ( pTmpRow && !pTmpRow->IsRowFrame() )
2984 pTmpRow = pTmpRow->GetUpper();
2985 if ( pTmpRow && !static_cast<const SwRowFrame*>(pTmpRow)->IsRowSplitAllowed() )
2986 continue;
2989 sal_uInt16 nMinNumOfLines = nRepeat;
2991 if ( bTableRowKeep )
2993 const SwRowFrame* pTmpRow = GetFirstNonHeadlineRow();
2994 // Copying the "NEW TABLES" comment, apparently related to commit dcb682563b24bf487a40a9fe7710b4d500850a52
2995 if (pTmpRow && pTmpRow->IsRowSpanLine())
2997 ++nMinNumOfLines;
2998 pTmpRow = static_cast<const SwRowFrame*>(pTmpRow->GetNext());
3001 while ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() )
3003 ++nMinNumOfLines;
3004 pTmpRow = static_cast<const SwRowFrame*>(pTmpRow->GetNext());
3008 if ( !bTryToSplit )
3009 ++nMinNumOfLines;
3011 const SwTwips nBreakLine = aRectFnSet.YInc(
3012 aRectFnSet.GetTop(getFrameArea()),
3013 aRectFnSet.GetTopMargin(*this) +
3014 lcl_GetHeightOfRows( GetLower(), nMinNumOfLines ) );
3016 bool bHadFollowFlowLineBeforeSplit = false;
3017 // Some more checks if we want to call the split algorithm or not:
3018 // The repeating lines / keeping lines still fit into the upper or
3019 // if we do not have an (in)direct Prev, we split anyway.
3020 if( aRectFnSet.YDiff(nDeadLine, nBreakLine) >=0
3021 || !pIndPrev || bEmulateTableKeepSplitAllowed )
3023 aNotify.SetLowersComplete( false );
3024 bSplit = true;
3026 // An existing follow flow line has to be removed.
3027 if ( HasFollowFlowLine() )
3029 if (!nThrowAwayValidLayoutLimit)
3030 continue;
3031 const bool bInitialLoopEndCondition(isFrameAreaDefinitionValid());
3032 bHadFollowFlowLineBeforeSplit = true;
3033 RemoveFollowFlowLine();
3034 const bool bFinalLoopEndCondition(isFrameAreaDefinitionValid());
3036 if (bInitialLoopEndCondition && !bFinalLoopEndCondition)
3038 --nThrowAwayValidLayoutLimit;
3042 oAccess.reset();
3043 bool isFootnoteGrowth(false);
3044 bool bEffectiveTableRowKeep;
3045 if (bTryToSplit == true)
3047 bEffectiveTableRowKeep = bTableRowKeep && !(bAllowSplitOfRow || bEmulateTableKeepSplitAllowed);
3049 else
3051 // The second attempt; ignore all the flags allowing to split the row
3052 bEffectiveTableRowKeep = bTableRowKeep;
3054 const bool bSplitError = !Split(nDeadLine, bTryToSplit,
3055 bEffectiveTableRowKeep,
3056 isFootnoteGrowth);
3058 // tdf#130639 don't start table on a new page after the fallback "switch off repeating header"
3059 if (bSplitError && nRepeat > GetTable()->GetRowsToRepeat())
3061 setFrameAreaPositionValid(false);
3062 break;
3065 if (!bTryToSplit && !bSplitError)
3067 --nUnSplitted;
3070 // #i29771# Two tries to split the table
3071 // If an error occurred during splitting. We start a second
3072 // try, this time without splitting of table rows.
3073 if ( bSplitError && HasFollowFlowLine() )
3074 RemoveFollowFlowLine();
3076 // If splitting the table was successful or not,
3077 // we do not want to have 'empty' follow tables.
3078 if ( GetFollow() && !GetFollow()->GetFirstNonHeadlineRow() )
3080 // For split flys, if we just removed the follow flow line before split,
3081 // then avoid the join in the error + rowsplit case, so split can be called
3082 // again, this time without a rowsplit.
3083 if (!bFlySplit || !bHadFollowFlowLineBeforeSplit || !bSplitError || !bTryToSplit)
3085 Join();
3089 // We want to restore the situation before the failed
3090 // split operation as good as possible. Therefore we
3091 // do some more calculations. Note: Restricting this
3092 // to nDeadLine may not be enough.
3093 // tdf#161508 hack: treat oscillation likewise
3094 if ((bSplitError && bTryToSplit) // no restart if we did not try to split: i72847, i79426
3095 || posSizeOscillationControl.OscillationDetected(*this))
3097 lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), LONG_MAX);
3098 setFrameAreaPositionValid(false);
3099 // tdf#156724 if the table added footnotes, try to split *again*
3100 if (!isFootnoteGrowth)
3102 bTryToSplit = false;
3104 continue;
3107 // If split failed, then next time try without
3108 // allowing to split the table rows.
3109 bTryToSplit = !bSplitError;
3111 //To avoid oscillations the Follow must become valid now
3112 if ( GetFollow() )
3114 // #i80924#
3115 // After a successful split assure that the first row
3116 // is invalid. When graphics are present, this isn't hold.
3117 // Note: defect i80924 could also be fixed, if it is
3118 // assured, that <SwLayNotify::bLowersComplete> is only
3119 // set, if all lower are valid *and* are correct laid out.
3120 if ( !bSplitError && GetFollow()->GetLower() )
3122 GetFollow()->GetLower()->InvalidatePos();
3124 SwRectFnSet fnRectX(GetFollow());
3126 static sal_uInt8 nStack = 0;
3127 if ( !StackHack::IsLocked() && nStack < 4 )
3129 ++nStack;
3130 StackHack aHack;
3131 oAccess.reset();
3133 GetFollow()->MakeAll(pRenderContext);
3135 GetFollow()->SetLowersFormatted(false);
3136 // #i43913# - lock follow table
3137 // to avoid its formatting during the format of
3138 // its content.
3139 const bool bOldJoinLock = GetFollow()->IsJoinLocked();
3140 GetFollow()->LockJoin();
3141 ::lcl_RecalcRow(*static_cast<SwRowFrame*>(GetFollow()->Lower()),
3142 fnRectX.GetBottom(GetFollow()->GetUpper()->getFrameArea()) );
3143 // #i43913#
3144 // #i63632# Do not unlock the
3145 // follow if it wasn't locked before.
3146 if ( !bOldJoinLock )
3147 GetFollow()->UnlockJoin();
3149 if ( !GetFollow()->GetFollow() )
3151 SwFrame* pNxt = static_cast<SwFrame*>(GetFollow())->FindNext();
3152 if ( pNxt )
3154 // #i18103# - no formatting of found next
3155 // frame, if it's a follow section of the
3156 // 'ColLocked' section, the follow table is
3157 // in.
3158 bool bCalcNxt = true;
3159 if ( GetFollow()->IsInSct() && pNxt->IsSctFrame() )
3161 SwSectionFrame* pSct = GetFollow()->FindSctFrame();
3162 if ( pSct->IsColLocked() &&
3163 pSct->GetFollow() == pNxt )
3165 bCalcNxt = false;
3168 if (pNxt->IsHiddenNow())
3169 { // e.g. "testThemeCrash"
3170 bCalcNxt = false;
3172 if ( bCalcNxt )
3174 // tdf#119109 follow was just formatted,
3175 // don't do it again now
3176 FlowFrameJoinLockGuard g(GetFollow());
3177 pNxt->Calc(pRenderContext);
3182 --nStack;
3184 else if ( GetFollow() == GetNext() )
3185 GetFollow()->MoveFwd( true, false );
3187 continue;
3192 // Set to false again as early as possible.
3193 bLastRowHasToMoveToFollow = false;
3195 if( IsInSct() && bMovedFwd && bMakePage && GetUpper()->IsColBodyFrame() &&
3196 GetUpper()->GetUpper()->GetUpper()->IsSctFrame() &&
3197 ( GetUpper()->GetUpper()->GetPrev() || GetIndPrev() ) &&
3198 static_cast<SwSectionFrame*>(GetUpper()->GetUpper()->GetUpper())->MoveAllowed(this) )
3200 bMovedFwd = false;
3203 // #i29771# Reset bTryToSplit flag on change of upper
3204 const SwFrame* pOldUpper = GetUpper();
3206 //Let's see if we find some place anywhere...
3207 if (!bMovedFwd)
3209 bool bMoveAlways = false;
3210 SwFrame* pUpper = GetUpper();
3211 if (pUpper && pUpper->IsFlyFrame())
3213 auto pFlyFrame = static_cast<SwFlyFrame*>(pUpper);
3214 if (pFlyFrame->IsFlySplitAllowed())
3216 // If the anchor of the split has a previous frame, MoveFwd() is allowed to move
3217 // forward.
3218 bMoveAlways = true;
3221 // don't make the effort to move fwd if its known
3222 // conditions that are known not to work
3223 if (IsInFootnote() && ForbiddenForFootnoteCntFwd())
3224 bMakePage = false;
3225 else if (!MoveFwd(bMakePage, false, bMoveAlways))
3226 bMakePage = false;
3229 // #i29771# Reset bSplitError flag on change of upper
3230 if ( GetUpper() != pOldUpper )
3232 bTryToSplit = true;
3233 nUnSplitted = 5;
3236 aRectFnSet.Refresh(this);
3237 m_bCalcLowers = true;
3238 bMovedFwd = true;
3239 aNotify.SetLowersComplete( false );
3240 if ( IsFollow() )
3242 // To avoid oscillations, master should not remain invalid
3243 SwTabFrame *pTab = FindMaster();
3244 if ( pTab->GetUpper() )
3245 pTab->GetUpper()->Calc(pRenderContext);
3246 pTab->Calc(pRenderContext);
3247 pTab->SetLowersFormatted( false );
3250 //If my neighbour is my Follow at the same time, I'll swallow it up.
3251 if ( ( GetNext() && GetNext() == GetFollow() ) || !GetLower() )
3253 if ( HasFollowFlowLine() )
3254 RemoveFollowFlowLine();
3255 if ( GetFollow() )
3256 Join();
3258 else if (!GetNext() && !HasFollowFlowLine() && GetFollow()
3259 && (getFrameArea().Bottom() + GetFollow()->getFrameArea().Height())
3260 < GetUpper()->getFrameArea().Bottom())
3262 // We're the last lower of the upper, no split row and we have a follow. That follow
3263 // fits our upper, still. Prefer joining that follow in the next iteration, instead of
3264 // trying to split the current table.
3265 bSplit = false;
3268 if ( bMovedBwd && GetUpper() )
3270 //During flowing back the upper was animated to do a full repaint,
3271 //we can now skip this after the whole flowing back and forth.
3272 GetUpper()->ResetCompletePaint();
3275 if (m_bCalcLowers && isFrameAreaDefinitionValid())
3277 // #i44910# - format of lower frames unnecessary
3278 // and can cause layout loops, if table doesn't fit and isn't
3279 // allowed to split.
3280 SwTwips nDistToUpperPrtBottom =
3281 aRectFnSet.BottomDist( getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
3283 if (GetUpper()->IsFlyFrame())
3285 SwFlyFrame* pFlyFrame = GetUpper()->FindFlyFrame();
3286 if (pFlyFrame->IsFlySplitAllowed())
3288 SwTextFrame* pAnchor = pFlyFrame->FindAnchorCharFrame();
3289 if (pAnchor && pAnchor->HasFollow())
3291 // The split fly's anchor has a follow frame, we can move there & try to
3292 // split again.
3293 bTryToSplit = true;
3298 if ( nDistToUpperPrtBottom >= 0 || bTryToSplit )
3300 lcl_RecalcTable( *this, nullptr, aNotify );
3301 m_bLowersFormatted = true;
3302 m_bCalcLowers = false;
3303 if (!isFramePrintAreaValid())
3304 m_pTable->SetRowsToRepeat(1);
3306 #if OSL_DEBUG_LEVEL > 0
3307 else
3309 OSL_FAIL( "debug assertion: <SwTabFrame::MakeAll()> - format of table lowers suppressed by fix i44910" );
3311 #endif
3314 } //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
3316 //If my direct predecessor is my master now, it can destroy me during the
3317 //next best opportunity.
3318 if ( IsFollow() )
3320 SwFrame *pPre = GetPrev();
3321 if ( pPre && pPre->IsTabFrame() && static_cast<SwTabFrame*>(pPre)->GetFollow() == this)
3322 pPre->InvalidatePos();
3325 m_bCalcLowers = m_bONECalcLowers = false;
3326 oAccess.reset();
3327 UnlockJoin();
3328 if ( bMovedFwd || bMovedBwd || !bOldValidPos )
3329 aNotify.SetInvaKeep();
3332 static bool IsNextOnSamePage(SwPageFrame const& rPage,
3333 SwTabFrame const& rTabFrame, SwTextFrame const& rAnchorFrame)
3335 for (SwContentFrame const* pContentFrame = rTabFrame.FindNextCnt();
3336 pContentFrame && pContentFrame->FindPageFrame() == &rPage;
3337 pContentFrame = pContentFrame->FindNextCnt())
3339 if (pContentFrame == &rAnchorFrame)
3341 return true;
3344 return false;
3347 /// Calculate the offsets arising because of FlyFrames
3348 bool SwTabFrame::CalcFlyOffsets( SwTwips& rUpper,
3349 tools::Long& rLeftOffset,
3350 tools::Long& rRightOffset,
3351 SwTwips *const pSpaceBelowBottom) const
3353 if (IsHiddenNow())
3355 rUpper = 0;
3356 rLeftOffset = 0;
3357 rRightOffset = 0;
3358 if (pSpaceBelowBottom)
3359 *pSpaceBelowBottom = 0;
3360 return false;
3363 bool bInvalidatePrtArea = false;
3364 const SwPageFrame *pPage = FindPageFrame();
3365 const SwFlyFrame* pMyFly = FindFlyFrame();
3367 // --> #108724# Page header/footer content doesn't have to wrap around
3368 // floating screen objects
3370 const IDocumentSettingAccess& rIDSA = GetFormat()->getIDocumentSettingAccess();
3371 const bool bWrapAllowed = rIDSA.get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) ||
3372 ( !IsInFootnote() && nullptr == FindFooterOrHeader() );
3374 if (!bWrapAllowed || !pPage->GetSortedObjs())
3375 return bInvalidatePrtArea;
3377 SwRectFnSet aRectFnSet(this);
3378 const bool bConsiderWrapOnObjPos
3379 = rIDSA.get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION);
3380 tools::Long nPrtPos = aRectFnSet.GetTop(getFrameArea());
3381 nPrtPos = aRectFnSet.YInc(nPrtPos, rUpper);
3382 SwRect aRect(getFrameArea());
3383 if (pSpaceBelowBottom)
3385 // set to space below table frame
3386 aRectFnSet.SetTopAndHeight(aRect, aRectFnSet.GetBottom(aRect), *pSpaceBelowBottom);
3388 else
3390 tools::Long nYDiff = aRectFnSet.YDiff(aRectFnSet.GetTop(getFramePrintArea()), rUpper);
3391 if (nYDiff > 0)
3392 aRectFnSet.AddBottom(aRect, -nYDiff);
3395 bool bAddVerticalFlyOffsets = rIDSA.get(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS);
3397 for (size_t i = 0; i < pPage->GetSortedObjs()->size(); ++i)
3399 SwAnchoredObject* pAnchoredObj = (*pPage->GetSortedObjs())[i];
3400 auto pFly = pAnchoredObj->DynCastFlyFrame();
3401 if (!pFly)
3402 continue;
3404 const SwRect aFlyRect = pFly->GetObjRectWithSpaces();
3405 // #i26945# - correction of conditions,
3406 // if Writer fly frame has to be considered:
3407 // - no need to check, if top of Writer fly frame differs
3408 // from FAR_AWAY, because it's also checked, if the Writer
3409 // fly frame rectangle overlaps with <aRect>
3410 // - no check, if bottom of anchor frame is prior the top of
3411 // the table, because Writer fly frames can be negative positioned.
3412 // - correct check, if the Writer fly frame is a lower of the
3413 // table, because table lines/rows can split and an at-character
3414 // anchored Writer fly frame could be positioned in the follow
3415 // flow line.
3416 // - add condition, that an existing anchor character text frame
3417 // has to be on the same page as the table.
3418 // E.g., it could happen, that the fly frame is still registered
3419 // at the page frame, the table is on, but it's anchor character
3420 // text frame has already changed its page.
3421 const SwTextFrame* pAnchorCharFrame = pFly->FindAnchorCharFrame();
3422 const SwFormatHoriOrient& rHori= pFly->GetFormat()->GetHoriOrient();
3423 // TODO: why not just ignore HoriOrient?
3424 bool isHoriOrientShiftDown =
3425 rHori.GetHoriOrient() == text::HoriOrientation::NONE
3426 || rHori.GetHoriOrient() == text::HoriOrientation::LEFT
3427 || rHori.GetHoriOrient() == text::HoriOrientation::RIGHT;
3428 // Only consider invalid Writer fly frames if they'll be shifted down.
3429 bool bIgnoreFlyValidity = bAddVerticalFlyOffsets && isHoriOrientShiftDown;
3430 bool bConsiderFly =
3431 // #i46807# - do not consider invalid
3432 // Writer fly frames.
3433 (pFly->isFrameAreaDefinitionValid() || bIgnoreFlyValidity)
3434 // fly anchored at character or at paragraph
3435 && pFly->IsFlyAtContentFrame()
3436 // fly overlaps with corresponding table rectangle
3437 && aFlyRect.Overlaps(aRect)
3438 // fly isn't lower of table and
3439 // anchor character frame of fly isn't lower of table
3440 && (pSpaceBelowBottom // not if in ShouldBwdMoved
3441 || (!IsAnLower(pFly) && (!pAnchorCharFrame || !IsAnLower(pAnchorCharFrame))))
3442 // table isn't lower of fly
3443 && !pFly->IsAnLower(this)
3444 // fly is lower of fly, the table is in
3445 // #123274# - correction
3446 // assure that fly isn't a lower of a fly, the table isn't in.
3447 // E.g., a table in the body doesn't wrap around a graphic,
3448 // which is inside a frame.
3449 && (!pMyFly || pMyFly->IsAnLower(pFly))
3450 && pMyFly == pFly->GetAnchorFrameContainingAnchPos()->FindFlyFrame()
3451 // anchor frame not on following page
3452 && pPage->GetPhyPageNum() >= pFly->GetAnchorFrame()->FindPageFrame()->GetPhyPageNum()
3453 // anchor character text frame on same page
3454 && (!pAnchorCharFrame ||
3455 pAnchorCharFrame->FindPageFrame()->GetPhyPageNum() == pPage->GetPhyPageNum());
3457 if (!bConsiderFly)
3458 continue;
3460 const SwFrame* pFlyHeaderFooterFrame = pFly->GetAnchorFrame()->FindFooterOrHeader();
3461 const SwFrame* pThisHeaderFooterFrame = FindFooterOrHeader();
3462 if (pFlyHeaderFooterFrame != pThisHeaderFooterFrame
3463 // #148493# If bConsiderWrapOnObjPos is set,
3464 // we want to consider the fly if it is located in the header and
3465 // the table is located in the body:
3466 && (!bConsiderWrapOnObjPos || nullptr != pThisHeaderFooterFrame
3467 || !pFlyHeaderFooterFrame->IsHeaderFrame()))
3469 continue;
3472 text::WrapTextMode nSurround = pFly->GetFormat()->GetSurround().GetSurround();
3473 // If the frame format is a TextBox of a draw shape,
3474 // then use the surround of the original shape.
3476 bool bWrapThrough = nSurround == text::WrapTextMode_THROUGH;
3477 SwTextBoxHelper::getShapeWrapThrough(pFly->GetFormat(), bWrapThrough);
3478 if (bWrapThrough)
3479 continue;
3481 if (nSurround == text::WrapTextMode_THROUGH)
3482 nSurround = text::WrapTextMode_PARALLEL;
3484 bool bShiftDown = css::text::WrapTextMode_NONE == nSurround;
3485 bool bSplitFly = pFly->IsFlySplitAllowed();
3486 const SwRect aFlyRectWithoutSpaces = pFly->GetObjRect();
3487 if (!bShiftDown && bAddVerticalFlyOffsets)
3489 if (nSurround == text::WrapTextMode_PARALLEL && isHoriOrientShiftDown)
3491 // We know that wrapping was requested and the table frame overlaps with
3492 // the fly frame. Check if the print area overlaps with the fly frame as
3493 // well (in case the table does not use all the available width).
3494 basegfx::B1DRange aTabRange(
3495 aRectFnSet.GetLeft(aRect) + aRectFnSet.GetLeft(getFramePrintArea()),
3496 aRectFnSet.GetLeft(aRect) + aRectFnSet.GetLeft(getFramePrintArea())
3497 + aRectFnSet.GetWidth(getFramePrintArea()));
3499 // Ignore spacing when determining the left/right edge of the fly, like
3500 // Word does.
3501 basegfx::B1DRange aFlyRange(aRectFnSet.GetLeft(aFlyRectWithoutSpaces),
3502 aRectFnSet.GetRight(aFlyRectWithoutSpaces));
3504 // If it does, shift the table down. Do this only in the compat case,
3505 // normally an SwFlyPortion is created instead that increases the height
3506 // of the first table row.
3507 bShiftDown = aTabRange.overlaps(aFlyRange);
3509 if (bSplitFly && pFly->GetAnchorFrame()->GetUpper() == GetUpper())
3511 // Split fly followed by an inline table. Check if we have enough space to shift
3512 // to the right instead.
3513 SwTwips nShiftedTabRight = aFlyRectWithoutSpaces.Right() + getFramePrintArea().Width();
3514 SwTwips nRightShiftDeadline = pFly->GetAnchorFrame()->GetUpper()->getFrameArea().Right();
3515 if (aRectFnSet.XDiff(nRightShiftDeadline, nShiftedTabRight) >= 0)
3517 bShiftDown = false;
3523 if (bShiftDown)
3525 // possible cases:
3526 // both in body
3527 // both in same fly
3528 // any comb. of body, footnote, header/footer
3529 // to keep it safe, check only in doc body vs page margin for now
3530 tools::Long nBottom = aRectFnSet.GetBottom(aFlyRect);
3531 // tdf#138039 don't grow beyond the page body
3532 // if the fly is anchored below the table; the fly
3533 // must move with its anchor frame to the next page
3534 SwRectFnSet fnPage(pPage);
3535 if (!IsInDocBody() // TODO
3536 || fnPage.YDiff(fnPage.GetBottom(aFlyRect), fnPage.GetPrtBottom(*pPage)) <= 0
3537 || !IsNextOnSamePage(
3538 *pPage, *this,
3539 *static_cast<SwTextFrame*>(pFly->GetAnchorFrameContainingAnchPos())))
3541 if (aRectFnSet.YDiff(nPrtPos, nBottom) < 0)
3542 nPrtPos = nBottom;
3543 // tdf#116501 subtract flys blocking space from below
3544 // TODO this may not work ideally for multiple flys
3545 if (pSpaceBelowBottom && aRectFnSet.YDiff(aRectFnSet.GetBottom(aRect), nBottom) < 0)
3547 if (aRectFnSet.YDiff(aRectFnSet.GetTop(aRect), aRectFnSet.GetTop(aFlyRect)) < 0)
3549 aRectFnSet.SetBottom(aRect, aRectFnSet.GetTop(aFlyRect));
3551 else
3553 aRectFnSet.SetHeight(aRect, 0);
3556 bInvalidatePrtArea = true;
3560 bool bFlyHoriOrientLeft = text::HoriOrientation::LEFT == rHori.GetHoriOrient();
3562 bool bToplevelSplitFly = false;
3563 if (bSplitFly)
3565 // Floating table wrapped by table: avoid this in the nested case.
3566 bToplevelSplitFly = !pFly->GetAnchorFrame()->IsInTab();
3569 if (bToplevelSplitFly && !bFlyHoriOrientLeft)
3571 // Only shift to the right if we don't have enough space on the left.
3572 SwTwips nTabWidth = getFramePrintArea().Width();
3573 SwTwips nWidthDeadline = aFlyRectWithoutSpaces.Left()
3574 - pFly->GetAnchorFrame()->GetUpper()->getFrameArea().Left();
3575 if (nTabWidth > nWidthDeadline)
3577 // If a split fly is oriented "from left", we already checked if it has enough space on
3578 // the right, so from-left and left means the same here.
3579 bFlyHoriOrientLeft = rHori.GetHoriOrient() == text::HoriOrientation::NONE;
3582 if ((css::text::WrapTextMode_RIGHT == nSurround
3583 || css::text::WrapTextMode_PARALLEL == nSurround)
3584 && bFlyHoriOrientLeft
3585 && !bShiftDown)
3587 const tools::Long nWidth
3588 = aRectFnSet.XDiff(aRectFnSet.GetRight(aFlyRect),
3589 aRectFnSet.GetLeft(pFly->GetAnchorFrame()->getFrameArea()));
3590 rLeftOffset = std::max(rLeftOffset, nWidth);
3591 bInvalidatePrtArea = true;
3593 if ((css::text::WrapTextMode_LEFT == nSurround
3594 || css::text::WrapTextMode_PARALLEL == nSurround)
3595 && text::HoriOrientation::RIGHT == rHori.GetHoriOrient()
3596 && !bShiftDown)
3598 const tools::Long nWidth
3599 = aRectFnSet.XDiff(aRectFnSet.GetRight(pFly->GetAnchorFrame()->getFrameArea()),
3600 aRectFnSet.GetLeft(aFlyRect));
3601 rRightOffset = std::max(rRightOffset, nWidth);
3602 bInvalidatePrtArea = true;
3605 rUpper = aRectFnSet.YDiff( nPrtPos, aRectFnSet.GetTop(getFrameArea()) );
3606 if (pSpaceBelowBottom)
3608 *pSpaceBelowBottom = aRectFnSet.GetHeight(aRect);
3611 return bInvalidatePrtArea;
3614 /// "Formats" the frame; Frame and PrtArea.
3615 /// The fixed size is not adjusted here.
3616 void SwTabFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
3618 OSL_ENSURE( pAttrs, "TabFrame::Format, pAttrs is 0." );
3620 SwRectFnSet aRectFnSet(this);
3621 if ( !isFrameAreaSizeValid() )
3623 tools::Long nDiff = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()) -
3624 aRectFnSet.GetWidth(getFrameArea());
3625 if( nDiff )
3627 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
3628 aRectFnSet.AddRight( aFrm, nDiff );
3632 //VarSize is always the height.
3633 //For the upper/lower margins the same rules apply as for ContentFrames (see
3634 //MakePrtArea() of those).
3636 SwTwips nUpper = CalcUpperSpace( pAttrs );
3638 // We want to dodge the flys. Two possibilities:
3639 // 1. There are flys with SurroundNone, dodge them completely
3640 // 2. There are flys which only wrap on the right or the left side and
3641 // those are right or left aligned, those set the minimum for the margins
3642 tools::Long nTmpRight = -1000000,
3643 nLeftOffset = 0;
3644 if (CalcFlyOffsets(nUpper, nLeftOffset, nTmpRight, nullptr))
3646 setFramePrintAreaValid(false);
3649 tools::Long nRightOffset = std::max( tools::Long(0), nTmpRight );
3651 SwTwips nLower = pAttrs->CalcBottomLine();
3652 // #i29550#
3653 if ( IsCollapsingBorders() )
3654 nLower += GetBottomLineSize();
3656 if ( !isFramePrintAreaValid() )
3658 setFramePrintAreaValid(true);
3660 // The width of the PrintArea is given by the FrameFormat, the margins
3661 // have to be set accordingly.
3662 // Minimum margins are determined depending on borders and shadows.
3663 // The margins are set so that the PrintArea is aligned into the
3664 // Frame according to the adjustment.
3665 // If the adjustment is 0, the margins are set according to the border
3666 // attributes.
3668 const SwTwips nOldHeight = aRectFnSet.GetHeight(getFramePrintArea());
3669 const SwTwips nMax = aRectFnSet.GetWidth(getFrameArea());
3671 // OD 14.03.2003 #i9040# - adjust variable names.
3672 const SwTwips nLeftLine = pAttrs->CalcLeftLine();
3673 const SwTwips nRightLine = pAttrs->CalcRightLine();
3675 // The width possibly is a percentage value. If the table is inside
3676 // something else, the value refers to the environment. If it's in the
3677 // body then in the BrowseView the value refers to the screen width.
3678 const SwFormatFrameSize &rSz = GetFormat()->GetFrameSize();
3679 // OD 14.03.2003 #i9040# - adjust variable name.
3680 const SwTwips nWishedTableWidth = CalcRel( rSz );
3682 bool bCheckBrowseWidth = false;
3684 // OD 14.03.2003 #i9040# - insert new variables for left/right spacing.
3685 SwTwips nLeftSpacing = 0;
3686 SwTwips nRightSpacing = 0;
3687 switch ( GetFormat()->GetHoriOrient().GetHoriOrient() )
3689 case text::HoriOrientation::LEFT:
3691 // left indent:
3692 nLeftSpacing = nLeftLine + nLeftOffset;
3693 // OD 06.03.2003 #i9040# - correct calculation of right indent:
3694 // - Consider right indent given by right line attributes.
3695 // - Consider negative right indent.
3696 // wished right indent determined by wished table width and
3697 // left offset given by surround fly frames on the left:
3698 const SwTwips nWishRight = nMax - nWishedTableWidth - nLeftOffset;
3699 if ( nRightOffset > 0 )
3701 // surrounding fly frames on the right
3702 // -> right indent is maximum of given right offset
3703 // and wished right offset.
3704 nRightSpacing = nRightLine + std::max( SwTwips(nRightOffset), nWishRight );
3706 else
3708 // no surrounding fly frames on the right
3709 // If intrinsic right indent (intrinsic means not considering
3710 // determined left indent) is negative,
3711 // then hold this intrinsic indent,
3712 // otherwise non negative wished right indent is hold.
3713 nRightSpacing = nRightLine +
3714 ( ( (nWishRight+nLeftOffset) < 0 ) ?
3715 (nWishRight+nLeftOffset) :
3716 std::max( SwTwips(0), nWishRight ) );
3719 break;
3720 case text::HoriOrientation::RIGHT:
3722 // right indent:
3723 nRightSpacing = nRightLine + nRightOffset;
3724 // OD 06.03.2003 #i9040# - correct calculation of left indent:
3725 // - Consider left indent given by left line attributes.
3726 // - Consider negative left indent.
3727 // wished left indent determined by wished table width and
3728 // right offset given by surrounding fly frames on the right:
3729 const SwTwips nWishLeft = nMax - nWishedTableWidth - nRightOffset;
3730 if ( nLeftOffset > 0 )
3732 // surrounding fly frames on the left
3733 // -> right indent is maximum of given left offset
3734 // and wished left offset.
3735 nLeftSpacing = nLeftLine + std::max( SwTwips(nLeftOffset), nWishLeft );
3737 else
3739 // no surrounding fly frames on the left
3740 // If intrinsic left indent (intrinsic = not considering
3741 // determined right indent) is negative,
3742 // then hold this intrinsic indent,
3743 // otherwise non negative wished left indent is hold.
3744 nLeftSpacing = nLeftLine +
3745 ( ( (nWishLeft+nRightOffset) < 0 ) ?
3746 (nWishLeft+nRightOffset) :
3747 std::max( SwTwips(0), nWishLeft ) );
3750 break;
3751 case text::HoriOrientation::CENTER:
3753 // OD 07.03.2003 #i9040# - consider left/right line attribute.
3754 const SwTwips nCenterSpacing = ( nMax - nWishedTableWidth ) / 2;
3755 nLeftSpacing = nLeftLine +
3756 ( (nLeftOffset > 0) ?
3757 std::max( nCenterSpacing, SwTwips(nLeftOffset) ) :
3758 nCenterSpacing );
3759 nRightSpacing = nRightLine +
3760 ( (nRightOffset > 0) ?
3761 std::max( nCenterSpacing, SwTwips(nRightOffset) ) :
3762 nCenterSpacing );
3764 break;
3765 case text::HoriOrientation::FULL:
3766 //This things grows over the whole width.
3767 //Only the free space needed for the border is taken into
3768 //account. The attribute values of LRSpace are ignored
3769 //intentionally.
3770 bCheckBrowseWidth = true;
3771 nLeftSpacing = nLeftLine + nLeftOffset;
3772 nRightSpacing = nRightLine + nRightOffset;
3773 break;
3774 case text::HoriOrientation::NONE:
3776 // The margins are defined by the LRSpace attribute.
3777 nLeftSpacing = pAttrs->CalcLeft( this );
3778 if( nLeftOffset )
3780 // OD 07.03.2003 #i9040# - surround fly frames only, if
3781 // they overlap with the table.
3782 // Thus, take maximum of left spacing and left offset.
3783 // OD 10.03.2003 #i9040# - consider left line attribute.
3784 nLeftSpacing = std::max( nLeftSpacing, SwTwips( nLeftOffset + nLeftLine ) );
3786 // OD 23.01.2003 #106895# - add 1st param to <SwBorderAttrs::CalcRight(..)>
3787 nRightSpacing = pAttrs->CalcRight( this );
3788 if( nRightOffset )
3790 // OD 07.03.2003 #i9040# - surround fly frames only, if
3791 // they overlap with the table.
3792 // Thus, take maximum of right spacing and right offset.
3793 // OD 10.03.2003 #i9040# - consider right line attribute.
3794 nRightSpacing = std::max( nRightSpacing, SwTwips( nRightOffset + nRightLine ) );
3797 break;
3798 case text::HoriOrientation::LEFT_AND_WIDTH:
3800 // count left border and width (Word specialty)
3801 // OD 10.03.2003 #i9040# - no width alignment in online mode.
3802 //bCheckBrowseWidth = true;
3803 nLeftSpacing = pAttrs->CalcLeft( this );
3804 if( nLeftOffset )
3806 // OD 10.03.2003 #i9040# - surround fly frames only, if
3807 // they overlap with the table.
3808 // Thus, take maximum of right spacing and right offset.
3809 // OD 10.03.2003 #i9040# - consider left line attribute.
3810 nLeftSpacing = std::max( nLeftSpacing, SwTwips( pAttrs->CalcLeftLine() + nLeftOffset ) );
3812 // OD 10.03.2003 #i9040# - consider right and left line attribute.
3813 const SwTwips nWishRight =
3814 nMax - (nLeftSpacing-pAttrs->CalcLeftLine()) - nWishedTableWidth;
3815 nRightSpacing = nRightLine +
3816 ( (nRightOffset > 0) ?
3817 std::max( nWishRight, SwTwips(nRightOffset) ) :
3818 nWishRight );
3820 break;
3821 default:
3822 OSL_FAIL( "Invalid orientation for table." );
3825 // #i26250# - extend bottom printing area, if table
3826 // is last content inside a table cell.
3827 if ( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS) &&
3828 GetUpper()->IsInTab() && !GetIndNext() )
3830 nLower += pAttrs->GetULSpace().GetLower();
3832 aRectFnSet.SetYMargins( *this, nUpper, nLower );
3833 if( (nMax - MINLAY) < (nLeftSpacing + nRightSpacing) )
3834 aRectFnSet.SetXMargins( *this, 0, 0 );
3835 else
3836 aRectFnSet.SetXMargins( *this, nLeftSpacing, nRightSpacing );
3838 SwViewShell *pSh = getRootFrame()->GetCurrShell();
3839 if ( bCheckBrowseWidth &&
3840 pSh && pSh->GetViewOptions()->getBrowseMode() &&
3841 GetUpper()->IsPageBodyFrame() && // only PageBodyFrames and not ColBodyFrames
3842 pSh->VisArea().Width() )
3844 //Don't go beyond the edge of the visible area.
3845 //The page width can be bigger because objects with
3846 //"over-size" are possible (RootFrame::ImplCalcBrowseWidth())
3847 tools::Long nWidth = pSh->GetBrowseWidth();
3848 nWidth -= getFramePrintArea().Left();
3849 nWidth -= pAttrs->CalcRightLine();
3851 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
3852 aPrt.Width( std::min( nWidth, aPrt.Width() ) );
3855 if ( nOldHeight != aRectFnSet.GetHeight(getFramePrintArea()) )
3857 setFrameAreaSizeValid(false);
3861 if ( isFrameAreaSizeValid() )
3862 return;
3864 setFrameAreaSizeValid(true);
3866 // The size is defined by the content plus the margins.
3867 SwTwips nRemaining = 0, nDiff;
3868 SwFrame *pFrame = m_pLower;
3869 while ( pFrame )
3871 nRemaining += aRectFnSet.GetHeight(pFrame->getFrameArea());
3872 pFrame = pFrame->GetNext();
3874 // And now add the margins
3875 nRemaining += nUpper + nLower;
3877 nDiff = aRectFnSet.GetHeight(getFrameArea()) - nRemaining;
3878 if ( nDiff > 0 )
3879 Shrink( nDiff );
3880 else if ( nDiff < 0 )
3881 Grow( -nDiff );
3884 SwTwips SwTabFrame::GrowFrame(SwTwips nDist, SwResizeLimitReason& reason, bool bTst, bool bInfo)
3886 SwRectFnSet aRectFnSet(this);
3887 SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
3888 if( nHeight > 0 && nDist > ( LONG_MAX - nHeight ) )
3889 nDist = LONG_MAX - nHeight;
3891 reason = SwResizeLimitReason::Unspecified;
3893 if ( bTst && !IsRestrictTableGrowth() )
3894 return nDist;
3896 if ( GetUpper() )
3898 //The upper only grows as far as needed. nReal provides the distance
3899 //which is already available.
3900 SwTwips nReal = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea());
3901 for (SwFrame* pFrame = GetUpper()->Lower(); pFrame && GetFollow() != pFrame;
3902 pFrame = pFrame->GetNext())
3903 nReal -= aRectFnSet.GetHeight(pFrame->getFrameArea());
3905 if ( nReal < nDist )
3907 tools::Long nTmp = GetUpper()->Grow(nDist - std::max(nReal, SwTwips(0)), reason, bTst, bInfo);
3909 if ( IsRestrictTableGrowth() )
3911 nTmp = std::min( tools::Long(nDist), nReal + nTmp );
3912 nDist = nTmp < 0 ? 0 : nTmp;
3916 if ( !bTst )
3919 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
3920 aRectFnSet.AddBottom( aFrm, nDist );
3923 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
3924 SwRootFrame *pRootFrame = getRootFrame();
3925 if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
3926 pRootFrame->GetCurrShell() )
3928 SwRect aOldFrame( getFrameArea() );
3929 pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( this, aOldFrame );
3931 #endif
3935 if ( !bTst && ( nDist || IsRestrictTableGrowth() ) )
3937 SwPageFrame *pPage = FindPageFrame();
3938 if ( GetNext() )
3940 GetNext()->InvalidatePos_();
3941 if ( GetNext()->IsContentFrame() )
3942 GetNext()->InvalidatePage( pPage );
3944 // #i28701# - Due to the new object positioning the
3945 // frame on the next page/column can flow backward (e.g. it was moved
3946 // forward due to the positioning of its objects ). Thus, invalivate this
3947 // next frame, if document compatibility option 'Consider wrapping style
3948 // influence on object positioning' is ON.
3949 else if ( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
3951 InvalidateNextPos();
3953 InvalidateAll_();
3954 InvalidatePage( pPage );
3955 SetComplete();
3957 std::unique_ptr<SvxBrushItem> aBack = GetFormat()->makeBackgroundBrushItem();
3958 const SvxGraphicPosition ePos = aBack->GetGraphicPos();
3959 if ( GPOS_NONE != ePos && GPOS_TILED != ePos )
3960 SetCompletePaint();
3963 return nDist;
3966 void SwTabFrame::Invalidate(SwTabFrameInvFlags eInvFlags)
3968 if(eInvFlags == SwTabFrameInvFlags::NONE)
3969 return;
3970 SwPageFrame* pPage = FindPageFrame();
3971 InvalidatePage(pPage);
3972 if(eInvFlags & SwTabFrameInvFlags::InvalidatePrt)
3973 InvalidatePrt_();
3974 if(eInvFlags & SwTabFrameInvFlags::InvalidatePos)
3975 InvalidatePos_();
3976 SwFrame* pTmp = GetIndNext();
3977 if(nullptr != pTmp)
3979 if(eInvFlags & SwTabFrameInvFlags::InvalidateIndNextPrt)
3981 pTmp->InvalidatePrt_();
3982 if(pTmp->IsContentFrame())
3983 pTmp->InvalidatePage(pPage);
3985 if(eInvFlags & SwTabFrameInvFlags::SetIndNextCompletePaint)
3986 pTmp->SetCompletePaint();
3988 if(eInvFlags & SwTabFrameInvFlags::InvalidatePrevPrt && nullptr != (pTmp = GetPrev()))
3990 pTmp->InvalidatePrt_();
3991 if(pTmp->IsContentFrame())
3992 pTmp->InvalidatePage(pPage);
3994 if(eInvFlags & SwTabFrameInvFlags::InvalidateBrowseWidth)
3996 if(pPage && pPage->GetUpper() && !IsFollow())
3997 static_cast<SwRootFrame*>(pPage->GetUpper())->InvalidateBrowseWidth();
3999 if(eInvFlags & SwTabFrameInvFlags::InvalidateNextPos)
4000 InvalidateNextPos();
4003 void SwTabFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
4005 if(rHint.GetId() == SfxHintId::SwTableHeadingChange)
4007 HandleTableHeadlineChange();
4009 else if(rHint.GetId() == SfxHintId::SwVirtPageNumHint)
4011 auto& rVirtPageNumHint = const_cast<sw::VirtPageNumHint&>(static_cast<const sw::VirtPageNumHint&>(rHint));
4012 if(!IsInDocBody() || IsFollow() || rVirtPageNumHint.IsFound())
4013 return;
4014 if(const SwPageFrame* pPage = FindPageFrame())
4015 pPage->UpdateVirtPageNumInfo(rVirtPageNumHint, this);
4017 else if (rHint.GetId() == SfxHintId::SwAttrSetChange)
4019 auto pChangeHint = static_cast<const sw::AttrSetChangeHint*>(&rHint);
4020 SwTabFrameInvFlags eInvFlags = SwTabFrameInvFlags::NONE;
4021 if(pChangeHint->m_pNew)
4023 const SwAttrSetChg& rOldSetChg = *pChangeHint->m_pOld;
4024 const SwAttrSetChg& rNewSetChg = *pChangeHint->m_pNew;
4025 SfxItemIter aOIter(*rOldSetChg.GetChgSet());
4026 SfxItemIter aNIter(*rNewSetChg.GetChgSet());
4027 const SfxPoolItem* pOItem = aOIter.GetCurItem();
4028 const SfxPoolItem* pNItem = aNIter.GetCurItem();
4029 SwAttrSetChg aOldSet(rOldSetChg);
4030 SwAttrSetChg aNewSet(rNewSetChg);
4033 UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
4034 pNItem = aNIter.NextItem();
4035 pOItem = aOIter.NextItem();
4036 } while(pNItem);
4037 if(aOldSet.Count() || aNewSet.Count())
4038 SwLayoutFrame::SwClientNotify(rMod, sw::AttrSetChangeHint(&aOldSet, &aNewSet));
4040 Invalidate(eInvFlags);
4042 else if (rHint.GetId() == SfxHintId::SwLegacyModify)
4044 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
4045 SwTabFrameInvFlags eInvFlags = SwTabFrameInvFlags::NONE;
4046 UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
4047 Invalidate(eInvFlags);
4051 void SwTabFrame::HandleTableHeadlineChange()
4053 if(!IsFollow())
4054 return;
4055 // Delete remaining headlines:
4056 SwRowFrame* pLowerRow = nullptr;
4057 while(nullptr != (pLowerRow = static_cast<SwRowFrame*>(Lower())) && pLowerRow->IsRepeatedHeadline())
4059 pLowerRow->Cut();
4060 SwFrame::DestroyFrame(pLowerRow);
4063 // insert new headlines
4064 const sal_uInt16 nNewRepeat = GetTable()->GetRowsToRepeat();
4065 auto& rLines = GetTable()->GetTabLines();
4066 for(sal_uInt16 nIdx = 0; nIdx < nNewRepeat; ++nIdx)
4068 SwRowFrame* pHeadline = new SwRowFrame(*rLines[nIdx], this);
4070 sw::FlyCreationSuppressor aSuppressor;
4071 pHeadline->SetRepeatedHeadline(true);
4073 pHeadline->Paste(this, pLowerRow);
4075 Invalidate(SwTabFrameInvFlags::InvalidatePrt);
4078 void SwTabFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
4079 SwTabFrameInvFlags &rInvFlags,
4080 SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
4082 bool bClear = true;
4083 const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
4084 switch( nWhich )
4086 case RES_FRM_SIZE:
4087 case RES_HORI_ORIENT:
4088 rInvFlags |= SwTabFrameInvFlags::InvalidatePrt | SwTabFrameInvFlags::InvalidateBrowseWidth;
4089 break;
4091 case RES_PAGEDESC: //Attribute changes (on/off)
4092 if ( IsInDocBody() )
4094 rInvFlags |= SwTabFrameInvFlags::InvalidatePos;
4095 SwPageFrame *pPage = FindPageFrame();
4096 if (pPage)
4098 if ( !GetPrev() )
4099 CheckPageDescs( pPage );
4100 if (GetFormat()->GetPageDesc().GetNumOffset())
4101 static_cast<SwRootFrame*>(pPage->GetUpper())->SetVirtPageNum( true );
4102 GetFormat()->GetDoc()->getIDocumentFieldsAccess().UpdatePageFields(pPage->getFrameArea().Top());
4105 break;
4107 case RES_BREAK:
4108 rInvFlags |= SwTabFrameInvFlags::InvalidatePos | SwTabFrameInvFlags::InvalidateNextPos;
4109 break;
4111 case RES_LAYOUT_SPLIT:
4112 if ( !IsFollow() )
4113 rInvFlags |= SwTabFrameInvFlags::InvalidatePos;
4114 break;
4115 case RES_FRAMEDIR :
4116 SetDerivedR2L( false );
4117 CheckDirChange();
4118 break;
4119 case RES_COLLAPSING_BORDERS :
4120 rInvFlags |= SwTabFrameInvFlags::InvalidatePrt;
4121 lcl_InvalidateAllLowersPrt( this );
4122 break;
4123 case RES_UL_SPACE:
4124 rInvFlags |= SwTabFrameInvFlags::InvalidateIndNextPrt | SwTabFrameInvFlags::InvalidatePrevPrt | SwTabFrameInvFlags::SetIndNextCompletePaint;
4125 [[fallthrough]];
4127 default:
4128 bClear = false;
4130 if ( !bClear )
4131 return;
4133 if ( pOldSet || pNewSet )
4135 if ( pOldSet )
4136 pOldSet->ClearItem( nWhich );
4137 if ( pNewSet )
4138 pNewSet->ClearItem( nWhich );
4140 else
4142 SwModify aMod;
4143 SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
4147 SwFrame *SwTabFrame::FindLastContentOrTable()
4149 SwFrame *pRet = m_pLower;
4151 while ( pRet && !pRet->IsContentFrame() )
4153 SwFrame *pOld = pRet;
4155 SwFrame *pTmp = pRet; // To skip empty section frames
4156 while ( pRet->GetNext() )
4158 pRet = pRet->GetNext();
4159 if( !pRet->IsSctFrame() || static_cast<SwSectionFrame*>(pRet)->GetSection() )
4160 pTmp = pRet;
4162 pRet = pTmp;
4164 if ( pRet->GetLower() )
4165 pRet = pRet->GetLower();
4166 if ( pRet == pOld )
4168 // Check all other columns if there is a column based section with
4169 // an empty last column at the end of the last cell - this is done
4170 // by SwSectionFrame::FindLastContent
4171 if( pRet->IsColBodyFrame() )
4173 #if OSL_DEBUG_LEVEL > 0
4174 SwSectionFrame* pSect = pRet->FindSctFrame();
4175 OSL_ENSURE( pSect, "Where does this column come from?");
4176 OSL_ENSURE( IsAnLower( pSect ), "Split cell?" );
4177 #endif
4178 return pRet->FindSctFrame()->FindLastContent();
4181 // pRet may be a cell frame without a lower (cell has been split).
4182 // We have to find the last content the hard way:
4184 OSL_ENSURE( pRet->IsCellFrame(), "SwTabFrame::FindLastContent failed" );
4185 SwFrame* pRow = pRet->GetUpper();
4186 while ( pRow && !pRow->GetUpper()->IsTabFrame() )
4187 pRow = pRow->GetUpper();
4188 SwContentFrame* pContentFrame = pRow ? static_cast<SwLayoutFrame*>(pRow)->ContainsContent() : nullptr;
4189 pRet = nullptr;
4191 while ( pContentFrame && static_cast<const SwLayoutFrame*>(pRow)->IsAnLower( pContentFrame ) )
4193 pRet = pContentFrame;
4194 pContentFrame = pContentFrame->GetNextContentFrame();
4199 // #112929# There actually is a situation, which results in pRet = 0:
4200 // Insert frame, insert table via text <-> table. This gives you a frame
4201 // containing a table without any other content frames. Split the table
4202 // and undo the splitting. This operation gives us a table frame without
4203 // a lower.
4204 if ( pRet )
4206 while ( pRet->GetNext() )
4207 pRet = pRet->GetNext();
4209 if (pRet->IsSctFrame())
4210 pRet = static_cast<SwSectionFrame*>(pRet)->FindLastContent();
4213 assert(pRet == nullptr || dynamic_cast<SwContentFrame*>(pRet) || dynamic_cast<SwTabFrame*>(pRet));
4214 return pRet;
4217 SwContentFrame *SwTabFrame::FindLastContent()
4219 SwFrame * pRet(FindLastContentOrTable());
4221 while (pRet && pRet->IsTabFrame()) // possibly there's only tables here!
4222 { // tdf#126138 skip table, don't look inside
4223 pRet = pRet->GetPrev();
4226 assert(pRet == nullptr || dynamic_cast<SwContentFrame*>(pRet));
4227 return static_cast<SwContentFrame*>(pRet);
4230 /// Return value defines if the frm needs to be relocated
4231 bool SwTabFrame::ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool &rReformat )
4233 rReformat = false;
4234 if ( SwFlowFrame::IsMoveBwdJump() || !IsPrevObjMove() )
4236 //Flowing back Frames is quite time consuming unfortunately.
4237 //Most often the location where the Frame wants to flow to has the same
4238 //FixSize as the Frame itself. In such a situation it's easy to check if
4239 //the Frame will find enough space for its VarSize, if this is not the
4240 //case, the relocation can be skipped.
4241 //Checking if the Frame will find enough space is done by the Frame itself,
4242 //this also takes the possibility of splitting the Frame into account.
4243 //If the FixSize is different or Flys are involved (at the old or the
4244 //new position) the checks are pointless, the Frame then
4245 //needs to be relocated tentatively (if a bit of space is available).
4247 //The FixSize of the environments which contain tables is always the
4248 //width.
4250 SwPageFrame *pOldPage = FindPageFrame(),
4251 *pNewPage = pNewUpper->FindPageFrame();
4252 bool bMoveAnyway = false;
4253 SwTwips nSpace = 0;
4255 SwRectFnSet aRectFnSet(this);
4256 if ( !SwFlowFrame::IsMoveBwdJump() )
4259 tools::Long nOldWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
4260 SwRectFnSet fnRectX(pNewUpper);
4261 tools::Long nNewWidth = fnRectX.GetWidth(pNewUpper->getFramePrintArea());
4262 if( std::abs( nNewWidth - nOldWidth ) < 2 )
4264 bMoveAnyway = BwdMoveNecessary( pOldPage, getFrameArea() ) > 1;
4265 if( !bMoveAnyway )
4267 SwRect aRect( pNewUpper->getFramePrintArea() );
4268 aRect.Pos() += pNewUpper->getFrameArea().Pos();
4269 const SwFrame *pPrevFrame = pNewUpper->Lower();
4270 while ( pPrevFrame && pPrevFrame != this )
4272 fnRectX.SetTop( aRect, fnRectX.GetBottom(pPrevFrame->getFrameArea()) );
4273 pPrevFrame = pPrevFrame->GetNext();
4275 bMoveAnyway = BwdMoveNecessary( pNewPage, aRect) > 1;
4277 // #i54861# Due to changes made in PrepareMake,
4278 // the tabfrm may not have a correct position. Therefore
4279 // it is possible that pNewUpper->getFramePrintArea().Height == 0. In this
4280 // case the above calculation of nSpace might give wrong
4281 // results and we really do not want to MoveBackward into a
4282 // 0 height frame. If nTmpSpace is already <= 0, we take this
4283 // value:
4284 const SwTwips nTmpSpace = fnRectX.GetHeight(aRect);
4285 if ( fnRectX.GetHeight(pNewUpper->getFramePrintArea()) > 0 || nTmpSpace <= 0 )
4286 nSpace = nTmpSpace;
4288 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
4289 if( pSh && pSh->GetViewOptions()->getBrowseMode() )
4290 nSpace += pNewUpper->Grow( LONG_MAX, true );
4291 if (0 < nSpace && GetPrecede())
4293 SwTwips nUpperDummy(0);
4294 tools::Long nLeftOffsetDummy(0), nRightOffsetDummy(0);
4295 // tdf#116501 check for no-wrap fly overlap
4296 static_cast<const SwTabFrame*>(GetPrecede())->CalcFlyOffsets(
4297 nUpperDummy, nLeftOffsetDummy, nRightOffsetDummy, &nSpace);
4301 else if (!m_bLockBackMove)
4302 bMoveAnyway = true;
4303 else
4305 m_bWantBackMove = true;
4308 else if (!m_bLockBackMove)
4309 bMoveAnyway = true;
4310 else
4312 m_bWantBackMove = true;
4315 if ( bMoveAnyway )
4317 rReformat = true;
4318 return true;
4321 bool bFits = nSpace > 0;
4322 if (!bFits && aRectFnSet.GetHeight(getFrameArea()) == 0)
4323 // This frame fits into pNewUpper in case it has no space, but this
4324 // frame is empty.
4325 bFits = nSpace >= 0;
4326 if (bFits)
4328 // #i26945# - check, if follow flow line
4329 // contains frame, which are moved forward due to its object
4330 // positioning.
4331 const SwRowFrame* pFirstRow = GetFirstNonHeadlineRow();
4332 if ( pFirstRow && pFirstRow->IsInFollowFlowRow() &&
4333 SwLayouter::DoesRowContainMovedFwdFrame(
4334 *(pFirstRow->GetFormat()->GetDoc()),
4335 *pFirstRow ) )
4337 return false;
4339 SwTwips nTmpHeight = CalcHeightOfFirstContentLine();
4341 // For some mysterious reason, I changed the good old
4342 // 'return nHeight <= nSpace' to 'return nTmpHeight < nSpace'.
4343 // This obviously results in problems with table frames in
4344 // sections. Remember: Every twip is sacred.
4345 if (nTmpHeight <= nSpace)
4347 if (m_bLockBackMove)
4349 m_bWantBackMove = true;
4351 else
4353 return true;
4358 return false;
4361 void SwTabFrame::Cut()
4363 OSL_ENSURE( GetUpper(), "Cut without Upper()." );
4365 SwPageFrame *pPage = FindPageFrame();
4366 InvalidatePage( pPage );
4367 SwFrame *pFrame = GetNext();
4368 if( pFrame )
4370 // Possibly the old follow calculated a spacing to the predecessor
4371 // which is obsolete now when it becomes the first frame
4372 pFrame->InvalidatePrt_();
4373 pFrame->InvalidatePos_();
4374 if ( pFrame->IsContentFrame() )
4375 pFrame->InvalidatePage( pPage );
4376 if( IsInSct() && !GetPrev() )
4378 SwSectionFrame* pSct = FindSctFrame();
4379 if( !pSct->IsFollow() )
4381 pSct->InvalidatePrt_();
4382 pSct->InvalidatePage( pPage );
4386 else
4388 InvalidateNextPos();
4389 //Someone has to do the retouch: predecessor or upper
4390 pFrame = GetPrev();
4391 if ( nullptr != pFrame )
4393 pFrame->SetRetouche();
4394 pFrame->Prepare( PrepareHint::WidowsOrphans );
4395 pFrame->InvalidatePos_();
4396 if ( pFrame->IsContentFrame() )
4397 pFrame->InvalidatePage( pPage );
4399 //If I am (was) the only FlowFrame in my own upper, it has to do
4400 //the retouch. Moreover a new empty page might be created.
4401 else
4402 { SwRootFrame *pRoot = static_cast<SwRootFrame*>(pPage->GetUpper());
4403 pRoot->SetSuperfluous();
4404 GetUpper()->SetCompletePaint();
4405 if( IsInSct() )
4407 SwSectionFrame* pSct = FindSctFrame();
4408 if( !pSct->IsFollow() )
4410 pSct->InvalidatePrt_();
4411 pSct->InvalidatePage( pPage );
4417 //First remove, then shrink the upper.
4418 SwLayoutFrame *pUp = GetUpper();
4419 SwRectFnSet aRectFnSet(this);
4420 RemoveFromLayout();
4421 if ( pUp )
4423 OSL_ENSURE( !pUp->IsFootnoteFrame(), "Table in Footnote." );
4424 SwSectionFrame *pSct = nullptr;
4425 SwFlyFrame *pFly = nullptr;
4426 // #126020# - adjust check for empty section
4427 // #130797# - correct fix #126020#
4428 if ( !pUp->Lower() && pUp->IsInSct() &&
4429 !(pSct = pUp->FindSctFrame())->ContainsContent() &&
4430 !pSct->ContainsAny( true ) )
4432 if ( pUp->GetUpper() )
4434 pSct->DelEmpty( false );
4435 pSct->InvalidateSize_();
4438 else if (!pUp->Lower() && pUp->IsInFly() &&
4439 !(pFly = pUp->FindFlyFrame())->ContainsContent() &&
4440 !pFly->ContainsAny())
4442 bool bSplitFly = pFly->IsFlySplitAllowed();
4443 if (!bSplitFly && pFly->IsFlyAtContentFrame())
4445 // If the fly is not allowed to split, it's still possible that it was allowed to
4446 // split. That is definitely the case when the fly is a follow.
4447 auto pFlyAtContent = static_cast<SwFlyAtContentFrame*>(pFly);
4448 bSplitFly = pFlyAtContent->IsFollow();
4450 if (pUp == pFly && bSplitFly)
4452 auto pFlyAtContent = static_cast<SwFlyAtContentFrame*>(pFly);
4453 pFlyAtContent->DelEmpty();
4456 // table-in-footnote: delete empty footnote frames (like SwContentFrame::Cut)
4457 else if (!pUp->Lower() && pUp->IsFootnoteFrame() && !pUp->IsColLocked())
4459 if (pUp->GetNext() && !pUp->GetPrev())
4461 if (SwFrame *const pTmp = static_cast<SwLayoutFrame*>(pUp->GetNext())->ContainsAny())
4463 pTmp->InvalidatePrt_();
4466 if (!pUp->IsDeleteForbidden())
4468 pUp->Cut();
4469 SwFrame::DestroyFrame(pUp);
4472 else if( aRectFnSet.GetHeight(getFrameArea()) )
4474 // OD 26.08.2003 #i18103# - *no* 'ColUnlock' of section -
4475 // undo changes of fix for #104992#
4476 pUp->Shrink( getFrameArea().Height() );
4481 if ( pPage && !IsFollow() && pPage->GetUpper() )
4482 static_cast<SwRootFrame*>(pPage->GetUpper())->InvalidateBrowseWidth();
4485 void SwTabFrame::Paste( SwFrame* pParent, SwFrame* pSibling )
4487 OSL_ENSURE( pParent, "No parent for pasting." );
4488 OSL_ENSURE( pParent->IsLayoutFrame(), "Parent is ContentFrame." );
4489 OSL_ENSURE( pParent != this, "I'm the parent myself." );
4490 OSL_ENSURE( pSibling != this, "I'm my own neighbour." );
4491 OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(),
4492 "I'm still registered somewhere." );
4494 //Insert in the tree.
4495 InsertBefore( static_cast<SwLayoutFrame*>(pParent), pSibling );
4497 InvalidateAll_();
4498 SwPageFrame *pPage = FindPageFrame();
4499 InvalidatePage( pPage );
4501 if ( GetNext() )
4503 GetNext()->InvalidatePos_();
4504 GetNext()->InvalidatePrt_();
4505 if ( GetNext()->IsContentFrame() )
4506 GetNext()->InvalidatePage( pPage );
4509 SwRectFnSet aRectFnSet(this);
4510 if( aRectFnSet.GetHeight(getFrameArea()) )
4511 pParent->Grow( aRectFnSet.GetHeight(getFrameArea()) );
4513 if( aRectFnSet.GetWidth(getFrameArea()) != aRectFnSet.GetWidth(pParent->getFramePrintArea()) )
4514 Prepare( PrepareHint::FixSizeChanged );
4515 if ( GetPrev() )
4517 if ( !IsFollow() )
4519 GetPrev()->InvalidateSize();
4520 if ( GetPrev()->IsContentFrame() )
4521 GetPrev()->InvalidatePage( pPage );
4524 else if ( GetNext() )
4525 // Take the spacing into account when dealing with ContentFrames.
4526 // There are two situations (both always happen at the same time):
4527 // a) The Content becomes the first in a chain
4528 // b) The new follower was previously the first in a chain
4529 GetNext()->InvalidatePrt_();
4531 if ( !pPage || IsFollow() )
4532 return;
4534 if ( pPage->GetUpper() )
4535 static_cast<SwRootFrame*>(pPage->GetUpper())->InvalidateBrowseWidth();
4537 if ( !GetPrev() )//At least needed for HTML with a table at the beginning.
4539 const SwPageDesc *pDesc = GetFormat()->GetPageDesc().GetPageDesc();
4540 if ( (pDesc && pDesc != pPage->GetPageDesc()) ||
4541 (!pDesc && pPage->GetPageDesc() != &GetFormat()->GetDoc()->GetPageDesc(0)) )
4542 CheckPageDescs( pPage );
4546 bool SwTabFrame::Prepare( const PrepareHint eHint, const void *, bool )
4548 if( PrepareHint::BossChanged == eHint )
4549 CheckDirChange();
4550 return false;
4553 SwRowFrame::SwRowFrame(const SwTableLine &rLine, SwFrame* pSib, bool bInsertContent)
4554 : SwLayoutFrame( rLine.GetFrameFormat(), pSib )
4555 , m_pTabLine( &rLine )
4556 , m_pFollowRow( nullptr )
4557 // #i29550#
4558 , mnTopMarginForLowers( 0 )
4559 , mnBottomMarginForLowers( 0 )
4560 , mnBottomLineSize( 0 )
4561 // --> split table rows
4562 , m_bIsFollowFlowRow( false )
4563 // <-- split table rows
4564 , m_bIsRepeatedHeadline( false )
4565 , m_bIsRowSpanLine( false )
4566 , m_bForceRowSplitAllowed( false )
4567 , m_bIsInSplit( false )
4569 mnFrameType = SwFrameType::Row;
4571 //Create the boxes and insert them.
4572 const SwTableBoxes &rBoxes = rLine.GetTabBoxes();
4573 SwFrame *pTmpPrev = nullptr;
4575 bool bHiddenRedlines = getRootFrame()->IsHideRedlines() &&
4576 !GetFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable().empty();
4577 for ( size_t i = 0; i < rBoxes.size(); ++i )
4579 // skip cells deleted with track changes
4580 if ( bHiddenRedlines && RedlineType::Delete == rBoxes[i]->GetRedlineType() )
4581 continue;
4583 SwCellFrame *pNew = new SwCellFrame( *rBoxes[i], this, bInsertContent );
4584 pNew->InsertBehind( this, pTmpPrev );
4585 pTmpPrev = pNew;
4589 void SwRowFrame::DestroyImpl()
4591 sw::BroadcastingModify* pMod = GetFormat();
4592 if( pMod )
4594 pMod->Remove(*this);
4595 if( !pMod->HasWriterListeners() )
4596 delete pMod;
4599 SwLayoutFrame::DestroyImpl();
4602 SwRowFrame::~SwRowFrame()
4606 void SwRowFrame::RegistFlys( SwPageFrame *pPage )
4608 ::RegistFlys( pPage ? pPage : FindPageFrame(), this );
4611 void SwRowFrame::OnFrameSize(const SfxPoolItem& rSize)
4613 SwTabFrame* pTab = FindTabFrame();
4614 if(pTab)
4616 const bool bInFirstNonHeadlineRow = pTab->IsFollow() && this == pTab->GetFirstNonHeadlineRow();
4617 // #i35063#
4618 // Invalidation required is pRow is last row
4619 if(bInFirstNonHeadlineRow)
4620 pTab = pTab->FindMaster();
4621 if(bInFirstNonHeadlineRow || !GetNext())
4622 pTab->InvalidatePos();
4624 const sw::BroadcastingModify aMod;
4625 SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(nullptr, &rSize));
4628 void SwRowFrame::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
4630 if(rHint.GetId() == SfxHintId::SwTableLineFormatChanged)
4632 auto pNewFormatHint = static_cast<const sw::TableLineFormatChanged*>(&rHint);
4633 if(GetTabLine() != &pNewFormatHint->m_rTabLine)
4634 return;
4635 RegisterToFormat(const_cast<SwTableLineFormat&>(pNewFormatHint->m_rNewFormat));
4636 InvalidateSize();
4637 InvalidatePrt_();
4638 SetCompletePaint();
4639 ReinitializeFrameSizeAttrFlags();
4641 // #i35063#
4642 // consider 'split row allowed' attribute
4643 SwTabFrame* pTab = FindTabFrame();
4644 bool bInFollowFlowRow = false;
4645 const bool bInFirstNonHeadlineRow = pTab->IsFollow() && this == pTab->GetFirstNonHeadlineRow();
4646 if(bInFirstNonHeadlineRow ||
4647 !GetNext() ||
4648 (bInFollowFlowRow = IsInFollowFlowRow()) ||
4649 nullptr != IsInSplitTableRow() )
4651 if(bInFirstNonHeadlineRow || bInFollowFlowRow)
4652 pTab = pTab->FindMaster();
4654 pTab->SetRemoveFollowFlowLinePending(true);
4655 pTab->InvalidatePos();
4658 else if(rHint.GetId() == SfxHintId::SwMoveTableLine)
4660 auto pMoveTableLineHint = static_cast<const sw::MoveTableLineHint*>(&rHint);
4661 if(GetTabLine() != &pMoveTableLineHint->m_rTableLine)
4662 return;
4663 const_cast<SwFrameFormat*>(&pMoveTableLineHint->m_rNewFormat)->Add(*this);
4664 InvalidateAll();
4665 ReinitializeFrameSizeAttrFlags();
4666 return;
4668 else if (rHint.GetId() == SfxHintId::SwAttrSetChange)
4670 auto pChangeHint = static_cast<const sw::AttrSetChangeHint*>(&rHint);
4671 if(!pChangeHint->m_pNew)
4673 // possibly not needed?
4674 SwLayoutFrame::SwClientNotify(rModify, rHint);
4675 return;
4677 const SwAttrSet* pChgSet = pChangeHint->m_pNew->GetChgSet();
4678 const SfxPoolItem* pItem = nullptr;
4679 pChgSet->GetItemState(RES_FRM_SIZE, false, &pItem);
4680 if(!pItem)
4681 pChgSet->GetItemState(RES_ROW_SPLIT, false, &pItem);
4682 if(pItem)
4683 OnFrameSize(*pItem);
4684 else
4685 SwLayoutFrame::SwClientNotify(rModify, rHint); // possibly not needed?
4686 return;
4688 if (rHint.GetId() != SfxHintId::SwLegacyModify)
4689 return;
4690 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
4691 if(!pLegacy->m_pNew)
4693 // possibly not needed?
4694 SwLayoutFrame::SwClientNotify(rModify, rHint);
4695 return;
4697 switch(pLegacy->m_pNew->Which())
4699 case RES_FRM_SIZE:
4700 case RES_ROW_SPLIT:
4701 OnFrameSize(*static_cast<const SwFormatFrameSize*>(pLegacy->m_pNew));
4702 return;
4706 void SwRowFrame::MakeAll(vcl::RenderContext* pRenderContext)
4708 if ( !GetNext() )
4710 setFrameAreaSizeValid(false);
4713 SwLayoutFrame::MakeAll(pRenderContext);
4716 void SwRowFrame::dumpAsXml(xmlTextWriterPtr writer) const
4718 (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("row"));
4719 dumpAsXmlAttributes(writer);
4721 (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
4722 dumpInfosAsXml(writer);
4723 (void)xmlTextWriterEndElement(writer);
4724 dumpChildrenAsXml(writer);
4726 (void)xmlTextWriterEndElement(writer);
4729 tools::Long CalcHeightWithFlys( const SwFrame *pFrame )
4731 SwRectFnSet aRectFnSet(pFrame);
4732 tools::Long nHeight = 0;
4733 const SwFrame* pTmp = pFrame->IsSctFrame() ?
4734 static_cast<const SwSectionFrame*>(pFrame)->ContainsContent() : pFrame;
4735 while( pTmp )
4737 // #i26945# - consider follow text frames
4738 const SwSortedObjs* pObjs( nullptr );
4739 bool bIsFollow( false );
4740 if ( pTmp->IsTextFrame() && static_cast<const SwTextFrame*>(pTmp)->IsFollow() )
4742 const SwFrame* pMaster;
4743 // #i46450# Master does not necessarily have
4744 // to exist if this function is called from JoinFrame() ->
4745 // Cut() -> Shrink()
4746 const SwTextFrame* pTmpFrame = static_cast<const SwTextFrame*>(pTmp);
4747 if ( pTmpFrame->GetPrev() && pTmpFrame->GetPrev()->IsTextFrame() &&
4748 static_cast<const SwTextFrame*>(pTmpFrame->GetPrev())->GetFollow() &&
4749 static_cast<const SwTextFrame*>(pTmpFrame->GetPrev())->GetFollow() != pTmp )
4750 pMaster = nullptr;
4751 else
4752 pMaster = pTmpFrame->FindMaster();
4754 if ( pMaster )
4756 pObjs = static_cast<const SwTextFrame*>(pTmp)->FindMaster()->GetDrawObjs();
4757 bIsFollow = true;
4760 else
4762 pObjs = pTmp->GetDrawObjs();
4764 if ( pObjs )
4766 for (SwAnchoredObject* pAnchoredObj : *pObjs)
4768 // #i26945# - if <pTmp> is follow, the
4769 // anchor character frame has to be <pTmp>.
4770 if ( bIsFollow &&
4771 pAnchoredObj->FindAnchorCharFrame() != pTmp )
4773 continue;
4775 // #i26945# - consider also drawing objects
4777 // OD 30.09.2003 #i18732# - only objects, which follow
4778 // the text flow have to be considered.
4779 const SwFrameFormat* pFrameFormat = pAnchoredObj->GetFrameFormat();
4780 bool bFollowTextFlow = pFrameFormat->GetFollowTextFlow().GetValue();
4781 bool bIsFarAway = pAnchoredObj->GetObjRect().Top() != FAR_AWAY;
4782 const SwPageFrame* pPageFrm = pTmp->FindPageFrame();
4783 bool bIsAnchoredToTmpFrm = false;
4784 if ( pPageFrm && pPageFrm->IsPageFrame() && pAnchoredObj->GetPageFrame())
4785 bIsAnchoredToTmpFrm = pAnchoredObj->GetPageFrame() == pPageFrm ||
4786 (pPageFrm->GetFormatPage().GetPhyPageNum() == pAnchoredObj->GetPageFrame()->GetFormatPage().GetPhyPageNum() + 1);
4787 const bool bConsiderObj =
4788 (pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) &&
4789 bIsFarAway &&
4790 bFollowTextFlow && bIsAnchoredToTmpFrm;
4791 bool bWrapThrough = pFrameFormat->GetSurround().GetValue() == text::WrapTextMode_THROUGH;
4792 bool bInBackground = !pFrameFormat->GetOpaque().GetValue();
4793 // Legacy render requires in-background setting, the new mode does not.
4794 bool bConsiderFollowTextFlow = bInBackground
4795 || !pFrameFormat->getIDocumentSettingAccess().get(
4796 DocumentSettingId::USE_FORMER_TEXT_WRAPPING);
4797 if (pFrame->IsInTab() && bFollowTextFlow && bWrapThrough && bConsiderFollowTextFlow)
4799 // Ignore wrap-through objects when determining the cell height.
4800 // Normally FollowTextFlow requires a resize of the cell, but not in case of
4801 // wrap-through.
4802 continue;
4805 if ( bConsiderObj )
4807 const SwFormatFrameSize &rSz = pFrameFormat->GetFrameSize();
4808 if( !rSz.GetHeightPercent() )
4810 const SwTwips nDistOfFlyBottomToAnchorTop =
4811 aRectFnSet.GetHeight(pAnchoredObj->GetObjRect()) +
4812 ( aRectFnSet.IsVert() ?
4813 pAnchoredObj->GetCurrRelPos().X() :
4814 pAnchoredObj->GetCurrRelPos().Y() );
4816 const SwTwips nFrameDiff =
4817 aRectFnSet.YDiff(
4818 aRectFnSet.GetTop(pTmp->getFrameArea()),
4819 aRectFnSet.GetTop(pFrame->getFrameArea()) );
4821 nHeight = std::max( nHeight, nDistOfFlyBottomToAnchorTop + nFrameDiff -
4822 aRectFnSet.GetHeight(pFrame->getFrameArea()) );
4824 // #i56115# The first height calculation
4825 // gives wrong results if pFrame->getFramePrintArea().Y() > 0. We do
4826 // a second calculation based on the actual rectangles of
4827 // pFrame and pAnchoredObj, and use the maximum of the results.
4828 // I do not want to remove the first calculation because
4829 // if clipping has been applied, using the GetCurrRelPos
4830 // might be the better option to calculate nHeight.
4831 const SwTwips nDistOfFlyBottomToAnchorTop2 = aRectFnSet.YDiff(
4832 aRectFnSet.GetBottom(pAnchoredObj->GetObjRect()),
4833 aRectFnSet.GetBottom(pFrame->getFrameArea()) );
4835 nHeight = std::max( nHeight, tools::Long(nDistOfFlyBottomToAnchorTop2 ));
4841 if( !pFrame->IsSctFrame() )
4842 break;
4843 pTmp = pTmp->FindNextCnt();
4844 if( !static_cast<const SwSectionFrame*>(pFrame)->IsAnLower( pTmp ) )
4845 break;
4847 return nHeight;
4850 static SwTwips lcl_CalcTopAndBottomMargin( const SwLayoutFrame& rCell, const SwBorderAttrs& rAttrs )
4852 const SwTabFrame* pTab = rCell.FindTabFrame();
4853 SwTwips nTopSpace = 0;
4854 SwTwips nBottomSpace = 0;
4856 // #i29550#
4857 const SwFrame* pLower = rCell.Lower();
4858 if ( pTab->IsCollapsingBorders() && pLower && !pLower->IsRowFrame() )
4860 nTopSpace = static_cast<const SwRowFrame*>(rCell.GetUpper())->GetTopMarginForLowers();
4861 nBottomSpace = static_cast<const SwRowFrame*>(rCell.GetUpper())->GetBottomMarginForLowers();
4863 else
4865 if ( pTab->IsVertical() != rCell.IsVertical() )
4867 nTopSpace = rAttrs.CalcLeft( &rCell );
4868 nBottomSpace = rAttrs.CalcRight( &rCell );
4870 else
4872 nTopSpace = rAttrs.CalcTop();
4873 nBottomSpace = rAttrs.CalcBottom();
4877 return nTopSpace + nBottomSpace;
4880 // #i26945# - add parameter <_bConsiderObjs> in order to
4881 // control, if floating screen objects have to be considered for the minimal
4882 // cell height.
4883 static SwTwips lcl_CalcMinCellHeight( const SwLayoutFrame *_pCell,
4884 const bool _bConsiderObjs,
4885 const SwBorderAttrs *pAttrs = nullptr )
4887 SwRectFnSet aRectFnSet(_pCell);
4888 SwTwips nHeight = 0;
4889 const SwFrame* pLow = _pCell->Lower();
4890 tools::Long nFlyAdd = 0;
4891 while ( pLow )
4893 if ( pLow->IsRowFrame() )
4895 // #i26945#
4896 nHeight += ::lcl_CalcMinRowHeight( static_cast<const SwRowFrame*>(pLow),
4897 _bConsiderObjs );
4899 else
4901 tools::Long nLowHeight = aRectFnSet.GetHeight(pLow->getFrameArea());
4902 nHeight += nLowHeight;
4903 // #i26945#
4904 if ( _bConsiderObjs )
4906 nFlyAdd = std::max( tools::Long(0), nFlyAdd - nLowHeight );
4907 nFlyAdd = std::max( nFlyAdd, ::CalcHeightWithFlys( pLow ) );
4911 pLow = pLow->GetNext();
4913 if ( nFlyAdd )
4914 nHeight += nFlyAdd;
4915 // The border/margin needs to be considered too, unfortunately it can't be
4916 // calculated using PrintArea and FrameArea because any or all of those
4917 // may be invalid.
4918 if ( _pCell->Lower() )
4920 if ( pAttrs )
4921 nHeight += lcl_CalcTopAndBottomMargin( *_pCell, *pAttrs );
4922 else
4924 SwBorderAttrAccess aAccess( SwFrame::GetCache(), _pCell );
4925 const SwBorderAttrs &rAttrs = *aAccess.Get();
4926 nHeight += lcl_CalcTopAndBottomMargin( *_pCell, rAttrs );
4929 return nHeight;
4932 // #i26945# - add parameter <_bConsiderObjs> in order to control,
4933 // if floating screen objects have to be considered for the minimal cell height
4934 static SwTwips lcl_CalcMinRowHeight( const SwRowFrame* _pRow,
4935 const bool _bConsiderObjs )
4937 //calc min height including width of horizontal border
4938 const bool bMinRowHeightInclBorder =
4939 _pRow->GetFormat()->GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::MIN_ROW_HEIGHT_INCL_BORDER);
4940 SwTwips nHeight = 0;
4941 if ( !_pRow->IsRowSpanLine() )
4943 const SwFormatFrameSize &rSz = _pRow->GetFormat()->GetFrameSize();
4944 if (_pRow->HasFixSize() && !_pRow->IsForceRowSplitAllowed())
4946 OSL_ENSURE(SwFrameSize::Fixed == rSz.GetHeightSizeType(), "pRow claims to have fixed size");
4947 return rSz.GetHeight();
4949 // If this row frame is being split, then row's minimal height shouldn't restrict
4950 // this frame's minimal height, because the rest will go to follow frame.
4951 else if ( !_pRow->IsInSplit() && rSz.GetHeightSizeType() == SwFrameSize::Minimum )
4953 bool bSplitFly = false;
4954 if (_pRow->IsInFly())
4956 // See if we're in a split fly that is anchored on a page that has enough space to
4957 // host this row with its minimum row height.
4958 const SwFlyFrame* pFly = _pRow->FindFlyFrame();
4959 if (pFly->IsFlySplitAllowed())
4961 SwFrame* pAnchor = const_cast<SwFlyFrame*>(pFly)->FindAnchorCharFrame();
4962 if (pAnchor)
4964 if (pAnchor->FindPageFrame()->getFramePrintArea().Height() > rSz.GetHeight())
4966 bSplitFly = true;
4972 if (bSplitFly)
4974 // Split fly: enforce minimum row height for the master and follows.
4975 nHeight = rSz.GetHeight();
4977 else
4979 nHeight = rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*_pRow);
4981 if (bMinRowHeightInclBorder)
4983 //get horizontal border(s)
4984 nHeight += lcl_GetTopSpace(*_pRow);
4989 SwRectFnSet aRectFnSet(_pRow);
4990 const SwCellFrame* pLow = static_cast<const SwCellFrame*>(_pRow->Lower());
4991 while ( pLow )
4993 SwTwips nTmp = 0;
4994 const tools::Long nRowSpan = pLow->GetLayoutRowSpan();
4995 // --> NEW TABLES
4996 // Consider height of
4997 // 1. current cell if RowSpan == 1
4998 // 2. current cell if cell is "follow" cell of a cell with RowSpan == -1
4999 // 3. master cell if RowSpan == -1
5000 if ( 1 == nRowSpan )
5002 nTmp = ::lcl_CalcMinCellHeight( pLow, _bConsiderObjs );
5004 else if ( -1 == nRowSpan )
5006 // Height of the last cell of a row span is height of master cell
5007 // minus the height of the other rows which are covered by the master
5008 // cell:
5009 const SwCellFrame& rMaster = pLow->FindStartEndOfRowSpanCell( true );
5010 nTmp = ::lcl_CalcMinCellHeight( &rMaster, _bConsiderObjs );
5011 const SwFrame* pMasterRow = rMaster.GetUpper();
5012 while ( pMasterRow && pMasterRow != _pRow )
5014 nTmp -= aRectFnSet.GetHeight(pMasterRow->getFrameArea());
5015 pMasterRow = pMasterRow->GetNext();
5018 // <-- NEW TABLES
5020 // Do not consider rotated cells:
5021 if ( pLow->IsVertical() == aRectFnSet.IsVert() && nTmp > nHeight )
5022 nHeight = nTmp;
5024 pLow = static_cast<const SwCellFrame*>(pLow->GetNext());
5027 return nHeight;
5030 // #i29550#
5032 // Calculate the maximum of (TopLineSize + TopLineDist) over all lowers:
5033 static sal_uInt16 lcl_GetTopSpace( const SwRowFrame& rRow )
5035 sal_uInt16 nTopSpace = 0;
5036 for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
5037 pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
5039 sal_uInt16 nTmpTopSpace = 0;
5040 const SwFrame* pLower = pCurrLower->Lower();
5041 if ( pLower && pLower->IsRowFrame() )
5042 nTmpTopSpace = lcl_GetTopSpace( *static_cast<const SwRowFrame*>(pLower) );
5043 else
5045 const SwAttrSet& rSet = pCurrLower->GetFormat()->GetAttrSet();
5046 const SvxBoxItem& rBoxItem = rSet.GetBox();
5047 nTmpTopSpace = rBoxItem.CalcLineSpace( SvxBoxItemLine::TOP, true );
5049 nTopSpace = std::max( nTopSpace, nTmpTopSpace );
5051 return nTopSpace;
5054 // Calculate the maximum of TopLineDist over all lowers:
5055 static sal_uInt16 lcl_GetTopLineDist( const SwRowFrame& rRow )
5057 sal_uInt16 nTopLineDist = 0;
5058 for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
5059 pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
5061 sal_uInt16 nTmpTopLineDist = 0;
5062 const SwFrame* pLower = pCurrLower->Lower();
5063 if ( pLower && pLower->IsRowFrame() )
5064 nTmpTopLineDist = lcl_GetTopLineDist( *static_cast<const SwRowFrame*>(pLower) );
5065 else
5067 const SwAttrSet& rSet = pCurrLower->GetFormat()->GetAttrSet();
5068 const SvxBoxItem& rBoxItem = rSet.GetBox();
5069 nTmpTopLineDist = rBoxItem.GetDistance( SvxBoxItemLine::TOP );
5071 nTopLineDist = std::max( nTopLineDist, nTmpTopLineDist );
5073 return nTopLineDist;
5076 // Calculate the maximum of BottomLineSize over all lowers:
5077 static sal_uInt16 lcl_GetBottomLineSize( const SwRowFrame& rRow )
5079 sal_uInt16 nBottomLineSize = 0;
5080 for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
5081 pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
5083 sal_uInt16 nTmpBottomLineSize = 0;
5084 const SwFrame* pLower = pCurrLower->Lower();
5085 if ( pLower && pLower->IsRowFrame() )
5087 const SwFrame* pRow = pCurrLower->GetLastLower();
5088 nTmpBottomLineSize = lcl_GetBottomLineSize( *static_cast<const SwRowFrame*>(pRow) );
5090 else
5092 const SwAttrSet& rSet = pCurrLower->GetFormat()->GetAttrSet();
5093 const SvxBoxItem& rBoxItem = rSet.GetBox();
5094 nTmpBottomLineSize = rBoxItem.CalcLineSpace( SvxBoxItemLine::BOTTOM, true ) -
5095 rBoxItem.GetDistance( SvxBoxItemLine::BOTTOM );
5097 nBottomLineSize = std::max( nBottomLineSize, nTmpBottomLineSize );
5099 return nBottomLineSize;
5102 // Calculate the maximum of BottomLineDist over all lowers:
5103 static sal_uInt16 lcl_GetBottomLineDist( const SwRowFrame& rRow )
5105 sal_uInt16 nBottomLineDist = 0;
5106 for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
5107 pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
5109 sal_uInt16 nTmpBottomLineDist = 0;
5110 const SwFrame* pLower = pCurrLower->Lower();
5111 if ( pLower && pLower->IsRowFrame() )
5113 const SwFrame* pRow = pCurrLower->GetLastLower();
5114 nTmpBottomLineDist = lcl_GetBottomLineDist( *static_cast<const SwRowFrame*>(pRow) );
5116 else
5118 const SwAttrSet& rSet = pCurrLower->GetFormat()->GetAttrSet();
5119 const SvxBoxItem& rBoxItem = rSet.GetBox();
5120 nTmpBottomLineDist = rBoxItem.GetDistance( SvxBoxItemLine::BOTTOM );
5122 nBottomLineDist = std::max( nBottomLineDist, nTmpBottomLineDist );
5124 return nBottomLineDist;
5127 // tdf#104425: calculate the height of all row frames,
5128 // for which this frame is a follow.
5129 // When a row has fixed/minimum height, it may span over
5130 // several pages. The minimal height on this page should
5131 // take into account the sum of all the heights of previous
5132 // frames that constitute the table row on previous pages.
5133 // Otherwise, trying to split a too high row frame will
5134 // result in loop trying to create that too high row
5135 // on each following page
5136 static SwTwips lcl_calcHeightOfRowBeforeThisFrame(const SwRowFrame& rRow)
5138 // We don't need to account for previous instances of repeated headlines
5139 if (rRow.IsRepeatedHeadline())
5140 return 0;
5141 SwRectFnSet aRectFnSet(&rRow);
5142 const SwTableLine* pLine = rRow.GetTabLine();
5143 const SwTabFrame* pTab = rRow.FindTabFrame();
5144 if (!pLine || !pTab || !pTab->IsFollow())
5145 return 0;
5146 SwTwips nResult = 0;
5147 SwIterator<SwRowFrame, SwFormat> aIter(*pLine->GetFrameFormat());
5148 for (const SwRowFrame* pCurRow = aIter.First(); pCurRow; pCurRow = aIter.Next())
5150 if (pCurRow != &rRow && pCurRow->GetTabLine() == pLine)
5152 // We've found another row frame that is part of the same table row
5153 const SwTabFrame* pCurTab = pCurRow->FindTabFrame();
5154 // A row frame may not belong to a table frame, when it is being cut, e.g., in
5155 // lcl_PostprocessRowsInCells().
5156 // Its SwRowFrame::Cut() has been called; it in turn called SwLayoutFrame::Cut(),
5157 // which nullified row's upper in RemoveFromLayout(), and then called Shrink()
5158 // for its former upper.
5159 // Regardless of whether it will be pasted back, or destroyed, currently it's not
5160 // part of layout, and its height does not count
5161 if (pCurTab && pCurTab->IsAnFollow(pTab))
5163 // The found row frame belongs to a table frame that precedes
5164 // (above) this one in chain. So, include it in the sum
5165 nResult += aRectFnSet.GetHeight(pCurRow->getFrameArea());
5169 return nResult;
5172 void SwRowFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
5174 SwRectFnSet aRectFnSet(this);
5175 OSL_ENSURE( pAttrs, "SwRowFrame::Format without Attrs." );
5177 const bool bFix = mbFixSize;
5178 SwTabFrame* pTabFrame = FindTabFrame();
5180 if ( !isFramePrintAreaValid() )
5182 // RowFrames don't have borders/margins therefore the PrintArea always
5183 // matches the FrameArea.
5184 setFramePrintAreaValid(true);
5187 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
5188 aPrt.Left( 0 );
5189 aPrt.Top( 0 );
5190 aPrt.Width ( getFrameArea().Width() );
5191 aPrt.Height( getFrameArea().Height() );
5194 // #i29550#
5195 // Here we calculate the top-printing area for the lower cell frames
5196 if ( pTabFrame->IsCollapsingBorders() )
5198 const sal_uInt16 nTopSpace = lcl_GetTopSpace( *this );
5199 const sal_uInt16 nTopLineDist = lcl_GetTopLineDist( *this );
5200 const sal_uInt16 nBottomLineSize = lcl_GetBottomLineSize( *this );
5201 const sal_uInt16 nBottomLineDist = lcl_GetBottomLineDist( *this );
5203 const SwRowFrame* pPreviousRow = nullptr;
5205 // #i32456#
5206 // In order to calculate the top printing area for the lower cell
5207 // frames, we have to find the 'previous' row frame and compare
5208 // the bottom values of the 'previous' row with the 'top' values
5209 // of this row. The best way to find the 'previous' row is to
5210 // use the table structure:
5211 const SwTable* pTable = pTabFrame->GetTable();
5212 const SwTableLine* pPrevTabLine = nullptr;
5213 const SwRowFrame* pTmpRow = this;
5215 while ( pTmpRow && !pPrevTabLine )
5217 size_t nIdx = 0;
5218 const SwTableLines& rLines = pTmpRow->GetTabLine()->GetUpper() ?
5219 pTmpRow->GetTabLine()->GetUpper()->GetTabLines() :
5220 pTable->GetTabLines();
5222 while ( rLines[ nIdx ] != pTmpRow->GetTabLine() )
5223 ++nIdx;
5225 if ( nIdx > 0 )
5227 // pTmpRow has a 'previous' row in the table structure:
5228 pPrevTabLine = rLines[ nIdx - 1 ];
5230 else
5232 // pTmpRow is a first row in the table structure.
5233 // We go up in the table structure:
5234 pTmpRow = pTmpRow->GetUpper()->GetUpper() &&
5235 pTmpRow->GetUpper()->GetUpper()->IsRowFrame() ?
5236 static_cast<const SwRowFrame*>( pTmpRow->GetUpper()->GetUpper() ) :
5237 nullptr;
5241 // If we found a 'previous' row, we look for the appropriate row frame:
5242 if ( pPrevTabLine )
5244 SwIterator<SwRowFrame,SwFormat> aIter( *pPrevTabLine->GetFrameFormat() );
5245 for ( SwRowFrame* pRow = aIter.First(); pRow; pRow = aIter.Next() )
5247 // #115759# - do *not* take repeated
5248 // headlines, because during split of table it can be
5249 // invalid and thus can't provide correct border values.
5250 if ( pRow->GetTabLine() == pPrevTabLine &&
5251 !pRow->IsRepeatedHeadline() )
5253 pPreviousRow = pRow;
5254 break;
5259 sal_uInt16 nTopPrtMargin = nTopSpace;
5260 if ( pPreviousRow )
5262 const sal_uInt16 nTmpPrtMargin = pPreviousRow->GetBottomLineSize() + nTopLineDist;
5263 if ( nTmpPrtMargin > nTopPrtMargin )
5264 nTopPrtMargin = nTmpPrtMargin;
5267 // table has to be notified if it has to change its lower
5268 // margin due to changes of nBottomLineSize:
5269 if ( !GetNext() && nBottomLineSize != GetBottomLineSize() )
5270 pTabFrame->InvalidatePrt_();
5272 // If there are rows nested inside this row, the nested rows
5273 // may not have been calculated yet. Therefore the
5274 // ::lcl_CalcMinRowHeight( this ) operation later in this
5275 // function cannot consider the correct border values. We
5276 // have to trigger the invalidation of the outer row frame
5277 // manually:
5278 // Note: If any further invalidations should be necessary, we
5279 // should consider moving the invalidation stuff to the
5280 // appropriate SwNotify object.
5281 if ( GetUpper()->GetUpper()->IsRowFrame() &&
5282 ( nBottomLineDist != GetBottomMarginForLowers() ||
5283 nTopPrtMargin != GetTopMarginForLowers() ) )
5284 GetUpper()->GetUpper()->InvalidateSize_();
5286 SetBottomMarginForLowers( nBottomLineDist ); // 3.
5287 SetBottomLineSize( nBottomLineSize ); // 4.
5288 SetTopMarginForLowers( nTopPrtMargin ); // 5.
5293 while ( !isFrameAreaSizeValid() )
5295 setFrameAreaSizeValid(true);
5297 #if OSL_DEBUG_LEVEL > 0
5298 if ( HasFixSize() )
5300 const SwFormatFrameSize &rFrameSize = GetFormat()->GetFrameSize();
5301 OSL_ENSURE( rFrameSize.GetSize().Height() > 0, "Has it" );
5303 #endif
5304 const SwTwips nDiff = aRectFnSet.GetHeight(getFrameArea()) -
5305 ( HasFixSize() && !IsRowSpanLine()
5306 ? pAttrs->GetSize().Height()
5307 // #i26945#
5308 : ::lcl_CalcMinRowHeight( this,
5309 FindTabFrame()->IsConsiderObjsForMinCellHeight() ) );
5310 if ( nDiff )
5312 mbFixSize = false;
5313 if ( nDiff > 0 )
5314 Shrink( nDiff, false, true );
5315 else if ( nDiff < 0 )
5316 Grow( -nDiff );
5317 mbFixSize = bFix;
5321 // last row will fill the space in its upper.
5322 if ( GetNext() )
5323 return;
5325 //The last fills the remaining space in the upper.
5326 SwTwips nDiff = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea());
5327 SwFrame *pSibling = GetUpper()->Lower();
5328 while ( pSibling )
5329 { nDiff -= aRectFnSet.GetHeight(pSibling->getFrameArea());
5330 pSibling = pSibling->GetNext();
5332 if (nDiff > 0 && pTabFrame->IsCollapsingBorders())
5334 // In SwTabFrame::Format, this value will be added to the table's bottom margin
5335 nDiff -= GetBottomLineSize();
5337 if ( nDiff > 0 )
5339 mbFixSize = false;
5340 Grow( nDiff );
5341 mbFixSize = bFix;
5342 setFrameAreaSizeValid(true);
5346 void SwRowFrame::AdjustCells( const SwTwips nHeight, const bool bHeight )
5348 SwFrame *pFrame = Lower();
5349 if ( bHeight )
5351 SwRectFnSet aRectFnSet(this);
5352 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
5353 SwRect aOldFrame;
5354 #endif
5356 while ( pFrame )
5358 SwFrame* pNotify = nullptr;
5360 SwCellFrame* pCellFrame = static_cast<SwCellFrame*>(pFrame);
5362 // NEW TABLES
5363 // Which cells need to be adjusted if the current row changes
5364 // its height?
5366 // Current frame is a covered frame:
5367 // Set new height for covered cell and adjust master cell:
5368 if ( pCellFrame->GetTabBox()->getRowSpan() < 1 )
5370 // Set height of current (covered) cell to new line height.
5371 const tools::Long nDiff = nHeight - aRectFnSet.GetHeight(pCellFrame->getFrameArea());
5372 if ( nDiff )
5375 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pCellFrame);
5376 aRectFnSet.AddBottom( aFrm, nDiff );
5379 pCellFrame->InvalidatePrt_();
5383 SwCellFrame* pToAdjust = nullptr;
5384 SwFrame* pToAdjustRow = nullptr;
5386 // If current frame is covered frame, we still want to adjust the
5387 // height of the cell starting the row span
5388 if ( pCellFrame->GetLayoutRowSpan() < 1 )
5390 pToAdjust = const_cast< SwCellFrame*>(&pCellFrame->FindStartEndOfRowSpanCell( true ));
5391 pToAdjustRow = pToAdjust->GetUpper();
5393 else
5395 pToAdjust = pCellFrame;
5396 pToAdjustRow = this;
5399 // Set height of master cell to height of all lines spanned by this line.
5400 tools::Long nRowSpan = pToAdjust->GetLayoutRowSpan();
5401 SwTwips nSumRowHeight = 0;
5402 while ( pToAdjustRow )
5404 // Use new height for the current row:
5405 nSumRowHeight += pToAdjustRow == this ?
5406 nHeight :
5407 aRectFnSet.GetHeight(pToAdjustRow->getFrameArea());
5409 if ( nRowSpan-- == 1 )
5410 break;
5412 pToAdjustRow = pToAdjustRow->GetNext();
5415 if ( pToAdjustRow && pToAdjustRow != this )
5416 pToAdjustRow->InvalidateSize_();
5418 const tools::Long nDiff = nSumRowHeight - aRectFnSet.GetHeight(pToAdjust->getFrameArea());
5419 if ( nDiff )
5421 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
5422 aOldFrame = pToAdjust->getFrameArea();
5423 #endif
5424 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pToAdjust);
5425 aRectFnSet.AddBottom( aFrm, nDiff );
5426 pNotify = pToAdjust;
5429 if ( pNotify )
5431 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
5432 SwRootFrame *pRootFrame = getRootFrame();
5433 if( pRootFrame && pRootFrame->IsAnyShellAccessible() && pRootFrame->GetCurrShell() )
5434 pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( pNotify, aOldFrame );
5435 #endif
5437 pNotify->InvalidatePrt_();
5440 pFrame = pFrame->GetNext();
5443 else
5444 { while ( pFrame )
5446 pFrame->InvalidateAll_();
5447 pFrame = pFrame->GetNext();
5450 InvalidatePage();
5453 void SwRowFrame::Cut()
5455 SwTabFrame *pTab = FindTabFrame();
5456 if ( pTab && pTab->IsFollow() && this == pTab->GetFirstNonHeadlineRow() )
5458 pTab->FindMaster()->InvalidatePos();
5461 SwLayoutFrame::Cut();
5464 SwTwips SwRowFrame::GrowFrame(SwTwips nDist, SwResizeLimitReason& reason, bool bTst, bool bInfo)
5466 const auto nOrigDist = nDist;
5467 SwTwips nReal = 0;
5469 SwTabFrame* pTab = FindTabFrame();
5470 SwRectFnSet aRectFnSet(pTab);
5472 bool bRestrictTableGrowth;
5473 bool bHasFollowFlowLine = pTab->HasFollowFlowLine();
5475 if ( GetUpper()->IsTabFrame() )
5477 const SwRowFrame* pFollowFlowRow = IsInSplitTableRow();
5478 bRestrictTableGrowth = pFollowFlowRow && !pFollowFlowRow->IsRowSpanLine();
5480 else
5482 OSL_ENSURE( GetUpper()->IsCellFrame(), "RowFrame->GetUpper neither table nor cell" );
5483 bRestrictTableGrowth = GetFollowRow() && bHasFollowFlowLine;
5484 OSL_ENSURE( !bRestrictTableGrowth || !GetNext(),
5485 "GetFollowRow for row frame that has a Next" );
5487 // There may still be some space left in my direct upper:
5488 const SwTwips nAdditionalSpace =
5489 aRectFnSet.BottomDist( getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()->GetUpper()) );
5490 if ( bRestrictTableGrowth && nAdditionalSpace > 0 )
5492 nReal = std::min( nAdditionalSpace, nDist );
5493 nDist -= nReal;
5494 if ( !bTst )
5496 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
5497 aRectFnSet.AddBottom( aFrm, nReal );
5502 if ( bRestrictTableGrowth )
5503 pTab->SetRestrictTableGrowth( true );
5504 else
5506 // Ok, this looks like a hack, indeed, it is a hack.
5507 // If the current row frame is inside another cell frame,
5508 // and the current row frame has no follow, it should not
5509 // be allowed to grow. In fact, setting bRestrictTableGrowth
5510 // to 'false' does not work, because the surrounding RowFrame
5511 // would set this to 'true'.
5512 pTab->SetFollowFlowLine( false );
5515 nReal += SwLayoutFrame::GrowFrame(nDist, reason, bTst, bInfo);
5517 pTab->SetRestrictTableGrowth( false );
5518 pTab->SetFollowFlowLine( bHasFollowFlowLine );
5520 //Update the height of the cells to the newest value.
5521 if ( !bTst )
5523 SwRectFnSet fnRectX(this);
5524 AdjustCells( fnRectX.GetHeight(getFramePrintArea()) + nReal, true );
5525 if ( nReal )
5526 SetCompletePaint();
5528 if (reason == SwResizeLimitReason::Unspecified && nReal < nOrigDist && IsInSplit())
5529 reason = SwResizeLimitReason::FlowToFollow;
5530 return nReal;
5533 SwTwips SwRowFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool bInfo )
5535 SwRectFnSet aRectFnSet(this);
5536 if( HasFixSize() )
5538 AdjustCells( aRectFnSet.GetHeight(getFramePrintArea()), true );
5539 return 0;
5542 // bInfo may be set to true by SwRowFrame::Format; we need to handle this
5543 // here accordingly
5544 const bool bShrinkAnyway = bInfo;
5546 //Only shrink as much as the content of the biggest cell allows.
5547 SwTwips nRealDist = nDist;
5548 SwFormat* pMod = GetFormat();
5549 if (pMod)
5551 const SwFormatFrameSize &rSz = pMod->GetFrameSize();
5552 SwTwips nMinHeight = 0;
5553 if (rSz.GetHeightSizeType() == SwFrameSize::Minimum)
5554 nMinHeight = std::max(rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*this),
5555 tools::Long(0));
5557 // Only necessary to calculate minimal row height if height
5558 // of pRow is at least nMinHeight. Otherwise nMinHeight is the
5559 // minimum height.
5560 if( nMinHeight < aRectFnSet.GetHeight(getFrameArea()) )
5562 // #i26945#
5563 OSL_ENSURE( FindTabFrame(), "<SwRowFrame::ShrinkFrame(..)> - no table frame -> crash." );
5564 const bool bConsiderObjs( FindTabFrame()->IsConsiderObjsForMinCellHeight() );
5565 nMinHeight = lcl_CalcMinRowHeight( this, bConsiderObjs );
5568 if ( (aRectFnSet.GetHeight(getFrameArea()) - nRealDist) < nMinHeight )
5569 nRealDist = aRectFnSet.GetHeight(getFrameArea()) - nMinHeight;
5571 if ( nRealDist < 0 )
5572 nRealDist = 0;
5574 SwTwips nReal = nRealDist;
5575 if ( nReal )
5577 if ( !bTst )
5579 SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
5580 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
5581 aRectFnSet.SetHeight( aFrm, nHeight - nReal );
5583 if( IsVertical() && !IsVertLR() )
5585 aFrm.Pos().AdjustX(nReal );
5589 SwLayoutFrame* pFrame = GetUpper();
5590 SwTwips nTmp = pFrame ? pFrame->Shrink(nReal, bTst) : 0;
5591 if ( !bShrinkAnyway && !GetNext() && nTmp != nReal )
5593 //The last one gets the leftover in the upper and therefore takes
5594 //care (otherwise: endless loop)
5595 if ( !bTst )
5597 nReal -= nTmp;
5598 SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
5599 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
5600 aRectFnSet.SetHeight( aFrm, nHeight + nReal );
5602 if( IsVertical() && !IsVertLR() )
5604 aFrm.Pos().AdjustX( -nReal );
5607 nReal = nTmp;
5611 // Invalidate appropriately and update the height to the newest value.
5612 if ( !bTst )
5614 if ( nReal )
5616 if ( GetNext() )
5617 GetNext()->InvalidatePos_();
5618 InvalidateAll_();
5619 SetCompletePaint();
5621 SwTabFrame *pTab = FindTabFrame();
5622 if ( !pTab->IsRebuildLastLine()
5623 && pTab->IsFollow()
5624 && this == pTab->GetFirstNonHeadlineRow()
5625 && !pTab->IsInRecalcLowerRow() )
5627 SwTabFrame* pMasterTab = pTab->FindMaster();
5628 pMasterTab->InvalidatePos();
5631 AdjustCells( aRectFnSet.GetHeight(getFramePrintArea()) - nReal, true );
5633 return nReal;
5636 bool SwRowFrame::IsRowSplitAllowed() const
5638 // Fixed size rows are never allowed to split:
5639 if ( HasFixSize() )
5641 OSL_ENSURE( SwFrameSize::Fixed == GetFormat()->GetFrameSize().GetHeightSizeType(), "pRow claims to have fixed size" );
5642 return false;
5645 // Repeated headlines are never allowed to split:
5646 const SwTabFrame* pTabFrame = FindTabFrame();
5647 if ( pTabFrame->GetTable()->GetRowsToRepeat() > 0 &&
5648 pTabFrame->IsInHeadline( *this ) )
5649 return false;
5651 if ( IsForceRowSplitAllowed() )
5652 return true;
5654 const SwTableLineFormat* pFrameFormat = GetTabLine()->GetFrameFormat();
5655 const SwFormatRowSplit& rLP = pFrameFormat->GetRowSplit();
5656 return rLP.GetValue();
5659 bool SwRowFrame::ShouldRowKeepWithNext( const bool bCheckParents ) const
5661 // No KeepWithNext if nested in another table
5662 if ( GetUpper()->GetUpper()->IsCellFrame() )
5663 return false;
5665 const SwCellFrame* pCell = static_cast<const SwCellFrame*>(Lower());
5666 const SwFrame* pText = pCell ? pCell->Lower() : nullptr;
5668 return pText && pText->IsTextFrame() && !pText->IsHiddenNow() &&
5669 static_cast<const SwTextFrame*>(pText)->GetTextNodeForParaProps()->GetSwAttrSet().GetKeep(bCheckParents).GetValue();
5672 SwCellFrame::SwCellFrame(const SwTableBox &rBox, SwFrame* pSib, bool bInsertContent)
5673 : SwLayoutFrame( rBox.GetFrameFormat(), pSib )
5674 , m_pTabBox( &rBox )
5676 mnFrameType = SwFrameType::Cell;
5678 if ( !bInsertContent )
5679 return;
5681 //If a StartIdx is available, ContentFrames are added in the cell, otherwise
5682 //Rows have to be present and those are added.
5683 if ( SwNodeOffset nIndex = rBox.GetSttIdx() )
5685 ::InsertCnt_( this, rBox.GetFrameFormat()->GetDoc(), ++nIndex );
5687 else
5689 const SwTableLines &rLines = rBox.GetTabLines();
5690 SwFrame *pTmpPrev = nullptr;
5691 for ( size_t i = 0; i < rLines.size(); ++i )
5693 SwRowFrame *pNew = new SwRowFrame( *rLines[i], this, bInsertContent );
5694 pNew->InsertBehind( this, pTmpPrev );
5695 pTmpPrev = pNew;
5700 void SwCellFrame::DestroyImpl()
5702 sw::BroadcastingModify* pMod = GetFormat();
5703 if( pMod )
5705 // At this stage the lower frames aren't destroyed already,
5706 // therefore we have to do a recursive dispose.
5707 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
5708 SwRootFrame *pRootFrame = getRootFrame();
5709 if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
5710 pRootFrame->GetCurrShell() )
5712 pRootFrame->GetCurrShell()->Imp()->DisposeAccessibleFrame( this, true );
5714 #endif
5716 pMod->Remove(*this);
5717 if( !pMod->HasWriterListeners() )
5718 delete pMod;
5721 SwLayoutFrame::DestroyImpl();
5724 SwCellFrame::~SwCellFrame()
5728 static bool lcl_ArrangeLowers( SwLayoutFrame *pLay, tools::Long lYStart, bool bInva )
5730 bool bRet = false;
5731 SwFrame *pFrame = pLay->Lower();
5732 SwRectFnSet aRectFnSet(pLay);
5733 while ( pFrame )
5735 tools::Long nFrameTop = aRectFnSet.GetTop(pFrame->getFrameArea());
5736 if( nFrameTop != lYStart )
5738 bRet = true;
5739 const tools::Long lDiff = aRectFnSet.YDiff( lYStart, nFrameTop );
5740 const tools::Long lDiffX = lYStart - nFrameTop;
5743 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFrame);
5744 aRectFnSet.SubTop( aFrm, -lDiff );
5745 aRectFnSet.AddBottom( aFrm, lDiff );
5748 pFrame->SetCompletePaint();
5750 if ( !pFrame->GetNext() )
5751 pFrame->SetRetouche();
5752 if( bInva )
5753 pFrame->Prepare( PrepareHint::FramePositionChanged );
5754 if ( pFrame->IsLayoutFrame() && static_cast<SwLayoutFrame*>(pFrame)->Lower() )
5755 lcl_ArrangeLowers( static_cast<SwLayoutFrame*>(pFrame),
5756 aRectFnSet.GetTop(static_cast<SwLayoutFrame*>(pFrame)->Lower()->getFrameArea())
5757 + lDiffX, bInva );
5758 if ( pFrame->GetDrawObjs() )
5760 for ( size_t i = 0; i < pFrame->GetDrawObjs()->size(); ++i )
5762 SwAnchoredObject* pAnchoredObj = (*pFrame->GetDrawObjs())[i];
5763 // #i26945# - check, if anchored object
5764 // is lower of layout frame by checking, if the anchor
5765 // frame, which contains the anchor position, is a lower
5766 // of the layout frame.
5767 if ( !pLay->IsAnLower( pAnchoredObj->GetAnchorFrameContainingAnchPos() ) )
5769 continue;
5771 // #i52904# - distinguish between anchored
5772 // objects, whose vertical position depends on its anchor
5773 // frame and whose vertical position is independent
5774 // from its anchor frame.
5775 bool bVertPosDepOnAnchor( true );
5777 SwFormatVertOrient aVert( pAnchoredObj->GetFrameFormat()->GetVertOrient() );
5778 switch ( aVert.GetRelationOrient() )
5780 case text::RelOrientation::PAGE_FRAME:
5781 case text::RelOrientation::PAGE_PRINT_AREA:
5782 bVertPosDepOnAnchor = false;
5783 break;
5784 default: break;
5787 if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
5790 // OD 2004-05-18 #i28701# - no direct move of objects,
5791 // which are anchored to-paragraph/to-character, if
5792 // the wrapping style influence has to be considered
5793 // on the object positioning.
5794 // #i52904# - no direct move of objects,
5795 // whose vertical position doesn't depend on anchor frame.
5796 const bool bDirectMove =
5797 FAR_AWAY != pFly->getFrameArea().Top() &&
5798 bVertPosDepOnAnchor &&
5799 !pFly->ConsiderObjWrapInfluenceOnObjPos();
5800 if ( bDirectMove )
5803 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFly);
5804 aRectFnSet.SubTop( aFrm, -lDiff );
5805 aRectFnSet.AddBottom( aFrm, lDiff );
5808 pFly->GetVirtDrawObj()->SetBoundAndSnapRectsDirty();
5809 // --> OD 2004-08-17 - also notify view of <SdrObject>
5810 // instance, which represents the Writer fly frame in
5811 // the drawing layer
5812 pFly->GetVirtDrawObj()->SetChanged();
5813 // #i58280#
5814 pFly->InvalidateObjRectWithSpaces();
5817 if ( pFly->IsFlyInContentFrame() )
5819 static_cast<SwFlyInContentFrame*>(pFly)->AddRefOfst( lDiff );
5820 // #115759# - reset current relative
5821 // position to get re-positioned, if not directly moved.
5822 if ( !bDirectMove )
5824 pAnchoredObj->SetCurrRelPos( Point( 0, 0 ) );
5827 else if( pFly->IsAutoPos() )
5829 pFly->AddLastCharY( lDiff );
5830 // OD 2004-05-18 #i28701# - follow-up of #i22341#
5831 // <mnLastTopOfLine> has also been adjusted.
5832 pFly->AddLastTopOfLineY( lDiff );
5834 // #i26945# - re-registration at
5835 // page frame of anchor frame, if table frame isn't
5836 // a follow table and table frame isn't in its
5837 // rebuild of last line.
5838 const SwTabFrame* pTabFrame = pLay->FindTabFrame();
5839 // - save: check, if table frame is found.
5840 if ( pTabFrame &&
5841 !( pTabFrame->IsFollow() &&
5842 pTabFrame->FindMaster()->IsRebuildLastLine() ) &&
5843 pFly->IsFlyFreeFrame() )
5845 SwPageFrame* pPageFrame = pFly->GetPageFrame();
5846 SwPageFrame* pPageOfAnchor = pFrame->FindPageFrame();
5847 if ( pPageFrame != pPageOfAnchor )
5849 pFly->InvalidatePos();
5850 pFly->RegisterAtPage(*pPageOfAnchor);
5853 // OD 2004-05-11 #i28701# - Because of the introduction
5854 // of new positionings and alignments (e.g. aligned at
5855 // page area, but anchored at-character), the position
5856 // of the Writer fly frame has to be invalidated.
5857 pFly->InvalidatePos();
5859 // #i26945# - follow-up of #i3317#
5860 // No arrangement of lowers, if Writer fly frame isn't
5861 // moved
5862 if ( bDirectMove &&
5863 ::lcl_ArrangeLowers( pFly,
5864 aRectFnSet.GetPrtTop(*pFly),
5865 bInva ) )
5867 pFly->SetCompletePaint();
5870 else if ( dynamic_cast< const SwAnchoredDrawObject *>( pAnchoredObj ) != nullptr )
5872 // #i26945#
5873 const SwTabFrame* pTabFrame = pLay->FindTabFrame();
5874 if ( pTabFrame &&
5875 !( pTabFrame->IsFollow() &&
5876 pTabFrame->FindMaster()->IsRebuildLastLine() ) &&
5877 (pAnchoredObj->GetFrameFormat()->GetAnchor().GetAnchorId()
5878 != RndStdIds::FLY_AS_CHAR))
5880 SwPageFrame* pPageFrame = pAnchoredObj->GetPageFrame();
5881 SwPageFrame* pPageOfAnchor = pFrame->FindPageFrame();
5882 if ( pPageFrame != pPageOfAnchor )
5884 pAnchoredObj->InvalidateObjPos();
5885 pAnchoredObj->RegisterAtPage(*pPageOfAnchor);
5888 // #i28701# - adjust last character
5889 // rectangle and last top of line.
5890 pAnchoredObj->AddLastCharY( lDiff );
5891 pAnchoredObj->AddLastTopOfLineY( lDiff );
5892 // #i52904# - re-introduce direct move
5893 // of drawing objects
5894 const bool bDirectMove =
5895 static_cast<const SwDrawFrameFormat*>(pAnchoredObj->GetFrameFormat())->IsPosAttrSet() &&
5896 bVertPosDepOnAnchor &&
5897 !pAnchoredObj->ConsiderObjWrapInfluenceOnObjPos();
5898 if ( bDirectMove )
5900 SwObjPositioningInProgress aObjPosInProgress( *pAnchoredObj );
5901 if ( aRectFnSet.IsVert() )
5903 pAnchoredObj->DrawObj()->Move( Size( lDiff, 0 ) );
5905 else
5907 pAnchoredObj->DrawObj()->Move( Size( 0, lDiff ) );
5909 // #i58280#
5910 pAnchoredObj->InvalidateObjRectWithSpaces();
5912 pAnchoredObj->InvalidateObjPos();
5914 else
5916 OSL_FAIL( "<lcl_ArrangeLowers(..)> - unknown type of anchored object!" );
5921 // Columns and cells are ordered horizontal, not vertical
5922 if( !pFrame->IsColumnFrame() && !pFrame->IsCellFrame() )
5923 lYStart = aRectFnSet.YInc( lYStart,
5924 aRectFnSet.GetHeight(pFrame->getFrameArea()) );
5926 // Nowadays, the content inside a cell can flow into the follow table.
5927 // Thus, the cell may only grow up to the end of the environment.
5928 // So the content may have grown, but the cell could not grow.
5929 // Therefore we have to trigger a formatting for the frames, which do
5930 // not fit into the cell anymore:
5931 SwTwips nDistanceToUpperPrtBottom =
5932 aRectFnSet.BottomDist( pFrame->getFrameArea(), aRectFnSet.GetPrtBottom(*pLay) );
5933 // #i56146# - Revise fix of issue #i26945#
5934 // do *not* consider content inside fly frames, if it's an undersized paragraph.
5935 // #i26945# - consider content inside fly frames
5936 if ( nDistanceToUpperPrtBottom < 0 &&
5937 ( ( pFrame->IsInFly() &&
5938 ( !pFrame->IsTextFrame() ||
5939 !static_cast<SwTextFrame*>(pFrame)->IsUndersized() ) ) ||
5940 pFrame->IsInSplitTableRow() ) )
5942 pFrame->InvalidatePos();
5945 pFrame = pFrame->GetNext();
5947 return bRet;
5950 void SwCellFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
5952 OSL_ENSURE( pAttrs, "CellFrame::Format, pAttrs is 0." );
5953 const SwTabFrame* pTab = FindTabFrame();
5954 SwRectFnSet aRectFnSet(pTab);
5955 SwFrame* pLower = Lower();
5957 if ( !isFramePrintAreaValid() )
5959 setFramePrintAreaValid(true);
5961 //Adjust position.
5962 if ( pLower )
5964 SwTwips nTopSpace, nBottomSpace, nLeftSpace, nRightSpace;
5965 // #i29550#
5966 if ( pTab->IsCollapsingBorders() && !pLower->IsRowFrame() )
5968 const SvxBoxItem& rBoxItem = pAttrs->GetBox();
5969 nLeftSpace = rBoxItem.GetDistance( SvxBoxItemLine::LEFT );
5970 nRightSpace = rBoxItem.GetDistance( SvxBoxItemLine::RIGHT );
5971 nTopSpace = static_cast<SwRowFrame*>(GetUpper())->GetTopMarginForLowers();
5972 nBottomSpace = static_cast<SwRowFrame*>(GetUpper())->GetBottomMarginForLowers();
5974 else
5976 // OD 23.01.2003 #106895# - add 1st param to <SwBorderAttrs::CalcRight(..)>
5977 nLeftSpace = pAttrs->CalcLeft( this );
5978 nRightSpace = pAttrs->CalcRight( this );
5979 nTopSpace = pAttrs->CalcTop();
5980 nBottomSpace = pAttrs->CalcBottom();
5982 aRectFnSet.SetXMargins( *this, nLeftSpace, nRightSpace );
5983 aRectFnSet.SetYMargins( *this, nTopSpace, nBottomSpace );
5986 // #i26945#
5987 tools::Long nRemaining = GetTabBox()->getRowSpan() >= 1 ?
5988 ::lcl_CalcMinCellHeight( this, pTab->IsConsiderObjsForMinCellHeight(), pAttrs ) :
5990 if ( !isFrameAreaSizeValid() )
5992 setFrameAreaSizeValid(true);
5994 //The VarSize of the CellFrames is always the width.
5995 //The width is not variable though, it is defined by the format.
5996 //This predefined value however does not necessary match the actual
5997 //width. The width is calculated based on the attribute, the value in
5998 //the attribute matches the desired value of the TabFrame. Changes which
5999 //were done there are taken into account here proportionately.
6000 //If the cell doesn't have a neighbour anymore, it does not take the
6001 //attribute into account and takes the rest of the upper instead.
6002 SwTwips nWidth;
6003 if ( GetNext() )
6005 const SwTwips nWish = pTab->GetFormat()->GetFrameSize().GetWidth();
6006 nWidth = pAttrs->GetSize().Width();
6008 OSL_ENSURE( nWish, "Table without width?" );
6009 OSL_ENSURE( nWidth <= nWish, "Width of cell larger than table." );
6010 OSL_ENSURE( nWidth > 0, "Box without width" );
6012 const tools::Long nPrtWidth = aRectFnSet.GetWidth(pTab->getFramePrintArea());
6013 if ( nWish != nPrtWidth )
6015 // Avoid rounding problems, at least for the new table model
6016 if ( pTab->GetTable()->IsNewModel() )
6018 // 1. sum of widths of cells up to this cell (in model)
6019 const SwTableLine* pTabLine = GetTabBox()->GetUpper();
6020 const SwTableBoxes& rBoxes = pTabLine->GetTabBoxes();
6021 const SwTableBox* pTmpBox = nullptr;
6023 SwTwips nSumWidth = 0;
6024 size_t i = 0;
6027 pTmpBox = rBoxes[ i++ ];
6028 nSumWidth += pTmpBox->GetFrameFormat()->GetFrameSize().GetWidth();
6030 while ( pTmpBox != GetTabBox() );
6032 // 2. calculate actual width of cells up to this one
6033 double nTmpWidth = nSumWidth;
6034 nTmpWidth *= nPrtWidth;
6035 nTmpWidth /= nWish;
6036 nWidth = static_cast<SwTwips>(nTmpWidth);
6038 // 3. calculate frame widths of cells up to this one:
6039 const SwFrame* pTmpCell = static_cast<const SwLayoutFrame*>(GetUpper())->Lower();
6040 SwTwips nSumFrameWidths = 0;
6041 while ( pTmpCell != this )
6043 nSumFrameWidths += aRectFnSet.GetWidth(pTmpCell->getFrameArea());
6044 pTmpCell = pTmpCell->GetNext();
6047 nWidth = nWidth - nSumFrameWidths;
6049 else
6051 // #i12092# use double instead of long,
6052 // otherwise this could lead to overflows
6053 double nTmpWidth = nWidth;
6054 nTmpWidth *= nPrtWidth;
6055 nTmpWidth /= nWish;
6056 nWidth = static_cast<SwTwips>(nTmpWidth);
6060 else
6062 OSL_ENSURE( pAttrs->GetSize().Width() > 0, "Box without width" );
6063 nWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
6064 SwFrame *pPre = GetUpper()->Lower();
6065 while ( pPre != this )
6067 nWidth -= aRectFnSet.GetWidth(pPre->getFrameArea());
6068 pPre = pPre->GetNext();
6072 const tools::Long nDiff = nWidth - aRectFnSet.GetWidth(getFrameArea());
6075 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
6077 if( IsNeighbourFrame() && IsRightToLeft() )
6079 aRectFnSet.SubLeft( aFrm, nDiff );
6081 else
6083 aRectFnSet.AddRight( aFrm, nDiff );
6088 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
6089 aRectFnSet.AddRight( aPrt, nDiff );
6092 //Adjust the height, it's defined through the content and the margins.
6093 const tools::Long nDiffHeight = nRemaining - aRectFnSet.GetHeight(getFrameArea());
6094 if ( nDiffHeight )
6096 if ( nDiffHeight > 0 )
6098 //Validate again if no growth happened. Invalidation is done
6099 //through AdjustCells of the row.
6100 if ( !Grow( nDiffHeight ) )
6102 setFrameAreaSizeValid(true);
6103 setFramePrintAreaValid(true);
6106 else
6108 // Only keep invalidated if shrinking was actually done; the
6109 // attempt can be ignored because all horizontally adjoined
6110 // cells have to be the same height.
6111 if ( !Shrink( -nDiffHeight ) )
6113 setFrameAreaSizeValid(true);
6114 setFramePrintAreaValid(true);
6119 const SwFormatVertOrient &rOri = pAttrs->GetAttrSet().GetVertOrient();
6121 if ( !pLower )
6122 return;
6124 // From now on, all operations are related to the table cell.
6125 aRectFnSet.Refresh(this);
6127 SwPageFrame* pPg = nullptr;
6128 if ( !FindTabFrame()->IsRebuildLastLine() && text::VertOrientation::NONE != rOri.GetVertOrient() &&
6129 // #158225# no vertical alignment of covered cells
6130 !IsCoveredCell() &&
6131 (pPg = FindPageFrame())!=nullptr )
6133 if ( !pLower->IsContentFrame() && pLower->IsSctFrame() && !pLower->IsTabFrame() )
6135 // OSL_ENSURE(for HTML-import!
6136 OSL_ENSURE( false, "VAlign to cell without content" );
6137 return;
6139 bool bVertDir = true;
6140 // #i43913# - no vertical alignment, if wrapping
6141 // style influence is considered on object positioning and
6142 // an object is anchored inside the cell.
6143 const bool bConsiderWrapOnObjPos( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) );
6144 // No alignment if fly with wrap overlaps the cell.
6145 if ( pPg->GetSortedObjs() )
6147 SwRect aRect( getFramePrintArea() ); aRect += getFrameArea().Pos();
6148 for (SwAnchoredObject* pAnchoredObj : *pPg->GetSortedObjs())
6150 SwRect aTmp( pAnchoredObj->GetObjRect() );
6151 const SwFrame* pAnch = pAnchoredObj->GetAnchorFrame();
6152 if ( (bConsiderWrapOnObjPos && IsAnLower( pAnch )) || (!bConsiderWrapOnObjPos && aTmp.Overlaps( aRect )) )
6154 const SwFrameFormat* pAnchoredObjFrameFormat = pAnchoredObj->GetFrameFormat();
6155 const SwFormatSurround &rSur = pAnchoredObjFrameFormat->GetSurround();
6157 if ( bConsiderWrapOnObjPos || css::text::WrapTextMode_THROUGH != rSur.GetSurround() )
6159 // frames, which the cell is a lower of, aren't relevant
6160 if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
6162 if ( pFly->IsAnLower( this ) )
6163 continue;
6166 // #i43913#
6167 // #i52904# - no vertical alignment,
6168 // if object, anchored inside cell, has temporarily
6169 // consider its wrapping style on object positioning.
6170 // #i58806# - no vertical alignment
6171 // if object does not follow the text flow.
6172 if ( bConsiderWrapOnObjPos ||
6173 !IsAnLower( pAnch ) ||
6174 pAnchoredObj->IsTmpConsiderWrapInfluence() ||
6175 !pAnchoredObjFrameFormat->GetFollowTextFlow().GetValue() )
6177 bVertDir = false;
6178 break;
6185 tools::Long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
6186 if( ( bVertDir && ( nRemaining -= lcl_CalcTopAndBottomMargin( *this, *pAttrs ) ) < nPrtHeight ) ||
6187 aRectFnSet.GetTop(pLower->getFrameArea()) != aRectFnSet.GetPrtTop(*this) )
6189 tools::Long nDiff = aRectFnSet.GetHeight(getFramePrintArea()) - nRemaining;
6190 if ( nDiff >= 0 )
6192 tools::Long lTopOfst = 0;
6193 if ( bVertDir )
6195 switch ( rOri.GetVertOrient() )
6197 case text::VertOrientation::CENTER: lTopOfst = nDiff / 2; break;
6198 case text::VertOrientation::BOTTOM: lTopOfst = nDiff; break;
6199 default: break;
6202 tools::Long nTmp = aRectFnSet.YInc(
6203 aRectFnSet.GetPrtTop(*this), lTopOfst );
6204 if ( lcl_ArrangeLowers( this, nTmp, !bVertDir ) )
6205 SetCompletePaint();
6209 else
6211 //Was an old alignment taken into account?
6212 if ( pLower->IsContentFrame() )
6214 const tools::Long lYStart = aRectFnSet.GetPrtTop(*this);
6215 lcl_ArrangeLowers( this, lYStart, true );
6219 // Handle rotated portions of lowers: it's possible that we have changed amount of vertical
6220 // space since the last format, and this affects how many rotated portions we need. So throw
6221 // away the current portions to build them using the new line width.
6222 for (SwFrame* pFrame = pLower; pFrame; pFrame = pFrame->GetNext())
6224 if (!pFrame->IsTextFrame())
6226 continue;
6229 auto pTextFrame = static_cast<SwTextFrame*>(pFrame);
6230 if (!pTextFrame->GetHasRotatedPortions())
6232 continue;
6235 pTextFrame->Prepare();
6239 void SwCellFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
6241 if(rHint.GetId() == SfxHintId::SwTableBoxFormatChanged)
6243 auto pNewFormatHint = static_cast<const sw::TableBoxFormatChanged*>(&rHint);
6244 if(GetTabBox() != &pNewFormatHint->m_rTableBox)
6245 return;
6246 RegisterToFormat(const_cast<SwTableBoxFormat&>(pNewFormatHint->m_rNewFormat));
6247 InvalidateSize();
6248 InvalidatePrt_();
6249 SetCompletePaint();
6250 SetDerivedVert(false);
6251 CheckDirChange();
6253 // #i47489#
6254 // make sure that the row will be formatted, in order
6255 // to have the correct Get(Top|Bottom)MarginForLowers values
6256 // set at the row.
6257 const SwTabFrame* pTab = FindTabFrame();
6258 if(pTab && pTab->IsCollapsingBorders())
6260 SwFrame* pRow = GetUpper();
6261 pRow->InvalidateSize_();
6262 pRow->InvalidatePrt_();
6265 else if(rHint.GetId() == SfxHintId::SwMoveTableBox)
6267 auto pMoveTableBoxHint = static_cast<const sw::MoveTableBoxHint*>(&rHint);
6268 if(GetTabBox() != &pMoveTableBoxHint->m_rTableBox)
6269 return;
6270 const_cast<SwFrameFormat*>(&pMoveTableBoxHint->m_rNewFormat)->Add(*this);
6271 InvalidateAll();
6272 ReinitializeFrameSizeAttrFlags();
6273 SetDerivedVert(false);
6274 CheckDirChange();
6276 else if (rHint.GetId() == SfxHintId::SwFormatChange
6277 || rHint.GetId() == SfxHintId::SwObjectDying
6278 || rHint.GetId() == SfxHintId::SwUpdateAttr)
6280 SwLayoutFrame::SwClientNotify(rMod, rHint);
6282 else if (rHint.GetId() == SfxHintId::SwLegacyModify || rHint.GetId() == SfxHintId::SwAttrSetChange)
6284 const SfxPoolItem* pVertOrientItem = nullptr;
6285 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
6286 const SfxPoolItem* pProtectItem = nullptr;
6287 #endif
6288 const SfxPoolItem* pFrameDirItem = nullptr;
6289 const SfxPoolItem* pBoxItem = nullptr;
6290 if (rHint.GetId() == SfxHintId::SwLegacyModify)
6292 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
6293 const auto nWhich = pLegacy->m_pNew ? pLegacy->m_pNew->Which() : 0;
6294 switch(nWhich)
6296 case RES_VERT_ORIENT:
6297 pVertOrientItem = pLegacy->m_pNew;
6298 break;
6299 case RES_PROTECT:
6300 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
6301 pProtectItem = pLegacy->m_pNew;
6302 #endif
6303 break;
6304 case RES_FRAMEDIR:
6305 pFrameDirItem = pLegacy->m_pNew;
6306 break;
6307 case RES_BOX:
6308 pBoxItem = pLegacy->m_pNew;
6309 break;
6312 else // rHint.GetId() == SfxHintId::SwAttrSetChange
6314 auto pChangeHint = static_cast<const sw::AttrSetChangeHint*>(&rHint);
6315 auto& rChgSet = *pChangeHint->m_pNew->GetChgSet();
6316 pVertOrientItem = rChgSet.GetItemIfSet(RES_VERT_ORIENT, false);
6317 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
6318 pProtectItem = rChgSet.GetItemIfSet(RES_PROTECT, false);
6319 #endif
6320 pFrameDirItem = rChgSet.GetItemIfSet(RES_FRAMEDIR, false);
6321 pBoxItem = rChgSet.GetItemIfSet(RES_BOX, false);
6323 if(pVertOrientItem)
6325 bool bInva = true;
6326 const auto eVertOrient = static_cast<const SwFormatVertOrient*>(pVertOrientItem)->GetVertOrient();
6327 SwFrame* pLower = Lower();
6328 if(text::VertOrientation::NONE == eVertOrient && pLower && pLower->IsContentFrame())
6330 SwRectFnSet aRectFnSet(this);
6331 const tools::Long lYStart = aRectFnSet.GetPrtTop(*this);
6332 bInva = lcl_ArrangeLowers(this, lYStart, false);
6334 if (bInva)
6336 SetCompletePaint();
6337 InvalidatePrt();
6340 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
6341 if(pProtectItem)
6343 SwViewShell* pSh = getRootFrame()->GetCurrShell();
6344 if(pSh && pSh->GetLayout()->IsAnyShellAccessible())
6345 pSh->Imp()->InvalidateAccessibleEditableState(true, this);
6347 #endif
6348 if(pFrameDirItem)
6350 SetDerivedVert(false);
6351 CheckDirChange();
6353 // #i29550#
6354 if(pBoxItem)
6356 SwFrame* pTmpUpper = GetUpper();
6357 while(pTmpUpper->GetUpper() && !pTmpUpper->GetUpper()->IsTabFrame())
6358 pTmpUpper = pTmpUpper->GetUpper();
6360 SwTabFrame* pTabFrame = static_cast<SwTabFrame*>(pTmpUpper->GetUpper());
6361 if(pTabFrame->IsCollapsingBorders())
6363 // Invalidate lowers of this and next row:
6364 lcl_InvalidateAllLowersPrt(static_cast<SwRowFrame*>(pTmpUpper));
6365 pTmpUpper = pTmpUpper->GetNext();
6366 if(pTmpUpper)
6367 lcl_InvalidateAllLowersPrt(static_cast<SwRowFrame*>(pTmpUpper));
6368 else
6369 pTabFrame->InvalidatePrt();
6372 SwLayoutFrame::SwClientNotify(rMod, rHint);
6376 tools::Long SwCellFrame::GetLayoutRowSpan() const
6378 const SwTableBox *pTabBox = GetTabBox();
6379 tools::Long nRet = pTabBox ? pTabBox->getRowSpan() : 0;
6380 if ( nRet < 1 )
6382 const SwFrame* pRow = GetUpper();
6383 const SwTabFrame* pTab = pRow ? static_cast<const SwTabFrame*>(pRow->GetUpper()) : nullptr;
6385 if ( pTab && pTab->IsFollow() && pRow == pTab->GetFirstNonHeadlineRow() )
6386 nRet = -nRet;
6388 return nRet;
6391 const SwCellFrame* SwCellFrame::GetCoveredCellInRow(const SwRowFrame& rRow) const
6393 if (GetLayoutRowSpan() <= 1)
6395 // Not merged vertically.
6396 return nullptr;
6399 for (const SwFrame* pCell = rRow.GetLower(); pCell; pCell = pCell->GetNext())
6401 if (!pCell->IsCellFrame())
6403 continue;
6406 auto pCellFrame = static_cast<const SwCellFrame*>(pCell);
6407 if (!pCellFrame->IsCoveredCell())
6409 continue;
6412 if (pCellFrame->getFrameArea().Left() != getFrameArea().Left())
6414 continue;
6417 if (pCellFrame->getFrameArea().Width() != getFrameArea().Width())
6419 continue;
6422 // pCellFrame is covered, there are only covered cell frames between "this" and pCellFrame
6423 // and the horizontal position/size matches "this".
6424 return pCellFrame;
6427 return nullptr;
6430 std::vector<const SwCellFrame*> SwCellFrame::GetCoveredCells() const
6432 std::vector<const SwCellFrame*> aRet;
6433 if (GetLayoutRowSpan() <= 1)
6435 return aRet;
6438 if (!GetUpper()->IsRowFrame())
6440 return aRet;
6443 auto pFirstRowFrame = static_cast<const SwRowFrame*>(GetUpper());
6444 if (!pFirstRowFrame->GetNext())
6446 return aRet;
6449 if (!pFirstRowFrame->GetNext()->IsRowFrame())
6451 return aRet;
6454 for (const SwFrame* pRow = pFirstRowFrame->GetNext(); pRow; pRow = pRow->GetNext())
6456 if (!pRow->IsRowFrame())
6458 continue;
6461 auto pRowFrame = static_cast<const SwRowFrame*>(pRow);
6462 const SwCellFrame* pCovered = GetCoveredCellInRow(*pRowFrame);
6463 if (!pCovered)
6465 continue;
6468 // Found a cell in a next row that is covered by "this".
6469 aRet.push_back(pCovered);
6472 return aRet;
6475 void SwCellFrame::dumpAsXmlAttributes(xmlTextWriterPtr pWriter) const
6477 SwFrame::dumpAsXmlAttributes(pWriter);
6478 if (SwCellFrame* pFollow = GetFollowCell())
6479 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("follow"), "%" SAL_PRIuUINT32, pFollow->GetFrameId());
6481 if (SwCellFrame* pPrevious = GetPreviousCell())
6482 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("precede"), "%" SAL_PRIuUINT32, pPrevious->GetFrameId());
6485 void SwCellFrame::dumpAsXml(xmlTextWriterPtr writer) const
6487 (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("cell"));
6488 dumpAsXmlAttributes(writer);
6489 (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "rowspan" ), "%ld", GetLayoutRowSpan() );
6491 (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
6492 dumpInfosAsXml(writer);
6493 (void)xmlTextWriterEndElement(writer);
6494 dumpChildrenAsXml(writer);
6496 (void)xmlTextWriterEndElement(writer);
6499 // #i103961#
6500 void SwCellFrame::Cut()
6502 // notification for accessibility
6503 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
6505 SwRootFrame *pRootFrame = getRootFrame();
6506 if( pRootFrame && pRootFrame->IsAnyShellAccessible() )
6508 SwViewShell* pVSh = pRootFrame->GetCurrShell();
6509 if ( pVSh && pVSh->Imp() )
6511 pVSh->Imp()->DisposeAccessibleFrame( this );
6515 #endif
6517 SwLayoutFrame::Cut();
6520 // Helper functions for repeated headlines:
6522 bool SwTabFrame::IsInHeadline( const SwFrame& rFrame ) const
6524 OSL_ENSURE( IsAnLower( &rFrame ) && rFrame.IsInTab(),
6525 "SwTabFrame::IsInHeadline called for frame not lower of table" );
6527 const SwFrame* pTmp = &rFrame;
6528 while ( !pTmp->GetUpper()->IsTabFrame() )
6529 pTmp = pTmp->GetUpper();
6531 return GetTable()->IsHeadline( *static_cast<const SwRowFrame*>(pTmp)->GetTabLine() );
6535 * If this is a master table, we can may assume, that there are at least
6536 * nRepeat lines in the table.
6537 * If this is a follow table, there are intermediate states for the table
6538 * layout, e.g., during deletion of rows, which makes it necessary to find
6539 * the first non-headline row by evaluating the headline flag at the row frame.
6541 SwRowFrame* SwTabFrame::GetFirstNonHeadlineRow() const
6543 SwRowFrame* pRet = const_cast<SwRowFrame*>(static_cast<const SwRowFrame*>(Lower()));
6544 if ( pRet )
6546 if ( IsFollow() )
6548 while ( pRet && pRet->IsRepeatedHeadline() )
6549 pRet = static_cast<SwRowFrame*>(pRet->GetNext());
6551 else
6553 sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat();
6554 while ( pRet && nRepeat > 0 )
6556 pRet = static_cast<SwRowFrame*>(pRet->GetNext());
6557 --nRepeat;
6562 return pRet;
6565 bool SwTable::IsHeadline( const SwTableLine& rLine ) const
6567 for ( sal_uInt16 i = 0; i < GetRowsToRepeat(); ++i )
6568 if ( GetTabLines()[ i ] == &rLine )
6569 return true;
6571 return false;
6574 bool SwTabFrame::IsLayoutSplitAllowed() const
6576 return GetFormat()->GetLayoutSplit().GetValue();
6579 // #i29550#
6581 sal_uInt16 SwTabFrame::GetBottomLineSize() const
6583 OSL_ENSURE( IsCollapsingBorders(),
6584 "BottomLineSize only required for collapsing borders" );
6586 OSL_ENSURE( Lower(), "Warning! Trying to prevent a crash" );
6588 const SwFrame* pTmp = GetLastLower();
6590 // #124755# Try to make code robust
6591 if ( !pTmp ) return 0;
6593 return static_cast<const SwRowFrame*>(pTmp)->GetBottomLineSize();
6596 bool SwTabFrame::IsCollapsingBorders() const
6598 return GetFormat()->GetAttrSet().Get( RES_COLLAPSING_BORDERS ).GetValue();
6601 void SwTabFrame::dumpAsXml(xmlTextWriterPtr writer) const
6603 (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("tab"));
6604 SwFrame::dumpAsXmlAttributes( writer );
6606 (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("has-follow-flow-line"),
6607 BAD_CAST(OString::boolean(m_bHasFollowFlowLine).getStr()));
6609 if ( HasFollow() )
6610 (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "follow" ), "%" SAL_PRIuUINT32, GetFollow()->GetFrameId() );
6612 if (m_pPrecede != nullptr)
6613 (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "precede" ), "%" SAL_PRIuUINT32, static_cast<SwTabFrame*>( m_pPrecede )->GetFrameId() );
6615 (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
6616 dumpInfosAsXml(writer);
6617 (void)xmlTextWriterEndElement(writer);
6618 dumpChildrenAsXml(writer);
6620 (void)xmlTextWriterEndElement(writer);
6623 /// Local helper function to calculate height of first text row
6624 static SwTwips lcl_CalcHeightOfFirstContentLine( const SwRowFrame& rSourceLine )
6626 // Find corresponding split line in master table
6627 const SwTabFrame* pTab = rSourceLine.FindTabFrame();
6628 SwRectFnSet aRectFnSet(pTab);
6629 const SwCellFrame* pCurrSourceCell = static_cast<const SwCellFrame*>(rSourceLine.Lower());
6631 // 1. Case: rSourceLine is a follow flow line.
6632 // In this case we have to return the minimum of the heights
6633 // of the first lines in rSourceLine.
6635 // 2. Case: rSourceLine is not a follow flow line.
6636 // In this case we have to return the maximum of the heights
6637 // of the first lines in rSourceLine.
6638 bool bIsInFollowFlowLine = rSourceLine.IsInFollowFlowRow();
6639 SwTwips nHeight = bIsInFollowFlowLine ? LONG_MAX : 0;
6641 while ( pCurrSourceCell )
6643 // NEW TABLES
6644 // Skip cells which are not responsible for the height of
6645 // the follow flow line:
6646 if ( bIsInFollowFlowLine && pCurrSourceCell->GetLayoutRowSpan() > 1 )
6648 pCurrSourceCell = static_cast<const SwCellFrame*>(pCurrSourceCell->GetNext());
6649 continue;
6652 const SwFrame *pTmp = pCurrSourceCell->Lower();
6653 if ( pTmp )
6655 SwTwips nTmpHeight = std::numeric_limits<SwTwips>::max();
6656 // #i32456# Consider lower row frames
6657 if ( pTmp->IsRowFrame() )
6659 const SwRowFrame* pTmpSourceRow = static_cast<const SwRowFrame*>(pCurrSourceCell->Lower());
6660 nTmpHeight = lcl_CalcHeightOfFirstContentLine( *pTmpSourceRow );
6662 else if (pTmp->IsTabFrame() || (pTmp->IsSctFrame() && pTmp->GetLower() && pTmp->GetLower()->IsTabFrame()))
6664 SwTabFrame const*const pTabFrame(pTmp->IsTabFrame()
6665 ? static_cast<SwTabFrame const*>(pTmp)
6666 : static_cast<SwTabFrame const*>(pTmp->GetLower()));
6667 nTmpHeight = pTabFrame->CalcHeightOfFirstContentLine();
6669 else if (pTmp->IsTextFrame() || (pTmp->IsSctFrame() && pTmp->GetLower() && pTmp->GetLower()->IsTextFrame()))
6671 // Section frames don't influence the size/position of text
6672 // frames, so 'text frame' and 'text frame in section frame' is
6673 // the same case.
6674 SwTextFrame* pTextFrame = nullptr;
6675 if (pTmp->IsTextFrame())
6676 pTextFrame = const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pTmp));
6677 else
6678 pTextFrame = const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pTmp->GetLower()));
6679 pTextFrame->GetFormatted();
6680 nTmpHeight = pTextFrame->FirstLineHeight();
6683 if (std::numeric_limits<SwTwips>::max() != nTmpHeight)
6685 const SwCellFrame* pPrevCell = pCurrSourceCell->GetPreviousCell();
6686 if ( pPrevCell )
6688 // If we are in a split row, there may be some space
6689 // left in the cell frame of the master row.
6690 // We look for the minimum of all first line heights;
6691 SwTwips nReal = aRectFnSet.GetHeight(pPrevCell->getFramePrintArea());
6692 const SwFrame* pFrame = pPrevCell->Lower();
6693 const SwFrame* pLast = pFrame;
6694 while ( pFrame )
6696 nReal -= aRectFnSet.GetHeight(pFrame->getFrameArea());
6697 pLast = pFrame;
6698 pFrame = pFrame->GetNext();
6701 // #i26831#, #i26520#
6702 // The additional lower space of the current last.
6703 // #115759# - do *not* consider the
6704 // additional lower space for 'master' text frames
6705 if ( pLast && pLast->IsFlowFrame() &&
6706 ( !pLast->IsTextFrame() ||
6707 !static_cast<const SwTextFrame*>(pLast)->GetFollow() ) )
6709 nReal += SwFlowFrame::CastFlowFrame(pLast)->CalcAddLowerSpaceAsLastInTableCell();
6711 // Don't forget the upper space and lower space,
6712 // #115759# - do *not* consider the upper
6713 // and the lower space for follow text frames.
6714 if ( pTmp->IsFlowFrame() &&
6715 ( !pTmp->IsTextFrame() ||
6716 !static_cast<const SwTextFrame*>(pTmp)->IsFollow() ) )
6718 nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)->CalcUpperSpace( nullptr, pLast);
6719 nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)->CalcLowerSpace();
6721 // #115759# - consider additional lower
6722 // space of <pTmp>, if contains only one line.
6723 // In this case it would be the new last text frame, which
6724 // would have no follow and thus would add this space.
6725 if ( pTmp->IsTextFrame() &&
6726 const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pTmp))
6727 ->GetLineCount(TextFrameIndex(COMPLETE_STRING)) == 1)
6729 nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)
6730 ->CalcAddLowerSpaceAsLastInTableCell();
6732 if ( nReal > 0 )
6733 nTmpHeight -= nReal;
6735 else
6737 // pFirstRow is not a FollowFlowRow. In this case,
6738 // we look for the maximum of all first line heights:
6739 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCurrSourceCell );
6740 const SwBorderAttrs &rAttrs = *aAccess.Get();
6741 nTmpHeight += rAttrs.CalcTop() + rAttrs.CalcBottom();
6742 // #i26250#
6743 // Don't forget the upper space and lower space,
6744 if ( pTmp->IsFlowFrame() )
6746 nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)->CalcUpperSpace();
6747 nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)->CalcLowerSpace();
6752 if ( bIsInFollowFlowLine )
6754 // minimum
6755 if ( nTmpHeight < nHeight )
6756 nHeight = nTmpHeight;
6758 else
6760 // maximum
6761 if (nTmpHeight > nHeight && std::numeric_limits<SwTwips>::max() != nTmpHeight)
6762 nHeight = nTmpHeight;
6766 pCurrSourceCell = static_cast<const SwCellFrame*>(pCurrSourceCell->GetNext());
6769 return ( LONG_MAX == nHeight ) ? 0 : nHeight;
6772 /// Function to calculate height of first text row
6773 SwTwips SwTabFrame::CalcHeightOfFirstContentLine() const
6775 SwRectFnSet aRectFnSet(this);
6777 const bool bDontSplit = !IsFollow() && !GetFormat()->GetLayoutSplit().GetValue();
6779 if ( bDontSplit )
6781 // Table is not allowed to split: Take the whole height, that's all
6782 return aRectFnSet.GetHeight(getFrameArea());
6785 const SwRowFrame* pFirstRow = GetFirstNonHeadlineRow();
6786 OSL_ENSURE( !IsFollow() || pFirstRow, "FollowTable without Lower" );
6788 // NEW TABLES
6789 bool bHasRowSpanLine = pFirstRow && pFirstRow->IsRowSpanLine() && pFirstRow->GetNext();
6790 if (bHasRowSpanLine)
6791 pFirstRow = static_cast<const SwRowFrame*>(pFirstRow->GetNext());
6792 const SwRowFrame* pFirstKeepTogetherRow = pFirstRow;
6794 // Check how many rows want to keep together
6795 sal_uInt16 nKeepRows = 0;
6796 if ( GetFormat()->GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::TABLE_ROW_KEEP) )
6798 while ( pFirstRow && pFirstRow->ShouldRowKeepWithNext() )
6800 ++nKeepRows;
6801 pFirstRow = static_cast<const SwRowFrame*>(pFirstRow->GetNext());
6805 SwTwips nTmpHeight;
6807 // For master tables, the height of the headlines + the height of the
6808 // keeping lines (if any) has to be considered. For follow tables, we
6809 // only consider the height of the keeping rows without the repeated lines:
6810 if ( !IsFollow() )
6812 nKeepRows += GetTable()->GetRowsToRepeat();
6813 if (bHasRowSpanLine)
6814 ++nKeepRows;
6815 nTmpHeight = lcl_GetHeightOfRows(GetLower(), nKeepRows);
6817 else
6819 nTmpHeight = lcl_GetHeightOfRows(pFirstKeepTogetherRow, nKeepRows);
6822 // pFirstRow row is the first non-heading row.
6823 // nTmpHeight is the height of the heading row if we are a follow.
6824 if ( pFirstRow )
6826 const bool bSplittable = pFirstRow->IsRowSplitAllowed();
6827 const SwTwips nFirstLineHeight = aRectFnSet.GetHeight(pFirstRow->getFrameArea());
6829 if ( !bSplittable )
6831 // pFirstRow is not splittable, but it is still possible that the line height of pFirstRow
6832 // actually is determined by a lower cell with rowspan = -1. In this case we should not
6833 // just return the height of the first line. Basically we need to get the height of the
6834 // line as it would be on the last page. Since this is quite complicated to calculate,
6835 // we only calculate the height of the first line.
6836 SwFormatFrameSize const& rFrameSize(pFirstRow->GetAttrSet()->GetFrameSize());
6837 if ( pFirstRow->GetPrev() &&
6838 static_cast<const SwRowFrame*>(pFirstRow->GetPrev())->IsRowSpanLine()
6839 && rFrameSize.GetHeightSizeType() != SwFrameSize::Fixed)
6841 // Calculate maximum height of all cells with rowspan = 1:
6842 SwTwips nMaxHeight = rFrameSize.GetHeightSizeType() == SwFrameSize::Minimum
6843 ? rFrameSize.GetHeight()
6844 : 0;
6845 const SwCellFrame* pLower2 = static_cast<const SwCellFrame*>(pFirstRow->Lower());
6846 while ( pLower2 )
6848 if ( 1 == pLower2->GetTabBox()->getRowSpan() )
6850 const SwTwips nCellHeight = lcl_CalcMinCellHeight( pLower2, true );
6851 nMaxHeight = std::max( nCellHeight, nMaxHeight );
6853 pLower2 = static_cast<const SwCellFrame*>(pLower2->GetNext());
6855 nTmpHeight += nMaxHeight;
6857 else
6859 nTmpHeight += nFirstLineHeight;
6863 // Optimization: lcl_CalcHeightOfFirstContentLine actually can trigger
6864 // a formatting of the row frame (via the GetFormatted()). We don't
6865 // want this formatting if the row does not have a height.
6866 else if ( 0 != nFirstLineHeight )
6868 const bool bOldJoinLock = IsJoinLocked();
6869 const_cast<SwTabFrame*>(this)->LockJoin();
6870 const SwTwips nHeightOfFirstContentLine = lcl_CalcHeightOfFirstContentLine( *pFirstRow );
6872 // Consider minimum row height:
6873 const SwFormatFrameSize &rSz = pFirstRow->GetFormat()->GetFrameSize();
6875 SwTwips nMinRowHeight = 0;
6876 if (rSz.GetHeightSizeType() == SwFrameSize::Minimum)
6878 nMinRowHeight = std::max(rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*pFirstRow),
6879 tools::Long(0));
6882 nTmpHeight += std::max( nHeightOfFirstContentLine, nMinRowHeight );
6884 if ( !bOldJoinLock )
6885 const_cast<SwTabFrame*>(this)->UnlockJoin();
6889 return nTmpHeight;
6892 // Some more functions for covered/covering cells. This way inclusion of
6893 // SwCellFrame can be avoided
6895 bool SwFrame::IsLeaveUpperAllowed() const
6897 return false;
6900 bool SwCellFrame::IsLeaveUpperAllowed() const
6902 return GetLayoutRowSpan() > 1;
6905 bool SwFrame::IsCoveredCell() const
6907 return false;
6910 bool SwCellFrame::IsCoveredCell() const
6912 return GetLayoutRowSpan() < 1;
6915 bool SwFrame::IsInCoveredCell() const
6917 bool bRet = false;
6919 const SwFrame* pThis = this;
6920 while ( pThis && !pThis->IsCellFrame() )
6921 pThis = pThis->GetUpper();
6923 if ( pThis )
6924 bRet = pThis->IsCoveredCell();
6926 return bRet;
6929 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */