Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / layout / tabfrm.cxx
blobbbea3ddbb6f0df70140dc309524ef8a2d86c6eb3
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 <pagefrm.hxx>
23 #include <rootfrm.hxx>
24 #include <IDocumentFieldsAccess.hxx>
25 #include <IDocumentRedlineAccess.hxx>
26 #include <viewimp.hxx>
27 #include <fesh.hxx>
28 #include <swtable.hxx>
29 #include <deletelistener.hxx>
30 #include <dflyobj.hxx>
31 #include <anchoreddrawobject.hxx>
32 #include <fmtanchr.hxx>
33 #include <viewopt.hxx>
34 #include <hints.hxx>
35 #include <dbg_lay.hxx>
36 #include <ftnidx.hxx>
37 #include <svl/itemiter.hxx>
38 #include <editeng/keepitem.hxx>
39 #include <editeng/ulspitem.hxx>
40 #include <editeng/brushitem.hxx>
41 #include <editeng/boxitem.hxx>
42 #include <basegfx/range/b1drange.hxx>
43 #include <fmtlsplt.hxx>
44 #include <fmtrowsplt.hxx>
45 #include <fmtsrnd.hxx>
46 #include <fmtornt.hxx>
47 #include <fmtpdsc.hxx>
48 #include <fmtfsize.hxx>
49 #include <swtblfmt.hxx>
50 #include <tabfrm.hxx>
51 #include <rowfrm.hxx>
52 #include <cellfrm.hxx>
53 #include <flyfrms.hxx>
54 #include <txtfrm.hxx>
55 #include <ftnfrm.hxx>
56 #include <notxtfrm.hxx>
57 #include <htmltbl.hxx>
58 #include <sectfrm.hxx>
59 #include <fmtfollowtextflow.hxx>
60 #include <sortedobjs.hxx>
61 #include <objectformatter.hxx>
62 #include <layouter.hxx>
63 #include <calbck.hxx>
64 #include <DocumentSettingManager.hxx>
65 #include <sal/log.hxx>
66 #include <osl/diagnose.h>
67 #include <frmatr.hxx>
68 #include <frmtool.hxx>
69 #include <ndtxt.hxx>
70 #include <frameformats.hxx>
72 using namespace ::com::sun::star;
74 SwTabFrame::SwTabFrame( SwTable &rTab, SwFrame* pSib )
75 : SwLayoutFrame( rTab.GetFrameFormat(), pSib )
76 , SwFlowFrame( static_cast<SwFrame&>(*this) )
77 , m_pTable( &rTab )
78 , m_bComplete(false)
79 , m_bCalcLowers(false)
80 , m_bLowersFormatted(false)
81 , m_bLockBackMove(false)
82 , m_bWantBackMove(false)
83 , m_bResizeHTMLTable(false)
84 , m_bONECalcLowers(false)
85 , m_bHasFollowFlowLine(false)
86 , m_bIsRebuildLastLine(false)
87 , m_bRestrictTableGrowth(false)
88 , m_bRemoveFollowFlowLinePending(false)
89 , m_bConsiderObjsForMinCellHeight(true)
90 , m_bObjsDoesFit(true)
91 , m_bInRecalcLowerRow(false)
93 mbFixSize = false; //Don't fall for import filter again.
94 mnFrameType = SwFrameType::Tab;
96 //Create the lines and insert them.
97 const SwTableLines &rLines = rTab.GetTabLines();
98 SwFrame *pTmpPrev = nullptr;
99 bool bHiddenRedlines = getRootFrame()->IsHideRedlines() &&
100 !GetFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable().empty();
101 SwRedlineTable::size_type nRedlinePos = 0;
102 for ( size_t i = 0; i < rLines.size(); ++i )
104 // skip lines deleted with track changes
105 if ( bHiddenRedlines && rLines[i]->IsDeleted(nRedlinePos) )
106 continue;
108 SwRowFrame *pNew = new SwRowFrame( *rLines[i], this );
109 if( pNew->Lower() )
111 pNew->InsertBehind( this, pTmpPrev );
112 pTmpPrev = pNew;
114 else
115 SwFrame::DestroyFrame(pNew);
117 OSL_ENSURE( Lower() && Lower()->IsRowFrame(), "SwTabFrame::SwTabFrame: No rows." );
120 SwTabFrame::SwTabFrame( SwTabFrame &rTab )
121 : SwLayoutFrame( rTab.GetFormat(), &rTab )
122 , SwFlowFrame( static_cast<SwFrame&>(*this) )
123 , m_pTable( rTab.GetTable() )
124 , m_bComplete(false)
125 , m_bCalcLowers(false)
126 , m_bLowersFormatted(false)
127 , m_bLockBackMove(false)
128 , m_bWantBackMove(false)
129 , m_bResizeHTMLTable(false)
130 , m_bONECalcLowers(false)
131 , m_bHasFollowFlowLine(false)
132 , m_bIsRebuildLastLine(false)
133 , m_bRestrictTableGrowth(false)
134 , m_bRemoveFollowFlowLinePending(false)
135 , m_bConsiderObjsForMinCellHeight(true)
136 , m_bObjsDoesFit(true)
137 , m_bInRecalcLowerRow(false)
139 mbFixSize = false; //Don't fall for import filter again.
140 mnFrameType = SwFrameType::Tab;
142 SetFollow( rTab.GetFollow() );
143 rTab.SetFollow( this );
146 void SwTabFrame::DestroyImpl()
148 // There is some terrible code in fetab.cxx, that
149 // caches pointers to SwTabFrames.
150 ::ClearFEShellTabCols(*GetFormat()->GetDoc(), this);
152 SwLayoutFrame::DestroyImpl();
155 SwTabFrame::~SwTabFrame()
159 void SwTabFrame::JoinAndDelFollows()
161 SwTabFrame *pFoll = GetFollow();
162 if ( pFoll->HasFollow() )
163 pFoll->JoinAndDelFollows();
164 pFoll->Cut();
165 SetFollow( pFoll->GetFollow() );
166 SwFrame::DestroyFrame(pFoll);
169 void SwTabFrame::RegistFlys()
171 OSL_ENSURE( Lower() && Lower()->IsRowFrame(), "No rows." );
173 SwPageFrame *pPage = FindPageFrame();
174 if ( pPage )
176 SwRowFrame *pRow = static_cast<SwRowFrame*>(Lower());
179 pRow->RegistFlys( pPage );
180 pRow = static_cast<SwRowFrame*>(pRow->GetNext());
181 } while ( pRow );
185 static void SwInvalidateAll( SwFrame *pFrame, tools::Long nBottom );
186 static void lcl_RecalcRow( SwRowFrame& rRow, tools::Long nBottom );
187 static bool lcl_ArrangeLowers( SwLayoutFrame *pLay, tools::Long lYStart, bool bInva );
188 // #i26945# - add parameter <_bOnlyRowsAndCells> to control
189 // that only row and cell frames are formatted.
190 static bool lcl_InnerCalcLayout( SwFrame *pFrame,
191 tools::Long nBottom,
192 bool _bOnlyRowsAndCells = false );
193 // OD 2004-02-18 #106629# - correct type of 1st parameter
194 // #i26945# - add parameter <_bConsiderObjs> in order to
195 // control, if floating screen objects have to be considered for the minimal
196 // cell height.
197 static SwTwips lcl_CalcMinRowHeight( const SwRowFrame *pRow,
198 const bool _bConsiderObjs );
199 static SwTwips lcl_CalcTopAndBottomMargin( const SwLayoutFrame&, const SwBorderAttrs& );
201 static SwTwips lcl_calcHeightOfRowBeforeThisFrame(const SwRowFrame& rRow);
203 static SwTwips lcl_GetHeightOfRows( const SwFrame* pStart, tools::Long nCount )
205 if ( !nCount || !pStart)
206 return 0;
208 SwTwips nRet = 0;
209 SwRectFnSet aRectFnSet(pStart);
210 while ( pStart && nCount > 0 )
212 nRet += aRectFnSet.GetHeight(pStart->getFrameArea());
213 pStart = pStart->GetNext();
214 --nCount;
217 return nRet;
220 // Local helper function to insert a new follow flow line
221 static SwRowFrame* lcl_InsertNewFollowFlowLine( SwTabFrame& rTab, const SwFrame& rTmpRow, bool bRowSpanLine )
223 OSL_ENSURE( rTmpRow.IsRowFrame(), "No row frame to copy for FollowFlowLine" );
224 const SwRowFrame& rRow = static_cast<const SwRowFrame&>(rTmpRow);
226 rTab.SetFollowFlowLine( true );
227 SwRowFrame *pFollowFlowLine = new SwRowFrame(*rRow.GetTabLine(), &rTab, false );
228 pFollowFlowLine->SetRowSpanLine( bRowSpanLine );
229 SwFrame* pFirstRow = rTab.GetFollow()->GetFirstNonHeadlineRow();
230 pFollowFlowLine->InsertBefore( rTab.GetFollow(), pFirstRow );
231 return pFollowFlowLine;
234 // #i26945# - local helper function to invalidate all lower
235 // objects. By parameter <_bMoveObjsOutOfRange> it can be controlled, if
236 // additionally the objects are moved 'out of range'.
237 static void lcl_InvalidateLowerObjs( SwLayoutFrame& _rLayoutFrame,
238 const bool _bMoveObjsOutOfRange = false,
239 SwPageFrame* _pPageFrame = nullptr )
241 // determine page frame, if needed
242 if ( !_pPageFrame )
244 _pPageFrame = _rLayoutFrame.FindPageFrame();
245 OSL_ENSURE( _pPageFrame,
246 "<lcl_InvalidateLowerObjs(..)> - missing page frame -> no move of lower objects out of range" );
247 if ( !_pPageFrame )
249 return;
253 // loop on lower frames
254 SwFrame* pLowerFrame = _rLayoutFrame.Lower();
255 while ( pLowerFrame )
257 if ( pLowerFrame->IsLayoutFrame() )
259 ::lcl_InvalidateLowerObjs( *static_cast<SwLayoutFrame*>(pLowerFrame),
260 _bMoveObjsOutOfRange, _pPageFrame );
262 if ( pLowerFrame->GetDrawObjs() )
264 for (size_t i = 0, nCount = pLowerFrame->GetDrawObjs()->size(); i < nCount; ++i)
266 SwAnchoredObject* pAnchoredObj = (*pLowerFrame->GetDrawObjs())[i];
268 // invalidate position of anchored object
269 pAnchoredObj->SetTmpConsiderWrapInfluence( false );
270 pAnchoredObj->SetConsiderForTextWrap( false );
271 pAnchoredObj->UnlockPosition();
272 pAnchoredObj->InvalidateObjPos();
274 SwFlyFrame *pFly = pAnchoredObj->DynCastFlyFrame();
276 // move anchored object 'out of range'
277 if ( _bMoveObjsOutOfRange )
279 // indicate, that positioning is progress to avoid
280 // modification of the anchored object resp. it's attributes
281 // due to the movement
282 SwObjPositioningInProgress aObjPosInProgress( *pAnchoredObj );
283 pAnchoredObj->SetObjLeft( _pPageFrame->getFrameArea().Right() );
284 // #115759# - reset character rectangle,
285 // top of line and relative position in order to assure,
286 // that anchored object is correctly positioned.
287 pAnchoredObj->ClearCharRectAndTopOfLine();
288 pAnchoredObj->SetCurrRelPos( Point( 0, 0 ) );
289 if ( pAnchoredObj->GetFrameFormat().GetAnchor().GetAnchorId()
290 == RndStdIds::FLY_AS_CHAR )
292 pAnchoredObj->AnchorFrame()
293 ->Prepare( PrepareHint::FlyFrameAttributesChanged,
294 &(pAnchoredObj->GetFrameFormat()) );
296 if ( pFly != nullptr )
298 pFly->GetVirtDrawObj()->SetBoundAndSnapRectsDirty();
299 pFly->GetVirtDrawObj()->SetChanged();
303 // If anchored object is a fly frame, invalidate its lower objects
304 if ( pFly != nullptr )
306 ::lcl_InvalidateLowerObjs( *pFly, _bMoveObjsOutOfRange, _pPageFrame );
310 pLowerFrame = pLowerFrame->GetNext();
314 // Local helper function to shrink all lowers of pRow to 0 height
315 static void lcl_ShrinkCellsAndAllContent( SwRowFrame& rRow )
317 SwCellFrame* pCurrMasterCell = static_cast<SwCellFrame*>(rRow.Lower());
318 SwRectFnSet aRectFnSet(pCurrMasterCell);
320 bool bAllCellsCollapsed = true;
321 while ( pCurrMasterCell )
323 // NEW TABLES
324 SwCellFrame& rToAdjust = pCurrMasterCell->GetTabBox()->getRowSpan() < 1 ?
325 const_cast<SwCellFrame&>(pCurrMasterCell->FindStartEndOfRowSpanCell( true )) :
326 *pCurrMasterCell;
328 // #i26945#
329 // all lowers should have the correct position
330 lcl_ArrangeLowers( &rToAdjust,
331 aRectFnSet.GetPrtTop(rToAdjust),
332 false );
333 // TODO: Optimize number of frames which are set to 0 height
334 // we have to start with the last lower frame, otherwise
335 // the shrink will not shrink the current cell
336 SwFrame* pTmp = rToAdjust.GetLastLower();
337 bool bAllLowersCollapsed = true;
339 if ( pTmp && pTmp->IsRowFrame() )
341 SwRowFrame* pTmpRow = static_cast<SwRowFrame*>(pTmp);
342 lcl_ShrinkCellsAndAllContent( *pTmpRow );
344 else
346 // TODO: Optimize number of frames which are set to 0 height
347 while ( pTmp )
349 // the frames have to be shrunk
350 if ( pTmp->IsTabFrame() )
352 SwRowFrame* pTmpRow = static_cast<SwRowFrame*>(static_cast<SwTabFrame*>(pTmp)->Lower());
353 bool bAllRowsCollapsed = true;
355 while ( pTmpRow )
357 lcl_ShrinkCellsAndAllContent( *pTmpRow );
359 if (aRectFnSet.GetHeight(pTmpRow->getFrameArea()) > 0)
360 bAllRowsCollapsed = false;
362 pTmpRow = static_cast<SwRowFrame*>(pTmpRow->GetNext());
365 if (bAllRowsCollapsed)
367 // All rows of this table have 0 height -> set height of the table itself as well.
368 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pTmp);
369 aRectFnSet.SetHeight(aFrm, 0);
371 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pTmp);
372 aRectFnSet.SetTop(aPrt, 0);
373 aRectFnSet.SetHeight(aPrt, 0);
375 else
376 bAllLowersCollapsed = false;
378 else
380 pTmp->Shrink(aRectFnSet.GetHeight(pTmp->getFrameArea()));
381 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pTmp);
382 aRectFnSet.SetTop(aPrt, 0);
383 aRectFnSet.SetHeight(aPrt, 0);
385 if (aRectFnSet.GetHeight(pTmp->getFrameArea()) > 0)
387 bAllLowersCollapsed = false;
391 pTmp = pTmp->GetPrev();
394 // all lowers should have the correct position
395 lcl_ArrangeLowers( &rToAdjust,
396 aRectFnSet.GetPrtTop(rToAdjust),
397 false );
400 if (bAllLowersCollapsed)
402 // All lower frame of this cell have 0 height -> set height of the cell itself as well.
403 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pCurrMasterCell);
404 aRectFnSet.SetHeight(aFrm, 0);
406 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pCurrMasterCell);
407 aRectFnSet.SetTop(aPrt, 0);
408 aRectFnSet.SetHeight(aPrt, 0);
410 else
411 bAllCellsCollapsed = false;
413 pCurrMasterCell = static_cast<SwCellFrame*>(pCurrMasterCell->GetNext());
416 if (bAllCellsCollapsed)
418 // All cells have 0 height -> set height of row as well.
419 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(rRow);
420 aRectFnSet.SetHeight(aFrm, 0);
422 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(rRow);
423 aRectFnSet.SetTop(aPrt, 0);
424 aRectFnSet.SetHeight(aPrt, 0);
428 // Local helper function to move the content from rSourceLine to rDestLine
429 // The content is inserted behind the last content in the corresponding
430 // cell in rDestLine.
431 static void lcl_MoveRowContent( SwRowFrame& rSourceLine, SwRowFrame& rDestLine )
433 SwCellFrame* pCurrDestCell = static_cast<SwCellFrame*>(rDestLine.Lower());
434 SwCellFrame* pCurrSourceCell = static_cast<SwCellFrame*>(rSourceLine.Lower());
436 // Move content of follow cells into master cells
437 while ( pCurrSourceCell )
439 if ( pCurrSourceCell->Lower() && pCurrSourceCell->Lower()->IsRowFrame() )
441 SwRowFrame* pTmpSourceRow = static_cast<SwRowFrame*>(pCurrSourceCell->Lower());
442 while ( pTmpSourceRow )
444 // #125926# Attention! It is possible,
445 // that pTmpSourceRow->IsFollowFlowRow() but pTmpDestRow
446 // cannot be found. In this case, we have to move the complete
447 // row.
448 SwRowFrame* pTmpDestRow = static_cast<SwRowFrame*>(pCurrDestCell->Lower());
450 if ( pTmpSourceRow->IsFollowFlowRow() && pTmpDestRow )
452 // move content from follow flow row to pTmpDestRow:
453 while ( pTmpDestRow->GetNext() )
454 pTmpDestRow = static_cast<SwRowFrame*>(pTmpDestRow->GetNext());
456 assert(pTmpDestRow->GetFollowRow() == pTmpSourceRow);
458 lcl_MoveRowContent( *pTmpSourceRow, *pTmpDestRow );
459 pTmpDestRow->SetFollowRow( pTmpSourceRow->GetFollowRow() );
460 pTmpSourceRow->RemoveFromLayout();
461 SwFrame::DestroyFrame(pTmpSourceRow);
463 else
465 // move complete row:
466 pTmpSourceRow->RemoveFromLayout();
467 pTmpSourceRow->InsertBefore( pCurrDestCell, nullptr );
470 pTmpSourceRow = static_cast<SwRowFrame*>(pCurrSourceCell->Lower());
473 else
475 SwFrame *pTmp = ::SaveContent( pCurrSourceCell );
476 if ( pTmp )
478 // NEW TABLES
479 SwCellFrame* pDestCell = pCurrDestCell;
480 if ( pDestCell->GetTabBox()->getRowSpan() < 1 )
481 pDestCell = & const_cast<SwCellFrame&>(pDestCell->FindStartEndOfRowSpanCell( true ));
483 // Find last content
484 SwFrame* pFrame = pDestCell->GetLastLower();
485 ::RestoreContent( pTmp, pDestCell, pFrame );
488 pCurrDestCell = static_cast<SwCellFrame*>(pCurrDestCell->GetNext());
489 pCurrSourceCell = static_cast<SwCellFrame*>(pCurrSourceCell->GetNext());
493 // Local helper function to move all footnotes in rRowFrame from
494 // the footnote boss of rSource to the footnote boss of rDest.
495 static void lcl_MoveFootnotes( SwTabFrame& rSource, SwTabFrame& rDest, SwLayoutFrame& rRowFrame )
497 if ( !rSource.GetFormat()->GetDoc()->GetFootnoteIdxs().empty() )
499 SwFootnoteBossFrame* pOldBoss = rSource.FindFootnoteBossFrame( true );
500 SwFootnoteBossFrame* pNewBoss = rDest.FindFootnoteBossFrame( true );
501 rRowFrame.MoveLowerFootnotes( nullptr, pOldBoss, pNewBoss, true );
505 // Local helper function to handle nested table cells before the split process
506 static void lcl_PreprocessRowsInCells( SwTabFrame& rTab, SwRowFrame& rLastLine,
507 SwRowFrame& rFollowFlowLine, SwTwips nRemain )
509 SwCellFrame* pCurrLastLineCell = static_cast<SwCellFrame*>(rLastLine.Lower());
510 SwCellFrame* pCurrFollowFlowLineCell = static_cast<SwCellFrame*>(rFollowFlowLine.Lower());
512 SwRectFnSet aRectFnSet(pCurrLastLineCell);
514 // Move content of follow cells into master cells
515 while ( pCurrLastLineCell )
517 if ( pCurrLastLineCell->Lower() && pCurrLastLineCell->Lower()->IsRowFrame() )
519 SwTwips nTmpCut = nRemain;
520 SwRowFrame* pTmpLastLineRow = static_cast<SwRowFrame*>(pCurrLastLineCell->Lower());
522 // #i26945#
523 SwTwips nCurrentHeight =
524 lcl_CalcMinRowHeight( pTmpLastLineRow,
525 rTab.IsConsiderObjsForMinCellHeight() );
526 while ( pTmpLastLineRow->GetNext() && nTmpCut > nCurrentHeight )
528 nTmpCut -= nCurrentHeight;
529 pTmpLastLineRow = static_cast<SwRowFrame*>(pTmpLastLineRow->GetNext());
530 // #i26945#
531 nCurrentHeight =
532 lcl_CalcMinRowHeight( pTmpLastLineRow,
533 rTab.IsConsiderObjsForMinCellHeight() );
536 // pTmpLastLineRow does not fit to the line or it is the last line
537 // Check if we can move pTmpLastLineRow to the follow table,
538 // or if we have to split the line:
539 bool bTableLayoutTooComplex = false;
540 tools::Long nMinHeight = 0;
542 // We have to take into account:
543 // 1. The fixed height of the row
544 // 2. The borders of the cells inside the row
545 // 3. The minimum height of the row
546 if ( pTmpLastLineRow->HasFixSize() )
547 nMinHeight = aRectFnSet.GetHeight(pTmpLastLineRow->getFrameArea());
548 else
551 const SwFormatFrameSize &rSz = pTmpLastLineRow->GetFormat()->GetFrameSize();
552 if ( rSz.GetHeightSizeType() == SwFrameSize::Minimum )
553 nMinHeight = rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*pTmpLastLineRow);
556 SwFrame* pCell = pTmpLastLineRow->Lower();
557 while ( pCell )
559 if ( static_cast<SwCellFrame*>(pCell)->Lower() &&
560 static_cast<SwCellFrame*>(pCell)->Lower()->IsRowFrame() )
562 bTableLayoutTooComplex = true;
563 break;
566 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCell );
567 const SwBorderAttrs &rAttrs = *aAccess.Get();
568 nMinHeight = std::max( nMinHeight, tools::Long(lcl_CalcTopAndBottomMargin( *static_cast<SwLayoutFrame*>(pCell), rAttrs )) );
569 pCell = pCell->GetNext();
573 // 1. Case:
574 // The line completely fits into the master table.
575 // Nevertheless, we build a follow (otherwise painting problems
576 // with empty cell).
578 // 2. Case:
579 // The line has to be split, the minimum height still fits into
580 // the master table, and the table structure is not too complex.
581 if ( nTmpCut > nCurrentHeight ||
582 ( pTmpLastLineRow->IsRowSplitAllowed() &&
583 !bTableLayoutTooComplex && nMinHeight < nTmpCut ) )
585 // The line has to be split:
586 SwRowFrame* pNewRow = new SwRowFrame( *pTmpLastLineRow->GetTabLine(), &rTab, false );
587 pNewRow->SetFollowFlowRow( true );
588 pNewRow->SetFollowRow( pTmpLastLineRow->GetFollowRow() );
589 pTmpLastLineRow->SetFollowRow( pNewRow );
590 pNewRow->InsertBehind( pCurrFollowFlowLineCell, nullptr );
591 pTmpLastLineRow = static_cast<SwRowFrame*>(pTmpLastLineRow->GetNext());
594 // The following lines have to be moved:
595 while ( pTmpLastLineRow )
597 SwRowFrame* pTmp = static_cast<SwRowFrame*>(pTmpLastLineRow->GetNext());
598 lcl_MoveFootnotes( rTab, *rTab.GetFollow(), *pTmpLastLineRow );
599 pTmpLastLineRow->RemoveFromLayout();
600 pTmpLastLineRow->InsertBefore( pCurrFollowFlowLineCell, nullptr );
601 pTmpLastLineRow->Shrink( aRectFnSet.GetHeight(pTmpLastLineRow->getFrameArea()) );
602 pCurrFollowFlowLineCell->Grow( aRectFnSet.GetHeight(pTmpLastLineRow->getFrameArea()) );
603 pTmpLastLineRow = pTmp;
607 pCurrLastLineCell = static_cast<SwCellFrame*>(pCurrLastLineCell->GetNext());
608 pCurrFollowFlowLineCell = static_cast<SwCellFrame*>(pCurrFollowFlowLineCell->GetNext());
612 // Local helper function to handle nested table cells after the split process
613 static void lcl_PostprocessRowsInCells( SwTabFrame& rTab, SwRowFrame& rLastLine )
615 SwCellFrame* pCurrMasterCell = static_cast<SwCellFrame*>(rLastLine.Lower());
616 while ( pCurrMasterCell )
618 if ( pCurrMasterCell->Lower() &&
619 pCurrMasterCell->Lower()->IsRowFrame() )
621 SwRowFrame* pRowFrame = static_cast<SwRowFrame*>(pCurrMasterCell->GetLastLower());
623 if ( nullptr != pRowFrame->GetPrev() && !pRowFrame->ContainsContent() )
625 OSL_ENSURE( pRowFrame->GetFollowRow(), "Deleting row frame without follow" );
627 // The footnotes have to be moved:
628 lcl_MoveFootnotes( rTab, *rTab.GetFollow(), *pRowFrame );
629 pRowFrame->Cut();
630 SwRowFrame* pFollowRow = pRowFrame->GetFollowRow();
631 pRowFrame->Paste( pFollowRow->GetUpper(), pFollowRow );
632 pRowFrame->SetFollowRow( pFollowRow->GetFollowRow() );
633 lcl_MoveRowContent( *pFollowRow, *pRowFrame );
634 pFollowRow->Cut();
635 SwFrame::DestroyFrame(pFollowRow);
636 ::SwInvalidateAll( pCurrMasterCell, LONG_MAX );
640 pCurrMasterCell = static_cast<SwCellFrame*>(pCurrMasterCell->GetNext());
644 // Local helper function to re-calculate the split line.
645 inline void TableSplitRecalcLock( SwFlowFrame *pTab ) { pTab->LockJoin(); }
646 inline void TableSplitRecalcUnlock( SwFlowFrame *pTab ) { pTab->UnlockJoin(); }
648 static bool lcl_RecalcSplitLine( SwRowFrame& rLastLine, SwRowFrame& rFollowLine,
649 SwTwips nRemainingSpaceForLastRow, SwTwips nAlreadyFree,
650 bool & rIsFootnoteGrowth)
652 bool bRet = true;
654 vcl::RenderContext* pRenderContext = rLastLine.getRootFrame()->GetCurrShell()->GetOut();
655 SwTabFrame& rTab = static_cast<SwTabFrame&>(*rLastLine.GetUpper());
656 SwRectFnSet aRectFnSet(rTab.GetUpper());
657 SwTwips nCurLastLineHeight = aRectFnSet.GetHeight(rLastLine.getFrameArea());
659 SwTwips nFootnoteHeight(0);
660 if (SwFootnoteBossFrame const*const pBoss = rTab.FindFootnoteBossFrame())
662 if (SwFootnoteContFrame const*const pCont = pBoss->FindFootnoteCont())
664 for (SwFootnoteFrame const* pFootnote = static_cast<SwFootnoteFrame const*>(pCont->Lower());
665 pFootnote != nullptr;
666 pFootnote = static_cast<SwFootnoteFrame const*>(pFootnote->GetNext()))
668 SwContentFrame const*const pAnchor = pFootnote->GetRef();
669 SwTabFrame const* pTab = pAnchor->FindTabFrame();
670 if (pTab == &rTab)
672 while (pTab->GetUpper()->IsInTab())
674 pTab = pTab->GetUpper()->FindTabFrame();
676 // TODO currently do this only for top-level tables?
677 // otherwise would need to check rTab's follow and any upper table's follow?
678 if (pTab == &rTab)
680 nFootnoteHeight += aRectFnSet.GetHeight(pFootnote->getFrameArea());
687 // If there are nested cells in rLastLine, the recalculation of the last
688 // line needs some preprocessing.
689 lcl_PreprocessRowsInCells( rTab, rLastLine, rFollowLine, nRemainingSpaceForLastRow );
691 // Here the recalculation process starts:
692 rTab.SetRebuildLastLine( true );
693 // #i26945#
694 rTab.SetDoesObjsFit( true );
696 // #i26945# - invalidate and move floating screen
697 // objects 'out of range'
698 ::lcl_InvalidateLowerObjs( rLastLine, true );
700 // manipulate row and cell sizes
702 // #i26945# - Do *not* consider floating screen objects
703 // for the minimal cell height.
704 rTab.SetConsiderObjsForMinCellHeight( false );
705 ::lcl_ShrinkCellsAndAllContent( rLastLine );
706 rTab.SetConsiderObjsForMinCellHeight( true );
708 // invalidate last line
709 ::SwInvalidateAll( &rLastLine, LONG_MAX );
711 // Shrink the table to account for the shrunk last row, as well as lower rows
712 // that had been moved to follow table in SwTabFrame::Split.
713 // It will grow later when last line will recalc its height.
714 rTab.Shrink(nAlreadyFree + nCurLastLineHeight - nRemainingSpaceForLastRow + 1);
716 // Lock this tab frame and its follow
717 bool bUnlockMaster = false;
718 SwFlowFrame * pFollow = nullptr;
719 SwTabFrame* pMaster = rTab.IsFollow() ? rTab.FindMaster() : nullptr;
720 if ( pMaster && !pMaster->IsJoinLocked() )
722 bUnlockMaster = true;
723 ::TableSplitRecalcLock( pMaster );
725 if ( !rTab.GetFollow()->IsJoinLocked() )
727 pFollow = rTab.GetFollow();
728 ::TableSplitRecalcLock( pFollow );
731 bool bInSplit = rLastLine.IsInSplit();
732 rLastLine.SetInSplit();
734 // Do the recalculation
735 lcl_RecalcRow( rLastLine, LONG_MAX );
736 // #115759# - force a format of the last line in order to
737 // get the correct height.
738 rLastLine.InvalidateSize();
739 rLastLine.Calc(pRenderContext);
741 rLastLine.SetInSplit(bInSplit);
743 // Unlock this tab frame and its follow
744 if ( pFollow )
745 ::TableSplitRecalcUnlock( pFollow );
746 if ( bUnlockMaster )
747 ::TableSplitRecalcUnlock( pMaster );
749 // If there are nested cells in rLastLine, the recalculation of the last
750 // line needs some postprocessing.
751 lcl_PostprocessRowsInCells( rTab, rLastLine );
753 // Do a couple of checks on the current situation.
755 // If we are not happy with the current situation we return false.
756 // This will start a new try to split the table, this time we do not
757 // try to split the table rows.
759 // 1. Check if table fits to its upper.
760 // #i26945# - include check, if objects fit
761 const SwTwips nDistanceToUpperPrtBottom =
762 aRectFnSet.BottomDist(rTab.getFrameArea(), aRectFnSet.GetPrtBottom(*rTab.GetUpper()));
763 // tdf#125685 ignore footnotes that are anchored in follow-table of this
764 // table - if split is successful they move to the next page/column anyway
765 assert(rTab.GetFollow() == rFollowLine.GetUpper());
766 SwTwips nFollowFootnotes(0);
767 // actually there should always be a boss frame, except if "this" isn't
768 // connected to a page yet; not sure if that can happen
769 if (SwFootnoteBossFrame const*const pBoss = rTab.FindFootnoteBossFrame())
771 if (SwFootnoteContFrame const*const pCont = pBoss->FindFootnoteCont())
773 for (SwFootnoteFrame const* pFootnote = static_cast<SwFootnoteFrame const*>(pCont->Lower());
774 pFootnote != nullptr;
775 pFootnote = static_cast<SwFootnoteFrame const*>(pFootnote->GetNext()))
777 SwContentFrame const*const pAnchor = pFootnote->GetRef();
778 SwTabFrame const* pTab = pAnchor->FindTabFrame();
779 if (pTab)
781 while (pTab->GetUpper()->IsInTab())
783 pTab = pTab->GetUpper()->FindTabFrame();
785 // TODO currently do this only for top-level tables?
786 // otherwise would need to check rTab's follow and any upper table's follow?
787 if (pTab == rTab.GetFollow())
789 nFollowFootnotes += aRectFnSet.GetHeight(pFootnote->getFrameArea());
791 if (pTab == &rTab)
793 nFootnoteHeight -= aRectFnSet.GetHeight(pFootnote->getFrameArea());
797 if (nFootnoteHeight < 0)
798 { // tdf#156724 footnotes have grown, try to split again
799 rIsFootnoteGrowth = true;
803 if (nDistanceToUpperPrtBottom + nFollowFootnotes < 0 || !rTab.DoesObjsFit())
804 bRet = false;
806 // 2. Check if each cell in the last line has at least one content frame.
808 // Note: a FollowFlowRow may contains empty cells!
809 if ( bRet )
811 if ( !rLastLine.IsInFollowFlowRow() )
813 SwCellFrame* pCurrMasterCell = static_cast<SwCellFrame*>(rLastLine.Lower());
814 while ( pCurrMasterCell )
816 if ( !pCurrMasterCell->ContainsContent() && pCurrMasterCell->GetTabBox()->getRowSpan() >= 1 )
818 bRet = false;
819 break;
821 pCurrMasterCell = static_cast<SwCellFrame*>(pCurrMasterCell->GetNext());
826 // 3. Check if last line does not contain any content:
827 if ( bRet )
829 if ( !rLastLine.ContainsContent() )
831 bRet = false;
835 // 4. Check if follow flow line does not contain content:
836 if ( bRet )
838 if ( !rFollowLine.IsRowSpanLine() && !rFollowLine.ContainsContent() )
840 bRet = false;
844 if ( bRet )
846 // Everything looks fine. Splitting seems to be successful. We invalidate
847 // rFollowLine to force a new formatting.
848 ::SwInvalidateAll( &rFollowLine, LONG_MAX );
850 else
852 // Splitting the table row gave us an unexpected result.
853 // Everything has to be prepared for a second try to split
854 // the table, this time without splitting the row.
855 ::SwInvalidateAll( &rLastLine, LONG_MAX );
858 rTab.SetRebuildLastLine( false );
859 // #i26945#
860 rTab.SetDoesObjsFit( true );
862 return bRet;
865 // Sets the correct height for all spanned cells
866 static void lcl_AdjustRowSpanCells( SwRowFrame* pRow )
868 SwRectFnSet aRectFnSet(pRow);
869 SwCellFrame* pCellFrame = static_cast<SwCellFrame*>(pRow->GetLower());
870 while ( pCellFrame )
872 const tools::Long nLayoutRowSpan = pCellFrame->GetLayoutRowSpan();
873 if ( nLayoutRowSpan > 1 )
875 // calculate height of cell:
876 const tools::Long nNewCellHeight = lcl_GetHeightOfRows( pRow, nLayoutRowSpan );
877 const tools::Long nDiff = nNewCellHeight - aRectFnSet.GetHeight(pCellFrame->getFrameArea());
879 if ( nDiff )
881 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pCellFrame);
882 aRectFnSet.AddBottom(aFrm, nDiff);
886 pCellFrame = static_cast<SwCellFrame*>(pCellFrame->GetNext());
890 // Returns the maximum layout row span of the row
891 // Looking for the next row that contains no covered cells:
892 static tools::Long lcl_GetMaximumLayoutRowSpan( const SwRowFrame& rRow )
894 tools::Long nRet = 1;
896 const SwRowFrame* pCurrentRowFrame = static_cast<const SwRowFrame*>(rRow.GetNext());
897 bool bNextRow = false;
899 while ( pCurrentRowFrame )
901 // if there is any covered cell, we proceed to the next row frame
902 const SwCellFrame* pLower = static_cast<const SwCellFrame*>( pCurrentRowFrame->Lower());
903 while ( pLower )
905 if ( pLower->GetTabBox()->getRowSpan() < 0 )
907 ++nRet;
908 bNextRow = true;
909 break;
911 pLower = static_cast<const SwCellFrame*>(pLower->GetNext());
913 pCurrentRowFrame = bNextRow ?
914 static_cast<const SwRowFrame*>(pCurrentRowFrame->GetNext() ) :
915 nullptr;
918 return nRet;
921 // Function to remove the FollowFlowLine of rTab.
922 // The content of the FollowFlowLine is moved to the associated line in the
923 // master table.
924 bool SwTabFrame::RemoveFollowFlowLine()
926 // find FollowFlowLine
927 SwTabFrame *pFoll = GetFollow();
928 SwRowFrame* pFollowFlowLine = pFoll ? pFoll->GetFirstNonHeadlineRow() : nullptr;
930 // find last row in master
931 SwFrame* pLastLine = GetLastLower();
933 OSL_ENSURE( HasFollowFlowLine() &&
934 pFollowFlowLine &&
935 pLastLine, "There should be a flowline in the follow" );
937 // #140081# Make code robust.
938 if ( !pFollowFlowLine || !pLastLine )
939 return true;
940 if (pFollowFlowLine->IsDeleteForbidden())
942 SAL_WARN("sw.layout", "Cannot remove in-use Follow Flow Line");
943 return false;
946 // We have to reset the flag here, because lcl_MoveRowContent
947 // calls a GrowFrame(), which has a different behavior if
948 // this flag is set.
949 SetFollowFlowLine( false );
951 // Move content
952 lcl_MoveRowContent( *pFollowFlowLine, *static_cast<SwRowFrame*>(pLastLine) );
954 // NEW TABLES
955 // If a row span follow flow line is removed, we want to move the whole span
956 // to the master:
957 tools::Long nRowsToMove = lcl_GetMaximumLayoutRowSpan( *pFollowFlowLine );
959 if ( nRowsToMove > 1 )
961 SwRectFnSet aRectFnSet(this);
962 SwFrame* pRow = pFollowFlowLine->GetNext();
963 SwFrame* pInsertBehind = GetLastLower();
964 SwTwips nGrow = 0;
966 while ( pRow && nRowsToMove-- > 1 )
968 SwFrame* pNxt = pRow->GetNext();
969 nGrow += aRectFnSet.GetHeight(pRow->getFrameArea());
971 // The footnotes have to be moved:
972 lcl_MoveFootnotes( *GetFollow(), *this, static_cast<SwRowFrame&>(*pRow) );
974 pRow->RemoveFromLayout();
975 pRow->InsertBehind( this, pInsertBehind );
976 pRow->InvalidateAll_();
977 pRow->CheckDirChange();
978 pInsertBehind = pRow;
979 pRow = pNxt;
982 SwFrame* pFirstRow = Lower();
983 while ( pFirstRow )
985 lcl_AdjustRowSpanCells( static_cast<SwRowFrame*>(pFirstRow) );
986 pFirstRow = pFirstRow->GetNext();
989 Grow( nGrow );
990 GetFollow()->Shrink( nGrow );
993 bool bJoin = !pFollowFlowLine->GetNext();
994 pFollowFlowLine->Cut();
995 SwFrame::DestroyFrame(pFollowFlowLine);
997 return bJoin;
1000 // #i26945# - Floating screen objects are no longer searched.
1001 static bool lcl_FindSectionsInRow( const SwRowFrame& rRow )
1003 bool bRet = false;
1004 const SwCellFrame* pLower = static_cast<const SwCellFrame*>(rRow.Lower());
1005 while ( pLower )
1007 if ( pLower->IsVertical() != rRow.IsVertical() )
1008 return true;
1010 const SwFrame* pTmpFrame = pLower->Lower();
1011 while ( pTmpFrame )
1013 if ( pTmpFrame->IsRowFrame() )
1015 bRet = lcl_FindSectionsInRow( *static_cast<const SwRowFrame*>(pTmpFrame) );
1017 else
1019 // #i26945# - search only for sections
1020 if (pTmpFrame->IsSctFrame())
1022 bRet = true;
1024 if (!rRow.IsInSct())
1026 // This row is not in a section.
1027 if (const SwFrame* pSectionLower = pTmpFrame->GetLower())
1029 if (!pSectionLower->IsColumnFrame())
1031 // Section has a single column only, try to
1032 // split that.
1033 bRet = false;
1035 for (const SwFrame* pFrame = pSectionLower; pFrame; pFrame = pFrame->GetNext())
1037 if (pFrame->IsTabFrame())
1039 // Section contains a table, no split in that case.
1040 bRet = true;
1041 break;
1050 if ( bRet )
1051 return true;
1052 pTmpFrame = pTmpFrame->GetNext();
1055 pLower = static_cast<const SwCellFrame*>(pLower->GetNext());
1057 return bRet;
1060 bool SwTabFrame::Split(const SwTwips nCutPos, bool bTryToSplit,
1061 bool bTableRowKeep, bool & rIsFootnoteGrowth)
1063 bool bRet = true;
1065 SwRectFnSet aRectFnSet(this);
1067 // #i26745# - format row and cell frames of table
1069 Lower()->InvalidatePos_();
1070 // #i43913# - correction
1071 // call method <lcl_InnerCalcLayout> with first lower.
1072 lcl_InnerCalcLayout( Lower(), LONG_MAX, true );
1075 //In order to be able to compare the positions of the cells with CutPos,
1076 //they have to be calculated consecutively starting from the table.
1077 //They can definitely be invalid because of position changes of the table.
1078 SwRowFrame *pRow = static_cast<SwRowFrame*>(Lower());
1079 if( !pRow )
1080 return bRet;
1082 const sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat();
1083 sal_uInt16 nRowCount = 0; // pRow currently points to the first row
1085 SwTwips nRemainingSpaceForLastRow =
1086 aRectFnSet.YDiff(nCutPos, aRectFnSet.GetTop(getFrameArea()));
1087 nRemainingSpaceForLastRow -= aRectFnSet.GetTopMargin(*this);
1089 // Make pRow point to the line that does not fit anymore:
1090 while( pRow->GetNext() &&
1091 nRemainingSpaceForLastRow >= ( aRectFnSet.GetHeight(pRow->getFrameArea()) +
1092 (IsCollapsingBorders() ?
1093 pRow->GetBottomLineSize() :
1094 0 ) ) )
1096 if( bTryToSplit || !pRow->IsRowSpanLine() ||
1097 0 != aRectFnSet.GetHeight(pRow->getFrameArea()) )
1098 ++nRowCount;
1099 nRemainingSpaceForLastRow -= aRectFnSet.GetHeight(pRow->getFrameArea());
1100 pRow = static_cast<SwRowFrame*>(pRow->GetNext());
1103 // bSplitRowAllowed: Row may be split according to its attributes.
1104 // bTryToSplit: Row will never be split if bTryToSplit = false.
1105 // This can either be passed as a parameter, indicating
1106 // that we are currently doing the second try to split the
1107 // table, or it will be set to false under certain
1108 // conditions that are not suitable for splitting
1109 // the row.
1110 bool bSplitRowAllowed = true;
1111 if (!pRow->IsRowSplitAllowed())
1113 // A row larger than the entire page ought to be allowed to split regardless of setting,
1114 // otherwise it has hidden content and that makes no sense
1115 if ( pRow->getFrameArea().Height() > FindPageFrame()->getFramePrintArea().Height() )
1116 pRow->SetForceRowSplitAllowed( true );
1117 else
1118 bSplitRowAllowed = false;
1120 // #i29438#
1121 // #i26945# - Floating screen objects no longer forbid
1122 // a splitting of the table row.
1123 // Special DoNotSplit case 1:
1124 // Search for sections inside pRow:
1125 if ( lcl_FindSectionsInRow( *pRow ) )
1127 bTryToSplit = false;
1130 SwFlyFrame* pFly = FindFlyFrame();
1131 if (bSplitRowAllowed && pFly && pFly->IsFlySplitAllowed())
1133 // The remaining size is less than the minimum row height, then don't even try to split the
1134 // row, just move it forward.
1135 const SwFormatFrameSize& rRowSize = pRow->GetFormat()->GetFrameSize();
1136 if (rRowSize.GetHeightSizeType() == SwFrameSize::Minimum)
1138 SwTwips nMinHeight = rRowSize.GetHeight();
1139 if (nMinHeight > nRemainingSpaceForLastRow)
1141 bSplitRowAllowed = false;
1143 if (!pRow->GetPrev() && aRectFnSet.GetHeight(pRow->getFrameArea()) > nRemainingSpaceForLastRow)
1145 // Split of pRow is not allowed, no previous row, the current row doesn't fit:
1146 // that's a failure, we'll have to move forward instead.
1147 return false;
1153 // #i29771#
1154 // To avoid loops, we do some checks before actually trying to split
1155 // the row. Maybe we should keep the next row in this table.
1156 // Note: This is only done if we are at the beginning of our upper
1157 bool bKeepNextRow = false;
1158 if ( nRowCount < nRepeat )
1160 // First case: One of the repeated headline does not fit to the page anymore.
1161 // tdf#88496 Disable repeated headline (like for #i44910#) to avoid loops and
1162 // to fix interoperability problems (very long tables only with headline)
1163 // tdf#150149 except in multi-column sections, where it's possible to enlarge
1164 // the height of the section frame instead of using this fallback
1165 OSL_ENSURE( !GetIndPrev(), "Table is supposed to be at beginning" );
1166 if ( !IsInSct() )
1168 m_pTable->SetRowsToRepeat(0);
1169 return false;
1171 else
1172 bKeepNextRow = true;
1174 else if ( !GetIndPrev() && nRepeat == nRowCount )
1176 // Second case: The first non-headline row does not fit to the page.
1177 // If it is not allowed to be split, or it contains a sub-row that
1178 // is not allowed to be split, we keep the row in this table:
1179 if ( bTryToSplit && bSplitRowAllowed )
1181 // Check if there are (first) rows inside this row,
1182 // which are not allowed to be split.
1183 SwCellFrame* pLowerCell = static_cast<SwCellFrame*>(pRow->Lower());
1184 while ( pLowerCell )
1186 if ( pLowerCell->Lower() && pLowerCell->Lower()->IsRowFrame() )
1188 const SwRowFrame* pLowerRow = static_cast<SwRowFrame*>(pLowerCell->Lower());
1189 if ( !pLowerRow->IsRowSplitAllowed() &&
1190 aRectFnSet.GetHeight(pLowerRow->getFrameArea()) > nRemainingSpaceForLastRow )
1192 bKeepNextRow = true;
1193 break;
1196 pLowerCell = static_cast<SwCellFrame*>(pLowerCell->GetNext());
1199 else
1200 bKeepNextRow = true;
1203 // Better keep the next row in this table:
1204 if ( bKeepNextRow )
1206 pRow = GetFirstNonHeadlineRow();
1207 if ( pRow && pRow->IsRowSpanLine() && 0 == aRectFnSet.GetHeight(pRow->getFrameArea()) )
1208 pRow = static_cast<SwRowFrame*>(pRow->GetNext());
1209 if ( pRow )
1211 pRow = static_cast<SwRowFrame*>(pRow->GetNext());
1212 ++nRowCount;
1216 // No more row to split or to move to follow table:
1217 if ( !pRow )
1218 return bRet;
1220 // We try to split the row if
1221 // - the attributes of the row are set accordingly and
1222 // - we are allowed to do so
1223 // - it should not be kept with the next row
1224 bSplitRowAllowed = bSplitRowAllowed && bTryToSplit &&
1225 ( !bTableRowKeep ||
1226 !pRow->ShouldRowKeepWithNext() );
1228 // Adjust pRow according to the keep-with-next attribute:
1229 if ( !bSplitRowAllowed && bTableRowKeep )
1231 SwRowFrame* pTmpRow = static_cast<SwRowFrame*>(pRow->GetPrev());
1232 SwRowFrame* pOldRow = pRow;
1233 while ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() &&
1234 nRowCount > nRepeat )
1236 pRow = pTmpRow;
1237 --nRowCount;
1238 pTmpRow = static_cast<SwRowFrame*>(pTmpRow->GetPrev());
1241 // loop prevention
1242 if ( nRowCount == nRepeat && !GetIndPrev())
1244 pRow = pOldRow;
1248 // If we do not intend to split pRow, we check if we are
1249 // allowed to move pRow to a follow. Otherwise we return
1250 // false, indicating an error
1251 if ( !bSplitRowAllowed )
1253 SwRowFrame* pFirstNonHeadlineRow = GetFirstNonHeadlineRow();
1254 if ( pRow == pFirstNonHeadlineRow )
1255 return false;
1257 // #i91764#
1258 // Ignore row span lines
1259 SwRowFrame* pTmpRow = pFirstNonHeadlineRow;
1260 while ( pTmpRow && pTmpRow->IsRowSpanLine() )
1262 pTmpRow = static_cast<SwRowFrame*>(pTmpRow->GetNext());
1264 if ( !pTmpRow || pRow == pTmpRow )
1266 return false;
1270 // Build follow table if not already done:
1271 bool bNewFollow;
1272 SwTabFrame *pFoll;
1273 if ( GetFollow() )
1275 pFoll = GetFollow();
1276 bNewFollow = false;
1278 else
1280 bNewFollow = true;
1281 pFoll = new SwTabFrame( *this );
1283 // We give the follow table an initial width.
1285 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFoll);
1286 aRectFnSet.AddWidth(aFrm, aRectFnSet.GetWidth(getFrameArea()));
1287 aRectFnSet.SetLeft(aFrm, aRectFnSet.GetLeft(getFrameArea()));
1291 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pFoll);
1292 aRectFnSet.AddWidth(aPrt, aRectFnSet.GetWidth(getFramePrintArea()));
1295 // Insert the new follow table
1296 pFoll->InsertBehind( GetUpper(), this );
1298 // Repeat the headlines.
1299 auto& rLines = GetTable()->GetTabLines();
1300 for ( nRowCount = 0; nRowCount < nRepeat; ++nRowCount )
1302 // Insert new headlines:
1303 SwRowFrame* pHeadline = new SwRowFrame(*rLines[nRowCount], this);
1305 sw::FlyCreationSuppressor aSuppressor;
1306 pHeadline->SetRepeatedHeadline(true);
1308 pHeadline->InsertBefore( pFoll, nullptr );
1310 SwPageFrame *pPage = pHeadline->FindPageFrame();
1311 const sw::SpzFrameFormats* pSpzs = GetFormat()->GetDoc()->GetSpzFrameFormats();
1312 if( !pSpzs->empty() )
1314 SwNodeOffset nIndex;
1315 SwContentFrame* pFrame = pHeadline->ContainsContent();
1316 while( pFrame )
1318 // sw_redlinehide: the implementation of AppendObjs
1319 // takes care of iterating merged SwTextFrame
1320 nIndex = pFrame->IsTextFrame()
1321 ? static_cast<SwTextFrame*>(pFrame)->GetTextNodeFirst()->GetIndex()
1322 : static_cast<SwNoTextFrame*>(pFrame)->GetNode()->GetIndex();
1323 AppendObjs(pSpzs, nIndex, pFrame, pPage, GetFormat()->GetDoc());
1324 pFrame = pFrame->GetNextContentFrame();
1325 if( !pHeadline->IsAnLower( pFrame ) )
1326 break;
1332 SwRowFrame* pLastRow = nullptr; // points to the last remaining line in master
1333 SwRowFrame* pFollowRow = nullptr; // points to either the follow flow line or the
1334 // first regular line in the follow
1336 if ( bSplitRowAllowed )
1338 // If the row that does not fit anymore is allowed
1339 // to be split, the next row has to be moved to the follow table.
1340 pLastRow = pRow;
1341 pRow = static_cast<SwRowFrame*>(pRow->GetNext());
1343 // new follow flow line for last row of master table
1344 pFollowRow = lcl_InsertNewFollowFlowLine( *this, *pLastRow, false );
1346 else
1348 pFollowRow = pRow;
1350 // NEW TABLES
1351 // check if we will break a row span by moving pFollowRow to the follow:
1352 // In this case we want to reformat the last line.
1353 const SwCellFrame* pCellFrame = static_cast<const SwCellFrame*>(pFollowRow->GetLower());
1354 while ( pCellFrame )
1356 if ( pCellFrame->GetTabBox()->getRowSpan() < 1 )
1358 pLastRow = static_cast<SwRowFrame*>(pRow->GetPrev());
1359 break;
1362 pCellFrame = static_cast<const SwCellFrame*>(pCellFrame->GetNext());
1365 // new follow flow line for last row of master table
1366 if ( pLastRow )
1367 pFollowRow = lcl_InsertNewFollowFlowLine( *this, *pLastRow, true );
1370 SwTwips nShrink = 0;
1372 //Optimization: There is no paste needed for the new Follow and the
1373 //optimized insert can be used (large numbers of rows luckily only occur in
1374 //such situations).
1375 if ( bNewFollow )
1377 SwFrame* pInsertBehind = pFoll->GetLastLower();
1379 while ( pRow )
1381 SwFrame* pNxt = pRow->GetNext();
1382 nShrink += aRectFnSet.GetHeight(pRow->getFrameArea());
1383 // The footnotes do not have to be moved, this is done in the
1384 // MoveFwd of the follow table!!!
1385 pRow->RemoveFromLayout();
1386 pRow->InsertBehind( pFoll, pInsertBehind );
1387 pRow->InvalidateAll_();
1388 pInsertBehind = pRow;
1389 pRow = static_cast<SwRowFrame*>(pNxt);
1392 else
1394 SwFrame* pPasteBefore = HasFollowFlowLine() ?
1395 pFollowRow->GetNext() :
1396 pFoll->GetFirstNonHeadlineRow();
1398 while ( pRow )
1400 SwFrame* pNxt = pRow->GetNext();
1401 nShrink += aRectFnSet.GetHeight(pRow->getFrameArea());
1403 // The footnotes have to be moved:
1404 lcl_MoveFootnotes( *this, *GetFollow(), *pRow );
1406 pRow->RemoveFromLayout();
1407 pRow->Paste( pFoll, pPasteBefore );
1409 pRow->CheckDirChange();
1410 pRow = static_cast<SwRowFrame*>(pNxt);
1414 if ( !pLastRow )
1415 Shrink( nShrink );
1416 else
1418 // we rebuild the last line to assure that it will be fully formatted
1419 // we also don't shrink here, because we will be doing that in lcl_RecalcSplitLine
1421 // recalculate the split line
1422 bRet = lcl_RecalcSplitLine(*pLastRow, *pFollowRow, nRemainingSpaceForLastRow, nShrink, rIsFootnoteGrowth);
1424 // RecalcSplitLine did not work. In this case we conceal the split error:
1425 if (!bRet && !bSplitRowAllowed)
1427 bRet = true;
1430 // NEW TABLES
1431 // check if each cell in the row span line has a good height
1432 if ( bRet && pFollowRow->IsRowSpanLine() )
1433 lcl_AdjustRowSpanCells( pFollowRow );
1436 return bRet;
1439 namespace
1441 bool CanDeleteFollow(SwTabFrame *pFoll)
1443 if (pFoll->IsJoinLocked())
1444 return false;
1446 if (pFoll->IsDeleteForbidden())
1448 SAL_WARN("sw.layout", "Delete Forbidden");
1449 return false;
1452 return true;
1456 void SwTabFrame::Join()
1458 OSL_ENSURE( !HasFollowFlowLine(), "Joining follow flow line" );
1460 SwTabFrame *pFoll = GetFollow();
1462 if (!pFoll || !CanDeleteFollow(pFoll))
1463 return;
1465 SwRectFnSet aRectFnSet(this);
1466 pFoll->Cut(); //Cut out first to avoid unnecessary notifications.
1468 SwFrame *pRow = pFoll->GetFirstNonHeadlineRow(),
1469 *pNxt;
1471 SwFrame* pPrv = GetLastLower();
1473 SwTwips nHeight = 0; //Total height of the inserted rows as return value.
1475 while ( pRow )
1477 pNxt = pRow->GetNext();
1478 nHeight += aRectFnSet.GetHeight(pRow->getFrameArea());
1479 pRow->RemoveFromLayout();
1480 pRow->InvalidateAll_();
1481 pRow->InsertBehind( this, pPrv );
1482 pRow->CheckDirChange();
1483 pPrv = pRow;
1484 pRow = pNxt;
1487 SetFollow( pFoll->GetFollow() );
1488 SetFollowFlowLine( pFoll->HasFollowFlowLine() );
1489 SwFrame::DestroyFrame(pFoll);
1491 Grow( nHeight );
1494 static void SwInvalidatePositions( SwFrame *pFrame, tools::Long nBottom )
1496 // LONG_MAX == nBottom means we have to calculate all
1497 bool bAll = LONG_MAX == nBottom;
1498 SwRectFnSet aRectFnSet(pFrame);
1500 { pFrame->InvalidatePos_();
1501 pFrame->InvalidateSize_();
1502 if( pFrame->IsLayoutFrame() )
1504 if ( static_cast<SwLayoutFrame*>(pFrame)->Lower() )
1506 ::SwInvalidatePositions( static_cast<SwLayoutFrame*>(pFrame)->Lower(), nBottom);
1507 // #i26945#
1508 ::lcl_InvalidateLowerObjs( *static_cast<SwLayoutFrame*>(pFrame) );
1511 else
1512 pFrame->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
1513 pFrame = pFrame->GetNext();
1514 } while ( pFrame &&
1515 ( bAll ||
1516 aRectFnSet.YDiff( aRectFnSet.GetTop(pFrame->getFrameArea()), nBottom ) < 0 ) );
1519 void SwInvalidateAll( SwFrame *pFrame, tools::Long nBottom )
1521 // LONG_MAX == nBottom means we have to calculate all
1522 bool bAll = LONG_MAX == nBottom;
1523 SwRectFnSet aRectFnSet(pFrame);
1526 pFrame->InvalidatePos_();
1527 pFrame->InvalidateSize_();
1528 pFrame->InvalidatePrt_();
1529 if( pFrame->IsLayoutFrame() )
1531 // NEW TABLES
1532 SwLayoutFrame* pToInvalidate = static_cast<SwLayoutFrame*>(pFrame);
1533 if (pFrame->IsCellFrame())
1535 SwCellFrame* pThisCell = static_cast<SwCellFrame*>(pFrame);
1536 if ( pThisCell->GetTabBox()->getRowSpan() < 1 )
1538 pToInvalidate = & const_cast<SwCellFrame&>(pThisCell->FindStartEndOfRowSpanCell( true ));
1539 pToInvalidate->InvalidatePos_();
1540 pToInvalidate->InvalidateSize_();
1541 pToInvalidate->InvalidatePrt_();
1544 if ( pToInvalidate->Lower() )
1545 ::SwInvalidateAll( pToInvalidate->Lower(), nBottom);
1547 else
1548 pFrame->Prepare();
1550 pFrame = pFrame->GetNext();
1551 } while ( pFrame &&
1552 ( bAll ||
1553 aRectFnSet.YDiff( aRectFnSet.GetTop(pFrame->getFrameArea()), nBottom ) < 0 ) );
1556 // #i29550#
1557 static void lcl_InvalidateAllLowersPrt( SwLayoutFrame* pLayFrame )
1559 pLayFrame->InvalidatePrt_();
1560 pLayFrame->InvalidateSize_();
1561 pLayFrame->SetCompletePaint();
1563 SwFrame* pFrame = pLayFrame->Lower();
1565 while ( pFrame )
1567 if ( pFrame->IsLayoutFrame() )
1568 lcl_InvalidateAllLowersPrt( static_cast<SwLayoutFrame*>(pFrame) );
1569 else
1571 pFrame->InvalidatePrt_();
1572 pFrame->InvalidateSize_();
1573 pFrame->SetCompletePaint();
1576 pFrame = pFrame->GetNext();
1580 bool SwContentFrame::CalcLowers(SwLayoutFrame & rLay, SwLayoutFrame const& rDontLeave,
1581 tools::Long nBottom, bool bSkipRowSpanCells )
1583 vcl::RenderContext* pRenderContext = rLay.getRootFrame()->GetCurrShell()->GetOut();
1584 // LONG_MAX == nBottom means we have to calculate all
1585 bool bAll = LONG_MAX == nBottom;
1586 bool bRet = false;
1587 SwContentFrame *pCnt = rLay.ContainsContent();
1588 SwRectFnSet aRectFnSet(&rLay);
1590 // FME 2007-08-30 #i81146# new loop control
1591 int nLoopControlRuns = 0;
1592 const int nLoopControlMax = 10;
1593 const sw::BroadcastingModify* pLoopControlCond = nullptr;
1595 while (pCnt && rDontLeave.IsAnLower(pCnt))
1597 // #115759# - check, if a format of content frame is
1598 // possible. Thus, 'copy' conditions, found at the beginning of
1599 // <SwContentFrame::MakeAll(..)>, and check these.
1600 const bool bFormatPossible = !pCnt->IsJoinLocked() &&
1601 ( !pCnt->IsTextFrame() ||
1602 !static_cast<SwTextFrame*>(pCnt)->IsLocked() ) &&
1603 ( pCnt->IsFollow() || !StackHack::IsLocked() );
1605 // NEW TABLES
1606 bool bSkipContent = false;
1607 if ( bSkipRowSpanCells && pCnt->IsInTab() )
1609 const SwFrame* pCell = pCnt->GetUpper();
1610 while ( pCell && !pCell->IsCellFrame() )
1611 pCell = pCell->GetUpper();
1612 if ( pCell && 1 != static_cast<const SwCellFrame*>( pCell )->GetLayoutRowSpan() )
1613 bSkipContent = true;
1616 if ( bFormatPossible && !bSkipContent )
1618 bRet |= !pCnt->isFrameAreaDefinitionValid();
1619 // #i26945# - no extra invalidation of floating
1620 // screen objects needed.
1621 // Thus, delete call of method <SwFrame::InvalidateObjs( true )>
1622 pCnt->Calc(pRenderContext);
1623 // #i46941# - frame has to be valid
1624 // Note: frame could be invalid after calling its format, if it's locked.
1625 OSL_ENSURE( !pCnt->IsTextFrame() ||
1626 pCnt->isFrameAreaDefinitionValid() ||
1627 static_cast<SwTextFrame*>(pCnt)->IsJoinLocked(),
1628 "<SwContentFrame::CalcLowers(..)> - text frame invalid and not locked." );
1629 if ( pCnt->IsTextFrame() && pCnt->isFrameAreaDefinitionValid() )
1631 // #i23129#, #i36347# - pass correct page frame to
1632 // the object formatter
1633 if ( !SwObjectFormatter::FormatObjsAtFrame( *pCnt,
1634 *(pCnt->FindPageFrame()) ) )
1636 SwTextNode const*const pTextNode(
1637 static_cast<SwTextFrame*>(pCnt)->GetTextNodeFirst());
1638 if (pTextNode == pLoopControlCond)
1639 ++nLoopControlRuns;
1640 else
1642 nLoopControlRuns = 0;
1643 pLoopControlCond = pTextNode;
1646 if ( nLoopControlRuns < nLoopControlMax )
1648 // restart format with first content
1649 pCnt = rLay.ContainsContent();
1650 continue;
1653 SAL_WARN("sw.layout", "LoopControl in SwContentFrame::CalcLowers");
1656 if (!rDontLeave.IsAnLower(pCnt)) // moved backward?
1658 pCnt = rLay.ContainsContent();
1659 continue; // avoid formatting new upper on different page
1661 pCnt->GetUpper()->Calc(pRenderContext);
1663 if( ! bAll && aRectFnSet.YDiff(aRectFnSet.GetTop(pCnt->getFrameArea()), nBottom) > 0 )
1664 break;
1665 pCnt = pCnt->GetNextContentFrame();
1667 return bRet;
1670 // #i26945# - add parameter <_bOnlyRowsAndCells> to control
1671 // that only row and cell frames are formatted.
1672 static bool lcl_InnerCalcLayout( SwFrame *pFrame,
1673 tools::Long nBottom,
1674 bool _bOnlyRowsAndCells )
1676 vcl::RenderContext* pRenderContext = pFrame->getRootFrame()->GetCurrShell() ? pFrame->getRootFrame()->GetCurrShell()->GetOut() : nullptr;
1677 // LONG_MAX == nBottom means we have to calculate all
1678 bool bAll = LONG_MAX == nBottom;
1679 bool bRet = false;
1680 const SwFrame* pOldUp = pFrame->GetUpper();
1681 SwRectFnSet aRectFnSet(pFrame);
1684 // #i26945# - parameter <_bOnlyRowsAndCells> controls,
1685 // if only row and cell frames are formatted.
1686 if ( pFrame->IsLayoutFrame() &&
1687 ( !_bOnlyRowsAndCells || pFrame->IsRowFrame() || pFrame->IsCellFrame() ) )
1689 SwFrameDeleteGuard aDeleteGuard(pFrame);
1691 // #130744# An invalid locked table frame will
1692 // not be calculated => It will not become valid =>
1693 // Loop in lcl_RecalcRow(). Therefore we do not consider them for bRet.
1694 bRet |= !pFrame->isFrameAreaDefinitionValid() && ( !pFrame->IsTabFrame() || !static_cast<SwTabFrame*>(pFrame)->IsJoinLocked() );
1695 pFrame->Calc(pRenderContext);
1696 if( static_cast<SwLayoutFrame*>(pFrame)->Lower() )
1697 bRet |= lcl_InnerCalcLayout( static_cast<SwLayoutFrame*>(pFrame)->Lower(), nBottom);
1699 // NEW TABLES
1700 if (pFrame->IsCellFrame())
1702 SwCellFrame* pThisCell = static_cast<SwCellFrame*>(pFrame);
1703 if ( pThisCell->GetTabBox()->getRowSpan() < 1 )
1705 SwCellFrame& rToCalc = const_cast<SwCellFrame&>(pThisCell->FindStartEndOfRowSpanCell( true ));
1706 bRet |= !rToCalc.isFrameAreaDefinitionValid();
1707 rToCalc.Calc(pRenderContext);
1708 if ( rToCalc.Lower() )
1709 bRet |= lcl_InnerCalcLayout( rToCalc.Lower(), nBottom);
1713 pFrame = pFrame->GetNext();
1714 } while( pFrame &&
1715 ( bAll ||
1716 aRectFnSet.YDiff(aRectFnSet.GetTop(pFrame->getFrameArea()), nBottom) < 0 )
1717 && pFrame->GetUpper() == pOldUp );
1718 return bRet;
1721 static void lcl_RecalcRow(SwRowFrame & rRow, tools::Long const nBottom)
1723 // FME 2007-08-30 #i81146# new loop control
1724 int nLoopControlRuns_1 = 0;
1725 sal_uInt16 nLoopControlStage_1 = 0;
1726 const int nLoopControlMax = 10;
1728 bool bCheck = true;
1731 // FME 2007-08-30 #i81146# new loop control
1732 int nLoopControlRuns_2 = 0;
1733 sal_uInt16 nLoopControlStage_2 = 0;
1735 while (lcl_InnerCalcLayout(&rRow, nBottom))
1737 if ( ++nLoopControlRuns_2 > nLoopControlMax )
1739 SAL_WARN_IF(nLoopControlStage_2 == 0, "sw.layout", "LoopControl_2 in lcl_RecalcRow: Stage 1!");
1740 SAL_WARN_IF(nLoopControlStage_2 == 1, "sw.layout", "LoopControl_2 in lcl_RecalcRow: Stage 2!!");
1741 SAL_WARN_IF(nLoopControlStage_2 >= 2, "sw.layout", "LoopControl_2 in lcl_RecalcRow: Stage 3!!!");
1742 rRow.ValidateThisAndAllLowers( nLoopControlStage_2++ );
1743 nLoopControlRuns_2 = 0;
1744 if( nLoopControlStage_2 > 2 )
1745 break;
1748 bCheck = true;
1751 if( bCheck )
1753 SwFrameDeleteGuard g(&rRow);
1755 // #115759# - force another format of the
1756 // lowers, if at least one of it was invalid.
1757 bCheck = SwContentFrame::CalcLowers(rRow, *rRow.GetUpper(), nBottom, true);
1759 // NEW TABLES
1760 // First we calculate the cells with row span of < 1, afterwards
1761 // all cells with row span of > 1:
1762 for ( int i = 0; i < 2; ++i )
1764 SwCellFrame* pCellFrame = static_cast<SwCellFrame*>(rRow.Lower());
1765 while ( pCellFrame )
1767 const bool bCalc = 0 == i ?
1768 pCellFrame->GetLayoutRowSpan() < 1 :
1769 pCellFrame->GetLayoutRowSpan() > 1;
1771 if ( bCalc )
1773 SwCellFrame& rToRecalc = 0 == i ?
1774 const_cast<SwCellFrame&>(pCellFrame->FindStartEndOfRowSpanCell( true )) :
1775 *pCellFrame;
1776 bCheck |= SwContentFrame::CalcLowers(rToRecalc, rToRecalc, nBottom, false);
1779 pCellFrame = static_cast<SwCellFrame*>(pCellFrame->GetNext());
1783 if ( bCheck )
1785 if ( ++nLoopControlRuns_1 > nLoopControlMax )
1787 SAL_WARN_IF(nLoopControlStage_1 == 0, "sw.layout", "LoopControl_1 in lcl_RecalcRow: Stage 1!");
1788 SAL_WARN_IF(nLoopControlStage_1 == 1, "sw.layout", "LoopControl_1 in lcl_RecalcRow: Stage 2!!");
1789 SAL_WARN_IF(nLoopControlStage_1 >= 2, "sw.layout", "LoopControl_1 in lcl_RecalcRow: Stage 3!!!");
1790 rRow.ValidateThisAndAllLowers( nLoopControlStage_1++ );
1791 nLoopControlRuns_1 = 0;
1792 if( nLoopControlStage_1 > 2 )
1793 break;
1796 continue;
1799 break;
1800 } while( true );
1803 static void lcl_RecalcTable( SwTabFrame& rTab,
1804 SwLayoutFrame *pFirstRow,
1805 SwLayNotify &rNotify )
1807 if ( rTab.Lower() )
1809 if ( !pFirstRow )
1811 pFirstRow = static_cast<SwLayoutFrame*>(rTab.Lower());
1812 rNotify.SetLowersComplete( true );
1814 ::SwInvalidatePositions( pFirstRow, LONG_MAX );
1815 lcl_RecalcRow( *static_cast<SwRowFrame*>(pFirstRow), LONG_MAX );
1819 // This is a new function to check the first condition whether
1820 // a tab frame may move backward. It replaces the formerly used
1821 // GetIndPrev(), which did not work correctly for #i5947#
1822 static bool lcl_NoPrev( const SwFrame& rFrame )
1824 // #i79774#
1825 // skip empty sections on investigation of direct previous frame.
1826 // use information, that at least one empty section is skipped in the following code.
1827 bool bSkippedDirectPrevEmptySection( false );
1828 if ( rFrame.GetPrev() )
1830 const SwFrame* pPrev( rFrame.GetPrev() );
1831 while ( pPrev &&
1832 pPrev->IsSctFrame() &&
1833 !dynamic_cast<const SwSectionFrame&>(*pPrev).GetSection() )
1835 pPrev = pPrev->GetPrev();
1836 bSkippedDirectPrevEmptySection = true;
1838 if ( pPrev )
1840 return false;
1844 if ( ( !bSkippedDirectPrevEmptySection && !rFrame.GetIndPrev() ) ||
1845 ( bSkippedDirectPrevEmptySection &&
1846 ( !rFrame.IsInSct() || !rFrame.GetIndPrev_() ) ) )
1848 return true;
1851 // I do not have a direct prev, but I have an indirect prev.
1852 // In section frames I have to check if I'm located inside
1853 // the first column:
1854 if ( rFrame.IsInSct() )
1856 const SwFrame* pSct = rFrame.GetUpper();
1857 if ( pSct && pSct->IsColBodyFrame() &&
1858 pSct->GetUpper()->GetUpper()->IsSctFrame() )
1860 const SwFrame* pPrevCol = rFrame.GetUpper()->GetUpper()->GetPrev();
1861 if ( pPrevCol )
1862 // I'm not inside the first column and do not have a direct
1863 // prev. I can try to go backward.
1864 return true;
1868 return false;
1871 #define KEEPTAB ( !GetFollow() && !IsFollow() )
1873 // - helper method to find next content frame of
1874 // a table frame and format it to assure keep attribute.
1875 // method return true, if a next content frame is formatted.
1876 // Precondition: The given table frame hasn't a follow and isn't a follow.
1877 SwFrame* sw_FormatNextContentForKeep( SwTabFrame* pTabFrame )
1879 vcl::RenderContext* pRenderContext = pTabFrame->getRootFrame()->GetCurrShell()->GetOut();
1880 // find next content, table or section
1881 SwFrame* pNxt = pTabFrame->FindNext();
1883 // skip empty sections
1884 while ( pNxt && pNxt->IsSctFrame() &&
1885 !static_cast<SwSectionFrame*>(pNxt)->GetSection() )
1887 pNxt = pNxt->FindNext();
1890 // if found next frame is a section, get its first content.
1891 if ( pNxt && pNxt->IsSctFrame() )
1893 pNxt = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
1896 // format found next frame.
1897 // if table frame is inside another table, method <SwFrame::MakeAll()> is
1898 // called to avoid that the superior table frame is formatted.
1899 if ( pNxt )
1901 if ( pTabFrame->GetUpper()->IsInTab() )
1902 pNxt->MakeAll(pNxt->getRootFrame()->GetCurrShell()->GetOut());
1903 else
1904 pNxt->Calc(pRenderContext);
1907 return pNxt;
1910 namespace {
1911 bool AreAllRowsKeepWithNext( const SwRowFrame* pFirstRowFrame, const bool bCheckParents = true )
1913 bool bRet = pFirstRowFrame != nullptr &&
1914 pFirstRowFrame->ShouldRowKeepWithNext( bCheckParents );
1916 while ( bRet && pFirstRowFrame->GetNext() != nullptr )
1918 pFirstRowFrame = dynamic_cast<const SwRowFrame*>(pFirstRowFrame->GetNext());
1919 bRet = pFirstRowFrame != nullptr &&
1920 pFirstRowFrame->ShouldRowKeepWithNext( bCheckParents );
1923 return bRet;
1927 // extern because static can't be friend
1928 void FriendHackInvalidateRowFrame(SwFrameAreaDefinition & rRowFrame)
1930 // hilariously static_cast<SwTabFrame*>(GetLower()) would not require friend declaration, but it's UB...
1931 rRowFrame.setFrameAreaPositionValid(false);
1934 static void InvalidateFramePositions(SwFrame * pFrame)
1936 while (pFrame)
1938 if (pFrame->IsLayoutFrame())
1940 InvalidateFramePositions(pFrame->GetLower());
1942 else if (pFrame->IsTextFrame())
1944 pFrame->Prepare(PrepareHint::FramePositionChanged);
1946 pFrame = pFrame->GetNext();
1950 void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext)
1952 if ( IsJoinLocked() || StackHack::IsLocked() || StackHack::Count() > 50 )
1953 return;
1955 if ( HasFollow() )
1957 SwTabFrame* pFollowFrame = GetFollow();
1958 OSL_ENSURE( !pFollowFrame->IsJoinLocked() || !pFollowFrame->IsRebuildLastLine(),
1959 "SwTabFrame::MakeAll for master while follow is in RebuildLastLine()" );
1960 if ( pFollowFrame->IsJoinLocked() && pFollowFrame->IsRebuildLastLine() )
1961 return;
1964 PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )
1966 LockJoin(); //I don't want to be destroyed on the way.
1967 SwLayNotify aNotify( this ); //does the notification in the DTor
1968 // If pos is invalid, we have to call a SetInvaKeep at aNotify.
1969 // Otherwise the keep attribute would not work in front of a table.
1970 const bool bOldValidPos = isFrameAreaPositionValid();
1972 //If my neighbour is my Follow at the same time, I'll swallow it up.
1973 // OD 09.04.2003 #108698# - join all follows, which are placed on the
1974 // same page/column.
1975 // OD 29.04.2003 #109213# - join follow, only if join for the follow
1976 // is not locked. Otherwise, join will not be performed and this loop
1977 // will be endless.
1978 while ( GetNext() && GetNext() == GetFollow() &&
1979 CanDeleteFollow(GetFollow())
1982 if ( HasFollowFlowLine() )
1983 RemoveFollowFlowLine();
1984 Join();
1987 // The bRemoveFollowFlowLinePending is set if the split attribute of the
1988 // last line is set:
1989 if ( IsRemoveFollowFlowLinePending() && HasFollowFlowLine() )
1991 if ( RemoveFollowFlowLine() )
1992 Join();
1993 SetRemoveFollowFlowLinePending( false );
1996 if (m_bResizeHTMLTable) //Optimized interplay with grow/shrink of the content
1998 m_bResizeHTMLTable = false;
1999 SwHTMLTableLayout *pLayout = GetTable()->GetHTMLTableLayout();
2000 if ( pLayout )
2001 m_bCalcLowers = pLayout->Resize(
2002 pLayout->GetBrowseWidthByTabFrame( *this ) );
2005 // as long as bMakePage is true, a new page can be created (exactly once)
2006 bool bMakePage = true;
2007 // bMovedBwd gets set to true when the frame flows backwards
2008 bool bMovedBwd = false;
2009 // as long as bMovedFwd is false, the Frame may flow backwards (until
2010 // it has been moved forward once)
2011 bool bMovedFwd = false;
2012 // gets set to true when the Frame is split
2013 bool bSplit = false;
2014 const bool bFootnotesInDoc = !GetFormat()->GetDoc()->GetFootnoteIdxs().empty();
2015 const bool bFly = IsInFly();
2017 std::optional<SwBorderAttrAccess> oAccess(std::in_place, SwFrame::GetCache(), this);
2018 const SwBorderAttrs *pAttrs = oAccess->Get();
2020 // All rows should keep together
2021 bool bDontSplit = !IsFollow() &&
2022 ( !GetFormat()->GetLayoutSplit().GetValue() );
2024 // The number of repeated headlines
2025 const sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat();
2027 // This flag indicates that we are allowed to try to split the
2028 // table rows.
2029 bool bTryToSplit = true;
2031 // Indicates that two individual rows may keep together, based on the keep
2032 // attribute set at the first paragraph in the first cell.
2033 bool bTableRowKeep = !bDontSplit && GetFormat()->GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::TABLE_ROW_KEEP);
2034 if (SwFlyFrame* pFly = FindFlyFrame())
2036 if (pFly->IsFlySplitAllowed())
2038 // Ignore the above text node -> row inheritance for floating tables.
2039 bTableRowKeep = false;
2041 else if (!pFly->GetNextLink())
2043 // If the fly is not allowed to split and is not chained, then it makes no sense to
2044 // split the table.
2045 bDontSplit = true;
2049 // The Magic Move: Used for the table row keep feature.
2050 // If only the last row of the table wants to keep (implicitly by setting
2051 // keep for the first paragraph in the first cell), and this table does
2052 // not have a next, the last line will be cut. Loop prevention: Only
2053 // one try.
2054 // WHAT IS THIS??? It "magically" hides last line (paragraph) in a table,
2055 // if first is set to keep with next???
2056 bool bLastRowHasToMoveToFollow = false;
2057 bool bLastRowMoveNoMoreTries = false;
2059 const bool bLargeTable = GetTable()->GetTabLines().size() > 64; //arbitrary value, virtually guaranteed to be larger than one page.
2060 const bool bEmulateTableKeep = !bLargeTable && bTableRowKeep
2061 && !pAttrs->GetAttrSet().GetKeep().GetValue()
2062 && AreAllRowsKeepWithNext(GetFirstNonHeadlineRow(), /*bCheckParents=*/false);
2063 // The beloved keep attribute
2064 const bool bKeep = IsKeep(pAttrs->GetAttrSet().GetKeep(), GetBreakItem(), bEmulateTableKeep);
2066 // Join follow table, if this table is not allowed to split:
2067 if ( bDontSplit )
2069 while ( GetFollow() && !GetFollow()->IsJoinLocked() )
2071 if ( HasFollowFlowLine() )
2072 RemoveFollowFlowLine();
2073 Join();
2077 // Join follow table, if this does not have enough (repeated) lines:
2078 if ( nRepeat )
2080 if( GetFollow() && !GetFollow()->IsJoinLocked() &&
2081 nullptr == GetFirstNonHeadlineRow() )
2083 if ( HasFollowFlowLine() )
2084 RemoveFollowFlowLine();
2085 Join();
2089 // Join follow table, if last row of this table should keep:
2090 if ( bTableRowKeep && GetFollow() && !GetFollow()->IsJoinLocked() )
2092 const SwRowFrame* pTmpRow = static_cast<const SwRowFrame*>(GetLastLower());
2093 if ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() )
2095 if ( HasFollowFlowLine() )
2096 RemoveFollowFlowLine();
2097 Join();
2101 // a new one is moved forwards immediately
2102 if ( !getFrameArea().Top() && IsFollow() )
2104 SwFrame *pPre = GetPrev();
2105 if ( pPre && pPre->IsTabFrame() && static_cast<SwTabFrame*>(pPre)->GetFollow() == this)
2107 // don't make the effort to move fwd if its known
2108 // conditions that are known not to work
2109 if (IsInFootnote() && ForbiddenForFootnoteCntFwd())
2110 bMakePage = false;
2111 else if (!MoveFwd(bMakePage, false))
2112 bMakePage = false;
2113 bMovedFwd = true;
2117 int nUnSplitted = 5; // Just another loop control :-(
2118 int nThrowAwayValidLayoutLimit = 5; // And another one :-(
2119 SwRectFnSet aRectFnSet(this);
2120 while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
2122 const bool bMoveable = IsMoveable();
2123 if (bMoveable &&
2124 !(bMovedFwd && bEmulateTableKeep) )
2125 if ( CheckMoveFwd( bMakePage, bKeep && KEEPTAB, bEmulateTableKeep ) )
2127 bMovedFwd = true;
2128 m_bCalcLowers = true;
2129 // #i99267#
2130 // reset <bSplit> after forward move to assure that follows
2131 // can be joined, if further space is available.
2132 bSplit = false;
2135 Point aOldPos( aRectFnSet.GetPos(getFrameArea()) );
2136 MakePos();
2138 if ( aOldPos != aRectFnSet.GetPos(getFrameArea()) )
2140 if ( aOldPos.Y() != aRectFnSet.GetTop(getFrameArea()) )
2142 SwHTMLTableLayout *pLayout = GetTable()->GetHTMLTableLayout();
2143 if( pLayout )
2145 oAccess.reset();
2146 m_bCalcLowers |= pLayout->Resize(
2147 pLayout->GetBrowseWidthByTabFrame( *this ) );
2150 setFramePrintAreaValid(false);
2151 aNotify.SetLowersComplete( false );
2153 SwFrame *pPre;
2154 if ( bKeep || (nullptr != (pPre = FindPrev()) &&
2155 pPre->GetAttrSet()->GetKeep().GetValue()) )
2157 m_bCalcLowers = true;
2159 if (GetLower())
2160 { // it's possible that the rows already have valid pos - but it is surely wrong if the table's pos changed!
2161 FriendHackInvalidateRowFrame(*GetLower());
2162 // invalidate text frames to get rid of their SwFlyPortions
2163 InvalidateFramePositions(GetLower());
2167 //We need to know the height of the first row, because the master needs
2168 //to be invalidated if it shrinks and then absorb the row if possible.
2169 tools::Long n1StLineHeight = 0;
2170 if ( IsFollow() )
2172 SwFrame* pFrame = GetFirstNonHeadlineRow();
2173 if ( pFrame )
2174 n1StLineHeight = aRectFnSet.GetHeight(pFrame->getFrameArea());
2177 if ( !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
2179 const tools::Long nOldPrtWidth = aRectFnSet.GetWidth(getFramePrintArea());
2180 const tools::Long nOldFrameWidth = aRectFnSet.GetWidth(getFrameArea());
2181 const Point aOldPrtPos = aRectFnSet.GetPos(getFramePrintArea());
2183 if (!oAccess)
2185 oAccess.emplace(SwFrame::GetCache(), this);
2186 pAttrs = oAccess->Get();
2188 Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs );
2190 SwHTMLTableLayout *pLayout = GetTable()->GetHTMLTableLayout();
2191 if ( pLayout &&
2192 (aRectFnSet.GetWidth(getFramePrintArea()) != nOldPrtWidth ||
2193 aRectFnSet.GetWidth(getFrameArea()) != nOldFrameWidth) )
2195 oAccess.reset();
2196 m_bCalcLowers |= pLayout->Resize(
2197 pLayout->GetBrowseWidthByTabFrame( *this ) );
2199 if ( aOldPrtPos != aRectFnSet.GetPos(getFramePrintArea()) )
2200 aNotify.SetLowersComplete( false );
2203 // If this is the first one in a chain, check if this can flow
2204 // backwards (if this is movable at all).
2205 // To prevent oscillations/loops, check that this has not just
2206 // flowed forwards.
2207 if ( !bMovedFwd && (bMoveable || bFly) && lcl_NoPrev( *this ) )
2209 // for Follows notify Master.
2210 // only move Follow if it has to skip empty pages.
2211 if ( IsFollow() )
2213 // Only if the height of the first line got smaller.
2214 SwFrame *pFrame = GetFirstNonHeadlineRow();
2215 if( pFrame && n1StLineHeight >aRectFnSet.GetHeight(pFrame->getFrameArea()) )
2217 SwTabFrame *pMaster = FindMaster();
2218 bool bDummy;
2219 if ( ShouldBwdMoved( pMaster->GetUpper(), bDummy ) )
2220 pMaster->InvalidatePos();
2223 SwFootnoteBossFrame *pOldBoss = bFootnotesInDoc ? FindFootnoteBossFrame( true ) : nullptr;
2224 bool bReformat;
2225 std::optional<SfxDeleteListener> oDeleteListener;
2226 if (pOldBoss)
2227 oDeleteListener.emplace(*pOldBoss);
2228 SwFrameDeleteGuard g(this);
2229 if ( MoveBwd( bReformat ) )
2231 SAL_WARN_IF(oDeleteListener && oDeleteListener->WasDeleted(), "sw.layout", "SwFootnoteBossFrame unexpectedly deleted");
2233 aRectFnSet.Refresh(this);
2234 bMovedBwd = true;
2235 aNotify.SetLowersComplete( false );
2236 if (bFootnotesInDoc && !oDeleteListener->WasDeleted())
2237 MoveLowerFootnotes( nullptr, pOldBoss, nullptr, true );
2238 if ( bReformat || bKeep )
2240 tools::Long nOldTop = aRectFnSet.GetTop(getFrameArea());
2241 MakePos();
2242 if( nOldTop != aRectFnSet.GetTop(getFrameArea()) )
2244 SwHTMLTableLayout *pHTMLLayout =
2245 GetTable()->GetHTMLTableLayout();
2246 if( pHTMLLayout )
2248 oAccess.reset();
2249 m_bCalcLowers |= pHTMLLayout->Resize(
2250 pHTMLLayout->GetBrowseWidthByTabFrame( *this ) );
2253 setFramePrintAreaValid(false);
2255 if (!oAccess)
2257 oAccess.emplace(SwFrame::GetCache(), this);
2258 pAttrs = oAccess->Get();
2260 Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs );
2263 oAccess.reset();
2265 lcl_RecalcTable( *this, nullptr, aNotify );
2267 m_bLowersFormatted = true;
2268 if ( bKeep && KEEPTAB )
2271 // Consider case that table is inside another table,
2272 // because it has to be avoided, that superior table
2273 // is formatted.
2274 // Thus, find next content, table or section
2275 // and, if a section is found, get its first
2276 // content.
2277 if ( nullptr != sw_FormatNextContentForKeep( this ) && !GetNext() )
2279 setFrameAreaPositionValid(false);
2286 //Again an invalid value? - do it again...
2287 if ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
2288 continue;
2290 // check, if calculation of table frame is ready.
2292 // Local variable <nDistanceToUpperPrtBottom>
2293 // Introduce local variable and init it with the distance from the
2294 // table frame bottom to the bottom of the upper printing area.
2295 // Note: negative values denotes the situation that table frame doesn't fit in its upper.
2296 SwTwips nDistanceToUpperPrtBottom =
2297 aRectFnSet.BottomDist(getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
2299 /// In online layout try to grow upper of table frame, if table frame doesn't fit in its upper.
2300 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
2301 const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
2302 if ( nDistanceToUpperPrtBottom < 0 && bBrowseMode )
2304 if ( GetUpper()->Grow( -nDistanceToUpperPrtBottom ) )
2306 // upper is grown --> recalculate <nDistanceToUpperPrtBottom>
2307 nDistanceToUpperPrtBottom = aRectFnSet.BottomDist(getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
2311 if (GetFollow() && GetUpper()->IsFlyFrame())
2313 auto pUpper = static_cast<SwFlyFrame*>(GetUpper());
2314 if (pUpper->IsFlySplitAllowed())
2316 // We have a follow tab frame that may be joined, and we're directly in a split fly.
2317 // See if the fly could grow.
2318 SwTwips nTest = GetUpper()->Grow(LONG_MAX, /*bTst=*/true);
2319 if (nTest >= aRectFnSet.GetHeight(GetFollow()->getFrameArea()))
2321 // We have space to join at least one follow tab frame.
2322 SwTwips nRequest = 0;
2323 for (SwTabFrame* pFollow = GetFollow(); pFollow; pFollow = pFollow->GetFollow())
2325 nRequest += aRectFnSet.GetHeight(pFollow->getFrameArea());
2327 // Try to grow the split fly to join all follows.
2328 pUpper->Grow(nRequest);
2329 // Determine what is space we actually got from the requested space.
2330 nDistanceToUpperPrtBottom = aRectFnSet.BottomDist(getFrameArea(), aRectFnSet.GetPrtBottom(*pUpper));
2335 // If there is still some space left in the upper, we check if we
2336 // can join some rows of the follow.
2337 // Setting bLastRowHasToMoveToFollow to true means we want to force
2338 // the table to be split! Only skip this if condition once.
2339 if( nDistanceToUpperPrtBottom >= 0 && !bLastRowHasToMoveToFollow )
2341 // If there is space left in the upper printing area, join as for trial
2342 // at least one further row of an existing follow.
2343 if ( !bSplit && GetFollow() )
2345 bool bDummy;
2346 if (!(HasFollowFlowLine()
2347 && GetFollow()->GetFirstNonHeadlineRow()->IsDeleteForbidden())
2348 && GetFollow()->ShouldBwdMoved(GetUpper(), bDummy))
2350 SwFrame *pTmp = GetUpper();
2351 SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*pTmp);
2352 if ( bBrowseMode )
2353 nDeadLine += pTmp->Grow( LONG_MAX, true );
2354 bool bFits = aRectFnSet.BottomDist(getFrameArea(), nDeadLine) > 0;
2355 if (!bFits && aRectFnSet.GetHeight(GetFollow()->getFrameArea()) == 0)
2356 // The follow should move backwards, so allow the case
2357 // when the upper has no space, but the follow is
2358 // empty.
2359 bFits = aRectFnSet.BottomDist(getFrameArea(), nDeadLine) >= 0;
2361 if (bFits)
2363 // The follow table's wants to move backwards, see if the first row has a
2364 // split fly anchored in it that would have more space than what we have:
2365 SwRowFrame* pRow = GetFollow()->GetFirstNonHeadlineRow();
2366 if (pRow)
2368 SwPageFrame* pPage = GetFollow()->FindPageFrame();
2369 SwSortedObjs* pPageObjs = pPage->GetSortedObjs();
2370 if (pPageObjs)
2372 bool bSplitFly = false;
2373 for (size_t i = 0; i < pPageObjs->size(); ++i)
2375 SwAnchoredObject* pAnchoredObj = (*pPage->GetSortedObjs())[i];
2376 auto pFly = pAnchoredObj->DynCastFlyFrame();
2377 if (!pFly || !pFly->IsFlySplitAllowed())
2379 continue;
2382 SwFrame* pFlyAnchor = pFly->FindAnchorCharFrame();
2383 if (!pFlyAnchor || !pRow->IsAnLower(pFlyAnchor))
2385 continue;
2388 bSplitFly = true;
2389 break;
2391 SwTwips nFollowFirstRowHeight = aRectFnSet.GetHeight(pRow->getFrameArea());
2392 SwTwips nSpace = aRectFnSet.BottomDist(getFrameArea(), nDeadLine);
2393 if (bSplitFly && nFollowFirstRowHeight > 0 && nSpace < nFollowFirstRowHeight)
2395 // The row has at least one split fly and the row would not fit
2396 // to our remaining space, when also taking flys into account,
2397 // so that's not a fit.
2398 bFits = false;
2404 if (bFits)
2406 // First, we remove an existing follow flow line.
2407 if ( HasFollowFlowLine() )
2409 SwFrame* pLastLine = GetLastLower();
2410 RemoveFollowFlowLine();
2411 // invalidate and rebuild last row
2412 if ( pLastLine )
2414 ::SwInvalidateAll( pLastLine, LONG_MAX );
2415 SetRebuildLastLine( true );
2416 lcl_RecalcRow(*static_cast<SwRowFrame*>(pLastLine), LONG_MAX);
2417 SetRebuildLastLine( false );
2420 SwFrame* pRow = GetFollow()->GetFirstNonHeadlineRow();
2422 if ( !pRow || !pRow->GetNext() )
2423 // The follow became empty and hence useless
2424 Join();
2426 continue;
2429 // If there is no follow flow line, we move the first
2430 // row in the follow table to the master table.
2431 SwRowFrame *pRow = GetFollow()->GetFirstNonHeadlineRow();
2433 // The follow became empty and hence useless
2434 if ( !pRow )
2436 Join();
2437 continue;
2440 const SwTwips nOld = aRectFnSet.GetHeight(getFrameArea());
2441 tools::Long nRowsToMove = lcl_GetMaximumLayoutRowSpan( *pRow );
2442 SwFrame* pRowToMove = pRow;
2444 while ( pRowToMove && nRowsToMove-- > 0 )
2446 const bool bMoveFootnotes = bFootnotesInDoc && !GetFollow()->IsJoinLocked();
2448 SwFootnoteBossFrame *pOldBoss = nullptr;
2449 if ( bMoveFootnotes )
2450 pOldBoss = pRowToMove->FindFootnoteBossFrame( true );
2452 SwFrame* pNextRow = pRowToMove->GetNext();
2454 if ( !pNextRow )
2456 // The follow became empty and hence useless
2457 Join();
2459 else
2461 pRowToMove->Cut();
2462 pRowToMove->Paste( this );
2465 // Move the footnotes!
2466 if ( bMoveFootnotes )
2467 if ( static_cast<SwLayoutFrame*>(pRowToMove)->MoveLowerFootnotes( nullptr, pOldBoss, FindFootnoteBossFrame( true ), true ) )
2468 GetUpper()->Calc(pRenderContext);
2470 pRowToMove = pNextRow;
2473 if ( nOld != aRectFnSet.GetHeight(getFrameArea()) )
2474 lcl_RecalcTable( *this, static_cast<SwLayoutFrame*>(pRow), aNotify );
2476 continue;
2480 else if ( KEEPTAB )
2482 bool bFormat = false;
2483 if ( bKeep )
2484 bFormat = true;
2485 else if ( bTableRowKeep && !bLastRowMoveNoMoreTries )
2487 // We only want to give the last row one chance to move
2488 // to the follow table. Set the flag as early as possible:
2489 bLastRowMoveNoMoreTries = true;
2491 // The last line of the table has to be cut off if:
2492 // 1. The table does not want to keep with its next
2493 // 2. The compatibility option is set and the table is allowed to split
2494 // 3. We did not already cut off the last row
2495 // 4. There is not break after attribute set at the table
2496 // 5. There is no break before attribute set behind the table
2497 // 6. There is no section change behind the table (see IsKeep)
2498 // 7. The last table row wants to keep with its next.
2499 const SwRowFrame* pLastRow = static_cast<const SwRowFrame*>(GetLastLower());
2500 if (pLastRow)
2502 if (!oAccess)
2504 oAccess.emplace(SwFrame::GetCache(), this);
2505 pAttrs = oAccess->Get();
2507 if (IsKeep(pAttrs->GetAttrSet().GetKeep(), GetBreakItem(), true)
2508 && pLastRow->ShouldRowKeepWithNext())
2510 bFormat = true;
2515 if ( bFormat )
2517 oAccess.reset();
2519 // Consider case that table is inside another table, because
2520 // it has to be avoided, that superior table is formatted.
2521 // Thus, find next content, table or section and, if a section
2522 // is found, get its first content.
2523 const SwFrame* pTmpNxt = sw_FormatNextContentForKeep( this );
2525 // The last row wants to keep with the frame behind the table.
2526 // Check if the next frame is on a different page and valid.
2527 // In this case we do a magic trick:
2528 if ( !bKeep && !GetNext() && pTmpNxt && pTmpNxt->isFrameAreaDefinitionValid() )
2530 setFrameAreaPositionValid(false);
2531 bLastRowHasToMoveToFollow = true;
2536 if ( isFrameAreaDefinitionValid() )
2538 if (m_bCalcLowers)
2540 lcl_RecalcTable( *this, nullptr, aNotify );
2541 m_bLowersFormatted = true;
2542 m_bCalcLowers = false;
2544 else if (m_bONECalcLowers)
2546 // tdf#147526 is a case of a macro which results in a null Lower() result
2547 if (SwRowFrame* pLower = static_cast<SwRowFrame*>(Lower()))
2548 lcl_RecalcRow(*pLower, LONG_MAX);
2549 m_bONECalcLowers = false;
2552 continue;
2555 // I don't fit in the upper Frame anymore, therefore it's the
2556 // right moment to do some preferably constructive changes.
2558 // If I'm NOT allowed to leave the upper Frame, I've got a problem.
2559 // Following Arthur Dent, we do the only thing that you can do with
2560 // an unsolvable problem: We ignore it with all our power.
2561 if ( !bMoveable )
2563 if (m_bCalcLowers && isFrameAreaDefinitionValid())
2565 lcl_RecalcTable( *this, nullptr, aNotify );
2566 m_bLowersFormatted = true;
2567 m_bCalcLowers = false;
2569 else if (m_bONECalcLowers)
2571 lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), LONG_MAX);
2572 m_bONECalcLowers = false;
2575 // It does not make sense to cut off the last line if we are
2576 // not moveable:
2577 bLastRowHasToMoveToFollow = false;
2579 continue;
2582 if (m_bCalcLowers && isFrameAreaDefinitionValid())
2584 lcl_RecalcTable( *this, nullptr, aNotify );
2585 m_bLowersFormatted = true;
2586 m_bCalcLowers = false;
2587 if( !isFrameAreaDefinitionValid() )
2588 continue;
2591 // First try to split the table. Condition:
2592 // 1. We have at least one non headline row
2593 // 2. If this row wants to keep, we need an additional row
2594 // 3. The table is allowed to split or we do not have a pIndPrev:
2595 SwFrame* pIndPrev = GetIndPrev();
2597 SwFlyFrame* pFly = FindFlyFrame();
2598 if (!pIndPrev && pFly && pFly->IsFlySplitAllowed())
2600 auto pFlyAtContent = static_cast<SwFlyAtContentFrame*>(pFly);
2601 SwFrame* pAnchor = pFlyAtContent->FindAnchorCharFrame();
2602 if (pAnchor)
2604 // If the anchor of the split has a previous frame, we're allowed to move forward.
2605 pIndPrev = pAnchor->GetIndPrev();
2609 const SwRowFrame* pFirstNonHeadlineRow = GetFirstNonHeadlineRow();
2610 // #i120016# if this row wants to keep, allow split in case that all rows want to keep with next,
2611 // the table can not move forward as it is the first one and a split is in general allowed.
2612 const bool bAllowSplitOfRow = bTableRowKeep && !pIndPrev && AreAllRowsKeepWithNext(pFirstNonHeadlineRow);
2613 // tdf91083 MSCompat: this extends bAllowSplitOfRow (and perhaps should just replace it).
2614 // If the kept-together items cannot move to a new page, a table split is in general allowed.
2615 const bool bEmulateTableKeepSplitAllowed = bEmulateTableKeep && !IsKeepFwdMoveAllowed(/*IgnoreMyOwnKeepValue=*/true);
2617 if ( pFirstNonHeadlineRow && nUnSplitted > 0 &&
2618 ( bEmulateTableKeepSplitAllowed || bAllowSplitOfRow ||
2619 ( ( !bTableRowKeep || pFirstNonHeadlineRow->GetNext() ||
2620 !pFirstNonHeadlineRow->ShouldRowKeepWithNext()
2621 ) && ( !bDontSplit || !pIndPrev )
2622 ) ) )
2624 // #i29438#
2625 // Special DoNotSplit cases:
2626 // We better avoid splitting of a row frame if we are inside a columned
2627 // section which has a height of 0, because this is not growable and thus
2628 // all kinds of unexpected things could happen.
2629 if ( IsInSct() && FindSctFrame()->Lower()->IsColumnFrame() &&
2630 0 == aRectFnSet.GetHeight(GetUpper()->getFrameArea())
2633 bTryToSplit = false;
2636 // 1. Try: bTryToSplit = true => Try to split the row.
2637 // 2. Try: bTryToSplit = false => Split the table between the rows.
2638 if ( pFirstNonHeadlineRow->GetNext() || bTryToSplit )
2640 SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
2641 bool bFlySplit = false;
2642 if (GetUpper()->IsFlyFrame())
2644 // See if this is a split fly that can also grow.
2645 auto pUpperFly = static_cast<SwFlyFrame*>(GetUpper());
2646 bFlySplit = pUpperFly->IsFlySplitAllowed();
2648 if (bFlySplit && bTryToSplit)
2650 // This is a split fly that wants to split the row itself. See if it's also
2651 // nested. If so, we'll want to know if the row split has rowspans.
2652 SwTextFrame* pAnchorCharFrame = pUpperFly->FindAnchorCharFrame();
2653 if (pAnchorCharFrame && pAnchorCharFrame->IsInFly())
2655 // Find the row we'll split.
2656 SwTwips nRemaining
2657 = aRectFnSet.YDiff(nDeadLine, aRectFnSet.GetTop(getFrameArea()));
2658 nRemaining -= aRectFnSet.GetTopMargin(*this);
2659 const SwFrame* pRow = Lower();
2660 for (; pRow->GetNext(); pRow = pRow->GetNext())
2662 if (nRemaining < aRectFnSet.GetHeight(pRow->getFrameArea()))
2664 break;
2667 nRemaining -= aRectFnSet.GetHeight(pRow->getFrameArea());
2669 // See if any cells have rowspans.
2670 for (const SwFrame* pLower = pRow->GetLower(); pLower;
2671 pLower = pLower->GetNext())
2673 auto pCellFrame = static_cast<const SwCellFrame*>(pLower);
2674 if (pCellFrame->GetTabBox()->getRowSpan() != 1)
2676 // The cell has a rowspan, don't split the row itself in this
2677 // case (but just move it forward, i.e. split between the rows).
2678 bTryToSplit = false;
2679 break;
2685 if( IsInSct() || GetUpper()->IsInTab() || bFlySplit )
2686 nDeadLine = aRectFnSet.YInc( nDeadLine,
2687 GetUpper()->Grow( LONG_MAX, true ) );
2690 SwFrameDeleteGuard g(Lower()); // tdf#134965 prevent RemoveFollowFlowLine()
2691 SetInRecalcLowerRow( true );
2692 ::lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), nDeadLine);
2693 SetInRecalcLowerRow( false );
2695 m_bLowersFormatted = true;
2696 aNotify.SetLowersComplete( true );
2698 // One more check if it's really necessary to split the table.
2699 // 1. The table either has to exceed the deadline or
2700 // 2. We explicitly want to cut off the last row.
2701 if( aRectFnSet.BottomDist( getFrameArea(), nDeadLine ) > 0 && !bLastRowHasToMoveToFollow )
2703 continue;
2706 // Set to false again as early as possible.
2707 bLastRowHasToMoveToFollow = false;
2709 // #i52781#
2710 // YaSC - Yet another special case:
2711 // If our upper is inside a table cell which is not allowed
2712 // to split, we do not try to split:
2713 if ( GetUpper()->IsInTab() )
2715 const SwFrame* pTmpRow = GetUpper();
2716 while ( pTmpRow && !pTmpRow->IsRowFrame() )
2717 pTmpRow = pTmpRow->GetUpper();
2718 if ( pTmpRow && !static_cast<const SwRowFrame*>(pTmpRow)->IsRowSplitAllowed() )
2719 continue;
2722 sal_uInt16 nMinNumOfLines = nRepeat;
2724 if ( bTableRowKeep )
2726 const SwRowFrame* pTmpRow = GetFirstNonHeadlineRow();
2727 while ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() )
2729 ++nMinNumOfLines;
2730 pTmpRow = static_cast<const SwRowFrame*>(pTmpRow->GetNext());
2734 if ( !bTryToSplit )
2735 ++nMinNumOfLines;
2737 const SwTwips nBreakLine = aRectFnSet.YInc(
2738 aRectFnSet.GetTop(getFrameArea()),
2739 aRectFnSet.GetTopMargin(*this) +
2740 lcl_GetHeightOfRows( GetLower(), nMinNumOfLines ) );
2742 // Some more checks if we want to call the split algorithm or not:
2743 // The repeating lines / keeping lines still fit into the upper or
2744 // if we do not have an (in)direct Prev, we split anyway.
2745 if( aRectFnSet.YDiff(nDeadLine, nBreakLine) >=0
2746 || !pIndPrev || bEmulateTableKeepSplitAllowed )
2748 aNotify.SetLowersComplete( false );
2749 bSplit = true;
2751 // An existing follow flow line has to be removed.
2752 if ( HasFollowFlowLine() )
2754 if (!nThrowAwayValidLayoutLimit)
2755 continue;
2756 const bool bInitialLoopEndCondition(isFrameAreaDefinitionValid());
2757 RemoveFollowFlowLine();
2758 const bool bFinalLoopEndCondition(isFrameAreaDefinitionValid());
2760 if (bInitialLoopEndCondition && !bFinalLoopEndCondition)
2762 --nThrowAwayValidLayoutLimit;
2766 oAccess.reset();
2767 bool isFootnoteGrowth(false);
2768 const bool bSplitError = !Split(nDeadLine, bTryToSplit,
2769 (bTableRowKeep && !(bAllowSplitOfRow || bEmulateTableKeepSplitAllowed)),
2770 isFootnoteGrowth);
2772 // tdf#130639 don't start table on a new page after the fallback "switch off repeating header"
2773 if (bSplitError && nRepeat > GetTable()->GetRowsToRepeat())
2775 setFrameAreaPositionValid(false);
2776 break;
2779 if (!bTryToSplit && !bSplitError)
2781 --nUnSplitted;
2784 // #i29771# Two tries to split the table
2785 // If an error occurred during splitting. We start a second
2786 // try, this time without splitting of table rows.
2787 if ( bSplitError && HasFollowFlowLine() )
2788 RemoveFollowFlowLine();
2790 // If splitting the table was successful or not,
2791 // we do not want to have 'empty' follow tables.
2792 if ( GetFollow() && !GetFollow()->GetFirstNonHeadlineRow() )
2793 Join();
2795 // We want to restore the situation before the failed
2796 // split operation as good as possible. Therefore we
2797 // do some more calculations. Note: Restricting this
2798 // to nDeadLine may not be enough.
2799 if ( bSplitError && bTryToSplit ) // no restart if we did not try to split: i72847, i79426
2801 lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), LONG_MAX);
2802 setFrameAreaPositionValid(false);
2803 // tdf#156724 if the table added footnotes, try to split *again*
2804 if (!isFootnoteGrowth)
2806 bTryToSplit = false;
2808 continue;
2811 bTryToSplit = !bSplitError;
2813 //To avoid oscillations the Follow must become valid now
2814 if ( GetFollow() )
2816 // #i80924#
2817 // After a successful split assure that the first row
2818 // is invalid. When graphics are present, this isn't hold.
2819 // Note: defect i80924 could also be fixed, if it is
2820 // assured, that <SwLayNotify::bLowersComplete> is only
2821 // set, if all lower are valid *and* are correct laid out.
2822 if ( !bSplitError && GetFollow()->GetLower() )
2824 GetFollow()->GetLower()->InvalidatePos();
2826 SwRectFnSet fnRectX(GetFollow());
2828 static sal_uInt8 nStack = 0;
2829 if ( !StackHack::IsLocked() && nStack < 4 )
2831 ++nStack;
2832 StackHack aHack;
2833 oAccess.reset();
2835 GetFollow()->MakeAll(pRenderContext);
2837 GetFollow()->SetLowersFormatted(false);
2838 // #i43913# - lock follow table
2839 // to avoid its formatting during the format of
2840 // its content.
2841 const bool bOldJoinLock = GetFollow()->IsJoinLocked();
2842 GetFollow()->LockJoin();
2843 ::lcl_RecalcRow(*static_cast<SwRowFrame*>(GetFollow()->Lower()),
2844 fnRectX.GetBottom(GetFollow()->GetUpper()->getFrameArea()) );
2845 // #i43913#
2846 // #i63632# Do not unlock the
2847 // follow if it wasn't locked before.
2848 if ( !bOldJoinLock )
2849 GetFollow()->UnlockJoin();
2851 if ( !GetFollow()->GetFollow() )
2853 SwFrame* pNxt = static_cast<SwFrame*>(GetFollow())->FindNext();
2854 if ( pNxt )
2856 // #i18103# - no formatting of found next
2857 // frame, if it's a follow section of the
2858 // 'ColLocked' section, the follow table is
2859 // in.
2860 bool bCalcNxt = true;
2861 if ( GetFollow()->IsInSct() && pNxt->IsSctFrame() )
2863 SwSectionFrame* pSct = GetFollow()->FindSctFrame();
2864 if ( pSct->IsColLocked() &&
2865 pSct->GetFollow() == pNxt )
2867 bCalcNxt = false;
2870 if ( bCalcNxt )
2872 // tdf#119109 follow was just formatted,
2873 // don't do it again now
2874 FlowFrameJoinLockGuard g(GetFollow());
2875 pNxt->Calc(pRenderContext);
2880 --nStack;
2882 else if ( GetFollow() == GetNext() )
2883 GetFollow()->MoveFwd( true, false );
2885 continue;
2890 // Set to false again as early as possible.
2891 bLastRowHasToMoveToFollow = false;
2893 if( IsInSct() && bMovedFwd && bMakePage && GetUpper()->IsColBodyFrame() &&
2894 GetUpper()->GetUpper()->GetUpper()->IsSctFrame() &&
2895 ( GetUpper()->GetUpper()->GetPrev() || GetIndPrev() ) &&
2896 static_cast<SwSectionFrame*>(GetUpper()->GetUpper()->GetUpper())->MoveAllowed(this) )
2898 bMovedFwd = false;
2901 // #i29771# Reset bTryToSplit flag on change of upper
2902 const SwFrame* pOldUpper = GetUpper();
2904 //Let's see if we find some place anywhere...
2905 if (!bMovedFwd)
2907 bool bMoveAlways = false;
2908 SwFrame* pUpper = GetUpper();
2909 if (pUpper && pUpper->IsFlyFrame())
2911 auto pFlyFrame = static_cast<SwFlyFrame*>(pUpper);
2912 if (pFlyFrame->IsFlySplitAllowed())
2914 // If the anchor of the split has a previous frame, MoveFwd() is allowed to move
2915 // forward.
2916 bMoveAlways = true;
2919 // don't make the effort to move fwd if its known
2920 // conditions that are known not to work
2921 if (IsInFootnote() && ForbiddenForFootnoteCntFwd())
2922 bMakePage = false;
2923 else if (!MoveFwd(bMakePage, false, bMoveAlways))
2924 bMakePage = false;
2927 // #i29771# Reset bSplitError flag on change of upper
2928 if ( GetUpper() != pOldUpper )
2930 bTryToSplit = true;
2931 nUnSplitted = 5;
2934 aRectFnSet.Refresh(this);
2935 m_bCalcLowers = true;
2936 bMovedFwd = true;
2937 aNotify.SetLowersComplete( false );
2938 if ( IsFollow() )
2940 // To avoid oscillations, master should not remain invalid
2941 SwTabFrame *pTab = FindMaster();
2942 if ( pTab->GetUpper() )
2943 pTab->GetUpper()->Calc(pRenderContext);
2944 pTab->Calc(pRenderContext);
2945 pTab->SetLowersFormatted( false );
2948 //If my neighbour is my Follow at the same time, I'll swallow it up.
2949 if ( ( GetNext() && GetNext() == GetFollow() ) || !GetLower() )
2951 if ( HasFollowFlowLine() )
2952 RemoveFollowFlowLine();
2953 if ( GetFollow() )
2954 Join();
2956 else if (!GetNext() && !HasFollowFlowLine() && GetFollow()
2957 && (getFrameArea().Bottom() + GetFollow()->getFrameArea().Height())
2958 < GetUpper()->getFrameArea().Bottom())
2960 // We're the last lower of the upper, no split row and we have a follow. That follow
2961 // fits our upper, still. Prefer joining that follow in the next iteration, instead of
2962 // trying to split the current table.
2963 bSplit = false;
2966 if ( bMovedBwd && GetUpper() )
2968 //During flowing back the upper was animated to do a full repaint,
2969 //we can now skip this after the whole flowing back and forth.
2970 GetUpper()->ResetCompletePaint();
2973 if (m_bCalcLowers && isFrameAreaDefinitionValid())
2975 // #i44910# - format of lower frames unnecessary
2976 // and can cause layout loops, if table doesn't fit and isn't
2977 // allowed to split.
2978 SwTwips nDistToUpperPrtBottom =
2979 aRectFnSet.BottomDist( getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
2981 if (GetUpper()->IsFlyFrame())
2983 SwFlyFrame* pFlyFrame = GetUpper()->FindFlyFrame();
2984 if (pFlyFrame->IsFlySplitAllowed())
2986 SwTextFrame* pAnchor = pFlyFrame->FindAnchorCharFrame();
2987 if (pAnchor && pAnchor->HasFollow())
2989 // The split fly's anchor has a follow frame, we can move there & try to
2990 // split again.
2991 bTryToSplit = true;
2996 if ( nDistToUpperPrtBottom >= 0 || bTryToSplit )
2998 lcl_RecalcTable( *this, nullptr, aNotify );
2999 m_bLowersFormatted = true;
3000 m_bCalcLowers = false;
3001 if (!isFramePrintAreaValid())
3002 m_pTable->SetRowsToRepeat(1);
3004 #if OSL_DEBUG_LEVEL > 0
3005 else
3007 OSL_FAIL( "debug assertion: <SwTabFrame::MakeAll()> - format of table lowers suppressed by fix i44910" );
3009 #endif
3012 } //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
3014 //If my direct predecessor is my master now, it can destroy me during the
3015 //next best opportunity.
3016 if ( IsFollow() )
3018 SwFrame *pPre = GetPrev();
3019 if ( pPre && pPre->IsTabFrame() && static_cast<SwTabFrame*>(pPre)->GetFollow() == this)
3020 pPre->InvalidatePos();
3023 m_bCalcLowers = m_bONECalcLowers = false;
3024 oAccess.reset();
3025 UnlockJoin();
3026 if ( bMovedFwd || bMovedBwd || !bOldValidPos )
3027 aNotify.SetInvaKeep();
3030 static bool IsNextOnSamePage(SwPageFrame const& rPage,
3031 SwTabFrame const& rTabFrame, SwTextFrame const& rAnchorFrame)
3033 for (SwContentFrame const* pContentFrame = rTabFrame.FindNextCnt();
3034 pContentFrame && pContentFrame->FindPageFrame() == &rPage;
3035 pContentFrame = pContentFrame->FindNextCnt())
3037 if (pContentFrame == &rAnchorFrame)
3039 return true;
3042 return false;
3045 /// Calculate the offsets arising because of FlyFrames
3046 bool SwTabFrame::CalcFlyOffsets( SwTwips& rUpper,
3047 tools::Long& rLeftOffset,
3048 tools::Long& rRightOffset,
3049 SwTwips *const pSpaceBelowBottom) const
3051 bool bInvalidatePrtArea = false;
3052 const SwPageFrame *pPage = FindPageFrame();
3053 const SwFlyFrame* pMyFly = FindFlyFrame();
3055 // --> #108724# Page header/footer content doesn't have to wrap around
3056 // floating screen objects
3058 const IDocumentSettingAccess& rIDSA = GetFormat()->getIDocumentSettingAccess();
3059 const bool bWrapAllowed = rIDSA.get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) ||
3060 ( !IsInFootnote() && nullptr == FindFooterOrHeader() );
3062 if (!bWrapAllowed || !pPage->GetSortedObjs())
3063 return bInvalidatePrtArea;
3065 SwRectFnSet aRectFnSet(this);
3066 const bool bConsiderWrapOnObjPos
3067 = rIDSA.get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION);
3068 tools::Long nPrtPos = aRectFnSet.GetTop(getFrameArea());
3069 nPrtPos = aRectFnSet.YInc(nPrtPos, rUpper);
3070 SwRect aRect(getFrameArea());
3071 if (pSpaceBelowBottom)
3073 // set to space below table frame
3074 aRectFnSet.SetTopAndHeight(aRect, aRectFnSet.GetBottom(aRect), *pSpaceBelowBottom);
3076 else
3078 tools::Long nYDiff = aRectFnSet.YDiff(aRectFnSet.GetTop(getFramePrintArea()), rUpper);
3079 if (nYDiff > 0)
3080 aRectFnSet.AddBottom(aRect, -nYDiff);
3083 bool bAddVerticalFlyOffsets = rIDSA.get(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS);
3085 for (size_t i = 0; i < pPage->GetSortedObjs()->size(); ++i)
3087 SwAnchoredObject* pAnchoredObj = (*pPage->GetSortedObjs())[i];
3088 auto pFly = pAnchoredObj->DynCastFlyFrame();
3089 if (!pFly)
3090 continue;
3092 const SwRect aFlyRect = pFly->GetObjRectWithSpaces();
3093 // #i26945# - correction of conditions,
3094 // if Writer fly frame has to be considered:
3095 // - no need to check, if top of Writer fly frame differs
3096 // from FAR_AWAY, because it's also checked, if the Writer
3097 // fly frame rectangle overlaps with <aRect>
3098 // - no check, if bottom of anchor frame is prior the top of
3099 // the table, because Writer fly frames can be negative positioned.
3100 // - correct check, if the Writer fly frame is a lower of the
3101 // table, because table lines/rows can split and an at-character
3102 // anchored Writer fly frame could be positioned in the follow
3103 // flow line.
3104 // - add condition, that an existing anchor character text frame
3105 // has to be on the same page as the table.
3106 // E.g., it could happen, that the fly frame is still registered
3107 // at the page frame, the table is on, but it's anchor character
3108 // text frame has already changed its page.
3109 const SwTextFrame* pAnchorCharFrame = pFly->FindAnchorCharFrame();
3110 const SwFormatHoriOrient& rHori= pFly->GetFormat()->GetHoriOrient();
3111 // TODO: why not just ignore HoriOrient?
3112 bool isHoriOrientShiftDown =
3113 rHori.GetHoriOrient() == text::HoriOrientation::NONE
3114 || rHori.GetHoriOrient() == text::HoriOrientation::LEFT;
3115 // Only consider invalid Writer fly frames if they'll be shifted down.
3116 bool bIgnoreFlyValidity = bAddVerticalFlyOffsets && isHoriOrientShiftDown;
3117 bool bConsiderFly =
3118 // #i46807# - do not consider invalid
3119 // Writer fly frames.
3120 (pFly->isFrameAreaDefinitionValid() || bIgnoreFlyValidity)
3121 // fly anchored at character or at paragraph
3122 && pFly->IsFlyAtContentFrame()
3123 // fly overlaps with corresponding table rectangle
3124 && aFlyRect.Overlaps(aRect)
3125 // fly isn't lower of table and
3126 // anchor character frame of fly isn't lower of table
3127 && (pSpaceBelowBottom // not if in ShouldBwdMoved
3128 || (!IsAnLower(pFly) && (!pAnchorCharFrame || !IsAnLower(pAnchorCharFrame))))
3129 // table isn't lower of fly
3130 && !pFly->IsAnLower(this)
3131 // fly is lower of fly, the table is in
3132 // #123274# - correction
3133 // assure that fly isn't a lower of a fly, the table isn't in.
3134 // E.g., a table in the body doesn't wrap around a graphic,
3135 // which is inside a frame.
3136 && (!pMyFly || pMyFly->IsAnLower(pFly))
3137 && pMyFly == pFly->GetAnchorFrameContainingAnchPos()->FindFlyFrame()
3138 // anchor frame not on following page
3139 && pPage->GetPhyPageNum() >= pFly->GetAnchorFrame()->FindPageFrame()->GetPhyPageNum()
3140 // anchor character text frame on same page
3141 && (!pAnchorCharFrame ||
3142 pAnchorCharFrame->FindPageFrame()->GetPhyPageNum() == pPage->GetPhyPageNum());
3144 if (!bConsiderFly)
3145 continue;
3147 const SwFrame* pFlyHeaderFooterFrame = pFly->GetAnchorFrame()->FindFooterOrHeader();
3148 const SwFrame* pThisHeaderFooterFrame = FindFooterOrHeader();
3149 if (pFlyHeaderFooterFrame != pThisHeaderFooterFrame
3150 // #148493# If bConsiderWrapOnObjPos is set,
3151 // we want to consider the fly if it is located in the header and
3152 // the table is located in the body:
3153 && (!bConsiderWrapOnObjPos || nullptr != pThisHeaderFooterFrame
3154 || !pFlyHeaderFooterFrame->IsHeaderFrame()))
3156 continue;
3159 text::WrapTextMode nSurround = pFly->GetFormat()->GetSurround().GetSurround();
3160 // If the frame format is a TextBox of a draw shape,
3161 // then use the surround of the original shape.
3162 bool bWrapThrough = nSurround == text::WrapTextMode_THROUGH;
3163 SwTextBoxHelper::getShapeWrapThrough(pFly->GetFormat(), bWrapThrough);
3164 if (bWrapThrough)
3165 continue;
3166 if (!bWrapThrough && nSurround == text::WrapTextMode_THROUGH)
3167 nSurround = text::WrapTextMode_PARALLEL;
3169 bool bShiftDown = css::text::WrapTextMode_NONE == nSurround;
3170 if (!bShiftDown && bAddVerticalFlyOffsets)
3172 if (nSurround == text::WrapTextMode_PARALLEL && isHoriOrientShiftDown)
3174 // We know that wrapping was requested and the table frame overlaps with
3175 // the fly frame. Check if the print area overlaps with the fly frame as
3176 // well (in case the table does not use all the available width).
3177 basegfx::B1DRange aTabRange(
3178 aRectFnSet.GetLeft(aRect) + aRectFnSet.GetLeft(getFramePrintArea()),
3179 aRectFnSet.GetLeft(aRect) + aRectFnSet.GetLeft(getFramePrintArea())
3180 + aRectFnSet.GetWidth(getFramePrintArea()));
3182 // Ignore spacing when determining the left/right edge of the fly, like
3183 // Word does.
3184 const SwRect aFlyRectWithoutSpaces = pFly->GetObjRect();
3185 basegfx::B1DRange aFlyRange(aRectFnSet.GetLeft(aFlyRectWithoutSpaces),
3186 aRectFnSet.GetRight(aFlyRectWithoutSpaces));
3188 // If it does, shift the table down. Do this only in the compat case,
3189 // normally an SwFlyPortion is created instead that increases the height
3190 // of the first table row.
3191 bShiftDown = aTabRange.overlaps(aFlyRange);
3195 if (bShiftDown)
3197 // possible cases:
3198 // both in body
3199 // both in same fly
3200 // any comb. of body, footnote, header/footer
3201 // to keep it safe, check only in doc body vs page margin for now
3202 tools::Long nBottom = aRectFnSet.GetBottom(aFlyRect);
3203 // tdf#138039 don't grow beyond the page body
3204 // if the fly is anchored below the table; the fly
3205 // must move with its anchor frame to the next page
3206 SwRectFnSet fnPage(pPage);
3207 if (!IsInDocBody() // TODO
3208 || fnPage.YDiff(fnPage.GetBottom(aFlyRect), fnPage.GetPrtBottom(*pPage)) <= 0
3209 || !IsNextOnSamePage(
3210 *pPage, *this,
3211 *static_cast<SwTextFrame*>(pFly->GetAnchorFrameContainingAnchPos())))
3213 if (aRectFnSet.YDiff(nPrtPos, nBottom) < 0)
3214 nPrtPos = nBottom;
3215 // tdf#116501 subtract flys blocking space from below
3216 // TODO this may not work ideally for multiple flys
3217 if (pSpaceBelowBottom && aRectFnSet.YDiff(aRectFnSet.GetBottom(aRect), nBottom) < 0)
3219 if (aRectFnSet.YDiff(aRectFnSet.GetTop(aRect), aRectFnSet.GetTop(aFlyRect)) < 0)
3221 aRectFnSet.SetBottom(aRect, aRectFnSet.GetTop(aFlyRect));
3223 else
3225 aRectFnSet.SetHeight(aRect, 0);
3228 bInvalidatePrtArea = true;
3232 if ((css::text::WrapTextMode_RIGHT == nSurround
3233 || css::text::WrapTextMode_PARALLEL == nSurround)
3234 && text::HoriOrientation::LEFT == rHori.GetHoriOrient()
3235 && !bShiftDown)
3237 const tools::Long nWidth
3238 = aRectFnSet.XDiff(aRectFnSet.GetRight(aFlyRect),
3239 aRectFnSet.GetLeft(pFly->GetAnchorFrame()->getFrameArea()));
3240 rLeftOffset = std::max(rLeftOffset, nWidth);
3241 bInvalidatePrtArea = true;
3243 if ((css::text::WrapTextMode_LEFT == nSurround
3244 || css::text::WrapTextMode_PARALLEL == nSurround)
3245 && text::HoriOrientation::RIGHT == rHori.GetHoriOrient())
3247 const tools::Long nWidth
3248 = aRectFnSet.XDiff(aRectFnSet.GetRight(pFly->GetAnchorFrame()->getFrameArea()),
3249 aRectFnSet.GetLeft(aFlyRect));
3250 rRightOffset = std::max(rRightOffset, nWidth);
3251 bInvalidatePrtArea = true;
3254 rUpper = aRectFnSet.YDiff( nPrtPos, aRectFnSet.GetTop(getFrameArea()) );
3255 if (pSpaceBelowBottom)
3257 *pSpaceBelowBottom = aRectFnSet.GetHeight(aRect);
3260 return bInvalidatePrtArea;
3263 /// "Formats" the frame; Frame and PrtArea.
3264 /// The fixed size is not adjusted here.
3265 void SwTabFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
3267 OSL_ENSURE( pAttrs, "TabFrame::Format, pAttrs is 0." );
3269 SwRectFnSet aRectFnSet(this);
3270 if ( !isFrameAreaSizeValid() )
3272 tools::Long nDiff = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()) -
3273 aRectFnSet.GetWidth(getFrameArea());
3274 if( nDiff )
3276 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
3277 aRectFnSet.AddRight( aFrm, nDiff );
3281 //VarSize is always the height.
3282 //For the upper/lower margins the same rules apply as for ContentFrames (see
3283 //MakePrtArea() of those).
3285 SwTwips nUpper = CalcUpperSpace( pAttrs );
3287 // We want to dodge the flys. Two possibilities:
3288 // 1. There are flys with SurroundNone, dodge them completely
3289 // 2. There are flys which only wrap on the right or the left side and
3290 // those are right or left aligned, those set the minimum for the margins
3291 tools::Long nTmpRight = -1000000,
3292 nLeftOffset = 0;
3293 if (CalcFlyOffsets(nUpper, nLeftOffset, nTmpRight, nullptr))
3295 setFramePrintAreaValid(false);
3298 tools::Long nRightOffset = std::max( tools::Long(0), nTmpRight );
3300 SwTwips nLower = pAttrs->CalcBottomLine();
3301 // #i29550#
3302 if ( IsCollapsingBorders() )
3303 nLower += GetBottomLineSize();
3305 if ( !isFramePrintAreaValid() )
3307 setFramePrintAreaValid(true);
3309 // The width of the PrintArea is given by the FrameFormat, the margins
3310 // have to be set accordingly.
3311 // Minimum margins are determined depending on borders and shadows.
3312 // The margins are set so that the PrintArea is aligned into the
3313 // Frame according to the adjustment.
3314 // If the adjustment is 0, the margins are set according to the border
3315 // attributes.
3317 const SwTwips nOldHeight = aRectFnSet.GetHeight(getFramePrintArea());
3318 const SwTwips nMax = aRectFnSet.GetWidth(getFrameArea());
3320 // OD 14.03.2003 #i9040# - adjust variable names.
3321 const SwTwips nLeftLine = pAttrs->CalcLeftLine();
3322 const SwTwips nRightLine = pAttrs->CalcRightLine();
3324 // The width possibly is a percentage value. If the table is inside
3325 // something else, the value refers to the environment. If it's in the
3326 // body then in the BrowseView the value refers to the screen width.
3327 const SwFormatFrameSize &rSz = GetFormat()->GetFrameSize();
3328 // OD 14.03.2003 #i9040# - adjust variable name.
3329 const SwTwips nWishedTableWidth = CalcRel( rSz );
3331 bool bCheckBrowseWidth = false;
3333 // OD 14.03.2003 #i9040# - insert new variables for left/right spacing.
3334 SwTwips nLeftSpacing = 0;
3335 SwTwips nRightSpacing = 0;
3336 switch ( GetFormat()->GetHoriOrient().GetHoriOrient() )
3338 case text::HoriOrientation::LEFT:
3340 // left indent:
3341 nLeftSpacing = nLeftLine + nLeftOffset;
3342 // OD 06.03.2003 #i9040# - correct calculation of right indent:
3343 // - Consider right indent given by right line attributes.
3344 // - Consider negative right indent.
3345 // wished right indent determined by wished table width and
3346 // left offset given by surround fly frames on the left:
3347 const SwTwips nWishRight = nMax - nWishedTableWidth - nLeftOffset;
3348 if ( nRightOffset > 0 )
3350 // surrounding fly frames on the right
3351 // -> right indent is maximum of given right offset
3352 // and wished right offset.
3353 nRightSpacing = nRightLine + std::max( SwTwips(nRightOffset), nWishRight );
3355 else
3357 // no surrounding fly frames on the right
3358 // If intrinsic right indent (intrinsic means not considering
3359 // determined left indent) is negative,
3360 // then hold this intrinsic indent,
3361 // otherwise non negative wished right indent is hold.
3362 nRightSpacing = nRightLine +
3363 ( ( (nWishRight+nLeftOffset) < 0 ) ?
3364 (nWishRight+nLeftOffset) :
3365 std::max( SwTwips(0), nWishRight ) );
3368 break;
3369 case text::HoriOrientation::RIGHT:
3371 // right indent:
3372 nRightSpacing = nRightLine + nRightOffset;
3373 // OD 06.03.2003 #i9040# - correct calculation of left indent:
3374 // - Consider left indent given by left line attributes.
3375 // - Consider negative left indent.
3376 // wished left indent determined by wished table width and
3377 // right offset given by surrounding fly frames on the right:
3378 const SwTwips nWishLeft = nMax - nWishedTableWidth - nRightOffset;
3379 if ( nLeftOffset > 0 )
3381 // surrounding fly frames on the left
3382 // -> right indent is maximum of given left offset
3383 // and wished left offset.
3384 nLeftSpacing = nLeftLine + std::max( SwTwips(nLeftOffset), nWishLeft );
3386 else
3388 // no surrounding fly frames on the left
3389 // If intrinsic left indent (intrinsic = not considering
3390 // determined right indent) is negative,
3391 // then hold this intrinsic indent,
3392 // otherwise non negative wished left indent is hold.
3393 nLeftSpacing = nLeftLine +
3394 ( ( (nWishLeft+nRightOffset) < 0 ) ?
3395 (nWishLeft+nRightOffset) :
3396 std::max( SwTwips(0), nWishLeft ) );
3399 break;
3400 case text::HoriOrientation::CENTER:
3402 // OD 07.03.2003 #i9040# - consider left/right line attribute.
3403 const SwTwips nCenterSpacing = ( nMax - nWishedTableWidth ) / 2;
3404 nLeftSpacing = nLeftLine +
3405 ( (nLeftOffset > 0) ?
3406 std::max( nCenterSpacing, SwTwips(nLeftOffset) ) :
3407 nCenterSpacing );
3408 nRightSpacing = nRightLine +
3409 ( (nRightOffset > 0) ?
3410 std::max( nCenterSpacing, SwTwips(nRightOffset) ) :
3411 nCenterSpacing );
3413 break;
3414 case text::HoriOrientation::FULL:
3415 //This things grows over the whole width.
3416 //Only the free space needed for the border is taken into
3417 //account. The attribute values of LRSpace are ignored
3418 //intentionally.
3419 bCheckBrowseWidth = true;
3420 nLeftSpacing = nLeftLine + nLeftOffset;
3421 nRightSpacing = nRightLine + nRightOffset;
3422 break;
3423 case text::HoriOrientation::NONE:
3425 // The margins are defined by the LRSpace attribute.
3426 nLeftSpacing = pAttrs->CalcLeft( this );
3427 if( nLeftOffset )
3429 // OD 07.03.2003 #i9040# - surround fly frames only, if
3430 // they overlap with the table.
3431 // Thus, take maximum of left spacing and left offset.
3432 // OD 10.03.2003 #i9040# - consider left line attribute.
3433 nLeftSpacing = std::max( nLeftSpacing, SwTwips( nLeftOffset + nLeftLine ) );
3435 // OD 23.01.2003 #106895# - add 1st param to <SwBorderAttrs::CalcRight(..)>
3436 nRightSpacing = pAttrs->CalcRight( this );
3437 if( nRightOffset )
3439 // OD 07.03.2003 #i9040# - surround fly frames only, if
3440 // they overlap with the table.
3441 // Thus, take maximum of right spacing and right offset.
3442 // OD 10.03.2003 #i9040# - consider right line attribute.
3443 nRightSpacing = std::max( nRightSpacing, SwTwips( nRightOffset + nRightLine ) );
3446 break;
3447 case text::HoriOrientation::LEFT_AND_WIDTH:
3449 // count left border and width (Word specialty)
3450 // OD 10.03.2003 #i9040# - no width alignment in online mode.
3451 //bCheckBrowseWidth = true;
3452 nLeftSpacing = pAttrs->CalcLeft( this );
3453 if( nLeftOffset )
3455 // OD 10.03.2003 #i9040# - surround fly frames only, if
3456 // they overlap with the table.
3457 // Thus, take maximum of right spacing and right offset.
3458 // OD 10.03.2003 #i9040# - consider left line attribute.
3459 nLeftSpacing = std::max( nLeftSpacing, SwTwips( pAttrs->CalcLeftLine() + nLeftOffset ) );
3461 // OD 10.03.2003 #i9040# - consider right and left line attribute.
3462 const SwTwips nWishRight =
3463 nMax - (nLeftSpacing-pAttrs->CalcLeftLine()) - nWishedTableWidth;
3464 nRightSpacing = nRightLine +
3465 ( (nRightOffset > 0) ?
3466 std::max( nWishRight, SwTwips(nRightOffset) ) :
3467 nWishRight );
3469 break;
3470 default:
3471 OSL_FAIL( "Invalid orientation for table." );
3474 // #i26250# - extend bottom printing area, if table
3475 // is last content inside a table cell.
3476 if ( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS) &&
3477 GetUpper()->IsInTab() && !GetIndNext() )
3479 nLower += pAttrs->GetULSpace().GetLower();
3481 aRectFnSet.SetYMargins( *this, nUpper, nLower );
3482 if( (nMax - MINLAY) < (nLeftSpacing + nRightSpacing) )
3483 aRectFnSet.SetXMargins( *this, 0, 0 );
3484 else
3485 aRectFnSet.SetXMargins( *this, nLeftSpacing, nRightSpacing );
3487 SwViewShell *pSh = getRootFrame()->GetCurrShell();
3488 if ( bCheckBrowseWidth &&
3489 pSh && pSh->GetViewOptions()->getBrowseMode() &&
3490 GetUpper()->IsPageBodyFrame() && // only PageBodyFrames and not ColBodyFrames
3491 pSh->VisArea().Width() )
3493 //Don't go beyond the edge of the visible area.
3494 //The page width can be bigger because objects with
3495 //"over-size" are possible (RootFrame::ImplCalcBrowseWidth())
3496 tools::Long nWidth = pSh->GetBrowseWidth();
3497 nWidth -= getFramePrintArea().Left();
3498 nWidth -= pAttrs->CalcRightLine();
3500 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
3501 aPrt.Width( std::min( nWidth, aPrt.Width() ) );
3504 if ( nOldHeight != aRectFnSet.GetHeight(getFramePrintArea()) )
3506 setFrameAreaSizeValid(false);
3510 if ( isFrameAreaSizeValid() )
3511 return;
3513 setFrameAreaSizeValid(true);
3515 // The size is defined by the content plus the margins.
3516 SwTwips nRemaining = 0, nDiff;
3517 SwFrame *pFrame = m_pLower;
3518 while ( pFrame )
3520 nRemaining += aRectFnSet.GetHeight(pFrame->getFrameArea());
3521 pFrame = pFrame->GetNext();
3523 // And now add the margins
3524 nRemaining += nUpper + nLower;
3526 nDiff = aRectFnSet.GetHeight(getFrameArea()) - nRemaining;
3527 if ( nDiff > 0 )
3528 Shrink( nDiff );
3529 else if ( nDiff < 0 )
3530 Grow( -nDiff );
3533 SwTwips SwTabFrame::GrowFrame( SwTwips nDist, bool bTst, bool bInfo )
3535 SwRectFnSet aRectFnSet(this);
3536 SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
3537 if( nHeight > 0 && nDist > ( LONG_MAX - nHeight ) )
3538 nDist = LONG_MAX - nHeight;
3540 if ( bTst && !IsRestrictTableGrowth() )
3541 return nDist;
3543 if ( GetUpper() )
3545 //The upper only grows as far as needed. nReal provides the distance
3546 //which is already available.
3547 SwTwips nReal = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea());
3548 SwFrame *pFrame = GetUpper()->Lower();
3549 while ( pFrame && GetFollow() != pFrame )
3551 nReal -= aRectFnSet.GetHeight(pFrame->getFrameArea());
3552 pFrame = pFrame->GetNext();
3555 if ( nReal < nDist )
3557 tools::Long nTmp = GetUpper()->Grow( nDist - std::max<tools::Long>(nReal, 0), bTst, bInfo );
3559 if ( IsRestrictTableGrowth() )
3561 nTmp = std::min( tools::Long(nDist), nReal + nTmp );
3562 nDist = nTmp < 0 ? 0 : nTmp;
3566 if ( !bTst )
3569 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
3570 aRectFnSet.AddBottom( aFrm, nDist );
3573 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
3574 SwRootFrame *pRootFrame = getRootFrame();
3575 if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
3576 pRootFrame->GetCurrShell() )
3578 SwRect aOldFrame( getFrameArea() );
3579 pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( this, aOldFrame );
3581 #endif
3585 if ( !bTst && ( nDist || IsRestrictTableGrowth() ) )
3587 SwPageFrame *pPage = FindPageFrame();
3588 if ( GetNext() )
3590 GetNext()->InvalidatePos_();
3591 if ( GetNext()->IsContentFrame() )
3592 GetNext()->InvalidatePage( pPage );
3594 // #i28701# - Due to the new object positioning the
3595 // frame on the next page/column can flow backward (e.g. it was moved
3596 // forward due to the positioning of its objects ). Thus, invalivate this
3597 // next frame, if document compatibility option 'Consider wrapping style
3598 // influence on object positioning' is ON.
3599 else if ( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
3601 InvalidateNextPos();
3603 InvalidateAll_();
3604 InvalidatePage( pPage );
3605 SetComplete();
3607 std::unique_ptr<SvxBrushItem> aBack = GetFormat()->makeBackgroundBrushItem();
3608 const SvxGraphicPosition ePos = aBack->GetGraphicPos();
3609 if ( GPOS_NONE != ePos && GPOS_TILED != ePos )
3610 SetCompletePaint();
3613 return nDist;
3615 void SwTabFrame::Invalidate(SwTabFrameInvFlags eInvFlags)
3617 if(eInvFlags == SwTabFrameInvFlags::NONE)
3618 return;
3619 SwPageFrame* pPage = FindPageFrame();
3620 InvalidatePage(pPage);
3621 if(eInvFlags & SwTabFrameInvFlags::InvalidatePrt)
3622 InvalidatePrt_();
3623 if(eInvFlags & SwTabFrameInvFlags::InvalidatePos)
3624 InvalidatePos_();
3625 SwFrame* pTmp = GetIndNext();
3626 if(nullptr != pTmp)
3628 if(eInvFlags & SwTabFrameInvFlags::InvalidateIndNextPrt)
3630 pTmp->InvalidatePrt_();
3631 if(pTmp->IsContentFrame())
3632 pTmp->InvalidatePage(pPage);
3634 if(eInvFlags & SwTabFrameInvFlags::SetIndNextCompletePaint)
3635 pTmp->SetCompletePaint();
3637 if(eInvFlags & SwTabFrameInvFlags::InvalidatePrevPrt && nullptr != (pTmp = GetPrev()))
3639 pTmp->InvalidatePrt_();
3640 if(pTmp->IsContentFrame())
3641 pTmp->InvalidatePage(pPage);
3643 if(eInvFlags & SwTabFrameInvFlags::InvalidateBrowseWidth)
3645 if(pPage && pPage->GetUpper() && !IsFollow())
3646 static_cast<SwRootFrame*>(pPage->GetUpper())->InvalidateBrowseWidth();
3648 if(eInvFlags & SwTabFrameInvFlags::InvalidateNextPos)
3649 InvalidateNextPos();
3652 void SwTabFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
3654 if(rHint.GetId() == SfxHintId::SwTableHeadingChange)
3656 HandleTableHeadlineChange();
3657 return;
3659 else if (rHint.GetId() != SfxHintId::SwLegacyModify)
3660 return;
3661 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
3662 SwTabFrameInvFlags eInvFlags = SwTabFrameInvFlags::NONE;
3663 bool bAttrSetChg = pLegacy->m_pNew && RES_ATTRSET_CHG == pLegacy->m_pNew->Which();
3665 if(bAttrSetChg)
3667 auto& rOldSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pOld);
3668 auto& rNewSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew);
3669 SfxItemIter aOIter(*rOldSetChg.GetChgSet());
3670 SfxItemIter aNIter(*rNewSetChg.GetChgSet());
3671 const SfxPoolItem* pOItem = aOIter.GetCurItem();
3672 const SfxPoolItem* pNItem = aNIter.GetCurItem();
3673 SwAttrSetChg aOldSet(rOldSetChg);
3674 SwAttrSetChg aNewSet(rNewSetChg);
3677 UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
3678 pNItem = aNIter.NextItem();
3679 pOItem = aOIter.NextItem();
3680 } while(pNItem);
3681 if(aOldSet.Count() || aNewSet.Count())
3682 SwLayoutFrame::SwClientNotify(rMod, sw::LegacyModifyHint(&aOldSet, &aNewSet));
3684 else
3685 UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
3686 Invalidate(eInvFlags);
3689 void SwTabFrame::HandleTableHeadlineChange()
3691 if(!IsFollow())
3692 return;
3693 // Delete remaining headlines:
3694 SwRowFrame* pLowerRow = nullptr;
3695 while(nullptr != (pLowerRow = static_cast<SwRowFrame*>(Lower())) && pLowerRow->IsRepeatedHeadline())
3697 pLowerRow->Cut();
3698 SwFrame::DestroyFrame(pLowerRow);
3701 // insert new headlines
3702 const sal_uInt16 nNewRepeat = GetTable()->GetRowsToRepeat();
3703 auto& rLines = GetTable()->GetTabLines();
3704 for(sal_uInt16 nIdx = 0; nIdx < nNewRepeat; ++nIdx)
3706 SwRowFrame* pHeadline = new SwRowFrame(*rLines[nIdx], this);
3708 sw::FlyCreationSuppressor aSuppressor;
3709 pHeadline->SetRepeatedHeadline(true);
3711 pHeadline->Paste(this, pLowerRow);
3713 Invalidate(SwTabFrameInvFlags::InvalidatePrt);
3716 void SwTabFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
3717 SwTabFrameInvFlags &rInvFlags,
3718 SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
3720 bool bClear = true;
3721 const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
3722 switch( nWhich )
3724 case RES_FRM_SIZE:
3725 case RES_HORI_ORIENT:
3726 rInvFlags |= SwTabFrameInvFlags::InvalidatePrt | SwTabFrameInvFlags::InvalidateBrowseWidth;
3727 break;
3729 case RES_PAGEDESC: //Attribute changes (on/off)
3730 if ( IsInDocBody() )
3732 rInvFlags |= SwTabFrameInvFlags::InvalidatePos;
3733 SwPageFrame *pPage = FindPageFrame();
3734 if (pPage)
3736 if ( !GetPrev() )
3737 CheckPageDescs( pPage );
3738 if (GetFormat()->GetPageDesc().GetNumOffset())
3739 static_cast<SwRootFrame*>(pPage->GetUpper())->SetVirtPageNum( true );
3740 GetFormat()->GetDoc()->getIDocumentFieldsAccess().UpdatePageFields(pPage->getFrameArea().Top());
3743 break;
3745 case RES_BREAK:
3746 rInvFlags |= SwTabFrameInvFlags::InvalidatePos | SwTabFrameInvFlags::InvalidateNextPos;
3747 break;
3749 case RES_LAYOUT_SPLIT:
3750 if ( !IsFollow() )
3751 rInvFlags |= SwTabFrameInvFlags::InvalidatePos;
3752 break;
3753 case RES_FRAMEDIR :
3754 SetDerivedR2L( false );
3755 CheckDirChange();
3756 break;
3757 case RES_COLLAPSING_BORDERS :
3758 rInvFlags |= SwTabFrameInvFlags::InvalidatePrt;
3759 lcl_InvalidateAllLowersPrt( this );
3760 break;
3761 case RES_UL_SPACE:
3762 rInvFlags |= SwTabFrameInvFlags::InvalidateIndNextPrt | SwTabFrameInvFlags::InvalidatePrevPrt | SwTabFrameInvFlags::SetIndNextCompletePaint;
3763 [[fallthrough]];
3765 default:
3766 bClear = false;
3768 if ( !bClear )
3769 return;
3771 if ( pOldSet || pNewSet )
3773 if ( pOldSet )
3774 pOldSet->ClearItem( nWhich );
3775 if ( pNewSet )
3776 pNewSet->ClearItem( nWhich );
3778 else
3780 SwModify aMod;
3781 SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
3785 bool SwTabFrame::GetInfo( SfxPoolItem &rHint ) const
3787 if ( RES_VIRTPAGENUM_INFO == rHint.Which() && IsInDocBody() && !IsFollow() )
3789 SwVirtPageNumInfo &rInfo = static_cast<SwVirtPageNumInfo&>(rHint);
3790 const SwPageFrame *pPage = FindPageFrame();
3791 if ( pPage )
3793 if ( pPage == rInfo.GetOrigPage() && !GetPrev() )
3795 // Should be the one (can temporarily be different, should we be
3796 // concerned about this possibility?)
3797 rInfo.SetInfo( pPage, this );
3798 return false;
3800 if ( pPage->GetPhyPageNum() < rInfo.GetOrigPage()->GetPhyPageNum() &&
3801 (!rInfo.GetPage() || pPage->GetPhyPageNum() > rInfo.GetPage()->GetPhyPageNum()))
3803 //This could be the one.
3804 rInfo.SetInfo( pPage, this );
3808 return true;
3811 SwFrame *SwTabFrame::FindLastContentOrTable()
3813 SwFrame *pRet = m_pLower;
3815 while ( pRet && !pRet->IsContentFrame() )
3817 SwFrame *pOld = pRet;
3819 SwFrame *pTmp = pRet; // To skip empty section frames
3820 while ( pRet->GetNext() )
3822 pRet = pRet->GetNext();
3823 if( !pRet->IsSctFrame() || static_cast<SwSectionFrame*>(pRet)->GetSection() )
3824 pTmp = pRet;
3826 pRet = pTmp;
3828 if ( pRet->GetLower() )
3829 pRet = pRet->GetLower();
3830 if ( pRet == pOld )
3832 // Check all other columns if there is a column based section with
3833 // an empty last column at the end of the last cell - this is done
3834 // by SwSectionFrame::FindLastContent
3835 if( pRet->IsColBodyFrame() )
3837 #if OSL_DEBUG_LEVEL > 0
3838 SwSectionFrame* pSect = pRet->FindSctFrame();
3839 OSL_ENSURE( pSect, "Where does this column come from?");
3840 OSL_ENSURE( IsAnLower( pSect ), "Split cell?" );
3841 #endif
3842 return pRet->FindSctFrame()->FindLastContent();
3845 // pRet may be a cell frame without a lower (cell has been split).
3846 // We have to find the last content the hard way:
3848 OSL_ENSURE( pRet->IsCellFrame(), "SwTabFrame::FindLastContent failed" );
3849 const SwFrame* pRow = pRet->GetUpper();
3850 while ( pRow && !pRow->GetUpper()->IsTabFrame() )
3851 pRow = pRow->GetUpper();
3852 const SwContentFrame* pContentFrame = pRow ? static_cast<const SwLayoutFrame*>(pRow)->ContainsContent() : nullptr;
3853 pRet = nullptr;
3855 while ( pContentFrame && static_cast<const SwLayoutFrame*>(pRow)->IsAnLower( pContentFrame ) )
3857 pRet = const_cast<SwContentFrame*>(pContentFrame);
3858 pContentFrame = pContentFrame->GetNextContentFrame();
3863 // #112929# There actually is a situation, which results in pRet = 0:
3864 // Insert frame, insert table via text <-> table. This gives you a frame
3865 // containing a table without any other content frames. Split the table
3866 // and undo the splitting. This operation gives us a table frame without
3867 // a lower.
3868 if ( pRet )
3870 while ( pRet->GetNext() )
3871 pRet = pRet->GetNext();
3873 if (pRet->IsSctFrame())
3874 pRet = static_cast<SwSectionFrame*>(pRet)->FindLastContent();
3877 assert(pRet == nullptr || dynamic_cast<SwContentFrame*>(pRet) || dynamic_cast<SwTabFrame*>(pRet));
3878 return pRet;
3881 SwContentFrame *SwTabFrame::FindLastContent()
3883 SwFrame * pRet(FindLastContentOrTable());
3885 while (pRet && pRet->IsTabFrame()) // possibly there's only tables here!
3886 { // tdf#126138 skip table, don't look inside
3887 pRet = pRet->GetPrev();
3890 assert(pRet == nullptr || dynamic_cast<SwContentFrame*>(pRet));
3891 return static_cast<SwContentFrame*>(pRet);
3894 /// Return value defines if the frm needs to be relocated
3895 bool SwTabFrame::ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool &rReformat )
3897 rReformat = false;
3898 if ( SwFlowFrame::IsMoveBwdJump() || !IsPrevObjMove() )
3900 //Flowing back Frames is quite time consuming unfortunately.
3901 //Most often the location where the Frame wants to flow to has the same
3902 //FixSize as the Frame itself. In such a situation it's easy to check if
3903 //the Frame will find enough space for its VarSize, if this is not the
3904 //case, the relocation can be skipped.
3905 //Checking if the Frame will find enough space is done by the Frame itself,
3906 //this also takes the possibility of splitting the Frame into account.
3907 //If the FixSize is different or Flys are involved (at the old or the
3908 //new position) the checks are pointless, the Frame then
3909 //needs to be relocated tentatively (if a bit of space is available).
3911 //The FixSize of the environments which contain tables is always the
3912 //width.
3914 SwPageFrame *pOldPage = FindPageFrame(),
3915 *pNewPage = pNewUpper->FindPageFrame();
3916 bool bMoveAnyway = false;
3917 SwTwips nSpace = 0;
3919 SwRectFnSet aRectFnSet(this);
3920 if ( !SwFlowFrame::IsMoveBwdJump() )
3923 tools::Long nOldWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
3924 SwRectFnSet fnRectX(pNewUpper);
3925 tools::Long nNewWidth = fnRectX.GetWidth(pNewUpper->getFramePrintArea());
3926 if( std::abs( nNewWidth - nOldWidth ) < 2 )
3928 bMoveAnyway = BwdMoveNecessary( pOldPage, getFrameArea() ) > 1;
3929 if( !bMoveAnyway )
3931 SwRect aRect( pNewUpper->getFramePrintArea() );
3932 aRect.Pos() += pNewUpper->getFrameArea().Pos();
3933 const SwFrame *pPrevFrame = pNewUpper->Lower();
3934 while ( pPrevFrame && pPrevFrame != this )
3936 fnRectX.SetTop( aRect, fnRectX.GetBottom(pPrevFrame->getFrameArea()) );
3937 pPrevFrame = pPrevFrame->GetNext();
3939 bMoveAnyway = BwdMoveNecessary( pNewPage, aRect) > 1;
3941 // #i54861# Due to changes made in PrepareMake,
3942 // the tabfrm may not have a correct position. Therefore
3943 // it is possible that pNewUpper->getFramePrintArea().Height == 0. In this
3944 // case the above calculation of nSpace might give wrong
3945 // results and we really do not want to MoveBackward into a
3946 // 0 height frame. If nTmpSpace is already <= 0, we take this
3947 // value:
3948 const SwTwips nTmpSpace = fnRectX.GetHeight(aRect);
3949 if ( fnRectX.GetHeight(pNewUpper->getFramePrintArea()) > 0 || nTmpSpace <= 0 )
3950 nSpace = nTmpSpace;
3952 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
3953 if( pSh && pSh->GetViewOptions()->getBrowseMode() )
3954 nSpace += pNewUpper->Grow( LONG_MAX, true );
3955 if (0 < nSpace && GetPrecede())
3957 SwTwips nUpperDummy(0);
3958 tools::Long nLeftOffsetDummy(0), nRightOffsetDummy(0);
3959 // tdf#116501 check for no-wrap fly overlap
3960 static_cast<const SwTabFrame*>(GetPrecede())->CalcFlyOffsets(
3961 nUpperDummy, nLeftOffsetDummy, nRightOffsetDummy, &nSpace);
3965 else if (!m_bLockBackMove)
3966 bMoveAnyway = true;
3967 else
3969 m_bWantBackMove = true;
3972 else if (!m_bLockBackMove)
3973 bMoveAnyway = true;
3974 else
3976 m_bWantBackMove = true;
3979 if ( bMoveAnyway )
3981 rReformat = true;
3982 return true;
3985 bool bFits = nSpace > 0;
3986 if (!bFits && aRectFnSet.GetHeight(getFrameArea()) == 0)
3987 // This frame fits into pNewUpper in case it has no space, but this
3988 // frame is empty.
3989 bFits = nSpace >= 0;
3990 if (bFits)
3992 // #i26945# - check, if follow flow line
3993 // contains frame, which are moved forward due to its object
3994 // positioning.
3995 const SwRowFrame* pFirstRow = GetFirstNonHeadlineRow();
3996 if ( pFirstRow && pFirstRow->IsInFollowFlowRow() &&
3997 SwLayouter::DoesRowContainMovedFwdFrame(
3998 *(pFirstRow->GetFormat()->GetDoc()),
3999 *pFirstRow ) )
4001 return false;
4003 SwTwips nTmpHeight = CalcHeightOfFirstContentLine();
4005 // For some mysterious reason, I changed the good old
4006 // 'return nHeight <= nSpace' to 'return nTmpHeight < nSpace'.
4007 // This obviously results in problems with table frames in
4008 // sections. Remember: Every twip is sacred.
4009 if (nTmpHeight <= nSpace)
4011 if (m_bLockBackMove)
4013 m_bWantBackMove = true;
4015 else
4017 return true;
4022 return false;
4025 void SwTabFrame::Cut()
4027 OSL_ENSURE( GetUpper(), "Cut without Upper()." );
4029 SwPageFrame *pPage = FindPageFrame();
4030 InvalidatePage( pPage );
4031 SwFrame *pFrame = GetNext();
4032 if( pFrame )
4034 // Possibly the old follow calculated a spacing to the predecessor
4035 // which is obsolete now when it becomes the first frame
4036 pFrame->InvalidatePrt_();
4037 pFrame->InvalidatePos_();
4038 if ( pFrame->IsContentFrame() )
4039 pFrame->InvalidatePage( pPage );
4040 if( IsInSct() && !GetPrev() )
4042 SwSectionFrame* pSct = FindSctFrame();
4043 if( !pSct->IsFollow() )
4045 pSct->InvalidatePrt_();
4046 pSct->InvalidatePage( pPage );
4050 else
4052 InvalidateNextPos();
4053 //Someone has to do the retouch: predecessor or upper
4054 pFrame = GetPrev();
4055 if ( nullptr != pFrame )
4057 pFrame->SetRetouche();
4058 pFrame->Prepare( PrepareHint::WidowsOrphans );
4059 pFrame->InvalidatePos_();
4060 if ( pFrame->IsContentFrame() )
4061 pFrame->InvalidatePage( pPage );
4063 //If I am (was) the only FlowFrame in my own upper, it has to do
4064 //the retouch. Moreover a new empty page might be created.
4065 else
4066 { SwRootFrame *pRoot = static_cast<SwRootFrame*>(pPage->GetUpper());
4067 pRoot->SetSuperfluous();
4068 GetUpper()->SetCompletePaint();
4069 if( IsInSct() )
4071 SwSectionFrame* pSct = FindSctFrame();
4072 if( !pSct->IsFollow() )
4074 pSct->InvalidatePrt_();
4075 pSct->InvalidatePage( pPage );
4081 //First remove, then shrink the upper.
4082 SwLayoutFrame *pUp = GetUpper();
4083 SwRectFnSet aRectFnSet(this);
4084 RemoveFromLayout();
4085 if ( pUp )
4087 OSL_ENSURE( !pUp->IsFootnoteFrame(), "Table in Footnote." );
4088 SwSectionFrame *pSct = nullptr;
4089 SwFlyFrame *pFly = nullptr;
4090 // #126020# - adjust check for empty section
4091 // #130797# - correct fix #126020#
4092 if ( !pUp->Lower() && pUp->IsInSct() &&
4093 !(pSct = pUp->FindSctFrame())->ContainsContent() &&
4094 !pSct->ContainsAny( true ) )
4096 if ( pUp->GetUpper() )
4098 pSct->DelEmpty( false );
4099 pSct->InvalidateSize_();
4102 else if (!pUp->Lower() && pUp->IsInFly() &&
4103 !(pFly = pUp->FindFlyFrame())->ContainsContent() &&
4104 !pFly->ContainsAny())
4106 bool bSplitFly = pFly->IsFlySplitAllowed();
4107 if (!bSplitFly && pFly->IsFlyAtContentFrame())
4109 // If the fly is not allowed to split, it's still possible that it was allowed to
4110 // split. That is definitely the case when the fly is a follow.
4111 auto pFlyAtContent = static_cast<SwFlyAtContentFrame*>(pFly);
4112 bSplitFly = pFlyAtContent->IsFollow();
4114 if (pUp == pFly && bSplitFly)
4116 auto pFlyAtContent = static_cast<SwFlyAtContentFrame*>(pFly);
4117 pFlyAtContent->DelEmpty();
4120 // table-in-footnote: delete empty footnote frames (like SwContentFrame::Cut)
4121 else if (!pUp->Lower() && pUp->IsFootnoteFrame() && !pUp->IsColLocked())
4123 if (pUp->GetNext() && !pUp->GetPrev())
4125 if (SwFrame *const pTmp = static_cast<SwLayoutFrame*>(pUp->GetNext())->ContainsAny())
4127 pTmp->InvalidatePrt_();
4130 if (!pUp->IsDeleteForbidden())
4132 pUp->Cut();
4133 SwFrame::DestroyFrame(pUp);
4136 else if( aRectFnSet.GetHeight(getFrameArea()) )
4138 // OD 26.08.2003 #i18103# - *no* 'ColUnlock' of section -
4139 // undo changes of fix for #104992#
4140 pUp->Shrink( getFrameArea().Height() );
4145 if ( pPage && !IsFollow() && pPage->GetUpper() )
4146 static_cast<SwRootFrame*>(pPage->GetUpper())->InvalidateBrowseWidth();
4149 void SwTabFrame::Paste( SwFrame* pParent, SwFrame* pSibling )
4151 OSL_ENSURE( pParent, "No parent for pasting." );
4152 OSL_ENSURE( pParent->IsLayoutFrame(), "Parent is ContentFrame." );
4153 OSL_ENSURE( pParent != this, "I'm the parent myself." );
4154 OSL_ENSURE( pSibling != this, "I'm my own neighbour." );
4155 OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(),
4156 "I'm still registered somewhere." );
4158 //Insert in the tree.
4159 InsertBefore( static_cast<SwLayoutFrame*>(pParent), pSibling );
4161 InvalidateAll_();
4162 SwPageFrame *pPage = FindPageFrame();
4163 InvalidatePage( pPage );
4165 if ( GetNext() )
4167 GetNext()->InvalidatePos_();
4168 GetNext()->InvalidatePrt_();
4169 if ( GetNext()->IsContentFrame() )
4170 GetNext()->InvalidatePage( pPage );
4173 SwRectFnSet aRectFnSet(this);
4174 if( aRectFnSet.GetHeight(getFrameArea()) )
4175 pParent->Grow( aRectFnSet.GetHeight(getFrameArea()) );
4177 if( aRectFnSet.GetWidth(getFrameArea()) != aRectFnSet.GetWidth(pParent->getFramePrintArea()) )
4178 Prepare( PrepareHint::FixSizeChanged );
4179 if ( GetPrev() )
4181 if ( !IsFollow() )
4183 GetPrev()->InvalidateSize();
4184 if ( GetPrev()->IsContentFrame() )
4185 GetPrev()->InvalidatePage( pPage );
4188 else if ( GetNext() )
4189 // Take the spacing into account when dealing with ContentFrames.
4190 // There are two situations (both always happen at the same time):
4191 // a) The Content becomes the first in a chain
4192 // b) The new follower was previously the first in a chain
4193 GetNext()->InvalidatePrt_();
4195 if ( !pPage || IsFollow() )
4196 return;
4198 if ( pPage->GetUpper() )
4199 static_cast<SwRootFrame*>(pPage->GetUpper())->InvalidateBrowseWidth();
4201 if ( !GetPrev() )//At least needed for HTML with a table at the beginning.
4203 const SwPageDesc *pDesc = GetFormat()->GetPageDesc().GetPageDesc();
4204 if ( (pDesc && pDesc != pPage->GetPageDesc()) ||
4205 (!pDesc && pPage->GetPageDesc() != &GetFormat()->GetDoc()->GetPageDesc(0)) )
4206 CheckPageDescs( pPage );
4210 bool SwTabFrame::Prepare( const PrepareHint eHint, const void *, bool )
4212 if( PrepareHint::BossChanged == eHint )
4213 CheckDirChange();
4214 return false;
4217 SwRowFrame::SwRowFrame(const SwTableLine &rLine, SwFrame* pSib, bool bInsertContent)
4218 : SwLayoutFrame( rLine.GetFrameFormat(), pSib )
4219 , m_pTabLine( &rLine )
4220 , m_pFollowRow( nullptr )
4221 // #i29550#
4222 , mnTopMarginForLowers( 0 )
4223 , mnBottomMarginForLowers( 0 )
4224 , mnBottomLineSize( 0 )
4225 // --> split table rows
4226 , m_bIsFollowFlowRow( false )
4227 // <-- split table rows
4228 , m_bIsRepeatedHeadline( false )
4229 , m_bIsRowSpanLine( false )
4230 , m_bForceRowSplitAllowed( false )
4231 , m_bIsInSplit( false )
4233 mnFrameType = SwFrameType::Row;
4235 //Create the boxes and insert them.
4236 const SwTableBoxes &rBoxes = rLine.GetTabBoxes();
4237 SwFrame *pTmpPrev = nullptr;
4239 bool bHiddenRedlines = getRootFrame()->IsHideRedlines() &&
4240 !GetFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable().empty();
4241 for ( size_t i = 0; i < rBoxes.size(); ++i )
4243 // skip cells deleted with track changes
4244 if ( bHiddenRedlines && RedlineType::Delete == rBoxes[i]->GetRedlineType() )
4245 continue;
4247 SwCellFrame *pNew = new SwCellFrame( *rBoxes[i], this, bInsertContent );
4248 pNew->InsertBehind( this, pTmpPrev );
4249 pTmpPrev = pNew;
4253 void SwRowFrame::DestroyImpl()
4255 sw::BroadcastingModify* pMod = GetFormat();
4256 if( pMod )
4258 pMod->Remove( this );
4259 if( !pMod->HasWriterListeners() )
4260 delete pMod;
4263 SwLayoutFrame::DestroyImpl();
4266 SwRowFrame::~SwRowFrame()
4270 void SwRowFrame::RegistFlys( SwPageFrame *pPage )
4272 ::RegistFlys( pPage ? pPage : FindPageFrame(), this );
4275 void SwRowFrame::OnFrameSize(const SfxPoolItem& rSize)
4277 SwTabFrame* pTab = FindTabFrame();
4278 if(pTab)
4280 const bool bInFirstNonHeadlineRow = pTab->IsFollow() && this == pTab->GetFirstNonHeadlineRow();
4281 // #i35063#
4282 // Invalidation required is pRow is last row
4283 if(bInFirstNonHeadlineRow)
4284 pTab = pTab->FindMaster();
4285 if(bInFirstNonHeadlineRow || !GetNext())
4286 pTab->InvalidatePos();
4288 const sw::BroadcastingModify aMod;
4289 SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(nullptr, &rSize));
4292 void SwRowFrame::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
4294 if(auto pNewFormatHint = dynamic_cast<const sw::TableLineFormatChanged*>(&rHint))
4296 if(GetTabLine() != &pNewFormatHint->m_rTabLine)
4297 return;
4298 RegisterToFormat(const_cast<SwTableLineFormat&>(pNewFormatHint->m_rNewFormat));
4299 InvalidateSize();
4300 InvalidatePrt_();
4301 SetCompletePaint();
4302 ReinitializeFrameSizeAttrFlags();
4304 // #i35063#
4305 // consider 'split row allowed' attribute
4306 SwTabFrame* pTab = FindTabFrame();
4307 bool bInFollowFlowRow = false;
4308 const bool bInFirstNonHeadlineRow = pTab->IsFollow() && this == pTab->GetFirstNonHeadlineRow();
4309 if(bInFirstNonHeadlineRow ||
4310 !GetNext() ||
4311 (bInFollowFlowRow = IsInFollowFlowRow()) ||
4312 nullptr != IsInSplitTableRow() )
4314 if(bInFirstNonHeadlineRow || bInFollowFlowRow)
4315 pTab = pTab->FindMaster();
4317 pTab->SetRemoveFollowFlowLinePending(true);
4318 pTab->InvalidatePos();
4321 else if(auto pMoveTableLineHint = dynamic_cast<const sw::MoveTableLineHint*>(&rHint))
4324 if(GetTabLine() != &pMoveTableLineHint->m_rTableLine)
4325 return;
4326 const_cast<SwFrameFormat*>(&pMoveTableLineHint->m_rNewFormat)->Add(this);
4327 InvalidateAll();
4328 ReinitializeFrameSizeAttrFlags();
4329 return;
4331 if (rHint.GetId() != SfxHintId::SwLegacyModify)
4332 return;
4333 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
4334 if(!pLegacy->m_pNew)
4336 // possibly not needed?
4337 SwLayoutFrame::SwClientNotify(rModify, rHint);
4338 return;
4340 switch(pLegacy->m_pNew->Which())
4342 case RES_ATTRSET_CHG:
4344 const SwAttrSet* pChgSet = static_cast<const SwAttrSetChg*>(pLegacy->m_pNew)->GetChgSet();
4345 const SfxPoolItem* pItem = nullptr;
4346 pChgSet->GetItemState(RES_FRM_SIZE, false, &pItem);
4347 if(!pItem)
4348 pChgSet->GetItemState(RES_ROW_SPLIT, false, &pItem);
4349 if(pItem)
4350 OnFrameSize(*pItem);
4351 else
4352 SwLayoutFrame::SwClientNotify(rModify, rHint); // possibly not needed?
4353 return;
4355 case RES_FRM_SIZE:
4356 case RES_ROW_SPLIT:
4357 OnFrameSize(*static_cast<const SwFormatFrameSize*>(pLegacy->m_pNew));
4358 return;
4362 void SwRowFrame::MakeAll(vcl::RenderContext* pRenderContext)
4364 if ( !GetNext() )
4366 setFrameAreaSizeValid(false);
4369 SwLayoutFrame::MakeAll(pRenderContext);
4372 void SwRowFrame::dumpAsXml(xmlTextWriterPtr writer) const
4374 (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("row"));
4375 dumpAsXmlAttributes(writer);
4377 (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
4378 dumpInfosAsXml(writer);
4379 (void)xmlTextWriterEndElement(writer);
4380 dumpChildrenAsXml(writer);
4382 (void)xmlTextWriterEndElement(writer);
4385 tools::Long CalcHeightWithFlys( const SwFrame *pFrame )
4387 SwRectFnSet aRectFnSet(pFrame);
4388 tools::Long nHeight = 0;
4389 const SwFrame* pTmp = pFrame->IsSctFrame() ?
4390 static_cast<const SwSectionFrame*>(pFrame)->ContainsContent() : pFrame;
4391 while( pTmp )
4393 // #i26945# - consider follow text frames
4394 const SwSortedObjs* pObjs( nullptr );
4395 bool bIsFollow( false );
4396 if ( pTmp->IsTextFrame() && static_cast<const SwTextFrame*>(pTmp)->IsFollow() )
4398 const SwFrame* pMaster;
4399 // #i46450# Master does not necessarily have
4400 // to exist if this function is called from JoinFrame() ->
4401 // Cut() -> Shrink()
4402 const SwTextFrame* pTmpFrame = static_cast<const SwTextFrame*>(pTmp);
4403 if ( pTmpFrame->GetPrev() && pTmpFrame->GetPrev()->IsTextFrame() &&
4404 static_cast<const SwTextFrame*>(pTmpFrame->GetPrev())->GetFollow() &&
4405 static_cast<const SwTextFrame*>(pTmpFrame->GetPrev())->GetFollow() != pTmp )
4406 pMaster = nullptr;
4407 else
4408 pMaster = pTmpFrame->FindMaster();
4410 if ( pMaster )
4412 pObjs = static_cast<const SwTextFrame*>(pTmp)->FindMaster()->GetDrawObjs();
4413 bIsFollow = true;
4416 else
4418 pObjs = pTmp->GetDrawObjs();
4420 if ( pObjs )
4422 for (SwAnchoredObject* pAnchoredObj : *pObjs)
4424 // #i26945# - if <pTmp> is follow, the
4425 // anchor character frame has to be <pTmp>.
4426 if ( bIsFollow &&
4427 pAnchoredObj->FindAnchorCharFrame() != pTmp )
4429 continue;
4431 // #i26945# - consider also drawing objects
4433 // OD 30.09.2003 #i18732# - only objects, which follow
4434 // the text flow have to be considered.
4435 const SwFrameFormat& rFrameFormat = pAnchoredObj->GetFrameFormat();
4436 bool bFollowTextFlow = rFrameFormat.GetFollowTextFlow().GetValue();
4437 bool bIsFarAway = pAnchoredObj->GetObjRect().Top() != FAR_AWAY;
4438 const SwPageFrame* pPageFrm = pTmp->FindPageFrame();
4439 bool bIsAnchoredToTmpFrm = false;
4440 if ( pPageFrm && pPageFrm->IsPageFrame() && pAnchoredObj->GetPageFrame())
4441 bIsAnchoredToTmpFrm = pAnchoredObj->GetPageFrame() == pPageFrm ||
4442 (pPageFrm->GetFormatPage().GetPhyPageNum() == pAnchoredObj->GetPageFrame()->GetFormatPage().GetPhyPageNum() + 1);
4443 const bool bConsiderObj =
4444 (rFrameFormat.GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) &&
4445 bIsFarAway &&
4446 bFollowTextFlow && bIsAnchoredToTmpFrm;
4447 bool bWrapThrough = rFrameFormat.GetSurround().GetValue() == text::WrapTextMode_THROUGH;
4448 bool bInBackground = !rFrameFormat.GetOpaque().GetValue();
4449 // Legacy render requires in-background setting, the new mode does not.
4450 bool bConsiderFollowTextFlow = bInBackground
4451 || !rFrameFormat.getIDocumentSettingAccess().get(
4452 DocumentSettingId::USE_FORMER_TEXT_WRAPPING);
4453 if (pFrame->IsInTab() && bFollowTextFlow && bWrapThrough && bConsiderFollowTextFlow)
4455 // Ignore wrap-through objects when determining the cell height.
4456 // Normally FollowTextFlow requires a resize of the cell, but not in case of
4457 // wrap-through.
4458 continue;
4461 if ( bConsiderObj )
4463 const SwFormatFrameSize &rSz = rFrameFormat.GetFrameSize();
4464 if( !rSz.GetHeightPercent() )
4466 const SwTwips nDistOfFlyBottomToAnchorTop =
4467 aRectFnSet.GetHeight(pAnchoredObj->GetObjRect()) +
4468 ( aRectFnSet.IsVert() ?
4469 pAnchoredObj->GetCurrRelPos().X() :
4470 pAnchoredObj->GetCurrRelPos().Y() );
4472 const SwTwips nFrameDiff =
4473 aRectFnSet.YDiff(
4474 aRectFnSet.GetTop(pTmp->getFrameArea()),
4475 aRectFnSet.GetTop(pFrame->getFrameArea()) );
4477 nHeight = std::max( nHeight, nDistOfFlyBottomToAnchorTop + nFrameDiff -
4478 aRectFnSet.GetHeight(pFrame->getFrameArea()) );
4480 // #i56115# The first height calculation
4481 // gives wrong results if pFrame->getFramePrintArea().Y() > 0. We do
4482 // a second calculation based on the actual rectangles of
4483 // pFrame and pAnchoredObj, and use the maximum of the results.
4484 // I do not want to remove the first calculation because
4485 // if clipping has been applied, using the GetCurrRelPos
4486 // might be the better option to calculate nHeight.
4487 const SwTwips nDistOfFlyBottomToAnchorTop2 = aRectFnSet.YDiff(
4488 aRectFnSet.GetBottom(pAnchoredObj->GetObjRect()),
4489 aRectFnSet.GetBottom(pFrame->getFrameArea()) );
4491 nHeight = std::max( nHeight, tools::Long(nDistOfFlyBottomToAnchorTop2 ));
4497 if( !pFrame->IsSctFrame() )
4498 break;
4499 pTmp = pTmp->FindNextCnt();
4500 if( !static_cast<const SwSectionFrame*>(pFrame)->IsAnLower( pTmp ) )
4501 break;
4503 return nHeight;
4506 static SwTwips lcl_CalcTopAndBottomMargin( const SwLayoutFrame& rCell, const SwBorderAttrs& rAttrs )
4508 const SwTabFrame* pTab = rCell.FindTabFrame();
4509 SwTwips nTopSpace = 0;
4510 SwTwips nBottomSpace = 0;
4512 // #i29550#
4513 if ( pTab->IsCollapsingBorders() && rCell.Lower() && !rCell.Lower()->IsRowFrame() )
4515 nTopSpace = static_cast<const SwRowFrame*>(rCell.GetUpper())->GetTopMarginForLowers();
4516 nBottomSpace = static_cast<const SwRowFrame*>(rCell.GetUpper())->GetBottomMarginForLowers();
4518 else
4520 if ( pTab->IsVertical() != rCell.IsVertical() )
4522 nTopSpace = rAttrs.CalcLeft( &rCell );
4523 nBottomSpace = rAttrs.CalcRight( &rCell );
4525 else
4527 nTopSpace = rAttrs.CalcTop();
4528 nBottomSpace = rAttrs.CalcBottom();
4532 return nTopSpace + nBottomSpace;
4535 // #i26945# - add parameter <_bConsiderObjs> in order to
4536 // control, if floating screen objects have to be considered for the minimal
4537 // cell height.
4538 static SwTwips lcl_CalcMinCellHeight( const SwLayoutFrame *_pCell,
4539 const bool _bConsiderObjs,
4540 const SwBorderAttrs *pAttrs = nullptr )
4542 SwRectFnSet aRectFnSet(_pCell);
4543 SwTwips nHeight = 0;
4544 const SwFrame* pLow = _pCell->Lower();
4545 if ( pLow )
4547 tools::Long nFlyAdd = 0;
4548 while ( pLow )
4550 if ( pLow->IsRowFrame() )
4552 // #i26945#
4553 nHeight += ::lcl_CalcMinRowHeight( static_cast<const SwRowFrame*>(pLow),
4554 _bConsiderObjs );
4556 else
4558 tools::Long nLowHeight = aRectFnSet.GetHeight(pLow->getFrameArea());
4559 nHeight += nLowHeight;
4560 // #i26945#
4561 if ( _bConsiderObjs )
4563 nFlyAdd = std::max( tools::Long(0), nFlyAdd - nLowHeight );
4564 nFlyAdd = std::max( nFlyAdd, ::CalcHeightWithFlys( pLow ) );
4568 pLow = pLow->GetNext();
4570 if ( nFlyAdd )
4571 nHeight += nFlyAdd;
4573 // The border/margin needs to be considered too, unfortunately it can't be
4574 // calculated using PrintArea and FrameArea because any or all of those
4575 // may be invalid.
4576 if ( _pCell->Lower() )
4578 if ( pAttrs )
4579 nHeight += lcl_CalcTopAndBottomMargin( *_pCell, *pAttrs );
4580 else
4582 SwBorderAttrAccess aAccess( SwFrame::GetCache(), _pCell );
4583 const SwBorderAttrs &rAttrs = *aAccess.Get();
4584 nHeight += lcl_CalcTopAndBottomMargin( *_pCell, rAttrs );
4587 return nHeight;
4590 // #i26945# - add parameter <_bConsiderObjs> in order to control,
4591 // if floating screen objects have to be considered for the minimal cell height
4592 static SwTwips lcl_CalcMinRowHeight( const SwRowFrame* _pRow,
4593 const bool _bConsiderObjs )
4595 SwTwips nHeight = 0;
4596 if ( !_pRow->IsRowSpanLine() )
4598 const SwFormatFrameSize &rSz = _pRow->GetFormat()->GetFrameSize();
4599 if ( _pRow->HasFixSize() )
4601 OSL_ENSURE(SwFrameSize::Fixed == rSz.GetHeightSizeType(), "pRow claims to have fixed size");
4602 return rSz.GetHeight();
4604 // If this row frame is being split, then row's minimal height shouldn't restrict
4605 // this frame's minimal height, because the rest will go to follow frame.
4606 else if ( !_pRow->IsInSplit() && rSz.GetHeightSizeType() == SwFrameSize::Minimum )
4608 bool bSplitFly = false;
4609 if (_pRow->IsInFly())
4611 // See if we're in a split fly that is anchored on a page that has enough space to
4612 // host this row with its minimum row height.
4613 const SwFlyFrame* pFly = _pRow->FindFlyFrame();
4614 if (pFly->IsFlySplitAllowed())
4616 SwFrame* pAnchor = const_cast<SwFlyFrame*>(pFly)->FindAnchorCharFrame();
4617 if (pAnchor)
4619 if (pAnchor->FindPageFrame()->getFramePrintArea().Height() > rSz.GetHeight())
4621 bSplitFly = true;
4627 if (bSplitFly)
4629 // Split fly: enforce minimum row height for the master and follows.
4630 nHeight = rSz.GetHeight();
4632 else
4634 nHeight = rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*_pRow);
4639 SwRectFnSet aRectFnSet(_pRow);
4640 const SwCellFrame* pLow = static_cast<const SwCellFrame*>(_pRow->Lower());
4641 while ( pLow )
4643 SwTwips nTmp = 0;
4644 const tools::Long nRowSpan = pLow->GetLayoutRowSpan();
4645 // --> NEW TABLES
4646 // Consider height of
4647 // 1. current cell if RowSpan == 1
4648 // 2. current cell if cell is "follow" cell of a cell with RowSpan == -1
4649 // 3. master cell if RowSpan == -1
4650 if ( 1 == nRowSpan )
4652 nTmp = ::lcl_CalcMinCellHeight( pLow, _bConsiderObjs );
4654 else if ( -1 == nRowSpan )
4656 // Height of the last cell of a row span is height of master cell
4657 // minus the height of the other rows which are covered by the master
4658 // cell:
4659 const SwCellFrame& rMaster = pLow->FindStartEndOfRowSpanCell( true );
4660 nTmp = ::lcl_CalcMinCellHeight( &rMaster, _bConsiderObjs );
4661 const SwFrame* pMasterRow = rMaster.GetUpper();
4662 while ( pMasterRow && pMasterRow != _pRow )
4664 nTmp -= aRectFnSet.GetHeight(pMasterRow->getFrameArea());
4665 pMasterRow = pMasterRow->GetNext();
4668 // <-- NEW TABLES
4670 // Do not consider rotated cells:
4671 if ( pLow->IsVertical() == aRectFnSet.IsVert() && nTmp > nHeight )
4672 nHeight = nTmp;
4674 pLow = static_cast<const SwCellFrame*>(pLow->GetNext());
4677 return nHeight;
4680 // #i29550#
4682 // Calculate the maximum of (TopLineSize + TopLineDist) over all lowers:
4683 static sal_uInt16 lcl_GetTopSpace( const SwRowFrame& rRow )
4685 sal_uInt16 nTopSpace = 0;
4686 for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
4687 pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
4689 sal_uInt16 nTmpTopSpace = 0;
4690 if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
4691 nTmpTopSpace = lcl_GetTopSpace( *static_cast<const SwRowFrame*>(pCurrLower->Lower()) );
4692 else
4694 const SwAttrSet& rSet = const_cast<SwCellFrame*>(pCurrLower)->GetFormat()->GetAttrSet();
4695 const SvxBoxItem& rBoxItem = rSet.GetBox();
4696 nTmpTopSpace = rBoxItem.CalcLineSpace( SvxBoxItemLine::TOP, true );
4698 nTopSpace = std::max( nTopSpace, nTmpTopSpace );
4700 return nTopSpace;
4703 // Calculate the maximum of TopLineDist over all lowers:
4704 static sal_uInt16 lcl_GetTopLineDist( const SwRowFrame& rRow )
4706 sal_uInt16 nTopLineDist = 0;
4707 for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
4708 pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
4710 sal_uInt16 nTmpTopLineDist = 0;
4711 if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
4712 nTmpTopLineDist = lcl_GetTopLineDist( *static_cast<const SwRowFrame*>(pCurrLower->Lower()) );
4713 else
4715 const SwAttrSet& rSet = const_cast<SwCellFrame*>(pCurrLower)->GetFormat()->GetAttrSet();
4716 const SvxBoxItem& rBoxItem = rSet.GetBox();
4717 nTmpTopLineDist = rBoxItem.GetDistance( SvxBoxItemLine::TOP );
4719 nTopLineDist = std::max( nTopLineDist, nTmpTopLineDist );
4721 return nTopLineDist;
4724 // Calculate the maximum of BottomLineSize over all lowers:
4725 static sal_uInt16 lcl_GetBottomLineSize( const SwRowFrame& rRow )
4727 sal_uInt16 nBottomLineSize = 0;
4728 for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
4729 pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
4731 sal_uInt16 nTmpBottomLineSize = 0;
4732 if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
4734 const SwFrame* pRow = pCurrLower->GetLastLower();
4735 nTmpBottomLineSize = lcl_GetBottomLineSize( *static_cast<const SwRowFrame*>(pRow) );
4737 else
4739 const SwAttrSet& rSet = const_cast<SwCellFrame*>(pCurrLower)->GetFormat()->GetAttrSet();
4740 const SvxBoxItem& rBoxItem = rSet.GetBox();
4741 nTmpBottomLineSize = rBoxItem.CalcLineSpace( SvxBoxItemLine::BOTTOM, true ) -
4742 rBoxItem.GetDistance( SvxBoxItemLine::BOTTOM );
4744 nBottomLineSize = std::max( nBottomLineSize, nTmpBottomLineSize );
4746 return nBottomLineSize;
4749 // Calculate the maximum of BottomLineDist over all lowers:
4750 static sal_uInt16 lcl_GetBottomLineDist( const SwRowFrame& rRow )
4752 sal_uInt16 nBottomLineDist = 0;
4753 for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
4754 pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
4756 sal_uInt16 nTmpBottomLineDist = 0;
4757 if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
4759 const SwFrame* pRow = pCurrLower->GetLastLower();
4760 nTmpBottomLineDist = lcl_GetBottomLineDist( *static_cast<const SwRowFrame*>(pRow) );
4762 else
4764 const SwAttrSet& rSet = const_cast<SwCellFrame*>(pCurrLower)->GetFormat()->GetAttrSet();
4765 const SvxBoxItem& rBoxItem = rSet.GetBox();
4766 nTmpBottomLineDist = rBoxItem.GetDistance( SvxBoxItemLine::BOTTOM );
4768 nBottomLineDist = std::max( nBottomLineDist, nTmpBottomLineDist );
4770 return nBottomLineDist;
4773 // tdf#104425: calculate the height of all row frames,
4774 // for which this frame is a follow.
4775 // When a row has fixed/minimum height, it may span over
4776 // several pages. The minimal height on this page should
4777 // take into account the sum of all the heights of previous
4778 // frames that constitute the table row on previous pages.
4779 // Otherwise, trying to split a too high row frame will
4780 // result in loop trying to create that too high row
4781 // on each following page
4782 static SwTwips lcl_calcHeightOfRowBeforeThisFrame(const SwRowFrame& rRow)
4784 // We don't need to account for previous instances of repeated headlines
4785 if (rRow.IsRepeatedHeadline())
4786 return 0;
4787 SwRectFnSet aRectFnSet(&rRow);
4788 const SwTableLine* pLine = rRow.GetTabLine();
4789 const SwTabFrame* pTab = rRow.FindTabFrame();
4790 if (!pLine || !pTab || !pTab->IsFollow())
4791 return 0;
4792 SwTwips nResult = 0;
4793 SwIterator<SwRowFrame, SwFormat> aIter(*pLine->GetFrameFormat());
4794 for (const SwRowFrame* pCurRow = aIter.First(); pCurRow; pCurRow = aIter.Next())
4796 if (pCurRow != &rRow && pCurRow->GetTabLine() == pLine)
4798 // We've found another row frame that is part of the same table row
4799 const SwTabFrame* pCurTab = pCurRow->FindTabFrame();
4800 // A row frame may not belong to a table frame, when it is being cut, e.g., in
4801 // lcl_PostprocessRowsInCells().
4802 // Its SwRowFrame::Cut() has been called; it in turn called SwLayoutFrame::Cut(),
4803 // which nullified row's upper in RemoveFromLayout(), and then called Shrink()
4804 // for its former upper.
4805 // Regardless of whether it will be pasted back, or destroyed, currently it's not
4806 // part of layout, and its height does not count
4807 if (pCurTab && pCurTab->IsAnFollow(pTab))
4809 // The found row frame belongs to a table frame that precedes
4810 // (above) this one in chain. So, include it in the sum
4811 nResult += aRectFnSet.GetHeight(pCurRow->getFrameArea());
4815 return nResult;
4818 void SwRowFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
4820 SwRectFnSet aRectFnSet(this);
4821 OSL_ENSURE( pAttrs, "SwRowFrame::Format without Attrs." );
4823 const bool bFix = mbFixSize;
4825 if ( !isFramePrintAreaValid() )
4827 // RowFrames don't have borders/margins therefore the PrintArea always
4828 // matches the FrameArea.
4829 setFramePrintAreaValid(true);
4832 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
4833 aPrt.Left( 0 );
4834 aPrt.Top( 0 );
4835 aPrt.Width ( getFrameArea().Width() );
4836 aPrt.Height( getFrameArea().Height() );
4839 // #i29550#
4840 // Here we calculate the top-printing area for the lower cell frames
4841 SwTabFrame* pTabFrame = FindTabFrame();
4842 if ( pTabFrame->IsCollapsingBorders() )
4844 const sal_uInt16 nTopSpace = lcl_GetTopSpace( *this );
4845 const sal_uInt16 nTopLineDist = lcl_GetTopLineDist( *this );
4846 const sal_uInt16 nBottomLineSize = lcl_GetBottomLineSize( *this );
4847 const sal_uInt16 nBottomLineDist = lcl_GetBottomLineDist( *this );
4849 const SwRowFrame* pPreviousRow = nullptr;
4851 // #i32456#
4852 // In order to calculate the top printing area for the lower cell
4853 // frames, we have to find the 'previous' row frame and compare
4854 // the bottom values of the 'previous' row with the 'top' values
4855 // of this row. The best way to find the 'previous' row is to
4856 // use the table structure:
4857 const SwTable* pTable = pTabFrame->GetTable();
4858 const SwTableLine* pPrevTabLine = nullptr;
4859 const SwRowFrame* pTmpRow = this;
4861 while ( pTmpRow && !pPrevTabLine )
4863 size_t nIdx = 0;
4864 const SwTableLines& rLines = pTmpRow->GetTabLine()->GetUpper() ?
4865 pTmpRow->GetTabLine()->GetUpper()->GetTabLines() :
4866 pTable->GetTabLines();
4868 while ( rLines[ nIdx ] != pTmpRow->GetTabLine() )
4869 ++nIdx;
4871 if ( nIdx > 0 )
4873 // pTmpRow has a 'previous' row in the table structure:
4874 pPrevTabLine = rLines[ nIdx - 1 ];
4876 else
4878 // pTmpRow is a first row in the table structure.
4879 // We go up in the table structure:
4880 pTmpRow = pTmpRow->GetUpper()->GetUpper() &&
4881 pTmpRow->GetUpper()->GetUpper()->IsRowFrame() ?
4882 static_cast<const SwRowFrame*>( pTmpRow->GetUpper()->GetUpper() ) :
4883 nullptr;
4887 // If we found a 'previous' row, we look for the appropriate row frame:
4888 if ( pPrevTabLine )
4890 SwIterator<SwRowFrame,SwFormat> aIter( *pPrevTabLine->GetFrameFormat() );
4891 for ( SwRowFrame* pRow = aIter.First(); pRow; pRow = aIter.Next() )
4893 // #115759# - do *not* take repeated
4894 // headlines, because during split of table it can be
4895 // invalid and thus can't provide correct border values.
4896 if ( pRow->GetTabLine() == pPrevTabLine &&
4897 !pRow->IsRepeatedHeadline() )
4899 pPreviousRow = pRow;
4900 break;
4905 sal_uInt16 nTopPrtMargin = nTopSpace;
4906 if ( pPreviousRow )
4908 const sal_uInt16 nTmpPrtMargin = pPreviousRow->GetBottomLineSize() + nTopLineDist;
4909 if ( nTmpPrtMargin > nTopPrtMargin )
4910 nTopPrtMargin = nTmpPrtMargin;
4913 // table has to be notified if it has to change its lower
4914 // margin due to changes of nBottomLineSize:
4915 if ( !GetNext() && nBottomLineSize != GetBottomLineSize() )
4916 pTabFrame->InvalidatePrt_();
4918 // If there are rows nested inside this row, the nested rows
4919 // may not have been calculated yet. Therefore the
4920 // ::lcl_CalcMinRowHeight( this ) operation later in this
4921 // function cannot consider the correct border values. We
4922 // have to trigger the invalidation of the outer row frame
4923 // manually:
4924 // Note: If any further invalidations should be necessary, we
4925 // should consider moving the invalidation stuff to the
4926 // appropriate SwNotify object.
4927 if ( GetUpper()->GetUpper()->IsRowFrame() &&
4928 ( nBottomLineDist != GetBottomMarginForLowers() ||
4929 nTopPrtMargin != GetTopMarginForLowers() ) )
4930 GetUpper()->GetUpper()->InvalidateSize_();
4932 SetBottomMarginForLowers( nBottomLineDist ); // 3.
4933 SetBottomLineSize( nBottomLineSize ); // 4.
4934 SetTopMarginForLowers( nTopPrtMargin ); // 5.
4939 while ( !isFrameAreaSizeValid() )
4941 setFrameAreaSizeValid(true);
4943 #if OSL_DEBUG_LEVEL > 0
4944 if ( HasFixSize() )
4946 const SwFormatFrameSize &rFrameSize = GetFormat()->GetFrameSize();
4947 OSL_ENSURE( rFrameSize.GetSize().Height() > 0, "Has it" );
4949 #endif
4950 const SwTwips nDiff = aRectFnSet.GetHeight(getFrameArea()) -
4951 ( HasFixSize() && !IsRowSpanLine()
4952 ? pAttrs->GetSize().Height()
4953 // #i26945#
4954 : ::lcl_CalcMinRowHeight( this,
4955 FindTabFrame()->IsConsiderObjsForMinCellHeight() ) );
4956 if ( nDiff )
4958 mbFixSize = false;
4959 if ( nDiff > 0 )
4960 Shrink( nDiff, false, true );
4961 else if ( nDiff < 0 )
4962 Grow( -nDiff );
4963 mbFixSize = bFix;
4967 // last row will fill the space in its upper.
4968 if ( GetNext() )
4969 return;
4971 //The last fills the remaining space in the upper.
4972 SwTwips nDiff = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea());
4973 SwFrame *pSibling = GetUpper()->Lower();
4975 { nDiff -= aRectFnSet.GetHeight(pSibling->getFrameArea());
4976 pSibling = pSibling->GetNext();
4977 } while ( pSibling );
4978 if ( nDiff > 0 )
4980 mbFixSize = false;
4981 Grow( nDiff );
4982 mbFixSize = bFix;
4983 setFrameAreaSizeValid(true);
4987 void SwRowFrame::AdjustCells( const SwTwips nHeight, const bool bHeight )
4989 SwFrame *pFrame = Lower();
4990 if ( bHeight )
4992 SwRectFnSet aRectFnSet(this);
4993 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
4994 SwRect aOldFrame;
4995 #endif
4997 while ( pFrame )
4999 SwFrame* pNotify = nullptr;
5001 SwCellFrame* pCellFrame = static_cast<SwCellFrame*>(pFrame);
5003 // NEW TABLES
5004 // Which cells need to be adjusted if the current row changes
5005 // its height?
5007 // Current frame is a covered frame:
5008 // Set new height for covered cell and adjust master cell:
5009 if ( pCellFrame->GetTabBox()->getRowSpan() < 1 )
5011 // Set height of current (covered) cell to new line height.
5012 const tools::Long nDiff = nHeight - aRectFnSet.GetHeight(pCellFrame->getFrameArea());
5013 if ( nDiff )
5016 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pCellFrame);
5017 aRectFnSet.AddBottom( aFrm, nDiff );
5020 pCellFrame->InvalidatePrt_();
5024 SwCellFrame* pToAdjust = nullptr;
5025 SwFrame* pToAdjustRow = nullptr;
5027 // If current frame is covered frame, we still want to adjust the
5028 // height of the cell starting the row span
5029 if ( pCellFrame->GetLayoutRowSpan() < 1 )
5031 pToAdjust = const_cast< SwCellFrame*>(&pCellFrame->FindStartEndOfRowSpanCell( true ));
5032 pToAdjustRow = pToAdjust->GetUpper();
5034 else
5036 pToAdjust = pCellFrame;
5037 pToAdjustRow = this;
5040 // Set height of master cell to height of all lines spanned by this line.
5041 tools::Long nRowSpan = pToAdjust->GetLayoutRowSpan();
5042 SwTwips nSumRowHeight = 0;
5043 while ( pToAdjustRow )
5045 // Use new height for the current row:
5046 nSumRowHeight += pToAdjustRow == this ?
5047 nHeight :
5048 aRectFnSet.GetHeight(pToAdjustRow->getFrameArea());
5050 if ( nRowSpan-- == 1 )
5051 break;
5053 pToAdjustRow = pToAdjustRow->GetNext();
5056 if ( pToAdjustRow && pToAdjustRow != this )
5057 pToAdjustRow->InvalidateSize_();
5059 const tools::Long nDiff = nSumRowHeight - aRectFnSet.GetHeight(pToAdjust->getFrameArea());
5060 if ( nDiff )
5062 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
5063 aOldFrame = pToAdjust->getFrameArea();
5064 #endif
5065 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pToAdjust);
5066 aRectFnSet.AddBottom( aFrm, nDiff );
5067 pNotify = pToAdjust;
5070 if ( pNotify )
5072 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
5073 SwRootFrame *pRootFrame = getRootFrame();
5074 if( pRootFrame && pRootFrame->IsAnyShellAccessible() && pRootFrame->GetCurrShell() )
5075 pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( pNotify, aOldFrame );
5076 #endif
5078 pNotify->InvalidatePrt_();
5081 pFrame = pFrame->GetNext();
5084 else
5085 { while ( pFrame )
5087 pFrame->InvalidateAll_();
5088 pFrame = pFrame->GetNext();
5091 InvalidatePage();
5094 void SwRowFrame::Cut()
5096 SwTabFrame *pTab = FindTabFrame();
5097 if ( pTab && pTab->IsFollow() && this == pTab->GetFirstNonHeadlineRow() )
5099 pTab->FindMaster()->InvalidatePos();
5102 SwLayoutFrame::Cut();
5105 SwTwips SwRowFrame::GrowFrame( SwTwips nDist, bool bTst, bool bInfo )
5107 SwTwips nReal = 0;
5109 SwTabFrame* pTab = FindTabFrame();
5110 SwRectFnSet aRectFnSet(pTab);
5112 bool bRestrictTableGrowth;
5113 bool bHasFollowFlowLine = pTab->HasFollowFlowLine();
5115 if ( GetUpper()->IsTabFrame() )
5117 const SwRowFrame* pFollowFlowRow = IsInSplitTableRow();
5118 bRestrictTableGrowth = pFollowFlowRow && !pFollowFlowRow->IsRowSpanLine();
5120 else
5122 OSL_ENSURE( GetUpper()->IsCellFrame(), "RowFrame->GetUpper neither table nor cell" );
5123 bRestrictTableGrowth = GetFollowRow() && bHasFollowFlowLine;
5124 OSL_ENSURE( !bRestrictTableGrowth || !GetNext(),
5125 "GetFollowRow for row frame that has a Next" );
5127 // There may still be some space left in my direct upper:
5128 const SwTwips nAdditionalSpace =
5129 aRectFnSet.BottomDist( getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()->GetUpper()) );
5130 if ( bRestrictTableGrowth && nAdditionalSpace > 0 )
5132 nReal = std::min( nAdditionalSpace, nDist );
5133 nDist -= nReal;
5134 if ( !bTst )
5136 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
5137 aRectFnSet.AddBottom( aFrm, nReal );
5142 if ( bRestrictTableGrowth )
5143 pTab->SetRestrictTableGrowth( true );
5144 else
5146 // Ok, this looks like a hack, indeed, it is a hack.
5147 // If the current row frame is inside another cell frame,
5148 // and the current row frame has no follow, it should not
5149 // be allowed to grow. In fact, setting bRestrictTableGrowth
5150 // to 'false' does not work, because the surrounding RowFrame
5151 // would set this to 'true'.
5152 pTab->SetFollowFlowLine( false );
5155 nReal += SwLayoutFrame::GrowFrame( nDist, bTst, bInfo);
5157 pTab->SetRestrictTableGrowth( false );
5158 pTab->SetFollowFlowLine( bHasFollowFlowLine );
5160 //Update the height of the cells to the newest value.
5161 if ( !bTst )
5163 SwRectFnSet fnRectX(this);
5164 AdjustCells( fnRectX.GetHeight(getFramePrintArea()) + nReal, true );
5165 if ( nReal )
5166 SetCompletePaint();
5169 return nReal;
5172 SwTwips SwRowFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool bInfo )
5174 SwRectFnSet aRectFnSet(this);
5175 if( HasFixSize() )
5177 AdjustCells( aRectFnSet.GetHeight(getFramePrintArea()), true );
5178 return 0;
5181 // bInfo may be set to true by SwRowFrame::Format; we need to handle this
5182 // here accordingly
5183 const bool bShrinkAnyway = bInfo;
5185 //Only shrink as much as the content of the biggest cell allows.
5186 SwTwips nRealDist = nDist;
5187 SwFormat* pMod = GetFormat();
5188 if (pMod)
5190 const SwFormatFrameSize &rSz = pMod->GetFrameSize();
5191 SwTwips nMinHeight = 0;
5192 if (rSz.GetHeightSizeType() == SwFrameSize::Minimum)
5193 nMinHeight = std::max(rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*this),
5194 tools::Long(0));
5196 // Only necessary to calculate minimal row height if height
5197 // of pRow is at least nMinHeight. Otherwise nMinHeight is the
5198 // minimum height.
5199 if( nMinHeight < aRectFnSet.GetHeight(getFrameArea()) )
5201 // #i26945#
5202 OSL_ENSURE( FindTabFrame(), "<SwRowFrame::ShrinkFrame(..)> - no table frame -> crash." );
5203 const bool bConsiderObjs( FindTabFrame()->IsConsiderObjsForMinCellHeight() );
5204 nMinHeight = lcl_CalcMinRowHeight( this, bConsiderObjs );
5207 if ( (aRectFnSet.GetHeight(getFrameArea()) - nRealDist) < nMinHeight )
5208 nRealDist = aRectFnSet.GetHeight(getFrameArea()) - nMinHeight;
5210 if ( nRealDist < 0 )
5211 nRealDist = 0;
5213 SwTwips nReal = nRealDist;
5214 if ( nReal )
5216 if ( !bTst )
5218 SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
5219 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
5220 aRectFnSet.SetHeight( aFrm, nHeight - nReal );
5222 if( IsVertical() && !IsVertLR() )
5224 aFrm.Pos().AdjustX(nReal );
5228 SwLayoutFrame* pFrame = GetUpper();
5229 SwTwips nTmp = pFrame ? pFrame->Shrink(nReal, bTst) : 0;
5230 if ( !bShrinkAnyway && !GetNext() && nTmp != nReal )
5232 //The last one gets the leftover in the upper and therefore takes
5233 //care (otherwise: endless loop)
5234 if ( !bTst )
5236 nReal -= nTmp;
5237 SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
5238 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
5239 aRectFnSet.SetHeight( aFrm, nHeight + nReal );
5241 if( IsVertical() && !IsVertLR() )
5243 aFrm.Pos().AdjustX( -nReal );
5246 nReal = nTmp;
5250 // Invalidate appropriately and update the height to the newest value.
5251 if ( !bTst )
5253 if ( nReal )
5255 if ( GetNext() )
5256 GetNext()->InvalidatePos_();
5257 InvalidateAll_();
5258 SetCompletePaint();
5260 SwTabFrame *pTab = FindTabFrame();
5261 if ( !pTab->IsRebuildLastLine()
5262 && pTab->IsFollow()
5263 && this == pTab->GetFirstNonHeadlineRow()
5264 && !pTab->IsInRecalcLowerRow() )
5266 SwTabFrame* pMasterTab = pTab->FindMaster();
5267 pMasterTab->InvalidatePos();
5270 AdjustCells( aRectFnSet.GetHeight(getFramePrintArea()) - nReal, true );
5272 return nReal;
5275 bool SwRowFrame::IsRowSplitAllowed() const
5277 // Fixed size rows are never allowed to split:
5278 if ( HasFixSize() )
5280 OSL_ENSURE( SwFrameSize::Fixed == GetFormat()->GetFrameSize().GetHeightSizeType(), "pRow claims to have fixed size" );
5281 return false;
5284 // Repeated headlines are never allowed to split:
5285 const SwTabFrame* pTabFrame = FindTabFrame();
5286 if ( pTabFrame->GetTable()->GetRowsToRepeat() > 0 &&
5287 pTabFrame->IsInHeadline( *this ) )
5288 return false;
5290 if ( IsForceRowSplitAllowed() )
5291 return true;
5293 const SwTableLineFormat* pFrameFormat = static_cast<SwTableLineFormat*>(GetTabLine()->GetFrameFormat());
5294 const SwFormatRowSplit& rLP = pFrameFormat->GetRowSplit();
5295 return rLP.GetValue();
5298 bool SwRowFrame::ShouldRowKeepWithNext( const bool bCheckParents ) const
5300 // No KeepWithNext if nested in another table
5301 if ( GetUpper()->GetUpper()->IsCellFrame() )
5302 return false;
5304 const SwCellFrame* pCell = static_cast<const SwCellFrame*>(Lower());
5305 const SwFrame* pText = pCell->Lower();
5307 return pText && pText->IsTextFrame() &&
5308 static_cast<const SwTextFrame*>(pText)->GetTextNodeForParaProps()->GetSwAttrSet().GetKeep(bCheckParents).GetValue();
5311 SwCellFrame::SwCellFrame(const SwTableBox &rBox, SwFrame* pSib, bool bInsertContent)
5312 : SwLayoutFrame( rBox.GetFrameFormat(), pSib )
5313 , m_pTabBox( &rBox )
5315 mnFrameType = SwFrameType::Cell;
5317 if ( !bInsertContent )
5318 return;
5320 //If a StartIdx is available, ContentFrames are added in the cell, otherwise
5321 //Rows have to be present and those are added.
5322 if ( SwNodeOffset nIndex = rBox.GetSttIdx() )
5324 ::InsertCnt_( this, rBox.GetFrameFormat()->GetDoc(), ++nIndex );
5326 else
5328 const SwTableLines &rLines = rBox.GetTabLines();
5329 SwFrame *pTmpPrev = nullptr;
5330 for ( size_t i = 0; i < rLines.size(); ++i )
5332 SwRowFrame *pNew = new SwRowFrame( *rLines[i], this, bInsertContent );
5333 pNew->InsertBehind( this, pTmpPrev );
5334 pTmpPrev = pNew;
5339 void SwCellFrame::DestroyImpl()
5341 sw::BroadcastingModify* pMod = GetFormat();
5342 if( pMod )
5344 // At this stage the lower frames aren't destroyed already,
5345 // therefore we have to do a recursive dispose.
5346 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
5347 SwRootFrame *pRootFrame = getRootFrame();
5348 if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
5349 pRootFrame->GetCurrShell() )
5351 pRootFrame->GetCurrShell()->Imp()->DisposeAccessibleFrame( this, true );
5353 #endif
5355 pMod->Remove( this );
5356 if( !pMod->HasWriterListeners() )
5357 delete pMod;
5360 SwLayoutFrame::DestroyImpl();
5363 SwCellFrame::~SwCellFrame()
5367 static bool lcl_ArrangeLowers( SwLayoutFrame *pLay, tools::Long lYStart, bool bInva )
5369 bool bRet = false;
5370 SwFrame *pFrame = pLay->Lower();
5371 SwRectFnSet aRectFnSet(pLay);
5372 while ( pFrame )
5374 tools::Long nFrameTop = aRectFnSet.GetTop(pFrame->getFrameArea());
5375 if( nFrameTop != lYStart )
5377 bRet = true;
5378 const tools::Long lDiff = aRectFnSet.YDiff( lYStart, nFrameTop );
5379 const tools::Long lDiffX = lYStart - nFrameTop;
5382 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFrame);
5383 aRectFnSet.SubTop( aFrm, -lDiff );
5384 aRectFnSet.AddBottom( aFrm, lDiff );
5387 pFrame->SetCompletePaint();
5389 if ( !pFrame->GetNext() )
5390 pFrame->SetRetouche();
5391 if( bInva )
5392 pFrame->Prepare( PrepareHint::FramePositionChanged );
5393 if ( pFrame->IsLayoutFrame() && static_cast<SwLayoutFrame*>(pFrame)->Lower() )
5394 lcl_ArrangeLowers( static_cast<SwLayoutFrame*>(pFrame),
5395 aRectFnSet.GetTop(static_cast<SwLayoutFrame*>(pFrame)->Lower()->getFrameArea())
5396 + lDiffX, bInva );
5397 if ( pFrame->GetDrawObjs() )
5399 for ( size_t i = 0; i < pFrame->GetDrawObjs()->size(); ++i )
5401 SwAnchoredObject* pAnchoredObj = (*pFrame->GetDrawObjs())[i];
5402 // #i26945# - check, if anchored object
5403 // is lower of layout frame by checking, if the anchor
5404 // frame, which contains the anchor position, is a lower
5405 // of the layout frame.
5406 if ( !pLay->IsAnLower( pAnchoredObj->GetAnchorFrameContainingAnchPos() ) )
5408 continue;
5410 // #i52904# - distinguish between anchored
5411 // objects, whose vertical position depends on its anchor
5412 // frame and whose vertical position is independent
5413 // from its anchor frame.
5414 bool bVertPosDepOnAnchor( true );
5416 SwFormatVertOrient aVert( pAnchoredObj->GetFrameFormat().GetVertOrient() );
5417 switch ( aVert.GetRelationOrient() )
5419 case text::RelOrientation::PAGE_FRAME:
5420 case text::RelOrientation::PAGE_PRINT_AREA:
5421 bVertPosDepOnAnchor = false;
5422 break;
5423 default: break;
5426 if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
5429 // OD 2004-05-18 #i28701# - no direct move of objects,
5430 // which are anchored to-paragraph/to-character, if
5431 // the wrapping style influence has to be considered
5432 // on the object positioning.
5433 // #i52904# - no direct move of objects,
5434 // whose vertical position doesn't depend on anchor frame.
5435 const bool bDirectMove =
5436 FAR_AWAY != pFly->getFrameArea().Top() &&
5437 bVertPosDepOnAnchor &&
5438 !pFly->ConsiderObjWrapInfluenceOnObjPos();
5439 if ( bDirectMove )
5442 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFly);
5443 aRectFnSet.SubTop( aFrm, -lDiff );
5444 aRectFnSet.AddBottom( aFrm, lDiff );
5447 pFly->GetVirtDrawObj()->SetBoundAndSnapRectsDirty();
5448 // --> OD 2004-08-17 - also notify view of <SdrObject>
5449 // instance, which represents the Writer fly frame in
5450 // the drawing layer
5451 pFly->GetVirtDrawObj()->SetChanged();
5452 // #i58280#
5453 pFly->InvalidateObjRectWithSpaces();
5456 if ( pFly->IsFlyInContentFrame() )
5458 static_cast<SwFlyInContentFrame*>(pFly)->AddRefOfst( lDiff );
5459 // #115759# - reset current relative
5460 // position to get re-positioned, if not directly moved.
5461 if ( !bDirectMove )
5463 pAnchoredObj->SetCurrRelPos( Point( 0, 0 ) );
5466 else if( pFly->IsAutoPos() )
5468 pFly->AddLastCharY( lDiff );
5469 // OD 2004-05-18 #i28701# - follow-up of #i22341#
5470 // <mnLastTopOfLine> has also been adjusted.
5471 pFly->AddLastTopOfLineY( lDiff );
5473 // #i26945# - re-registration at
5474 // page frame of anchor frame, if table frame isn't
5475 // a follow table and table frame isn't in its
5476 // rebuild of last line.
5477 const SwTabFrame* pTabFrame = pLay->FindTabFrame();
5478 // - save: check, if table frame is found.
5479 if ( pTabFrame &&
5480 !( pTabFrame->IsFollow() &&
5481 pTabFrame->FindMaster()->IsRebuildLastLine() ) &&
5482 pFly->IsFlyFreeFrame() )
5484 SwPageFrame* pPageFrame = pFly->GetPageFrame();
5485 SwPageFrame* pPageOfAnchor = pFrame->FindPageFrame();
5486 if ( pPageFrame != pPageOfAnchor )
5488 pFly->InvalidatePos();
5489 if ( pPageFrame )
5490 pPageFrame->MoveFly( pFly, pPageOfAnchor );
5491 else
5492 pPageOfAnchor->AppendFlyToPage( pFly );
5495 // OD 2004-05-11 #i28701# - Because of the introduction
5496 // of new positionings and alignments (e.g. aligned at
5497 // page area, but anchored at-character), the position
5498 // of the Writer fly frame has to be invalidated.
5499 pFly->InvalidatePos();
5501 // #i26945# - follow-up of #i3317#
5502 // No arrangement of lowers, if Writer fly frame isn't
5503 // moved
5504 if ( bDirectMove &&
5505 ::lcl_ArrangeLowers( pFly,
5506 aRectFnSet.GetPrtTop(*pFly),
5507 bInva ) )
5509 pFly->SetCompletePaint();
5512 else if ( dynamic_cast< const SwAnchoredDrawObject *>( pAnchoredObj ) != nullptr )
5514 // #i26945#
5515 const SwTabFrame* pTabFrame = pLay->FindTabFrame();
5516 if ( pTabFrame &&
5517 !( pTabFrame->IsFollow() &&
5518 pTabFrame->FindMaster()->IsRebuildLastLine() ) &&
5519 (pAnchoredObj->GetFrameFormat().GetAnchor().GetAnchorId()
5520 != RndStdIds::FLY_AS_CHAR))
5522 SwPageFrame* pPageFrame = pAnchoredObj->GetPageFrame();
5523 SwPageFrame* pPageOfAnchor = pFrame->FindPageFrame();
5524 if ( pPageFrame != pPageOfAnchor )
5526 pAnchoredObj->InvalidateObjPos();
5527 if ( pPageFrame )
5529 pPageFrame->RemoveDrawObjFromPage( *pAnchoredObj );
5531 pPageOfAnchor->AppendDrawObjToPage( *pAnchoredObj );
5534 // #i28701# - adjust last character
5535 // rectangle and last top of line.
5536 pAnchoredObj->AddLastCharY( lDiff );
5537 pAnchoredObj->AddLastTopOfLineY( lDiff );
5538 // #i52904# - re-introduce direct move
5539 // of drawing objects
5540 const bool bDirectMove =
5541 static_cast<const SwDrawFrameFormat&>(pAnchoredObj->GetFrameFormat()).IsPosAttrSet() &&
5542 bVertPosDepOnAnchor &&
5543 !pAnchoredObj->ConsiderObjWrapInfluenceOnObjPos();
5544 if ( bDirectMove )
5546 SwObjPositioningInProgress aObjPosInProgress( *pAnchoredObj );
5547 if ( aRectFnSet.IsVert() )
5549 pAnchoredObj->DrawObj()->Move( Size( lDiff, 0 ) );
5551 else
5553 pAnchoredObj->DrawObj()->Move( Size( 0, lDiff ) );
5555 // #i58280#
5556 pAnchoredObj->InvalidateObjRectWithSpaces();
5558 pAnchoredObj->InvalidateObjPos();
5560 else
5562 OSL_FAIL( "<lcl_ArrangeLowers(..)> - unknown type of anchored object!" );
5567 // Columns and cells are ordered horizontal, not vertical
5568 if( !pFrame->IsColumnFrame() && !pFrame->IsCellFrame() )
5569 lYStart = aRectFnSet.YInc( lYStart,
5570 aRectFnSet.GetHeight(pFrame->getFrameArea()) );
5572 // Nowadays, the content inside a cell can flow into the follow table.
5573 // Thus, the cell may only grow up to the end of the environment.
5574 // So the content may have grown, but the cell could not grow.
5575 // Therefore we have to trigger a formatting for the frames, which do
5576 // not fit into the cell anymore:
5577 SwTwips nDistanceToUpperPrtBottom =
5578 aRectFnSet.BottomDist( pFrame->getFrameArea(), aRectFnSet.GetPrtBottom(*pLay) );
5579 // #i56146# - Revise fix of issue #i26945#
5580 // do *not* consider content inside fly frames, if it's an undersized paragraph.
5581 // #i26945# - consider content inside fly frames
5582 if ( nDistanceToUpperPrtBottom < 0 &&
5583 ( ( pFrame->IsInFly() &&
5584 ( !pFrame->IsTextFrame() ||
5585 !static_cast<SwTextFrame*>(pFrame)->IsUndersized() ) ) ||
5586 pFrame->IsInSplitTableRow() ) )
5588 pFrame->InvalidatePos();
5591 pFrame = pFrame->GetNext();
5593 return bRet;
5596 void SwCellFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
5598 OSL_ENSURE( pAttrs, "CellFrame::Format, pAttrs is 0." );
5599 const SwTabFrame* pTab = FindTabFrame();
5600 SwRectFnSet aRectFnSet(pTab);
5602 if ( !isFramePrintAreaValid() )
5604 setFramePrintAreaValid(true);
5606 //Adjust position.
5607 if ( Lower() )
5609 SwTwips nTopSpace, nBottomSpace, nLeftSpace, nRightSpace;
5610 // #i29550#
5611 if ( pTab->IsCollapsingBorders() && !Lower()->IsRowFrame() )
5613 const SvxBoxItem& rBoxItem = pAttrs->GetBox();
5614 nLeftSpace = rBoxItem.GetDistance( SvxBoxItemLine::LEFT );
5615 nRightSpace = rBoxItem.GetDistance( SvxBoxItemLine::RIGHT );
5616 nTopSpace = static_cast<SwRowFrame*>(GetUpper())->GetTopMarginForLowers();
5617 nBottomSpace = static_cast<SwRowFrame*>(GetUpper())->GetBottomMarginForLowers();
5619 else
5621 // OD 23.01.2003 #106895# - add 1st param to <SwBorderAttrs::CalcRight(..)>
5622 nLeftSpace = pAttrs->CalcLeft( this );
5623 nRightSpace = pAttrs->CalcRight( this );
5624 nTopSpace = pAttrs->CalcTop();
5625 nBottomSpace = pAttrs->CalcBottom();
5627 aRectFnSet.SetXMargins( *this, nLeftSpace, nRightSpace );
5628 aRectFnSet.SetYMargins( *this, nTopSpace, nBottomSpace );
5631 // #i26945#
5632 tools::Long nRemaining = GetTabBox()->getRowSpan() >= 1 ?
5633 ::lcl_CalcMinCellHeight( this, pTab->IsConsiderObjsForMinCellHeight(), pAttrs ) :
5635 if ( !isFrameAreaSizeValid() )
5637 setFrameAreaSizeValid(true);
5639 //The VarSize of the CellFrames is always the width.
5640 //The width is not variable though, it is defined by the format.
5641 //This predefined value however does not necessary match the actual
5642 //width. The width is calculated based on the attribute, the value in
5643 //the attribute matches the desired value of the TabFrame. Changes which
5644 //were done there are taken into account here proportionately.
5645 //If the cell doesn't have a neighbour anymore, it does not take the
5646 //attribute into account and takes the rest of the upper instead.
5647 SwTwips nWidth;
5648 if ( GetNext() )
5650 const SwTwips nWish = pTab->GetFormat()->GetFrameSize().GetWidth();
5651 nWidth = pAttrs->GetSize().Width();
5653 OSL_ENSURE( nWish, "Table without width?" );
5654 OSL_ENSURE( nWidth <= nWish, "Width of cell larger than table." );
5655 OSL_ENSURE( nWidth > 0, "Box without width" );
5657 const tools::Long nPrtWidth = aRectFnSet.GetWidth(pTab->getFramePrintArea());
5658 if ( nWish != nPrtWidth )
5660 // Avoid rounding problems, at least for the new table model
5661 if ( pTab->GetTable()->IsNewModel() )
5663 // 1. sum of widths of cells up to this cell (in model)
5664 const SwTableLine* pTabLine = GetTabBox()->GetUpper();
5665 const SwTableBoxes& rBoxes = pTabLine->GetTabBoxes();
5666 const SwTableBox* pTmpBox = nullptr;
5668 SwTwips nSumWidth = 0;
5669 size_t i = 0;
5672 pTmpBox = rBoxes[ i++ ];
5673 nSumWidth += pTmpBox->GetFrameFormat()->GetFrameSize().GetWidth();
5675 while ( pTmpBox != GetTabBox() );
5677 // 2. calculate actual width of cells up to this one
5678 double nTmpWidth = nSumWidth;
5679 nTmpWidth *= nPrtWidth;
5680 nTmpWidth /= nWish;
5681 nWidth = static_cast<SwTwips>(nTmpWidth);
5683 // 3. calculate frame widths of cells up to this one:
5684 const SwFrame* pTmpCell = static_cast<const SwLayoutFrame*>(GetUpper())->Lower();
5685 SwTwips nSumFrameWidths = 0;
5686 while ( pTmpCell != this )
5688 nSumFrameWidths += aRectFnSet.GetWidth(pTmpCell->getFrameArea());
5689 pTmpCell = pTmpCell->GetNext();
5692 nWidth = nWidth - nSumFrameWidths;
5694 else
5696 // #i12092# use double instead of long,
5697 // otherwise this could lead to overflows
5698 double nTmpWidth = nWidth;
5699 nTmpWidth *= nPrtWidth;
5700 nTmpWidth /= nWish;
5701 nWidth = static_cast<SwTwips>(nTmpWidth);
5705 else
5707 OSL_ENSURE( pAttrs->GetSize().Width() > 0, "Box without width" );
5708 nWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
5709 SwFrame *pPre = GetUpper()->Lower();
5710 while ( pPre != this )
5712 nWidth -= aRectFnSet.GetWidth(pPre->getFrameArea());
5713 pPre = pPre->GetNext();
5717 const tools::Long nDiff = nWidth - aRectFnSet.GetWidth(getFrameArea());
5720 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
5722 if( IsNeighbourFrame() && IsRightToLeft() )
5724 aRectFnSet.SubLeft( aFrm, nDiff );
5726 else
5728 aRectFnSet.AddRight( aFrm, nDiff );
5733 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
5734 aRectFnSet.AddRight( aPrt, nDiff );
5737 //Adjust the height, it's defined through the content and the margins.
5738 const tools::Long nDiffHeight = nRemaining - aRectFnSet.GetHeight(getFrameArea());
5739 if ( nDiffHeight )
5741 if ( nDiffHeight > 0 )
5743 //Validate again if no growth happened. Invalidation is done
5744 //through AdjustCells of the row.
5745 if ( !Grow( nDiffHeight ) )
5747 setFrameAreaSizeValid(true);
5748 setFramePrintAreaValid(true);
5751 else
5753 // Only keep invalidated if shrinking was actually done; the
5754 // attempt can be ignored because all horizontally adjoined
5755 // cells have to be the same height.
5756 if ( !Shrink( -nDiffHeight ) )
5758 setFrameAreaSizeValid(true);
5759 setFramePrintAreaValid(true);
5764 const SwFormatVertOrient &rOri = pAttrs->GetAttrSet().GetVertOrient();
5766 if ( !Lower() )
5767 return;
5769 // From now on, all operations are related to the table cell.
5770 aRectFnSet.Refresh(this);
5772 SwPageFrame* pPg = nullptr;
5773 if ( !FindTabFrame()->IsRebuildLastLine() && text::VertOrientation::NONE != rOri.GetVertOrient() &&
5774 // #158225# no vertical alignment of covered cells
5775 !IsCoveredCell() &&
5776 (pPg = FindPageFrame())!=nullptr )
5778 if ( !Lower()->IsContentFrame() && !Lower()->IsSctFrame() && !Lower()->IsTabFrame() )
5780 // OSL_ENSURE(for HTML-import!
5781 OSL_ENSURE( false, "VAlign to cell without content" );
5782 return;
5784 bool bVertDir = true;
5785 // #i43913# - no vertical alignment, if wrapping
5786 // style influence is considered on object positioning and
5787 // an object is anchored inside the cell.
5788 const bool bConsiderWrapOnObjPos( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) );
5789 // No alignment if fly with wrap overlaps the cell.
5790 if ( pPg->GetSortedObjs() )
5792 SwRect aRect( getFramePrintArea() ); aRect += getFrameArea().Pos();
5793 for (SwAnchoredObject* pAnchoredObj : *pPg->GetSortedObjs())
5795 SwRect aTmp( pAnchoredObj->GetObjRect() );
5796 const SwFrame* pAnch = pAnchoredObj->GetAnchorFrame();
5797 if ( (bConsiderWrapOnObjPos && IsAnLower( pAnch )) || (!bConsiderWrapOnObjPos && aTmp.Overlaps( aRect )) )
5799 const SwFrameFormat& rAnchoredObjFrameFormat = pAnchoredObj->GetFrameFormat();
5800 const SwFormatSurround &rSur = rAnchoredObjFrameFormat.GetSurround();
5802 if ( bConsiderWrapOnObjPos || css::text::WrapTextMode_THROUGH != rSur.GetSurround() )
5804 // frames, which the cell is a lower of, aren't relevant
5805 if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
5807 if ( pFly->IsAnLower( this ) )
5808 continue;
5811 // #i43913#
5812 // #i52904# - no vertical alignment,
5813 // if object, anchored inside cell, has temporarily
5814 // consider its wrapping style on object positioning.
5815 // #i58806# - no vertical alignment
5816 // if object does not follow the text flow.
5817 if ( bConsiderWrapOnObjPos ||
5818 !IsAnLower( pAnch ) ||
5819 pAnchoredObj->IsTmpConsiderWrapInfluence() ||
5820 !rAnchoredObjFrameFormat.GetFollowTextFlow().GetValue() )
5822 bVertDir = false;
5823 break;
5830 tools::Long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
5831 if( ( bVertDir && ( nRemaining -= lcl_CalcTopAndBottomMargin( *this, *pAttrs ) ) < nPrtHeight ) ||
5832 aRectFnSet.GetTop(Lower()->getFrameArea()) != aRectFnSet.GetPrtTop(*this) )
5834 tools::Long nDiff = aRectFnSet.GetHeight(getFramePrintArea()) - nRemaining;
5835 if ( nDiff >= 0 )
5837 tools::Long lTopOfst = 0;
5838 if ( bVertDir )
5840 switch ( rOri.GetVertOrient() )
5842 case text::VertOrientation::CENTER: lTopOfst = nDiff / 2; break;
5843 case text::VertOrientation::BOTTOM: lTopOfst = nDiff; break;
5844 default: break;
5847 tools::Long nTmp = aRectFnSet.YInc(
5848 aRectFnSet.GetPrtTop(*this), lTopOfst );
5849 if ( lcl_ArrangeLowers( this, nTmp, !bVertDir ) )
5850 SetCompletePaint();
5854 else
5856 //Was an old alignment taken into account?
5857 if ( Lower()->IsContentFrame() )
5859 const tools::Long lYStart = aRectFnSet.GetPrtTop(*this);
5860 lcl_ArrangeLowers( this, lYStart, true );
5864 // Handle rotated portions of lowers: it's possible that we have changed amount of vertical
5865 // space since the last format, and this affects how many rotated portions we need. So throw
5866 // away the current portions to build them using the new line width.
5867 for (SwFrame* pFrame = Lower(); pFrame; pFrame = pFrame->GetNext())
5869 if (!pFrame->IsTextFrame())
5871 continue;
5874 auto pTextFrame = static_cast<SwTextFrame*>(pFrame);
5875 if (!pTextFrame->GetHasRotatedPortions())
5877 continue;
5880 pTextFrame->Prepare();
5884 void SwCellFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
5886 if(auto pNewFormatHint = dynamic_cast<const sw::TableBoxFormatChanged*>(&rHint))
5888 if(GetTabBox() != &pNewFormatHint->m_rTableBox)
5889 return;
5890 RegisterToFormat(const_cast<SwTableBoxFormat&>(pNewFormatHint->m_rNewFormat));
5891 InvalidateSize();
5892 InvalidatePrt_();
5893 SetCompletePaint();
5894 SetDerivedVert(false);
5895 CheckDirChange();
5897 // #i47489#
5898 // make sure that the row will be formatted, in order
5899 // to have the correct Get(Top|Bottom)MarginForLowers values
5900 // set at the row.
5901 const SwTabFrame* pTab = FindTabFrame();
5902 if(pTab && pTab->IsCollapsingBorders())
5904 SwFrame* pRow = GetUpper();
5905 pRow->InvalidateSize_();
5906 pRow->InvalidatePrt_();
5909 else if(auto pMoveTableBoxHint = dynamic_cast<const sw::MoveTableBoxHint*>(&rHint))
5911 if(GetTabBox() != &pMoveTableBoxHint->m_rTableBox)
5912 return;
5913 const_cast<SwFrameFormat*>(&pMoveTableBoxHint->m_rNewFormat)->Add(this);
5914 InvalidateAll();
5915 ReinitializeFrameSizeAttrFlags();
5916 SetDerivedVert(false);
5917 CheckDirChange();
5918 return;
5920 else if (rHint.GetId() == SfxHintId::SwLegacyModify)
5922 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
5923 const SfxPoolItem* pVertOrientItem = nullptr;
5924 const SfxPoolItem* pProtectItem = nullptr;
5925 const SfxPoolItem* pFrameDirItem = nullptr;
5926 const SfxPoolItem* pBoxItem = nullptr;
5927 const auto nWhich = pLegacy->m_pNew ? pLegacy->m_pNew->Which() : 0;
5928 switch(nWhich)
5930 case RES_ATTRSET_CHG:
5932 auto& rChgSet = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew)->GetChgSet();
5933 pVertOrientItem = rChgSet.GetItemIfSet(RES_VERT_ORIENT, false);
5934 pProtectItem = rChgSet.GetItemIfSet(RES_PROTECT, false);
5935 pFrameDirItem = rChgSet.GetItemIfSet(RES_FRAMEDIR, false);
5936 pBoxItem = rChgSet.GetItemIfSet(RES_BOX, false);
5937 break;
5939 case RES_VERT_ORIENT:
5940 pVertOrientItem = pLegacy->m_pNew;
5941 break;
5942 case RES_PROTECT:
5943 pProtectItem = pLegacy->m_pNew;
5944 break;
5945 case RES_FRAMEDIR:
5946 pFrameDirItem = pLegacy->m_pNew;
5947 break;
5948 case RES_BOX:
5949 pBoxItem = pLegacy->m_pNew;
5950 break;
5952 if(pVertOrientItem)
5954 bool bInva = true;
5955 const auto eVertOrient = static_cast<const SwFormatVertOrient*>(pVertOrientItem)->GetVertOrient();
5956 if(text::VertOrientation::NONE == eVertOrient && Lower() && Lower()->IsContentFrame())
5958 SwRectFnSet aRectFnSet(this);
5959 const tools::Long lYStart = aRectFnSet.GetPrtTop(*this);
5960 bInva = lcl_ArrangeLowers(this, lYStart, false);
5962 if (bInva)
5964 SetCompletePaint();
5965 InvalidatePrt();
5968 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
5969 if(pProtectItem)
5971 SwViewShell* pSh = getRootFrame()->GetCurrShell();
5972 if(pSh && pSh->GetLayout()->IsAnyShellAccessible())
5973 pSh->Imp()->InvalidateAccessibleEditableState(true, this);
5975 #endif
5976 if(pFrameDirItem)
5978 SetDerivedVert(false);
5979 CheckDirChange();
5981 // #i29550#
5982 if(pBoxItem)
5984 SwFrame* pTmpUpper = GetUpper();
5985 while(pTmpUpper->GetUpper() && !pTmpUpper->GetUpper()->IsTabFrame())
5986 pTmpUpper = pTmpUpper->GetUpper();
5988 SwTabFrame* pTabFrame = static_cast<SwTabFrame*>(pTmpUpper->GetUpper());
5989 if(pTabFrame->IsCollapsingBorders())
5991 // Invalidate lowers of this and next row:
5992 lcl_InvalidateAllLowersPrt(static_cast<SwRowFrame*>(pTmpUpper));
5993 pTmpUpper = pTmpUpper->GetNext();
5994 if(pTmpUpper)
5995 lcl_InvalidateAllLowersPrt(static_cast<SwRowFrame*>(pTmpUpper));
5996 else
5997 pTabFrame->InvalidatePrt();
6000 SwLayoutFrame::SwClientNotify(rMod, rHint);
6004 tools::Long SwCellFrame::GetLayoutRowSpan() const
6006 const SwTableBox *pTabBox = GetTabBox();
6007 tools::Long nRet = pTabBox ? pTabBox->getRowSpan() : 0;
6008 if ( nRet < 1 )
6010 const SwFrame* pRow = GetUpper();
6011 const SwTabFrame* pTab = pRow ? static_cast<const SwTabFrame*>(pRow->GetUpper()) : nullptr;
6013 if ( pTab && pTab->IsFollow() && pRow == pTab->GetFirstNonHeadlineRow() )
6014 nRet = -nRet;
6016 return nRet;
6019 const SwCellFrame* SwCellFrame::GetCoveredCellInRow(const SwRowFrame& rRow) const
6021 if (GetLayoutRowSpan() <= 1)
6023 // Not merged vertically.
6024 return nullptr;
6027 for (const SwFrame* pCell = rRow.GetLower(); pCell; pCell = pCell->GetNext())
6029 if (!pCell->IsCellFrame())
6031 continue;
6034 auto pCellFrame = static_cast<const SwCellFrame*>(pCell);
6035 if (!pCellFrame->IsCoveredCell())
6037 continue;
6040 if (pCellFrame->getFrameArea().Left() != getFrameArea().Left())
6042 continue;
6045 if (pCellFrame->getFrameArea().Width() != getFrameArea().Width())
6047 continue;
6050 // pCellFrame is covered, there are only covered cell frames between "this" and pCellFrame
6051 // and the horizontal position/size matches "this".
6052 return pCellFrame;
6055 return nullptr;
6058 std::vector<const SwCellFrame*> SwCellFrame::GetCoveredCells() const
6060 std::vector<const SwCellFrame*> aRet;
6061 if (GetLayoutRowSpan() <= 1)
6063 return aRet;
6066 if (!GetUpper()->IsRowFrame())
6068 return aRet;
6071 auto pFirstRowFrame = static_cast<const SwRowFrame*>(GetUpper());
6072 if (!pFirstRowFrame->GetNext())
6074 return aRet;
6077 if (!pFirstRowFrame->GetNext()->IsRowFrame())
6079 return aRet;
6082 for (const SwFrame* pRow = pFirstRowFrame->GetNext(); pRow; pRow = pRow->GetNext())
6084 if (!pRow->IsRowFrame())
6086 continue;
6089 auto pRowFrame = static_cast<const SwRowFrame*>(pRow);
6090 const SwCellFrame* pCovered = GetCoveredCellInRow(*pRowFrame);
6091 if (!pCovered)
6093 continue;
6096 // Found a cell in a next row that is covered by "this".
6097 aRet.push_back(pCovered);
6100 return aRet;
6103 void SwCellFrame::dumpAsXmlAttributes(xmlTextWriterPtr pWriter) const
6105 SwFrame::dumpAsXmlAttributes(pWriter);
6106 if (SwCellFrame* pFollow = GetFollowCell())
6107 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("follow"), "%" SAL_PRIuUINT32, pFollow->GetFrameId());
6109 if (SwCellFrame* pPrevious = GetPreviousCell())
6110 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("precede"), "%" SAL_PRIuUINT32, pPrevious->GetFrameId());
6113 void SwCellFrame::dumpAsXml(xmlTextWriterPtr writer) const
6115 (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("cell"));
6116 dumpAsXmlAttributes(writer);
6117 (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "rowspan" ), "%ld", GetLayoutRowSpan() );
6119 (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
6120 dumpInfosAsXml(writer);
6121 (void)xmlTextWriterEndElement(writer);
6122 dumpChildrenAsXml(writer);
6124 (void)xmlTextWriterEndElement(writer);
6127 // #i103961#
6128 void SwCellFrame::Cut()
6130 // notification for accessibility
6131 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
6133 SwRootFrame *pRootFrame = getRootFrame();
6134 if( pRootFrame && pRootFrame->IsAnyShellAccessible() )
6136 SwViewShell* pVSh = pRootFrame->GetCurrShell();
6137 if ( pVSh && pVSh->Imp() )
6139 pVSh->Imp()->DisposeAccessibleFrame( this );
6143 #endif
6145 SwLayoutFrame::Cut();
6148 // Helper functions for repeated headlines:
6150 bool SwTabFrame::IsInHeadline( const SwFrame& rFrame ) const
6152 OSL_ENSURE( IsAnLower( &rFrame ) && rFrame.IsInTab(),
6153 "SwTabFrame::IsInHeadline called for frame not lower of table" );
6155 const SwFrame* pTmp = &rFrame;
6156 while ( !pTmp->GetUpper()->IsTabFrame() )
6157 pTmp = pTmp->GetUpper();
6159 return GetTable()->IsHeadline( *static_cast<const SwRowFrame*>(pTmp)->GetTabLine() );
6163 * If this is a master table, we can may assume, that there are at least
6164 * nRepeat lines in the table.
6165 * If this is a follow table, there are intermediate states for the table
6166 * layout, e.g., during deletion of rows, which makes it necessary to find
6167 * the first non-headline row by evaluating the headline flag at the row frame.
6169 SwRowFrame* SwTabFrame::GetFirstNonHeadlineRow() const
6171 SwRowFrame* pRet = const_cast<SwRowFrame*>(static_cast<const SwRowFrame*>(Lower()));
6172 if ( pRet )
6174 if ( IsFollow() )
6176 while ( pRet && pRet->IsRepeatedHeadline() )
6177 pRet = static_cast<SwRowFrame*>(pRet->GetNext());
6179 else
6181 sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat();
6182 while ( pRet && nRepeat > 0 )
6184 pRet = static_cast<SwRowFrame*>(pRet->GetNext());
6185 --nRepeat;
6190 return pRet;
6193 bool SwTable::IsHeadline( const SwTableLine& rLine ) const
6195 for ( sal_uInt16 i = 0; i < GetRowsToRepeat(); ++i )
6196 if ( GetTabLines()[ i ] == &rLine )
6197 return true;
6199 return false;
6202 bool SwTabFrame::IsLayoutSplitAllowed() const
6204 return GetFormat()->GetLayoutSplit().GetValue();
6207 // #i29550#
6209 sal_uInt16 SwTabFrame::GetBottomLineSize() const
6211 OSL_ENSURE( IsCollapsingBorders(),
6212 "BottomLineSize only required for collapsing borders" );
6214 OSL_ENSURE( Lower(), "Warning! Trying to prevent a crash" );
6216 const SwFrame* pTmp = GetLastLower();
6218 // #124755# Try to make code robust
6219 if ( !pTmp ) return 0;
6221 return static_cast<const SwRowFrame*>(pTmp)->GetBottomLineSize();
6224 bool SwTabFrame::IsCollapsingBorders() const
6226 return GetFormat()->GetAttrSet().Get( RES_COLLAPSING_BORDERS ).GetValue();
6229 void SwTabFrame::dumpAsXml(xmlTextWriterPtr writer) const
6231 (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("tab"));
6232 dumpAsXmlAttributes(writer);
6234 (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
6235 dumpInfosAsXml(writer);
6236 (void)xmlTextWriterEndElement(writer);
6237 dumpChildrenAsXml(writer);
6239 (void)xmlTextWriterEndElement(writer);
6242 /// Local helper function to calculate height of first text row
6243 static SwTwips lcl_CalcHeightOfFirstContentLine( const SwRowFrame& rSourceLine )
6245 // Find corresponding split line in master table
6246 const SwTabFrame* pTab = rSourceLine.FindTabFrame();
6247 SwRectFnSet aRectFnSet(pTab);
6248 const SwCellFrame* pCurrSourceCell = static_cast<const SwCellFrame*>(rSourceLine.Lower());
6250 // 1. Case: rSourceLine is a follow flow line.
6251 // In this case we have to return the minimum of the heights
6252 // of the first lines in rSourceLine.
6254 // 2. Case: rSourceLine is not a follow flow line.
6255 // In this case we have to return the maximum of the heights
6256 // of the first lines in rSourceLine.
6257 bool bIsInFollowFlowLine = rSourceLine.IsInFollowFlowRow();
6258 SwTwips nHeight = bIsInFollowFlowLine ? LONG_MAX : 0;
6260 while ( pCurrSourceCell )
6262 // NEW TABLES
6263 // Skip cells which are not responsible for the height of
6264 // the follow flow line:
6265 if ( bIsInFollowFlowLine && pCurrSourceCell->GetLayoutRowSpan() > 1 )
6267 pCurrSourceCell = static_cast<const SwCellFrame*>(pCurrSourceCell->GetNext());
6268 continue;
6271 const SwFrame *pTmp = pCurrSourceCell->Lower();
6272 if ( pTmp )
6274 SwTwips nTmpHeight = USHRT_MAX;
6275 // #i32456# Consider lower row frames
6276 if ( pTmp->IsRowFrame() )
6278 const SwRowFrame* pTmpSourceRow = static_cast<const SwRowFrame*>(pCurrSourceCell->Lower());
6279 nTmpHeight = lcl_CalcHeightOfFirstContentLine( *pTmpSourceRow );
6281 else if (pTmp->IsTabFrame() || (pTmp->IsSctFrame() && pTmp->GetLower() && pTmp->GetLower()->IsTabFrame()))
6283 SwTabFrame const*const pTabFrame(pTmp->IsTabFrame()
6284 ? static_cast<SwTabFrame const*>(pTmp)
6285 : static_cast<SwTabFrame const*>(pTmp->GetLower()));
6286 nTmpHeight = pTabFrame->CalcHeightOfFirstContentLine();
6288 else if (pTmp->IsTextFrame() || (pTmp->IsSctFrame() && pTmp->GetLower() && pTmp->GetLower()->IsTextFrame()))
6290 // Section frames don't influence the size/position of text
6291 // frames, so 'text frame' and 'text frame in section frame' is
6292 // the same case.
6293 SwTextFrame* pTextFrame = nullptr;
6294 if (pTmp->IsTextFrame())
6295 pTextFrame = const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pTmp));
6296 else
6297 pTextFrame = const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pTmp->GetLower()));
6298 pTextFrame->GetFormatted();
6299 nTmpHeight = pTextFrame->FirstLineHeight();
6302 if ( USHRT_MAX != nTmpHeight )
6304 const SwCellFrame* pPrevCell = pCurrSourceCell->GetPreviousCell();
6305 if ( pPrevCell )
6307 // If we are in a split row, there may be some space
6308 // left in the cell frame of the master row.
6309 // We look for the minimum of all first line heights;
6310 SwTwips nReal = aRectFnSet.GetHeight(pPrevCell->getFramePrintArea());
6311 const SwFrame* pFrame = pPrevCell->Lower();
6312 const SwFrame* pLast = pFrame;
6313 while ( pFrame )
6315 nReal -= aRectFnSet.GetHeight(pFrame->getFrameArea());
6316 pLast = pFrame;
6317 pFrame = pFrame->GetNext();
6320 // #i26831#, #i26520#
6321 // The additional lower space of the current last.
6322 // #115759# - do *not* consider the
6323 // additional lower space for 'master' text frames
6324 if ( pLast && pLast->IsFlowFrame() &&
6325 ( !pLast->IsTextFrame() ||
6326 !static_cast<const SwTextFrame*>(pLast)->GetFollow() ) )
6328 nReal += SwFlowFrame::CastFlowFrame(pLast)->CalcAddLowerSpaceAsLastInTableCell();
6330 // Don't forget the upper space and lower space,
6331 // #115759# - do *not* consider the upper
6332 // and the lower space for follow text frames.
6333 if ( pTmp->IsFlowFrame() &&
6334 ( !pTmp->IsTextFrame() ||
6335 !static_cast<const SwTextFrame*>(pTmp)->IsFollow() ) )
6337 nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)->CalcUpperSpace( nullptr, pLast);
6338 nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)->CalcLowerSpace();
6340 // #115759# - consider additional lower
6341 // space of <pTmp>, if contains only one line.
6342 // In this case it would be the new last text frame, which
6343 // would have no follow and thus would add this space.
6344 if ( pTmp->IsTextFrame() &&
6345 const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pTmp))
6346 ->GetLineCount(TextFrameIndex(COMPLETE_STRING)) == 1)
6348 nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)
6349 ->CalcAddLowerSpaceAsLastInTableCell();
6351 if ( nReal > 0 )
6352 nTmpHeight -= nReal;
6354 else
6356 // pFirstRow is not a FollowFlowRow. In this case,
6357 // we look for the maximum of all first line heights:
6358 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCurrSourceCell );
6359 const SwBorderAttrs &rAttrs = *aAccess.Get();
6360 nTmpHeight += rAttrs.CalcTop() + rAttrs.CalcBottom();
6361 // #i26250#
6362 // Don't forget the upper space and lower space,
6363 if ( pTmp->IsFlowFrame() )
6365 nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)->CalcUpperSpace();
6366 nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)->CalcLowerSpace();
6371 if ( bIsInFollowFlowLine )
6373 // minimum
6374 if ( nTmpHeight < nHeight )
6375 nHeight = nTmpHeight;
6377 else
6379 // maximum
6380 if ( nTmpHeight > nHeight && USHRT_MAX != nTmpHeight )
6381 nHeight = nTmpHeight;
6385 pCurrSourceCell = static_cast<const SwCellFrame*>(pCurrSourceCell->GetNext());
6388 return ( LONG_MAX == nHeight ) ? 0 : nHeight;
6391 /// Function to calculate height of first text row
6392 SwTwips SwTabFrame::CalcHeightOfFirstContentLine() const
6394 SwRectFnSet aRectFnSet(this);
6396 const bool bDontSplit = !IsFollow() && !GetFormat()->GetLayoutSplit().GetValue();
6398 if ( bDontSplit )
6400 // Table is not allowed to split: Take the whole height, that's all
6401 return aRectFnSet.GetHeight(getFrameArea());
6404 SwTwips nTmpHeight = 0;
6406 const SwRowFrame* pFirstRow = GetFirstNonHeadlineRow();
6407 OSL_ENSURE( !IsFollow() || pFirstRow, "FollowTable without Lower" );
6409 // NEW TABLES
6410 if ( pFirstRow && pFirstRow->IsRowSpanLine() && pFirstRow->GetNext() )
6411 pFirstRow = static_cast<const SwRowFrame*>(pFirstRow->GetNext());
6413 // Calculate the height of the headlines:
6414 const sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat();
6415 SwTwips nRepeatHeight = nRepeat ? lcl_GetHeightOfRows( GetLower(), nRepeat ) : 0;
6417 // Calculate the height of the keeping lines
6418 // (headlines + following keeping lines):
6419 SwTwips nKeepHeight = nRepeatHeight;
6420 if ( GetFormat()->GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::TABLE_ROW_KEEP) )
6422 sal_uInt16 nKeepRows = nRepeat;
6424 // Check how many rows want to keep together
6425 while ( pFirstRow && pFirstRow->ShouldRowKeepWithNext() )
6427 ++nKeepRows;
6428 pFirstRow = static_cast<const SwRowFrame*>(pFirstRow->GetNext());
6431 if ( nKeepRows > nRepeat )
6432 nKeepHeight = lcl_GetHeightOfRows( GetLower(), nKeepRows );
6435 // For master tables, the height of the headlines + the height of the
6436 // keeping lines (if any) has to be considered. For follow tables, we
6437 // only consider the height of the keeping rows without the repeated lines:
6438 if ( !IsFollow() )
6440 nTmpHeight = nKeepHeight;
6442 else
6444 nTmpHeight = nKeepHeight - nRepeatHeight;
6447 // pFirstRow row is the first non-heading row.
6448 // nTmpHeight is the height of the heading row if we are a follow.
6449 if ( pFirstRow )
6451 const bool bSplittable = pFirstRow->IsRowSplitAllowed();
6452 const SwTwips nFirstLineHeight = aRectFnSet.GetHeight(pFirstRow->getFrameArea());
6454 if ( !bSplittable )
6456 // pFirstRow is not splittable, but it is still possible that the line height of pFirstRow
6457 // actually is determined by a lower cell with rowspan = -1. In this case we should not
6458 // just return the height of the first line. Basically we need to get the height of the
6459 // line as it would be on the last page. Since this is quite complicated to calculate,
6460 // we only calculate the height of the first line.
6461 SwFormatFrameSize const& rFrameSize(pFirstRow->GetAttrSet()->GetFrameSize());
6462 if ( pFirstRow->GetPrev() &&
6463 static_cast<const SwRowFrame*>(pFirstRow->GetPrev())->IsRowSpanLine()
6464 && rFrameSize.GetHeightSizeType() != SwFrameSize::Fixed)
6466 // Calculate maximum height of all cells with rowspan = 1:
6467 SwTwips nMaxHeight = rFrameSize.GetHeightSizeType() == SwFrameSize::Minimum
6468 ? rFrameSize.GetHeight()
6469 : 0;
6470 const SwCellFrame* pLower2 = static_cast<const SwCellFrame*>(pFirstRow->Lower());
6471 while ( pLower2 )
6473 if ( 1 == pLower2->GetTabBox()->getRowSpan() )
6475 const SwTwips nCellHeight = lcl_CalcMinCellHeight( pLower2, true );
6476 nMaxHeight = std::max( nCellHeight, nMaxHeight );
6478 pLower2 = static_cast<const SwCellFrame*>(pLower2->GetNext());
6480 nTmpHeight += nMaxHeight;
6482 else
6484 nTmpHeight += nFirstLineHeight;
6488 // Optimization: lcl_CalcHeightOfFirstContentLine actually can trigger
6489 // a formatting of the row frame (via the GetFormatted()). We don't
6490 // want this formatting if the row does not have a height.
6491 else if ( 0 != nFirstLineHeight )
6493 const bool bOldJoinLock = IsJoinLocked();
6494 const_cast<SwTabFrame*>(this)->LockJoin();
6495 const SwTwips nHeightOfFirstContentLine = lcl_CalcHeightOfFirstContentLine( *pFirstRow );
6497 // Consider minimum row height:
6498 const SwFormatFrameSize &rSz = pFirstRow->GetFormat()->GetFrameSize();
6500 SwTwips nMinRowHeight = 0;
6501 if (rSz.GetHeightSizeType() == SwFrameSize::Minimum)
6503 nMinRowHeight = std::max(rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*pFirstRow),
6504 tools::Long(0));
6507 nTmpHeight += std::max( nHeightOfFirstContentLine, nMinRowHeight );
6509 if ( !bOldJoinLock )
6510 const_cast<SwTabFrame*>(this)->UnlockJoin();
6514 return nTmpHeight;
6517 // Some more functions for covered/covering cells. This way inclusion of
6518 // SwCellFrame can be avoided
6520 bool SwFrame::IsLeaveUpperAllowed() const
6522 return false;
6525 bool SwCellFrame::IsLeaveUpperAllowed() const
6527 return GetLayoutRowSpan() > 1;
6530 bool SwFrame::IsCoveredCell() const
6532 return false;
6535 bool SwCellFrame::IsCoveredCell() const
6537 return GetLayoutRowSpan() < 1;
6540 bool SwFrame::IsInCoveredCell() const
6542 bool bRet = false;
6544 const SwFrame* pThis = this;
6545 while ( pThis && !pThis->IsCellFrame() )
6546 pThis = pThis->GetUpper();
6548 if ( pThis )
6549 bRet = pThis->IsCoveredCell();
6551 return bRet;
6554 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */