LibreOffice Module sw (master) 1
tabfrm.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <config_wasm_strip.h>
21
22#include <pagefrm.hxx>
23#include <rootfrm.hxx>
26#include <viewimp.hxx>
27#include <fesh.hxx>
28#include <swtable.hxx>
29#include <deletelistener.hxx>
30#include <dflyobj.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>
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>
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>
71
72using namespace ::com::sun::star;
73
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_bResizeHTMLTable(false)
83 , m_bONECalcLowers(false)
84 , m_bHasFollowFlowLine(false)
85 , m_bIsRebuildLastLine(false)
86 , m_bRestrictTableGrowth(false)
87 , m_bRemoveFollowFlowLinePending(false)
88 , m_bConsiderObjsForMinCellHeight(true)
89 , m_bObjsDoesFit(true)
90 , m_bInRecalcLowerRow(false)
91{
92 mbFixSize = false; //Don't fall for import filter again.
94
95 //Create the lines and insert them.
96 const SwTableLines &rLines = rTab.GetTabLines();
97 SwFrame *pTmpPrev = nullptr;
98 bool bHiddenRedlines = getRootFrame()->IsHideRedlines() &&
100 SwRedlineTable::size_type nRedlinePos = 0;
101 for ( size_t i = 0; i < rLines.size(); ++i )
102 {
103 // skip lines deleted with track changes
104 if ( bHiddenRedlines && rLines[i]->IsDeleted(nRedlinePos) )
105 continue;
106
107 SwRowFrame *pNew = new SwRowFrame( *rLines[i], this );
108 if( pNew->Lower() )
109 {
110 pNew->InsertBehind( this, pTmpPrev );
111 pTmpPrev = pNew;
112 }
113 else
115 }
116 OSL_ENSURE( Lower() && Lower()->IsRowFrame(), "SwTabFrame::SwTabFrame: No rows." );
117}
118
120 : SwLayoutFrame( rTab.GetFormat(), &rTab )
121 , SwFlowFrame( static_cast<SwFrame&>(*this) )
122 , m_pTable( rTab.GetTable() )
123 , m_bComplete(false)
124 , m_bCalcLowers(false)
125 , m_bLowersFormatted(false)
126 , m_bLockBackMove(false)
127 , m_bResizeHTMLTable(false)
128 , m_bONECalcLowers(false)
129 , m_bHasFollowFlowLine(false)
130 , m_bIsRebuildLastLine(false)
131 , m_bRestrictTableGrowth(false)
132 , m_bRemoveFollowFlowLinePending(false)
133 , m_bConsiderObjsForMinCellHeight(true)
134 , m_bObjsDoesFit(true)
135 , m_bInRecalcLowerRow(false)
136{
137 mbFixSize = false; //Don't fall for import filter again.
139
140 SetFollow( rTab.GetFollow() );
141 rTab.SetFollow( this );
142}
143
145{
146 // There is some terrible code in fetab.cxx, that
147 // caches pointers to SwTabFrames.
148 ::ClearFEShellTabCols(*GetFormat()->GetDoc(), this);
149
151}
152
154{
155}
156
158{
159 SwTabFrame *pFoll = GetFollow();
160 if ( pFoll->HasFollow() )
161 pFoll->JoinAndDelFollows();
162 pFoll->Cut();
163 SetFollow( pFoll->GetFollow() );
165}
166
168{
169 OSL_ENSURE( Lower() && Lower()->IsRowFrame(), "No rows." );
170
171 SwPageFrame *pPage = FindPageFrame();
172 if ( pPage )
173 {
174 SwRowFrame *pRow = static_cast<SwRowFrame*>(Lower());
175 do
176 {
177 pRow->RegistFlys( pPage );
178 pRow = static_cast<SwRowFrame*>(pRow->GetNext());
179 } while ( pRow );
180 }
181}
182
183static void SwInvalidateAll( SwFrame *pFrame, tools::Long nBottom );
184static void lcl_RecalcRow( SwRowFrame& rRow, tools::Long nBottom );
185static bool lcl_ArrangeLowers( SwLayoutFrame *pLay, tools::Long lYStart, bool bInva );
186// #i26945# - add parameter <_bOnlyRowsAndCells> to control
187// that only row and cell frames are formatted.
188static bool lcl_InnerCalcLayout( SwFrame *pFrame,
189 tools::Long nBottom,
190 bool _bOnlyRowsAndCells = false );
191// OD 2004-02-18 #106629# - correct type of 1st parameter
192// #i26945# - add parameter <_bConsiderObjs> in order to
193// control, if floating screen objects have to be considered for the minimal
194// cell height.
195static SwTwips lcl_CalcMinRowHeight( const SwRowFrame *pRow,
196 const bool _bConsiderObjs );
198
200
201static SwTwips lcl_GetHeightOfRows( const SwFrame* pStart, tools::Long nCount )
202{
203 if ( !nCount || !pStart)
204 return 0;
205
206 SwTwips nRet = 0;
207 SwRectFnSet aRectFnSet(pStart);
208 while ( pStart && nCount > 0 )
209 {
210 nRet += aRectFnSet.GetHeight(pStart->getFrameArea());
211 pStart = pStart->GetNext();
212 --nCount;
213 }
214
215 return nRet;
216}
217
218// Local helper function to insert a new follow flow line
219static SwRowFrame* lcl_InsertNewFollowFlowLine( SwTabFrame& rTab, const SwFrame& rTmpRow, bool bRowSpanLine )
220{
221 OSL_ENSURE( rTmpRow.IsRowFrame(), "No row frame to copy for FollowFlowLine" );
222 const SwRowFrame& rRow = static_cast<const SwRowFrame&>(rTmpRow);
223
224 rTab.SetFollowFlowLine( true );
225 SwRowFrame *pFollowFlowLine = new SwRowFrame(*rRow.GetTabLine(), &rTab, false );
226 pFollowFlowLine->SetRowSpanLine( bRowSpanLine );
227 SwFrame* pFirstRow = rTab.GetFollow()->GetFirstNonHeadlineRow();
228 pFollowFlowLine->InsertBefore( rTab.GetFollow(), pFirstRow );
229 return pFollowFlowLine;
230}
231
232// #i26945# - local helper function to invalidate all lower
233// objects. By parameter <_bMoveObjsOutOfRange> it can be controlled, if
234// additionally the objects are moved 'out of range'.
235static void lcl_InvalidateLowerObjs( SwLayoutFrame& _rLayoutFrame,
236 const bool _bMoveObjsOutOfRange = false,
237 SwPageFrame* _pPageFrame = nullptr )
238{
239 // determine page frame, if needed
240 if ( !_pPageFrame )
241 {
242 _pPageFrame = _rLayoutFrame.FindPageFrame();
243 OSL_ENSURE( _pPageFrame,
244 "<lcl_InvalidateLowerObjs(..)> - missing page frame -> no move of lower objects out of range" );
245 if ( !_pPageFrame )
246 {
247 return;
248 }
249 }
250
251 // loop on lower frames
252 SwFrame* pLowerFrame = _rLayoutFrame.Lower();
253 while ( pLowerFrame )
254 {
255 if ( pLowerFrame->IsLayoutFrame() )
256 {
257 ::lcl_InvalidateLowerObjs( *static_cast<SwLayoutFrame*>(pLowerFrame),
258 _bMoveObjsOutOfRange, _pPageFrame );
259 }
260 if ( pLowerFrame->GetDrawObjs() )
261 {
262 for (size_t i = 0, nCount = pLowerFrame->GetDrawObjs()->size(); i < nCount; ++i)
263 {
264 SwAnchoredObject* pAnchoredObj = (*pLowerFrame->GetDrawObjs())[i];
265
266 // invalidate position of anchored object
267 pAnchoredObj->SetTmpConsiderWrapInfluence( false );
268 pAnchoredObj->SetConsiderForTextWrap( false );
269 pAnchoredObj->UnlockPosition();
270 pAnchoredObj->InvalidateObjPos();
271
272 SwFlyFrame *pFly = pAnchoredObj->DynCastFlyFrame();
273
274 // move anchored object 'out of range'
275 if ( _bMoveObjsOutOfRange )
276 {
277 // indicate, that positioning is progress to avoid
278 // modification of the anchored object resp. it's attributes
279 // due to the movement
280 SwObjPositioningInProgress aObjPosInProgress( *pAnchoredObj );
281 pAnchoredObj->SetObjLeft( _pPageFrame->getFrameArea().Right() );
282 // #115759# - reset character rectangle,
283 // top of line and relative position in order to assure,
284 // that anchored object is correctly positioned.
285 pAnchoredObj->ClearCharRectAndTopOfLine();
286 pAnchoredObj->SetCurrRelPos( Point( 0, 0 ) );
287 if ( pAnchoredObj->GetFrameFormat().GetAnchor().GetAnchorId()
288 == RndStdIds::FLY_AS_CHAR )
289 {
290 pAnchoredObj->AnchorFrame()
292 &(pAnchoredObj->GetFrameFormat()) );
293 }
294 if ( pFly != nullptr )
295 {
297 pFly->GetVirtDrawObj()->SetChanged();
298 }
299 }
300
301 // If anchored object is a fly frame, invalidate its lower objects
302 if ( pFly != nullptr )
303 {
304 ::lcl_InvalidateLowerObjs( *pFly, _bMoveObjsOutOfRange, _pPageFrame );
305 }
306 }
307 }
308 pLowerFrame = pLowerFrame->GetNext();
309 }
310}
311
312// Local helper function to shrink all lowers of pRow to 0 height
314{
315 SwCellFrame* pCurrMasterCell = static_cast<SwCellFrame*>(rRow.Lower());
316 SwRectFnSet aRectFnSet(pCurrMasterCell);
317
318 bool bAllCellsCollapsed = true;
319 while ( pCurrMasterCell )
320 {
321 // NEW TABLES
322 SwCellFrame& rToAdjust = pCurrMasterCell->GetTabBox()->getRowSpan() < 1 ?
323 const_cast<SwCellFrame&>(pCurrMasterCell->FindStartEndOfRowSpanCell( true )) :
324 *pCurrMasterCell;
325
326 // #i26945#
327 // all lowers should have the correct position
328 lcl_ArrangeLowers( &rToAdjust,
329 aRectFnSet.GetPrtTop(rToAdjust),
330 false );
331 // TODO: Optimize number of frames which are set to 0 height
332 // we have to start with the last lower frame, otherwise
333 // the shrink will not shrink the current cell
334 SwFrame* pTmp = rToAdjust.GetLastLower();
335 bool bAllLowersCollapsed = true;
336
337 if ( pTmp && pTmp->IsRowFrame() )
338 {
339 SwRowFrame* pTmpRow = static_cast<SwRowFrame*>(pTmp);
341 }
342 else
343 {
344 // TODO: Optimize number of frames which are set to 0 height
345 while ( pTmp )
346 {
347 // the frames have to be shrunk
348 if ( pTmp->IsTabFrame() )
349 {
350 SwRowFrame* pTmpRow = static_cast<SwRowFrame*>(static_cast<SwTabFrame*>(pTmp)->Lower());
351 bool bAllRowsCollapsed = true;
352
353 while ( pTmpRow )
354 {
356
357 if (aRectFnSet.GetHeight(pTmpRow->getFrameArea()) > 0)
358 bAllRowsCollapsed = false;
359
360 pTmpRow = static_cast<SwRowFrame*>(pTmpRow->GetNext());
361 }
362
363 if (bAllRowsCollapsed)
364 {
365 // All rows of this table have 0 height -> set height of the table itself as well.
367 aRectFnSet.SetHeight(aFrm, 0);
368
370 aRectFnSet.SetTop(aPrt, 0);
371 aRectFnSet.SetHeight(aPrt, 0);
372 }
373 else
374 bAllLowersCollapsed = false;
375 }
376 else
377 {
378 pTmp->Shrink(aRectFnSet.GetHeight(pTmp->getFrameArea()));
380 aRectFnSet.SetTop(aPrt, 0);
381 aRectFnSet.SetHeight(aPrt, 0);
382
383 if (aRectFnSet.GetHeight(pTmp->getFrameArea()) > 0)
384 {
385 bAllLowersCollapsed = false;
386 }
387 }
388
389 pTmp = pTmp->GetPrev();
390 }
391
392 // all lowers should have the correct position
393 lcl_ArrangeLowers( &rToAdjust,
394 aRectFnSet.GetPrtTop(rToAdjust),
395 false );
396 }
397
398 if (bAllLowersCollapsed)
399 {
400 // All lower frame of this cell have 0 height -> set height of the cell itself as well.
401 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pCurrMasterCell);
402 aRectFnSet.SetHeight(aFrm, 0);
403
405 aRectFnSet.SetTop(aPrt, 0);
406 aRectFnSet.SetHeight(aPrt, 0);
407 }
408 else
409 bAllCellsCollapsed = false;
410
411 pCurrMasterCell = static_cast<SwCellFrame*>(pCurrMasterCell->GetNext());
412 }
413
414 if (bAllCellsCollapsed)
415 {
416 // All cells have 0 height -> set height of row as well.
418 aRectFnSet.SetHeight(aFrm, 0);
419
421 aRectFnSet.SetTop(aPrt, 0);
422 aRectFnSet.SetHeight(aPrt, 0);
423 }
424}
425
426// Local helper function to move the content from rSourceLine to rDestLine
427// The content is inserted behind the last content in the corresponding
428// cell in rDestLine.
429static void lcl_MoveRowContent( SwRowFrame& rSourceLine, SwRowFrame& rDestLine )
430{
431 SwCellFrame* pCurrDestCell = static_cast<SwCellFrame*>(rDestLine.Lower());
432 SwCellFrame* pCurrSourceCell = static_cast<SwCellFrame*>(rSourceLine.Lower());
433
434 // Move content of follow cells into master cells
435 while ( pCurrSourceCell )
436 {
437 if ( pCurrSourceCell->Lower() && pCurrSourceCell->Lower()->IsRowFrame() )
438 {
439 SwRowFrame* pTmpSourceRow = static_cast<SwRowFrame*>(pCurrSourceCell->Lower());
440 while ( pTmpSourceRow )
441 {
442 // #125926# Attention! It is possible,
443 // that pTmpSourceRow->IsFollowFlowRow() but pTmpDestRow
444 // cannot be found. In this case, we have to move the complete
445 // row.
446 SwRowFrame* pTmpDestRow = static_cast<SwRowFrame*>(pCurrDestCell->Lower());
447
448 if ( pTmpSourceRow->IsFollowFlowRow() && pTmpDestRow )
449 {
450 // move content from follow flow row to pTmpDestRow:
451 while ( pTmpDestRow->GetNext() )
452 pTmpDestRow = static_cast<SwRowFrame*>(pTmpDestRow->GetNext());
453
454 assert(pTmpDestRow->GetFollowRow() == pTmpSourceRow);
455
456 lcl_MoveRowContent( *pTmpSourceRow, *pTmpDestRow );
457 pTmpDestRow->SetFollowRow( pTmpSourceRow->GetFollowRow() );
458 pTmpSourceRow->RemoveFromLayout();
459 SwFrame::DestroyFrame(pTmpSourceRow);
460 }
461 else
462 {
463 // move complete row:
464 pTmpSourceRow->RemoveFromLayout();
465 pTmpSourceRow->InsertBefore( pCurrDestCell, nullptr );
466 }
467
468 pTmpSourceRow = static_cast<SwRowFrame*>(pCurrSourceCell->Lower());
469 }
470 }
471 else
472 {
473 SwFrame *pTmp = ::SaveContent( pCurrSourceCell );
474 if ( pTmp )
475 {
476 // NEW TABLES
477 SwCellFrame* pDestCell = pCurrDestCell;
478 if ( pDestCell->GetTabBox()->getRowSpan() < 1 )
479 pDestCell = & const_cast<SwCellFrame&>(pDestCell->FindStartEndOfRowSpanCell( true ));
480
481 // Find last content
482 SwFrame* pFrame = pDestCell->GetLastLower();
483 ::RestoreContent( pTmp, pDestCell, pFrame );
484 }
485 }
486 pCurrDestCell = static_cast<SwCellFrame*>(pCurrDestCell->GetNext());
487 pCurrSourceCell = static_cast<SwCellFrame*>(pCurrSourceCell->GetNext());
488 }
489}
490
491// Local helper function to move all footnotes in rRowFrame from
492// the footnote boss of rSource to the footnote boss of rDest.
493static void lcl_MoveFootnotes( SwTabFrame& rSource, SwTabFrame& rDest, SwLayoutFrame& rRowFrame )
494{
495 if ( !rSource.GetFormat()->GetDoc()->GetFootnoteIdxs().empty() )
496 {
497 SwFootnoteBossFrame* pOldBoss = rSource.FindFootnoteBossFrame( true );
498 SwFootnoteBossFrame* pNewBoss = rDest.FindFootnoteBossFrame( true );
499 rRowFrame.MoveLowerFootnotes( nullptr, pOldBoss, pNewBoss, true );
500 }
501}
502
503// Local helper function to handle nested table cells before the split process
504static void lcl_PreprocessRowsInCells( SwTabFrame& rTab, SwRowFrame& rLastLine,
505 SwRowFrame& rFollowFlowLine, SwTwips nRemain )
506{
507 SwCellFrame* pCurrLastLineCell = static_cast<SwCellFrame*>(rLastLine.Lower());
508 SwCellFrame* pCurrFollowFlowLineCell = static_cast<SwCellFrame*>(rFollowFlowLine.Lower());
509
510 SwRectFnSet aRectFnSet(pCurrLastLineCell);
511
512 // Move content of follow cells into master cells
513 while ( pCurrLastLineCell )
514 {
515 if ( pCurrLastLineCell->Lower() && pCurrLastLineCell->Lower()->IsRowFrame() )
516 {
517 SwTwips nTmpCut = nRemain;
518 SwRowFrame* pTmpLastLineRow = static_cast<SwRowFrame*>(pCurrLastLineCell->Lower());
519
520 // #i26945#
521 SwTwips nCurrentHeight =
522 lcl_CalcMinRowHeight( pTmpLastLineRow,
524 while ( pTmpLastLineRow->GetNext() && nTmpCut > nCurrentHeight )
525 {
526 nTmpCut -= nCurrentHeight;
527 pTmpLastLineRow = static_cast<SwRowFrame*>(pTmpLastLineRow->GetNext());
528 // #i26945#
529 nCurrentHeight =
530 lcl_CalcMinRowHeight( pTmpLastLineRow,
532 }
533
534 // pTmpLastLineRow does not fit to the line or it is the last line
535 // Check if we can move pTmpLastLineRow to the follow table,
536 // or if we have to split the line:
537 bool bTableLayoutTooComplex = false;
538 tools::Long nMinHeight = 0;
539
540 // We have to take into account:
541 // 1. The fixed height of the row
542 // 2. The borders of the cells inside the row
543 // 3. The minimum height of the row
544 if ( pTmpLastLineRow->HasFixSize() )
545 nMinHeight = aRectFnSet.GetHeight(pTmpLastLineRow->getFrameArea());
546 else
547 {
548 {
549 const SwFormatFrameSize &rSz = pTmpLastLineRow->GetFormat()->GetFrameSize();
551 nMinHeight = rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*pTmpLastLineRow);
552 }
553
554 SwFrame* pCell = pTmpLastLineRow->Lower();
555 while ( pCell )
556 {
557 if ( static_cast<SwCellFrame*>(pCell)->Lower() &&
558 static_cast<SwCellFrame*>(pCell)->Lower()->IsRowFrame() )
559 {
560 bTableLayoutTooComplex = true;
561 break;
562 }
563
564 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCell );
565 const SwBorderAttrs &rAttrs = *aAccess.Get();
566 nMinHeight = std::max( nMinHeight, tools::Long(lcl_CalcTopAndBottomMargin( *static_cast<SwLayoutFrame*>(pCell), rAttrs )) );
567 pCell = pCell->GetNext();
568 }
569 }
570
571 // 1. Case:
572 // The line completely fits into the master table.
573 // Nevertheless, we build a follow (otherwise painting problems
574 // with empty cell).
575
576 // 2. Case:
577 // The line has to be split, the minimum height still fits into
578 // the master table, and the table structure is not too complex.
579 if ( nTmpCut > nCurrentHeight ||
580 ( pTmpLastLineRow->IsRowSplitAllowed() &&
581 !bTableLayoutTooComplex && nMinHeight < nTmpCut ) )
582 {
583 // The line has to be split:
584 SwRowFrame* pNewRow = new SwRowFrame( *pTmpLastLineRow->GetTabLine(), &rTab, false );
585 pNewRow->SetFollowFlowRow( true );
586 pNewRow->SetFollowRow( pTmpLastLineRow->GetFollowRow() );
587 pTmpLastLineRow->SetFollowRow( pNewRow );
588 pNewRow->InsertBehind( pCurrFollowFlowLineCell, nullptr );
589 pTmpLastLineRow = static_cast<SwRowFrame*>(pTmpLastLineRow->GetNext());
590 }
591
592 // The following lines have to be moved:
593 while ( pTmpLastLineRow )
594 {
595 SwRowFrame* pTmp = static_cast<SwRowFrame*>(pTmpLastLineRow->GetNext());
596 lcl_MoveFootnotes( rTab, *rTab.GetFollow(), *pTmpLastLineRow );
597 pTmpLastLineRow->RemoveFromLayout();
598 pTmpLastLineRow->InsertBefore( pCurrFollowFlowLineCell, nullptr );
599 pTmpLastLineRow->Shrink( aRectFnSet.GetHeight(pTmpLastLineRow->getFrameArea()) );
600 pCurrFollowFlowLineCell->Grow( aRectFnSet.GetHeight(pTmpLastLineRow->getFrameArea()) );
601 pTmpLastLineRow = pTmp;
602 }
603 }
604
605 pCurrLastLineCell = static_cast<SwCellFrame*>(pCurrLastLineCell->GetNext());
606 pCurrFollowFlowLineCell = static_cast<SwCellFrame*>(pCurrFollowFlowLineCell->GetNext());
607 }
608}
609
610// Local helper function to handle nested table cells after the split process
611static void lcl_PostprocessRowsInCells( SwTabFrame& rTab, SwRowFrame& rLastLine )
612{
613 SwCellFrame* pCurrMasterCell = static_cast<SwCellFrame*>(rLastLine.Lower());
614 while ( pCurrMasterCell )
615 {
616 if ( pCurrMasterCell->Lower() &&
617 pCurrMasterCell->Lower()->IsRowFrame() )
618 {
619 SwRowFrame* pRowFrame = static_cast<SwRowFrame*>(pCurrMasterCell->GetLastLower());
620
621 if ( nullptr != pRowFrame->GetPrev() && !pRowFrame->ContainsContent() )
622 {
623 OSL_ENSURE( pRowFrame->GetFollowRow(), "Deleting row frame without follow" );
624
625 // The footnotes have to be moved:
626 lcl_MoveFootnotes( rTab, *rTab.GetFollow(), *pRowFrame );
627 pRowFrame->Cut();
628 SwRowFrame* pFollowRow = pRowFrame->GetFollowRow();
629 pRowFrame->Paste( pFollowRow->GetUpper(), pFollowRow );
630 pRowFrame->SetFollowRow( pFollowRow->GetFollowRow() );
631 lcl_MoveRowContent( *pFollowRow, *pRowFrame );
632 pFollowRow->Cut();
633 SwFrame::DestroyFrame(pFollowRow);
634 ::SwInvalidateAll( pCurrMasterCell, LONG_MAX );
635 }
636 }
637
638 pCurrMasterCell = static_cast<SwCellFrame*>(pCurrMasterCell->GetNext());
639 }
640}
641
642// Local helper function to re-calculate the split line.
643inline void TableSplitRecalcLock( SwFlowFrame *pTab ) { pTab->LockJoin(); }
644inline void TableSplitRecalcUnlock( SwFlowFrame *pTab ) { pTab->UnlockJoin(); }
645
646static bool lcl_RecalcSplitLine( SwRowFrame& rLastLine, SwRowFrame& rFollowLine,
647 SwTwips nRemainingSpaceForLastRow, SwTwips nAlreadyFree )
648{
649 bool bRet = true;
650
651 vcl::RenderContext* pRenderContext = rLastLine.getRootFrame()->GetCurrShell()->GetOut();
652 SwTabFrame& rTab = static_cast<SwTabFrame&>(*rLastLine.GetUpper());
653 SwRectFnSet aRectFnSet(rTab.GetUpper());
654 SwTwips nCurLastLineHeight = aRectFnSet.GetHeight(rLastLine.getFrameArea());
655
656 // If there are nested cells in rLastLine, the recalculation of the last
657 // line needs some preprocessing.
658 lcl_PreprocessRowsInCells( rTab, rLastLine, rFollowLine, nRemainingSpaceForLastRow );
659
660 // Here the recalculation process starts:
661 rTab.SetRebuildLastLine( true );
662 // #i26945#
663 rTab.SetDoesObjsFit( true );
664
665 // #i26945# - invalidate and move floating screen
666 // objects 'out of range'
667 ::lcl_InvalidateLowerObjs( rLastLine, true );
668
669 // manipulate row and cell sizes
670
671 // #i26945# - Do *not* consider floating screen objects
672 // for the minimal cell height.
676
677 // invalidate last line
678 ::SwInvalidateAll( &rLastLine, LONG_MAX );
679
680 // Shrink the table to account for the shrunk last row, as well as lower rows
681 // that had been moved to follow table in SwTabFrame::Split.
682 // It will grow later when last line will recalc its height.
683 rTab.Shrink(nAlreadyFree + nCurLastLineHeight - nRemainingSpaceForLastRow + 1);
684
685 // Lock this tab frame and its follow
686 bool bUnlockMaster = false;
687 SwFlowFrame * pFollow = nullptr;
688 SwTabFrame* pMaster = rTab.IsFollow() ? rTab.FindMaster() : nullptr;
689 if ( pMaster && !pMaster->IsJoinLocked() )
690 {
691 bUnlockMaster = true;
692 ::TableSplitRecalcLock( pMaster );
693 }
694 if ( !rTab.GetFollow()->IsJoinLocked() )
695 {
696 pFollow = rTab.GetFollow();
697 ::TableSplitRecalcLock( pFollow );
698 }
699
700 bool bInSplit = rLastLine.IsInSplit();
701 rLastLine.SetInSplit();
702
703 // Do the recalculation
704 lcl_RecalcRow( rLastLine, LONG_MAX );
705 // #115759# - force a format of the last line in order to
706 // get the correct height.
707 rLastLine.InvalidateSize();
708 rLastLine.Calc(pRenderContext);
709
710 rLastLine.SetInSplit(bInSplit);
711
712 // Unlock this tab frame and its follow
713 if ( pFollow )
714 ::TableSplitRecalcUnlock( pFollow );
715 if ( bUnlockMaster )
716 ::TableSplitRecalcUnlock( pMaster );
717
718 // If there are nested cells in rLastLine, the recalculation of the last
719 // line needs some postprocessing.
720 lcl_PostprocessRowsInCells( rTab, rLastLine );
721
722 // Do a couple of checks on the current situation.
723
724 // If we are not happy with the current situation we return false.
725 // This will start a new try to split the table, this time we do not
726 // try to split the table rows.
727
728 // 1. Check if table fits to its upper.
729 // #i26945# - include check, if objects fit
730 const SwTwips nDistanceToUpperPrtBottom =
731 aRectFnSet.BottomDist(rTab.getFrameArea(), aRectFnSet.GetPrtBottom(*rTab.GetUpper()));
732 // tdf#125685 ignore footnotes that are anchored in follow-table of this
733 // table - if split is successful they move to the next page/column anyway
734 assert(rTab.GetFollow() == rFollowLine.GetUpper());
735 SwTwips nFollowFootnotes(0);
736 // actually there should always be a boss frame, except if "this" isn't
737 // connected to a page yet; not sure if that can happen
738 if (SwFootnoteBossFrame const*const pBoss = rTab.FindFootnoteBossFrame())
739 {
740 if (SwFootnoteContFrame const*const pCont = pBoss->FindFootnoteCont())
741 {
742 for (SwFootnoteFrame const* pFootnote = static_cast<SwFootnoteFrame const*>(pCont->Lower());
743 pFootnote != nullptr;
744 pFootnote = static_cast<SwFootnoteFrame const*>(pFootnote->GetNext()))
745 {
746 SwContentFrame const*const pAnchor = pFootnote->GetRef();
747 SwTabFrame const* pTab = pAnchor->FindTabFrame();
748 if (pTab)
749 {
750 while (pTab->GetUpper()->IsInTab())
751 {
752 pTab = pTab->GetUpper()->FindTabFrame();
753 }
754 // TODO currently do this only for top-level tables?
755 // otherwise would need to check rTab's follow and any upper table's follow?
756 if (pTab == rTab.GetFollow())
757 {
758 nFollowFootnotes += aRectFnSet.GetHeight(pFootnote->getFrameArea());
759 }
760 }
761 }
762 }
763 }
764 if (nDistanceToUpperPrtBottom + nFollowFootnotes < 0 || !rTab.DoesObjsFit())
765 bRet = false;
766
767 // 2. Check if each cell in the last line has at least one content frame.
768
769 // Note: a FollowFlowRow may contains empty cells!
770 if ( bRet )
771 {
772 if ( !rLastLine.IsInFollowFlowRow() )
773 {
774 SwCellFrame* pCurrMasterCell = static_cast<SwCellFrame*>(rLastLine.Lower());
775 while ( pCurrMasterCell )
776 {
777 if ( !pCurrMasterCell->ContainsContent() && pCurrMasterCell->GetTabBox()->getRowSpan() >= 1 )
778 {
779 bRet = false;
780 break;
781 }
782 pCurrMasterCell = static_cast<SwCellFrame*>(pCurrMasterCell->GetNext());
783 }
784 }
785 }
786
787 // 3. Check if last line does not contain any content:
788 if ( bRet )
789 {
790 if ( !rLastLine.ContainsContent() )
791 {
792 bRet = false;
793 }
794 }
795
796 // 4. Check if follow flow line does not contain content:
797 if ( bRet )
798 {
799 if ( !rFollowLine.IsRowSpanLine() && !rFollowLine.ContainsContent() )
800 {
801 bRet = false;
802 }
803 }
804
805 if ( bRet )
806 {
807 // Everything looks fine. Splitting seems to be successful. We invalidate
808 // rFollowLine to force a new formatting.
809 ::SwInvalidateAll( &rFollowLine, LONG_MAX );
810 }
811 else
812 {
813 // Splitting the table row gave us an unexpected result.
814 // Everything has to be prepared for a second try to split
815 // the table, this time without splitting the row.
816 ::SwInvalidateAll( &rLastLine, LONG_MAX );
817 }
818
819 rTab.SetRebuildLastLine( false );
820 // #i26945#
821 rTab.SetDoesObjsFit( true );
822
823 return bRet;
824}
825
826// Sets the correct height for all spanned cells
828{
829 SwRectFnSet aRectFnSet(pRow);
830 SwCellFrame* pCellFrame = static_cast<SwCellFrame*>(pRow->GetLower());
831 while ( pCellFrame )
832 {
833 const tools::Long nLayoutRowSpan = pCellFrame->GetLayoutRowSpan();
834 if ( nLayoutRowSpan > 1 )
835 {
836 // calculate height of cell:
837 const tools::Long nNewCellHeight = lcl_GetHeightOfRows( pRow, nLayoutRowSpan );
838 const tools::Long nDiff = nNewCellHeight - aRectFnSet.GetHeight(pCellFrame->getFrameArea());
839
840 if ( nDiff )
841 {
843 aRectFnSet.AddBottom(aFrm, nDiff);
844 }
845 }
846
847 pCellFrame = static_cast<SwCellFrame*>(pCellFrame->GetNext());
848 }
849}
850
851// Returns the maximum layout row span of the row
852// Looking for the next row that contains no covered cells:
854{
855 tools::Long nRet = 1;
856
857 const SwRowFrame* pCurrentRowFrame = static_cast<const SwRowFrame*>(rRow.GetNext());
858 bool bNextRow = false;
859
860 while ( pCurrentRowFrame )
861 {
862 // if there is any covered cell, we proceed to the next row frame
863 const SwCellFrame* pLower = static_cast<const SwCellFrame*>( pCurrentRowFrame->Lower());
864 while ( pLower )
865 {
866 if ( pLower->GetTabBox()->getRowSpan() < 0 )
867 {
868 ++nRet;
869 bNextRow = true;
870 break;
871 }
872 pLower = static_cast<const SwCellFrame*>(pLower->GetNext());
873 }
874 pCurrentRowFrame = bNextRow ?
875 static_cast<const SwRowFrame*>(pCurrentRowFrame->GetNext() ) :
876 nullptr;
877 }
878
879 return nRet;
880}
881
882// Function to remove the FollowFlowLine of rTab.
883// The content of the FollowFlowLine is moved to the associated line in the
884// master table.
886{
887 // find FollowFlowLine
888 SwTabFrame *pFoll = GetFollow();
889 SwRowFrame* pFollowFlowLine = pFoll ? pFoll->GetFirstNonHeadlineRow() : nullptr;
890
891 // find last row in master
892 SwFrame* pLastLine = GetLastLower();
893
894 OSL_ENSURE( HasFollowFlowLine() &&
895 pFollowFlowLine &&
896 pLastLine, "There should be a flowline in the follow" );
897
898 // #140081# Make code robust.
899 if ( !pFollowFlowLine || !pLastLine )
900 return true;
901 if (pFollowFlowLine->IsDeleteForbidden())
902 {
903 SAL_WARN("sw.layout", "Cannot remove in-use Follow Flow Line");
904 return false;
905 }
906
907 // We have to reset the flag here, because lcl_MoveRowContent
908 // calls a GrowFrame(), which has a different behavior if
909 // this flag is set.
910 SetFollowFlowLine( false );
911
912 // Move content
913 lcl_MoveRowContent( *pFollowFlowLine, *static_cast<SwRowFrame*>(pLastLine) );
914
915 // NEW TABLES
916 // If a row span follow flow line is removed, we want to move the whole span
917 // to the master:
918 tools::Long nRowsToMove = lcl_GetMaximumLayoutRowSpan( *pFollowFlowLine );
919
920 if ( nRowsToMove > 1 )
921 {
922 SwRectFnSet aRectFnSet(this);
923 SwFrame* pRow = pFollowFlowLine->GetNext();
924 SwFrame* pInsertBehind = GetLastLower();
925 SwTwips nGrow = 0;
926
927 while ( pRow && nRowsToMove-- > 1 )
928 {
929 SwFrame* pNxt = pRow->GetNext();
930 nGrow += aRectFnSet.GetHeight(pRow->getFrameArea());
931
932 // The footnotes have to be moved:
933 lcl_MoveFootnotes( *GetFollow(), *this, static_cast<SwRowFrame&>(*pRow) );
934
935 pRow->RemoveFromLayout();
936 pRow->InsertBehind( this, pInsertBehind );
937 pRow->InvalidateAll_();
938 pRow->CheckDirChange();
939 pInsertBehind = pRow;
940 pRow = pNxt;
941 }
942
943 SwFrame* pFirstRow = Lower();
944 while ( pFirstRow )
945 {
946 lcl_AdjustRowSpanCells( static_cast<SwRowFrame*>(pFirstRow) );
947 pFirstRow = pFirstRow->GetNext();
948 }
949
950 Grow( nGrow );
951 GetFollow()->Shrink( nGrow );
952 }
953
954 bool bJoin = !pFollowFlowLine->GetNext();
955 pFollowFlowLine->Cut();
956 SwFrame::DestroyFrame(pFollowFlowLine);
957
958 return bJoin;
959}
960
961// #i26945# - Floating screen objects are no longer searched.
962static bool lcl_FindSectionsInRow( const SwRowFrame& rRow )
963{
964 bool bRet = false;
965 const SwCellFrame* pLower = static_cast<const SwCellFrame*>(rRow.Lower());
966 while ( pLower )
967 {
968 if ( pLower->IsVertical() != rRow.IsVertical() )
969 return true;
970
971 const SwFrame* pTmpFrame = pLower->Lower();
972 while ( pTmpFrame )
973 {
974 if ( pTmpFrame->IsRowFrame() )
975 {
976 bRet = lcl_FindSectionsInRow( *static_cast<const SwRowFrame*>(pTmpFrame) );
977 }
978 else
979 {
980 // #i26945# - search only for sections
981 if (pTmpFrame->IsSctFrame())
982 {
983 bRet = true;
984
985 if (!rRow.IsInSct())
986 {
987 // This row is not in a section.
988 if (const SwFrame* pSectionLower = pTmpFrame->GetLower())
989 {
990 if (!pSectionLower->IsColumnFrame())
991 {
992 // Section has a single column only, try to
993 // split that.
994 bRet = false;
995
996 for (const SwFrame* pFrame = pSectionLower; pFrame; pFrame = pFrame->GetNext())
997 {
998 if (pFrame->IsTabFrame())
999 {
1000 // Section contains a table, no split in that case.
1001 bRet = true;
1002 break;
1003 }
1004 }
1005 }
1006 }
1007 }
1008 }
1009 }
1010
1011 if ( bRet )
1012 return true;
1013 pTmpFrame = pTmpFrame->GetNext();
1014 }
1015
1016 pLower = static_cast<const SwCellFrame*>(pLower->GetNext());
1017 }
1018 return bRet;
1019}
1020
1021bool SwTabFrame::Split( const SwTwips nCutPos, bool bTryToSplit, bool bTableRowKeep )
1022{
1023 bool bRet = true;
1024
1025 SwRectFnSet aRectFnSet(this);
1026
1027 // #i26745# - format row and cell frames of table
1028 {
1029 Lower()->InvalidatePos_();
1030 // #i43913# - correction
1031 // call method <lcl_InnerCalcLayout> with first lower.
1033 }
1034
1035 //In order to be able to compare the positions of the cells with CutPos,
1036 //they have to be calculated consecutively starting from the table.
1037 //They can definitely be invalid because of position changes of the table.
1038 SwRowFrame *pRow = static_cast<SwRowFrame*>(Lower());
1039 if( !pRow )
1040 return bRet;
1041
1042 const sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat();
1043 sal_uInt16 nRowCount = 0; // pRow currently points to the first row
1044
1045 SwTwips nRemainingSpaceForLastRow =
1046 aRectFnSet.YDiff(nCutPos, aRectFnSet.GetTop(getFrameArea()));
1047 nRemainingSpaceForLastRow -= aRectFnSet.GetTopMargin(*this);
1048
1049 // Make pRow point to the line that does not fit anymore:
1050 while( pRow->GetNext() &&
1051 nRemainingSpaceForLastRow >= ( aRectFnSet.GetHeight(pRow->getFrameArea()) +
1053 pRow->GetBottomLineSize() :
1054 0 ) ) )
1055 {
1056 if( bTryToSplit || !pRow->IsRowSpanLine() ||
1057 0 != aRectFnSet.GetHeight(pRow->getFrameArea()) )
1058 ++nRowCount;
1059 nRemainingSpaceForLastRow -= aRectFnSet.GetHeight(pRow->getFrameArea());
1060 pRow = static_cast<SwRowFrame*>(pRow->GetNext());
1061 }
1062
1063 // bSplitRowAllowed: Row may be split according to its attributes.
1064 // bTryToSplit: Row will never be split if bTryToSplit = false.
1065 // This can either be passed as a parameter, indicating
1066 // that we are currently doing the second try to split the
1067 // table, or it will be set to false under certain
1068 // conditions that are not suitable for splitting
1069 // the row.
1070 bool bSplitRowAllowed = true;
1071 if (!pRow->IsRowSplitAllowed())
1072 {
1073 // A row larger than the entire page ought to be allowed to split regardless of setting,
1074 // otherwise it has hidden content and that makes no sense
1075 if ( pRow->getFrameArea().Height() > FindPageFrame()->getFramePrintArea().Height() )
1076 pRow->SetForceRowSplitAllowed( true );
1077 else
1078 bSplitRowAllowed = false;
1079 }
1080 // #i29438#
1081 // #i26945# - Floating screen objects no longer forbid
1082 // a splitting of the table row.
1083 // Special DoNotSplit case 1:
1084 // Search for sections inside pRow:
1085 if ( lcl_FindSectionsInRow( *pRow ) )
1086 {
1087 bTryToSplit = false;
1088 }
1089
1090 SwFlyFrame* pFly = FindFlyFrame();
1091 if (bSplitRowAllowed && pFly && pFly->IsFlySplitAllowed())
1092 {
1093 // The remaining size is less than the minimum row height, then don't even try to split the
1094 // row, just move it forward.
1095 const SwFormatFrameSize& rRowSize = pRow->GetFormat()->GetFrameSize();
1096 if (rRowSize.GetHeightSizeType() == SwFrameSize::Minimum)
1097 {
1098 SwTwips nMinHeight = rRowSize.GetHeight();
1099 if (nMinHeight > nRemainingSpaceForLastRow)
1100 {
1101 bSplitRowAllowed = false;
1102 }
1103 }
1104 }
1105
1106 // #i29771#
1107 // To avoid loops, we do some checks before actually trying to split
1108 // the row. Maybe we should keep the next row in this table.
1109 // Note: This is only done if we are at the beginning of our upper
1110 bool bKeepNextRow = false;
1111 if ( nRowCount < nRepeat )
1112 {
1113 // First case: One of the repeated headline does not fit to the page anymore.
1114 // tdf#88496 Disable repeated headline (like for #i44910#) to avoid loops and
1115 // to fix interoperability problems (very long tables only with headline)
1116 // tdf#150149 except in multi-column sections, where it's possible to enlarge
1117 // the height of the section frame instead of using this fallback
1118 OSL_ENSURE( !GetIndPrev(), "Table is supposed to be at beginning" );
1119 if ( !IsInSct() )
1120 {
1122 return false;
1123 }
1124 else
1125 bKeepNextRow = true;
1126 }
1127 else if ( !GetIndPrev() && nRepeat == nRowCount )
1128 {
1129 // Second case: The first non-headline row does not fit to the page.
1130 // If it is not allowed to be split, or it contains a sub-row that
1131 // is not allowed to be split, we keep the row in this table:
1132 if ( bTryToSplit && bSplitRowAllowed )
1133 {
1134 // Check if there are (first) rows inside this row,
1135 // which are not allowed to be split.
1136 SwCellFrame* pLowerCell = static_cast<SwCellFrame*>(pRow->Lower());
1137 while ( pLowerCell )
1138 {
1139 if ( pLowerCell->Lower() && pLowerCell->Lower()->IsRowFrame() )
1140 {
1141 const SwRowFrame* pLowerRow = static_cast<SwRowFrame*>(pLowerCell->Lower());
1142 if ( !pLowerRow->IsRowSplitAllowed() &&
1143 aRectFnSet.GetHeight(pLowerRow->getFrameArea()) > nRemainingSpaceForLastRow )
1144 {
1145 bKeepNextRow = true;
1146 break;
1147 }
1148 }
1149 pLowerCell = static_cast<SwCellFrame*>(pLowerCell->GetNext());
1150 }
1151 }
1152 else
1153 bKeepNextRow = true;
1154 }
1155
1156 // Better keep the next row in this table:
1157 if ( bKeepNextRow )
1158 {
1159 pRow = GetFirstNonHeadlineRow();
1160 if ( pRow && pRow->IsRowSpanLine() && 0 == aRectFnSet.GetHeight(pRow->getFrameArea()) )
1161 pRow = static_cast<SwRowFrame*>(pRow->GetNext());
1162 if ( pRow )
1163 {
1164 pRow = static_cast<SwRowFrame*>(pRow->GetNext());
1165 ++nRowCount;
1166 }
1167 }
1168
1169 // No more row to split or to move to follow table:
1170 if ( !pRow )
1171 return bRet;
1172
1173 // We try to split the row if
1174 // - the attributes of the row are set accordingly and
1175 // - we are allowed to do so
1176 // - it should not be kept with the next row
1177 bSplitRowAllowed = bSplitRowAllowed && bTryToSplit &&
1178 ( !bTableRowKeep ||
1179 !pRow->ShouldRowKeepWithNext() );
1180
1181 // Adjust pRow according to the keep-with-next attribute:
1182 if ( !bSplitRowAllowed && bTableRowKeep )
1183 {
1184 SwRowFrame* pTmpRow = static_cast<SwRowFrame*>(pRow->GetPrev());
1185 SwRowFrame* pOldRow = pRow;
1186 while ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() &&
1187 nRowCount > nRepeat )
1188 {
1189 pRow = pTmpRow;
1190 --nRowCount;
1191 pTmpRow = static_cast<SwRowFrame*>(pTmpRow->GetPrev());
1192 }
1193
1194 // loop prevention
1195 if ( nRowCount == nRepeat && !GetIndPrev())
1196 {
1197 pRow = pOldRow;
1198 }
1199 }
1200
1201 // If we do not intend to split pRow, we check if we are
1202 // allowed to move pRow to a follow. Otherwise we return
1203 // false, indicating an error
1204 if ( !bSplitRowAllowed )
1205 {
1206 SwRowFrame* pFirstNonHeadlineRow = GetFirstNonHeadlineRow();
1207 if ( pRow == pFirstNonHeadlineRow )
1208 return false;
1209
1210 // #i91764#
1211 // Ignore row span lines
1212 SwRowFrame* pTmpRow = pFirstNonHeadlineRow;
1213 while ( pTmpRow && pTmpRow->IsRowSpanLine() )
1214 {
1215 pTmpRow = static_cast<SwRowFrame*>(pTmpRow->GetNext());
1216 }
1217 if ( !pTmpRow || pRow == pTmpRow )
1218 {
1219 return false;
1220 }
1221 }
1222
1223 // Build follow table if not already done:
1224 bool bNewFollow;
1225 SwTabFrame *pFoll;
1226 if ( GetFollow() )
1227 {
1228 pFoll = GetFollow();
1229 bNewFollow = false;
1230 }
1231 else
1232 {
1233 bNewFollow = true;
1234 pFoll = new SwTabFrame( *this );
1235
1236 // We give the follow table an initial width.
1237 {
1239 aRectFnSet.AddWidth(aFrm, aRectFnSet.GetWidth(getFrameArea()));
1240 aRectFnSet.SetLeft(aFrm, aRectFnSet.GetLeft(getFrameArea()));
1241 }
1242
1243 {
1245 aRectFnSet.AddWidth(aPrt, aRectFnSet.GetWidth(getFramePrintArea()));
1246 }
1247
1248 // Insert the new follow table
1249 pFoll->InsertBehind( GetUpper(), this );
1250
1251 // Repeat the headlines.
1252 auto& rLines = GetTable()->GetTabLines();
1253 for ( nRowCount = 0; nRowCount < nRepeat; ++nRowCount )
1254 {
1255 // Insert new headlines:
1256 SwRowFrame* pHeadline = new SwRowFrame(*rLines[nRowCount], this);
1257 {
1258 sw::FlyCreationSuppressor aSuppressor;
1259 pHeadline->SetRepeatedHeadline(true);
1260 }
1261 pHeadline->InsertBefore( pFoll, nullptr );
1262
1263 SwPageFrame *pPage = pHeadline->FindPageFrame();
1265 if( !pSpzs->empty() )
1266 {
1268 SwContentFrame* pFrame = pHeadline->ContainsContent();
1269 while( pFrame )
1270 {
1271 // sw_redlinehide: the implementation of AppendObjs
1272 // takes care of iterating merged SwTextFrame
1273 nIndex = pFrame->IsTextFrame()
1274 ? static_cast<SwTextFrame*>(pFrame)->GetTextNodeFirst()->GetIndex()
1275 : static_cast<SwNoTextFrame*>(pFrame)->GetNode()->GetIndex();
1276 AppendObjs(pSpzs, nIndex, pFrame, pPage, GetFormat()->GetDoc());
1277 pFrame = pFrame->GetNextContentFrame();
1278 if( !pHeadline->IsAnLower( pFrame ) )
1279 break;
1280 }
1281 }
1282 }
1283 }
1284
1285 SwRowFrame* pLastRow = nullptr; // points to the last remaining line in master
1286 SwRowFrame* pFollowRow = nullptr; // points to either the follow flow line or the
1287 // first regular line in the follow
1288
1289 if ( bSplitRowAllowed )
1290 {
1291 // If the row that does not fit anymore is allowed
1292 // to be split, the next row has to be moved to the follow table.
1293 pLastRow = pRow;
1294 pRow = static_cast<SwRowFrame*>(pRow->GetNext());
1295
1296 // new follow flow line for last row of master table
1297 pFollowRow = lcl_InsertNewFollowFlowLine( *this, *pLastRow, false );
1298 }
1299 else
1300 {
1301 pFollowRow = pRow;
1302
1303 // NEW TABLES
1304 // check if we will break a row span by moving pFollowRow to the follow:
1305 // In this case we want to reformat the last line.
1306 const SwCellFrame* pCellFrame = static_cast<const SwCellFrame*>(pFollowRow->GetLower());
1307 while ( pCellFrame )
1308 {
1309 if ( pCellFrame->GetTabBox()->getRowSpan() < 1 )
1310 {
1311 pLastRow = static_cast<SwRowFrame*>(pRow->GetPrev());
1312 break;
1313 }
1314
1315 pCellFrame = static_cast<const SwCellFrame*>(pCellFrame->GetNext());
1316 }
1317
1318 // new follow flow line for last row of master table
1319 if ( pLastRow )
1320 pFollowRow = lcl_InsertNewFollowFlowLine( *this, *pLastRow, true );
1321 }
1322
1323 SwTwips nShrink = 0;
1324
1325 //Optimization: There is no paste needed for the new Follow and the
1326 //optimized insert can be used (large numbers of rows luckily only occur in
1327 //such situations).
1328 if ( bNewFollow )
1329 {
1330 SwFrame* pInsertBehind = pFoll->GetLastLower();
1331
1332 while ( pRow )
1333 {
1334 SwFrame* pNxt = pRow->GetNext();
1335 nShrink += aRectFnSet.GetHeight(pRow->getFrameArea());
1336 // The footnotes do not have to be moved, this is done in the
1337 // MoveFwd of the follow table!!!
1338 pRow->RemoveFromLayout();
1339 pRow->InsertBehind( pFoll, pInsertBehind );
1340 pRow->InvalidateAll_();
1341 pInsertBehind = pRow;
1342 pRow = static_cast<SwRowFrame*>(pNxt);
1343 }
1344 }
1345 else
1346 {
1347 SwFrame* pPasteBefore = HasFollowFlowLine() ?
1348 pFollowRow->GetNext() :
1349 pFoll->GetFirstNonHeadlineRow();
1350
1351 while ( pRow )
1352 {
1353 SwFrame* pNxt = pRow->GetNext();
1354 nShrink += aRectFnSet.GetHeight(pRow->getFrameArea());
1355
1356 // The footnotes have to be moved:
1357 lcl_MoveFootnotes( *this, *GetFollow(), *pRow );
1358
1359 pRow->RemoveFromLayout();
1360 pRow->Paste( pFoll, pPasteBefore );
1361
1362 pRow->CheckDirChange();
1363 pRow = static_cast<SwRowFrame*>(pNxt);
1364 }
1365 }
1366
1367 if ( !pLastRow )
1368 Shrink( nShrink );
1369 else
1370 {
1371 // we rebuild the last line to assure that it will be fully formatted
1372 // we also don't shrink here, because we will be doing that in lcl_RecalcSplitLine
1373
1374 // recalculate the split line
1375 bRet = lcl_RecalcSplitLine( *pLastRow, *pFollowRow, nRemainingSpaceForLastRow, nShrink );
1376
1377 // RecalcSplitLine did not work. In this case we conceal the split error:
1378 if (!bRet && !bSplitRowAllowed)
1379 {
1380 bRet = true;
1381 }
1382
1383 // NEW TABLES
1384 // check if each cell in the row span line has a good height
1385 if ( bRet && pFollowRow->IsRowSpanLine() )
1386 lcl_AdjustRowSpanCells( pFollowRow );
1387 }
1388
1389 return bRet;
1390}
1391
1392namespace
1393{
1394 bool CanDeleteFollow(SwTabFrame *pFoll)
1395 {
1396 if (pFoll->IsJoinLocked())
1397 return false;
1398
1399 if (pFoll->IsDeleteForbidden())
1400 {
1401 SAL_WARN("sw.layout", "Delete Forbidden");
1402 return false;
1403 }
1404
1405 return true;
1406 }
1407}
1408
1410{
1411 OSL_ENSURE( !HasFollowFlowLine(), "Joining follow flow line" );
1412
1413 SwTabFrame *pFoll = GetFollow();
1414
1415 if (!pFoll || !CanDeleteFollow(pFoll))
1416 return;
1417
1418 SwRectFnSet aRectFnSet(this);
1419 pFoll->Cut(); //Cut out first to avoid unnecessary notifications.
1420
1421 SwFrame *pRow = pFoll->GetFirstNonHeadlineRow(),
1422 *pNxt;
1423
1424 SwFrame* pPrv = GetLastLower();
1425
1426 SwTwips nHeight = 0; //Total height of the inserted rows as return value.
1427
1428 while ( pRow )
1429 {
1430 pNxt = pRow->GetNext();
1431 nHeight += aRectFnSet.GetHeight(pRow->getFrameArea());
1432 pRow->RemoveFromLayout();
1433 pRow->InvalidateAll_();
1434 pRow->InsertBehind( this, pPrv );
1435 pRow->CheckDirChange();
1436 pPrv = pRow;
1437 pRow = pNxt;
1438 }
1439
1440 SetFollow( pFoll->GetFollow() );
1442 SwFrame::DestroyFrame(pFoll);
1443
1444 Grow( nHeight );
1445}
1446
1447static void SwInvalidatePositions( SwFrame *pFrame, tools::Long nBottom )
1448{
1449 // LONG_MAX == nBottom means we have to calculate all
1450 bool bAll = LONG_MAX == nBottom;
1451 SwRectFnSet aRectFnSet(pFrame);
1452 do
1453 { pFrame->InvalidatePos_();
1454 pFrame->InvalidateSize_();
1455 if( pFrame->IsLayoutFrame() )
1456 {
1457 if ( static_cast<SwLayoutFrame*>(pFrame)->Lower() )
1458 {
1459 ::SwInvalidatePositions( static_cast<SwLayoutFrame*>(pFrame)->Lower(), nBottom);
1460 // #i26945#
1461 ::lcl_InvalidateLowerObjs( *static_cast<SwLayoutFrame*>(pFrame) );
1462 }
1463 }
1464 else
1466 pFrame = pFrame->GetNext();
1467 } while ( pFrame &&
1468 ( bAll ||
1469 aRectFnSet.YDiff( aRectFnSet.GetTop(pFrame->getFrameArea()), nBottom ) < 0 ) );
1470}
1471
1472void SwInvalidateAll( SwFrame *pFrame, tools::Long nBottom )
1473{
1474 // LONG_MAX == nBottom means we have to calculate all
1475 bool bAll = LONG_MAX == nBottom;
1476 SwRectFnSet aRectFnSet(pFrame);
1477 do
1478 {
1479 pFrame->InvalidatePos_();
1480 pFrame->InvalidateSize_();
1481 pFrame->InvalidatePrt_();
1482 if( pFrame->IsLayoutFrame() )
1483 {
1484 // NEW TABLES
1485 SwLayoutFrame* pToInvalidate = static_cast<SwLayoutFrame*>(pFrame);
1486 if (pFrame->IsCellFrame())
1487 {
1488 SwCellFrame* pThisCell = static_cast<SwCellFrame*>(pFrame);
1489 if ( pThisCell->GetTabBox()->getRowSpan() < 1 )
1490 {
1491 pToInvalidate = & const_cast<SwCellFrame&>(pThisCell->FindStartEndOfRowSpanCell( true ));
1492 pToInvalidate->InvalidatePos_();
1493 pToInvalidate->InvalidateSize_();
1494 pToInvalidate->InvalidatePrt_();
1495 }
1496 }
1497 if ( pToInvalidate->Lower() )
1498 ::SwInvalidateAll( pToInvalidate->Lower(), nBottom);
1499 }
1500 else
1501 pFrame->Prepare();
1502
1503 pFrame = pFrame->GetNext();
1504 } while ( pFrame &&
1505 ( bAll ||
1506 aRectFnSet.YDiff( aRectFnSet.GetTop(pFrame->getFrameArea()), nBottom ) < 0 ) );
1507}
1508
1509// #i29550#
1511{
1512 pLayFrame->InvalidatePrt_();
1513 pLayFrame->InvalidateSize_();
1514 pLayFrame->SetCompletePaint();
1515
1516 SwFrame* pFrame = pLayFrame->Lower();
1517
1518 while ( pFrame )
1519 {
1520 if ( pFrame->IsLayoutFrame() )
1521 lcl_InvalidateAllLowersPrt( static_cast<SwLayoutFrame*>(pFrame) );
1522 else
1523 {
1524 pFrame->InvalidatePrt_();
1525 pFrame->InvalidateSize_();
1526 pFrame->SetCompletePaint();
1527 }
1528
1529 pFrame = pFrame->GetNext();
1530 }
1531}
1532
1534 tools::Long nBottom, bool bSkipRowSpanCells )
1535{
1536 vcl::RenderContext* pRenderContext = rLay.getRootFrame()->GetCurrShell()->GetOut();
1537 // LONG_MAX == nBottom means we have to calculate all
1538 bool bAll = LONG_MAX == nBottom;
1539 bool bRet = false;
1540 SwContentFrame *pCnt = rLay.ContainsContent();
1541 SwRectFnSet aRectFnSet(&rLay);
1542
1543 // FME 2007-08-30 #i81146# new loop control
1544 int nLoopControlRuns = 0;
1545 const int nLoopControlMax = 10;
1546 const sw::BroadcastingModify* pLoopControlCond = nullptr;
1547
1548 while (pCnt && rDontLeave.IsAnLower(pCnt))
1549 {
1550 // #115759# - check, if a format of content frame is
1551 // possible. Thus, 'copy' conditions, found at the beginning of
1552 // <SwContentFrame::MakeAll(..)>, and check these.
1553 const bool bFormatPossible = !pCnt->IsJoinLocked() &&
1554 ( !pCnt->IsTextFrame() ||
1555 !static_cast<SwTextFrame*>(pCnt)->IsLocked() ) &&
1556 ( pCnt->IsFollow() || !StackHack::IsLocked() );
1557
1558 // NEW TABLES
1559 bool bSkipContent = false;
1560 if ( bSkipRowSpanCells && pCnt->IsInTab() )
1561 {
1562 const SwFrame* pCell = pCnt->GetUpper();
1563 while ( pCell && !pCell->IsCellFrame() )
1564 pCell = pCell->GetUpper();
1565 if ( pCell && 1 != static_cast<const SwCellFrame*>( pCell )->GetLayoutRowSpan() )
1566 bSkipContent = true;
1567 }
1568
1569 if ( bFormatPossible && !bSkipContent )
1570 {
1571 bRet |= !pCnt->isFrameAreaDefinitionValid();
1572 // #i26945# - no extra invalidation of floating
1573 // screen objects needed.
1574 // Thus, delete call of method <SwFrame::InvalidateObjs( true )>
1575 pCnt->Calc(pRenderContext);
1576 // #i46941# - frame has to be valid
1577 // Note: frame could be invalid after calling its format, if it's locked.
1578 OSL_ENSURE( !pCnt->IsTextFrame() ||
1580 static_cast<SwTextFrame*>(pCnt)->IsJoinLocked(),
1581 "<SwContentFrame::CalcLowers(..)> - text frame invalid and not locked." );
1582 if ( pCnt->IsTextFrame() && pCnt->isFrameAreaDefinitionValid() )
1583 {
1584 // #i23129#, #i36347# - pass correct page frame to
1585 // the object formatter
1587 *(pCnt->FindPageFrame()) ) )
1588 {
1589 SwTextNode const*const pTextNode(
1590 static_cast<SwTextFrame*>(pCnt)->GetTextNodeFirst());
1591 if (pTextNode == pLoopControlCond)
1592 ++nLoopControlRuns;
1593 else
1594 {
1595 nLoopControlRuns = 0;
1596 pLoopControlCond = pTextNode;
1597 }
1598
1599 if ( nLoopControlRuns < nLoopControlMax )
1600 {
1601 // restart format with first content
1602 pCnt = rLay.ContainsContent();
1603 continue;
1604 }
1605
1606 SAL_WARN("sw.layout", "LoopControl in SwContentFrame::CalcLowers");
1607 }
1608 }
1609 if (!rDontLeave.IsAnLower(pCnt)) // moved backward?
1610 {
1611 pCnt = rLay.ContainsContent();
1612 continue; // avoid formatting new upper on different page
1613 }
1614 pCnt->GetUpper()->Calc(pRenderContext);
1615 }
1616 if( ! bAll && aRectFnSet.YDiff(aRectFnSet.GetTop(pCnt->getFrameArea()), nBottom) > 0 )
1617 break;
1618 pCnt = pCnt->GetNextContentFrame();
1619 }
1620 return bRet;
1621}
1622
1623// #i26945# - add parameter <_bOnlyRowsAndCells> to control
1624// that only row and cell frames are formatted.
1625static bool lcl_InnerCalcLayout( SwFrame *pFrame,
1626 tools::Long nBottom,
1627 bool _bOnlyRowsAndCells )
1628{
1629 vcl::RenderContext* pRenderContext = pFrame->getRootFrame()->GetCurrShell() ? pFrame->getRootFrame()->GetCurrShell()->GetOut() : nullptr;
1630 // LONG_MAX == nBottom means we have to calculate all
1631 bool bAll = LONG_MAX == nBottom;
1632 bool bRet = false;
1633 const SwFrame* pOldUp = pFrame->GetUpper();
1634 SwRectFnSet aRectFnSet(pFrame);
1635 do
1636 {
1637 // #i26945# - parameter <_bOnlyRowsAndCells> controls,
1638 // if only row and cell frames are formatted.
1639 if ( pFrame->IsLayoutFrame() &&
1640 ( !_bOnlyRowsAndCells || pFrame->IsRowFrame() || pFrame->IsCellFrame() ) )
1641 {
1642 SwFrameDeleteGuard aDeleteGuard(pFrame);
1643
1644 // #130744# An invalid locked table frame will
1645 // not be calculated => It will not become valid =>
1646 // Loop in lcl_RecalcRow(). Therefore we do not consider them for bRet.
1647 bRet |= !pFrame->isFrameAreaDefinitionValid() && ( !pFrame->IsTabFrame() || !static_cast<SwTabFrame*>(pFrame)->IsJoinLocked() );
1648 pFrame->Calc(pRenderContext);
1649 if( static_cast<SwLayoutFrame*>(pFrame)->Lower() )
1650 bRet |= lcl_InnerCalcLayout( static_cast<SwLayoutFrame*>(pFrame)->Lower(), nBottom);
1651
1652 // NEW TABLES
1653 if (pFrame->IsCellFrame())
1654 {
1655 SwCellFrame* pThisCell = static_cast<SwCellFrame*>(pFrame);
1656 if ( pThisCell->GetTabBox()->getRowSpan() < 1 )
1657 {
1658 SwCellFrame& rToCalc = const_cast<SwCellFrame&>(pThisCell->FindStartEndOfRowSpanCell( true ));
1659 bRet |= !rToCalc.isFrameAreaDefinitionValid();
1660 rToCalc.Calc(pRenderContext);
1661 if ( rToCalc.Lower() )
1662 bRet |= lcl_InnerCalcLayout( rToCalc.Lower(), nBottom);
1663 }
1664 }
1665 }
1666 pFrame = pFrame->GetNext();
1667 } while( pFrame &&
1668 ( bAll ||
1669 aRectFnSet.YDiff(aRectFnSet.GetTop(pFrame->getFrameArea()), nBottom) < 0 )
1670 && pFrame->GetUpper() == pOldUp );
1671 return bRet;
1672}
1673
1674static void lcl_RecalcRow(SwRowFrame & rRow, tools::Long const nBottom)
1675{
1676 // FME 2007-08-30 #i81146# new loop control
1677 int nLoopControlRuns_1 = 0;
1678 sal_uInt16 nLoopControlStage_1 = 0;
1679 const int nLoopControlMax = 10;
1680
1681 bool bCheck = true;
1682 do
1683 {
1684 // FME 2007-08-30 #i81146# new loop control
1685 int nLoopControlRuns_2 = 0;
1686 sal_uInt16 nLoopControlStage_2 = 0;
1687
1688 while (lcl_InnerCalcLayout(&rRow, nBottom))
1689 {
1690 if ( ++nLoopControlRuns_2 > nLoopControlMax )
1691 {
1692 SAL_WARN_IF(nLoopControlStage_2 == 0, "sw.layout", "LoopControl_2 in lcl_RecalcRow: Stage 1!");
1693 SAL_WARN_IF(nLoopControlStage_2 == 1, "sw.layout", "LoopControl_2 in lcl_RecalcRow: Stage 2!!");
1694 SAL_WARN_IF(nLoopControlStage_2 >= 2, "sw.layout", "LoopControl_2 in lcl_RecalcRow: Stage 3!!!");
1695 rRow.ValidateThisAndAllLowers( nLoopControlStage_2++ );
1696 nLoopControlRuns_2 = 0;
1697 if( nLoopControlStage_2 > 2 )
1698 break;
1699 }
1700
1701 bCheck = true;
1702 }
1703
1704 if( bCheck )
1705 {
1706 // #115759# - force another format of the
1707 // lowers, if at least one of it was invalid.
1708 bCheck = SwContentFrame::CalcLowers(rRow, *rRow.GetUpper(), nBottom, true);
1709
1710 // NEW TABLES
1711 // First we calculate the cells with row span of < 1, afterwards
1712 // all cells with row span of > 1:
1713 for ( int i = 0; i < 2; ++i )
1714 {
1715 SwCellFrame* pCellFrame = static_cast<SwCellFrame*>(rRow.Lower());
1716 while ( pCellFrame )
1717 {
1718 const bool bCalc = 0 == i ?
1719 pCellFrame->GetLayoutRowSpan() < 1 :
1720 pCellFrame->GetLayoutRowSpan() > 1;
1721
1722 if ( bCalc )
1723 {
1724 SwCellFrame& rToRecalc = 0 == i ?
1725 const_cast<SwCellFrame&>(pCellFrame->FindStartEndOfRowSpanCell( true )) :
1726 *pCellFrame;
1727 bCheck |= SwContentFrame::CalcLowers(rToRecalc, rToRecalc, nBottom, false);
1728 }
1729
1730 pCellFrame = static_cast<SwCellFrame*>(pCellFrame->GetNext());
1731 }
1732 }
1733
1734 if ( bCheck )
1735 {
1736 if ( ++nLoopControlRuns_1 > nLoopControlMax )
1737 {
1738 SAL_WARN_IF(nLoopControlStage_1 == 0, "sw.layout", "LoopControl_1 in lcl_RecalcRow: Stage 1!");
1739 SAL_WARN_IF(nLoopControlStage_1 == 1, "sw.layout", "LoopControl_1 in lcl_RecalcRow: Stage 2!!");
1740 SAL_WARN_IF(nLoopControlStage_1 >= 2, "sw.layout", "LoopControl_1 in lcl_RecalcRow: Stage 3!!!");
1741 rRow.ValidateThisAndAllLowers( nLoopControlStage_1++ );
1742 nLoopControlRuns_1 = 0;
1743 if( nLoopControlStage_1 > 2 )
1744 break;
1745 }
1746
1747 continue;
1748 }
1749 }
1750 break;
1751 } while( true );
1752}
1753
1754static void lcl_RecalcTable( SwTabFrame& rTab,
1755 SwLayoutFrame *pFirstRow,
1756 SwLayNotify &rNotify )
1757{
1758 if ( rTab.Lower() )
1759 {
1760 if ( !pFirstRow )
1761 {
1762 pFirstRow = static_cast<SwLayoutFrame*>(rTab.Lower());
1763 rNotify.SetLowersComplete( true );
1764 }
1765 ::SwInvalidatePositions( pFirstRow, LONG_MAX );
1766 lcl_RecalcRow( *static_cast<SwRowFrame*>(pFirstRow), LONG_MAX );
1767 }
1768}
1769
1770// This is a new function to check the first condition whether
1771// a tab frame may move backward. It replaces the formerly used
1772// GetIndPrev(), which did not work correctly for #i5947#
1773static bool lcl_NoPrev( const SwFrame& rFrame )
1774{
1775 // #i79774#
1776 // skip empty sections on investigation of direct previous frame.
1777 // use information, that at least one empty section is skipped in the following code.
1778 bool bSkippedDirectPrevEmptySection( false );
1779 if ( rFrame.GetPrev() )
1780 {
1781 const SwFrame* pPrev( rFrame.GetPrev() );
1782 while ( pPrev &&
1783 pPrev->IsSctFrame() &&
1784 !dynamic_cast<const SwSectionFrame&>(*pPrev).GetSection() )
1785 {
1786 pPrev = pPrev->GetPrev();
1787 bSkippedDirectPrevEmptySection = true;
1788 }
1789 if ( pPrev )
1790 {
1791 return false;
1792 }
1793 }
1794
1795 if ( ( !bSkippedDirectPrevEmptySection && !rFrame.GetIndPrev() ) ||
1796 ( bSkippedDirectPrevEmptySection &&
1797 ( !rFrame.IsInSct() || !rFrame.GetIndPrev_() ) ) )
1798 {
1799 return true;
1800 }
1801
1802 // I do not have a direct prev, but I have an indirect prev.
1803 // In section frames I have to check if I'm located inside
1804 // the first column:
1805 if ( rFrame.IsInSct() )
1806 {
1807 const SwFrame* pSct = rFrame.GetUpper();
1808 if ( pSct && pSct->IsColBodyFrame() &&
1809 pSct->GetUpper()->GetUpper()->IsSctFrame() )
1810 {
1811 const SwFrame* pPrevCol = rFrame.GetUpper()->GetUpper()->GetPrev();
1812 if ( pPrevCol )
1813 // I'm not inside the first column and do not have a direct
1814 // prev. I can try to go backward.
1815 return true;
1816 }
1817 }
1818
1819 return false;
1820}
1821
1822#define KEEPTAB ( !GetFollow() && !IsFollow() )
1823
1824// - helper method to find next content frame of
1825// a table frame and format it to assure keep attribute.
1826// method return true, if a next content frame is formatted.
1827// Precondition: The given table frame hasn't a follow and isn't a follow.
1829{
1830 vcl::RenderContext* pRenderContext = pTabFrame->getRootFrame()->GetCurrShell()->GetOut();
1831 // find next content, table or section
1832 SwFrame* pNxt = pTabFrame->FindNext();
1833
1834 // skip empty sections
1835 while ( pNxt && pNxt->IsSctFrame() &&
1836 !static_cast<SwSectionFrame*>(pNxt)->GetSection() )
1837 {
1838 pNxt = pNxt->FindNext();
1839 }
1840
1841 // if found next frame is a section, get its first content.
1842 if ( pNxt && pNxt->IsSctFrame() )
1843 {
1844 pNxt = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
1845 }
1846
1847 // format found next frame.
1848 // if table frame is inside another table, method <SwFrame::MakeAll()> is
1849 // called to avoid that the superior table frame is formatted.
1850 if ( pNxt )
1851 {
1852 if ( pTabFrame->GetUpper()->IsInTab() )
1853 pNxt->MakeAll(pNxt->getRootFrame()->GetCurrShell()->GetOut());
1854 else
1855 pNxt->Calc(pRenderContext);
1856 }
1857
1858 return pNxt;
1859}
1860
1861namespace {
1862 bool AreAllRowsKeepWithNext( const SwRowFrame* pFirstRowFrame, const bool bCheckParents = true )
1863 {
1864 bool bRet = pFirstRowFrame != nullptr &&
1865 pFirstRowFrame->ShouldRowKeepWithNext( bCheckParents );
1866
1867 while ( bRet && pFirstRowFrame->GetNext() != nullptr )
1868 {
1869 pFirstRowFrame = dynamic_cast<const SwRowFrame*>(pFirstRowFrame->GetNext());
1870 bRet = pFirstRowFrame != nullptr &&
1871 pFirstRowFrame->ShouldRowKeepWithNext( bCheckParents );
1872 }
1873
1874 return bRet;
1875 }
1876}
1877
1878// extern because static can't be friend
1880{
1881 // hilariously static_cast<SwTabFrame*>(GetLower()) would not require friend declaration, but it's UB...
1882 rRowFrame.setFrameAreaPositionValid(false);
1883}
1884
1886{
1887 while (pFrame)
1888 {
1889 if (pFrame->IsLayoutFrame())
1890 {
1892 }
1893 else if (pFrame->IsTextFrame())
1894 {
1896 }
1897 pFrame = pFrame->GetNext();
1898 }
1899}
1900
1902{
1903 if ( IsJoinLocked() || StackHack::IsLocked() || StackHack::Count() > 50 )
1904 return;
1905
1906 if ( HasFollow() )
1907 {
1908 SwTabFrame* pFollowFrame = GetFollow();
1909 OSL_ENSURE( !pFollowFrame->IsJoinLocked() || !pFollowFrame->IsRebuildLastLine(),
1910 "SwTabFrame::MakeAll for master while follow is in RebuildLastLine()" );
1911 if ( pFollowFrame->IsJoinLocked() && pFollowFrame->IsRebuildLastLine() )
1912 return;
1913 }
1914
1916
1917 LockJoin(); //I don't want to be destroyed on the way.
1918 SwLayNotify aNotify( this ); //does the notification in the DTor
1919 // If pos is invalid, we have to call a SetInvaKeep at aNotify.
1920 // Otherwise the keep attribute would not work in front of a table.
1921 const bool bOldValidPos = isFrameAreaPositionValid();
1922
1923 //If my neighbour is my Follow at the same time, I'll swallow it up.
1924 // OD 09.04.2003 #108698# - join all follows, which are placed on the
1925 // same page/column.
1926 // OD 29.04.2003 #109213# - join follow, only if join for the follow
1927 // is not locked. Otherwise, join will not be performed and this loop
1928 // will be endless.
1929 while ( GetNext() && GetNext() == GetFollow() &&
1930 CanDeleteFollow(GetFollow())
1931 )
1932 {
1933 if ( HasFollowFlowLine() )
1935 Join();
1936 }
1937
1938 // The bRemoveFollowFlowLinePending is set if the split attribute of the
1939 // last line is set:
1941 {
1942 if ( RemoveFollowFlowLine() )
1943 Join();
1945 }
1946
1947 if (m_bResizeHTMLTable) //Optimized interplay with grow/shrink of the content
1948 {
1949 m_bResizeHTMLTable = false;
1951 if ( pLayout )
1952 m_bCalcLowers = pLayout->Resize(
1953 pLayout->GetBrowseWidthByTabFrame( *this ) );
1954 }
1955
1956 // as long as bMakePage is true, a new page can be created (exactly once)
1957 bool bMakePage = true;
1958 // bMovedBwd gets set to true when the frame flows backwards
1959 bool bMovedBwd = false;
1960 // as long as bMovedFwd is false, the Frame may flow backwards (until
1961 // it has been moved forward once)
1962 bool bMovedFwd = false;
1963 // gets set to true when the Frame is split
1964 bool bSplit = false;
1965 const bool bFootnotesInDoc = !GetFormat()->GetDoc()->GetFootnoteIdxs().empty();
1966 const bool bFly = IsInFly();
1967
1968 std::optional<SwBorderAttrAccess> oAccess(std::in_place, SwFrame::GetCache(), this);
1969 const SwBorderAttrs *pAttrs = oAccess->Get();
1970
1971 // All rows should keep together
1972 bool bDontSplit = !IsFollow() &&
1973 ( !GetFormat()->GetLayoutSplit().GetValue() );
1974
1975 // The number of repeated headlines
1976 const sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat();
1977
1978 // This flag indicates that we are allowed to try to split the
1979 // table rows.
1980 bool bTryToSplit = true;
1981
1982 // Indicates that two individual rows may keep together, based on the keep
1983 // attribute set at the first paragraph in the first cell.
1984 bool bTableRowKeep = !bDontSplit && GetFormat()->GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::TABLE_ROW_KEEP);
1985 if (SwFlyFrame* pFly = FindFlyFrame())
1986 {
1987 if (pFly->IsFlySplitAllowed())
1988 {
1989 // Ignore the above text node -> row inheritance for floating tables.
1990 bTableRowKeep = false;
1991 }
1992 else if (!pFly->GetNextLink())
1993 {
1994 // If the fly is not allowed to split and is not chained, then it makes no sense to
1995 // split the table.
1996 bDontSplit = true;
1997 }
1998 }
1999
2000 // The Magic Move: Used for the table row keep feature.
2001 // If only the last row of the table wants to keep (implicitly by setting
2002 // keep for the first paragraph in the first cell), and this table does
2003 // not have a next, the last line will be cut. Loop prevention: Only
2004 // one try.
2005 // WHAT IS THIS??? It "magically" hides last line (paragraph) in a table,
2006 // if first is set to keep with next???
2007 bool bLastRowHasToMoveToFollow = false;
2008 bool bLastRowMoveNoMoreTries = false;
2009
2010 const bool bLargeTable = GetTable()->GetTabLines().size() > 64; //arbitrary value, virtually guaranteed to be larger than one page.
2011 const bool bEmulateTableKeep = !bLargeTable && bTableRowKeep
2012 && !pAttrs->GetAttrSet().GetKeep().GetValue()
2013 && AreAllRowsKeepWithNext(GetFirstNonHeadlineRow(), /*bCheckParents=*/false);
2014 // The beloved keep attribute
2015 const bool bKeep = IsKeep(pAttrs->GetAttrSet().GetKeep(), GetBreakItem(), bEmulateTableKeep);
2016
2017 // Join follow table, if this table is not allowed to split:
2018 if ( bDontSplit )
2019 {
2020 while ( GetFollow() && !GetFollow()->IsJoinLocked() )
2021 {
2022 if ( HasFollowFlowLine() )
2024 Join();
2025 }
2026 }
2027
2028 // Join follow table, if this does not have enough (repeated) lines:
2029 if ( nRepeat )
2030 {
2031 if( GetFollow() && !GetFollow()->IsJoinLocked() &&
2032 nullptr == GetFirstNonHeadlineRow() )
2033 {
2034 if ( HasFollowFlowLine() )
2036 Join();
2037 }
2038 }
2039
2040 // Join follow table, if last row of this table should keep:
2041 if ( bTableRowKeep && GetFollow() && !GetFollow()->IsJoinLocked() )
2042 {
2043 const SwRowFrame* pTmpRow = static_cast<const SwRowFrame*>(GetLastLower());
2044 if ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() )
2045 {
2046 if ( HasFollowFlowLine() )
2048 Join();
2049 }
2050 }
2051
2052 // a new one is moved forwards immediately
2053 if ( !getFrameArea().Top() && IsFollow() )
2054 {
2055 SwFrame *pPre = GetPrev();
2056 if ( pPre && pPre->IsTabFrame() && static_cast<SwTabFrame*>(pPre)->GetFollow() == this)
2057 {
2058 // don't make the effort to move fwd if its known
2059 // conditions that are known not to work
2060 if (IsInFootnote() && ForbiddenForFootnoteCntFwd())
2061 bMakePage = false;
2062 else if (!MoveFwd(bMakePage, false))
2063 bMakePage = false;
2064 bMovedFwd = true;
2065 }
2066 }
2067
2068 int nUnSplitted = 5; // Just another loop control :-(
2069 int nThrowAwayValidLayoutLimit = 5; // And another one :-(
2070 SwRectFnSet aRectFnSet(this);
2072 {
2073 const bool bMoveable = IsMoveable();
2074 if (bMoveable &&
2075 !(bMovedFwd && bEmulateTableKeep) )
2076 if ( CheckMoveFwd( bMakePage, bKeep && KEEPTAB, bEmulateTableKeep ) )
2077 {
2078 bMovedFwd = true;
2079 m_bCalcLowers = true;
2080 // #i99267#
2081 // reset <bSplit> after forward move to assure that follows
2082 // can be joined, if further space is available.
2083 bSplit = false;
2084 }
2085
2086 Point aOldPos( aRectFnSet.GetPos(getFrameArea()) );
2087 MakePos();
2088
2089 if ( aOldPos != aRectFnSet.GetPos(getFrameArea()) )
2090 {
2091 if ( aOldPos.Y() != aRectFnSet.GetTop(getFrameArea()) )
2092 {
2094 if( pLayout )
2095 {
2096 oAccess.reset();
2097 m_bCalcLowers |= pLayout->Resize(
2098 pLayout->GetBrowseWidthByTabFrame( *this ) );
2099 }
2100
2102 aNotify.SetLowersComplete( false );
2103 }
2104 SwFrame *pPre;
2105 if ( bKeep || (nullptr != (pPre = FindPrev()) &&
2106 pPre->GetAttrSet()->GetKeep().GetValue()) )
2107 {
2108 m_bCalcLowers = true;
2109 }
2110 if (GetLower())
2111 { // it's possible that the rows already have valid pos - but it is surely wrong if the table's pos changed!
2113 // invalidate text frames to get rid of their SwFlyPortions
2115 }
2116 }
2117
2118 //We need to know the height of the first row, because the master needs
2119 //to be invalidated if it shrinks and then absorb the row if possible.
2120 tools::Long n1StLineHeight = 0;
2121 if ( IsFollow() )
2122 {
2123 SwFrame* pFrame = GetFirstNonHeadlineRow();
2124 if ( pFrame )
2125 n1StLineHeight = aRectFnSet.GetHeight(pFrame->getFrameArea());
2126 }
2127
2129 {
2130 const tools::Long nOldPrtWidth = aRectFnSet.GetWidth(getFramePrintArea());
2131 const tools::Long nOldFrameWidth = aRectFnSet.GetWidth(getFrameArea());
2132 const Point aOldPrtPos = aRectFnSet.GetPos(getFramePrintArea());
2133
2134 if (!oAccess)
2135 {
2136 oAccess.emplace(SwFrame::GetCache(), this);
2137 pAttrs = oAccess->Get();
2138 }
2139 Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs );
2140
2142 if ( pLayout &&
2143 (aRectFnSet.GetWidth(getFramePrintArea()) != nOldPrtWidth ||
2144 aRectFnSet.GetWidth(getFrameArea()) != nOldFrameWidth) )
2145 {
2146 oAccess.reset();
2147 m_bCalcLowers |= pLayout->Resize(
2148 pLayout->GetBrowseWidthByTabFrame( *this ) );
2149 }
2150 if ( aOldPrtPos != aRectFnSet.GetPos(getFramePrintArea()) )
2151 aNotify.SetLowersComplete( false );
2152 }
2153
2154 // If this is the first one in a chain, check if this can flow
2155 // backwards (if this is movable at all).
2156 // To prevent oscillations/loops, check that this has not just
2157 // flowed forwards.
2158 if ( !bMovedFwd && (bMoveable || bFly) && lcl_NoPrev( *this ) )
2159 {
2160 // for Follows notify Master.
2161 // only move Follow if it has to skip empty pages.
2162 if ( IsFollow() )
2163 {
2164 // Only if the height of the first line got smaller.
2165 SwFrame *pFrame = GetFirstNonHeadlineRow();
2166 if( pFrame && n1StLineHeight >aRectFnSet.GetHeight(pFrame->getFrameArea()) )
2167 {
2168 SwTabFrame *pMaster = FindMaster();
2169 bool bDummy;
2170 if ( ShouldBwdMoved( pMaster->GetUpper(), bDummy ) )
2171 pMaster->InvalidatePos();
2172 }
2173 }
2174 SwFootnoteBossFrame *pOldBoss = bFootnotesInDoc ? FindFootnoteBossFrame( true ) : nullptr;
2175 bool bReformat;
2176 std::optional<SfxDeleteListener> oDeleteListener;
2177 if (pOldBoss)
2178 oDeleteListener.emplace(*pOldBoss);
2179 SwFrameDeleteGuard g(this);
2180 if ( MoveBwd( bReformat ) )
2181 {
2182 SAL_WARN_IF(oDeleteListener && oDeleteListener->WasDeleted(), "sw.layout", "SwFootnoteBossFrame unexpectedly deleted");
2183
2184 aRectFnSet.Refresh(this);
2185 bMovedBwd = true;
2186 aNotify.SetLowersComplete( false );
2187 if (bFootnotesInDoc && !oDeleteListener->WasDeleted())
2188 MoveLowerFootnotes( nullptr, pOldBoss, nullptr, true );
2189 if ( bReformat || bKeep )
2190 {
2191 tools::Long nOldTop = aRectFnSet.GetTop(getFrameArea());
2192 MakePos();
2193 if( nOldTop != aRectFnSet.GetTop(getFrameArea()) )
2194 {
2195 SwHTMLTableLayout *pHTMLLayout =
2197 if( pHTMLLayout )
2198 {
2199 oAccess.reset();
2200 m_bCalcLowers |= pHTMLLayout->Resize(
2201 pHTMLLayout->GetBrowseWidthByTabFrame( *this ) );
2202 }
2203
2205
2206 if (!oAccess)
2207 {
2208 oAccess.emplace(SwFrame::GetCache(), this);
2209 pAttrs = oAccess->Get();
2210 }
2211 Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs );
2212 }
2213
2214 oAccess.reset();
2215
2216 lcl_RecalcTable( *this, nullptr, aNotify );
2217
2218 m_bLowersFormatted = true;
2219 if ( bKeep && KEEPTAB )
2220 {
2221
2222 // Consider case that table is inside another table,
2223 // because it has to be avoided, that superior table
2224 // is formatted.
2225 // Thus, find next content, table or section
2226 // and, if a section is found, get its first
2227 // content.
2228 if ( nullptr != sw_FormatNextContentForKeep( this ) && !GetNext() )
2229 {
2231 }
2232 }
2233 }
2234 }
2235 }
2236
2237 //Again an invalid value? - do it again...
2239 continue;
2240
2241 // check, if calculation of table frame is ready.
2242
2243 // Local variable <nDistanceToUpperPrtBottom>
2244 // Introduce local variable and init it with the distance from the
2245 // table frame bottom to the bottom of the upper printing area.
2246 // Note: negative values denotes the situation that table frame doesn't fit in its upper.
2247 SwTwips nDistanceToUpperPrtBottom =
2248 aRectFnSet.BottomDist(getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
2249
2251 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
2252 const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
2253 if ( nDistanceToUpperPrtBottom < 0 && bBrowseMode )
2254 {
2255 if ( GetUpper()->Grow( -nDistanceToUpperPrtBottom ) )
2256 {
2257 // upper is grown --> recalculate <nDistanceToUpperPrtBottom>
2258 nDistanceToUpperPrtBottom = aRectFnSet.BottomDist(getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
2259 }
2260 }
2261
2262 if (GetFollow() && GetUpper()->IsFlyFrame())
2263 {
2264 auto pUpper = static_cast<SwFlyFrame*>(GetUpper());
2265 if (pUpper->IsFlySplitAllowed())
2266 {
2267 // We have a follow tab frame that may be joined, and we're directly in a split fly.
2268 // See if the fly could grow.
2269 SwTwips nTest = GetUpper()->Grow(LONG_MAX, /*bTst=*/true);
2270 if (nTest >= aRectFnSet.GetHeight(GetFollow()->getFrameArea()))
2271 {
2272 // We have space to join at least one follow tab frame.
2273 SwTwips nRequest = 0;
2274 for (SwTabFrame* pFollow = GetFollow(); pFollow; pFollow = pFollow->GetFollow())
2275 {
2276 nRequest += aRectFnSet.GetHeight(pFollow->getFrameArea());
2277 }
2278 // Try to grow the split fly to join all follows.
2279 pUpper->Grow(nRequest);
2280 // Determine what is space we actually got from the requested space.
2281 nDistanceToUpperPrtBottom = aRectFnSet.BottomDist(getFrameArea(), aRectFnSet.GetPrtBottom(*pUpper));
2282 }
2283 }
2284 }
2285
2286 // If there is still some space left in the upper, we check if we
2287 // can join some rows of the follow.
2288 // Setting bLastRowHasToMoveToFollow to true means we want to force
2289 // the table to be split! Only skip this if condition once.
2290 if( nDistanceToUpperPrtBottom >= 0 && !bLastRowHasToMoveToFollow )
2291 {
2292 // If there is space left in the upper printing area, join as for trial
2293 // at least one further row of an existing follow.
2294 if ( !bSplit && GetFollow() )
2295 {
2296 bool bDummy;
2297 if ( GetFollow()->ShouldBwdMoved( GetUpper(), bDummy ) )
2298 {
2299 SwFrame *pTmp = GetUpper();
2300 SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*pTmp);
2301 if ( bBrowseMode )
2302 nDeadLine += pTmp->Grow( LONG_MAX, true );
2303 bool bFits = aRectFnSet.BottomDist(getFrameArea(), nDeadLine) > 0;
2304 if (!bFits && aRectFnSet.GetHeight(GetFollow()->getFrameArea()) == 0)
2305 // The follow should move backwards, so allow the case
2306 // when the upper has no space, but the follow is
2307 // empty.
2308 bFits = aRectFnSet.BottomDist(getFrameArea(), nDeadLine) >= 0;
2309 if (bFits)
2310 {
2311 // First, we remove an existing follow flow line.
2312 if ( HasFollowFlowLine() )
2313 {
2314 SwFrame* pLastLine = GetLastLower();
2316 // invalidate and rebuild last row
2317 if ( pLastLine )
2318 {
2319 ::SwInvalidateAll( pLastLine, LONG_MAX );
2320 SetRebuildLastLine( true );
2321 lcl_RecalcRow(*static_cast<SwRowFrame*>(pLastLine), LONG_MAX);
2322 SetRebuildLastLine( false );
2323 }
2324
2326
2327 if ( !pRow || !pRow->GetNext() )
2328 // The follow became empty and hence useless
2329 Join();
2330
2331 continue;
2332 }
2333
2334 // If there is no follow flow line, we move the first
2335 // row in the follow table to the master table.
2337
2338 // The follow became empty and hence useless
2339 if ( !pRow )
2340 {
2341 Join();
2342 continue;
2343 }
2344
2345 const SwTwips nOld = aRectFnSet.GetHeight(getFrameArea());
2346 tools::Long nRowsToMove = lcl_GetMaximumLayoutRowSpan( *pRow );
2347 SwFrame* pRowToMove = pRow;
2348
2349 while ( pRowToMove && nRowsToMove-- > 0 )
2350 {
2351 const bool bMoveFootnotes = bFootnotesInDoc && !GetFollow()->IsJoinLocked();
2352
2353 SwFootnoteBossFrame *pOldBoss = nullptr;
2354 if ( bMoveFootnotes )
2355 pOldBoss = pRowToMove->FindFootnoteBossFrame( true );
2356
2357 SwFrame* pNextRow = pRowToMove->GetNext();
2358
2359 if ( !pNextRow )
2360 {
2361 // The follow became empty and hence useless
2362 Join();
2363 }
2364 else
2365 {
2366 pRowToMove->Cut();
2367 pRowToMove->Paste( this );
2368 }
2369
2370 // Move the footnotes!
2371 if ( bMoveFootnotes )
2372 if ( static_cast<SwLayoutFrame*>(pRowToMove)->MoveLowerFootnotes( nullptr, pOldBoss, FindFootnoteBossFrame( true ), true ) )
2373 GetUpper()->Calc(pRenderContext);
2374
2375 pRowToMove = pNextRow;
2376 }
2377
2378 if ( nOld != aRectFnSet.GetHeight(getFrameArea()) )
2379 lcl_RecalcTable( *this, static_cast<SwLayoutFrame*>(pRow), aNotify );
2380
2381 continue;
2382 }
2383 }
2384 }
2385 else if ( KEEPTAB )
2386 {
2387 bool bFormat = false;
2388 if ( bKeep )
2389 bFormat = true;
2390 else if ( bTableRowKeep && !bLastRowMoveNoMoreTries )
2391 {
2392 // We only want to give the last row one chance to move
2393 // to the follow table. Set the flag as early as possible:
2394 bLastRowMoveNoMoreTries = true;
2395
2396 // The last line of the table has to be cut off if:
2397 // 1. The table does not want to keep with its next
2398 // 2. The compatibility option is set and the table is allowed to split
2399 // 3. We did not already cut off the last row
2400 // 4. There is not break after attribute set at the table
2401 // 5. There is no break before attribute set behind the table
2402 // 6. There is no section change behind the table (see IsKeep)
2403 // 7. The last table row wants to keep with its next.
2404 const SwRowFrame* pLastRow = static_cast<const SwRowFrame*>(GetLastLower());
2405 if (pLastRow)
2406 {
2407 if (!oAccess)
2408 {
2409 oAccess.emplace(SwFrame::GetCache(), this);
2410 pAttrs = oAccess->Get();
2411 }
2412 if (IsKeep(pAttrs->GetAttrSet().GetKeep(), GetBreakItem(), true)
2413 && pLastRow->ShouldRowKeepWithNext())
2414 {
2415 bFormat = true;
2416 }
2417 }
2418 }
2419
2420 if ( bFormat )
2421 {
2422 oAccess.reset();
2423
2424 // Consider case that table is inside another table, because
2425 // it has to be avoided, that superior table is formatted.
2426 // Thus, find next content, table or section and, if a section
2427 // is found, get its first content.
2428 const SwFrame* pTmpNxt = sw_FormatNextContentForKeep( this );
2429
2430 // The last row wants to keep with the frame behind the table.
2431 // Check if the next frame is on a different page and valid.
2432 // In this case we do a magic trick:
2433 if ( !bKeep && !GetNext() && pTmpNxt && pTmpNxt->isFrameAreaDefinitionValid() )
2434 {
2436 bLastRowHasToMoveToFollow = true;
2437 }
2438 }
2439 }
2440
2442 {
2443 if (m_bCalcLowers)
2444 {
2445 lcl_RecalcTable( *this, nullptr, aNotify );
2446 m_bLowersFormatted = true;
2447 m_bCalcLowers = false;
2448 }
2449 else if (m_bONECalcLowers)
2450 {
2451 // tdf#147526 is a case of a macro which results in a null Lower() result
2452 if (SwRowFrame* pLower = static_cast<SwRowFrame*>(Lower()))
2453 lcl_RecalcRow(*pLower, LONG_MAX);
2454 m_bONECalcLowers = false;
2455 }
2456 }
2457 continue;
2458 }
2459
2460 // I don't fit in the upper Frame anymore, therefore it's the
2461 // right moment to do some preferably constructive changes.
2462
2463 // If I'm NOT allowed to leave the upper Frame, I've got a problem.
2464 // Following Arthur Dent, we do the only thing that you can do with
2465 // an unsolvable problem: We ignore it with all our power.
2466 if ( !bMoveable )
2467 {
2469 {
2470 lcl_RecalcTable( *this, nullptr, aNotify );
2471 m_bLowersFormatted = true;
2472 m_bCalcLowers = false;
2473 }
2474 else if (m_bONECalcLowers)
2475 {
2476 lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), LONG_MAX);
2477 m_bONECalcLowers = false;
2478 }
2479
2480 // It does not make sense to cut off the last line if we are
2481 // not moveable:
2482 bLastRowHasToMoveToFollow = false;
2483
2484 continue;
2485 }
2486
2488 {
2489 lcl_RecalcTable( *this, nullptr, aNotify );
2490 m_bLowersFormatted = true;
2491 m_bCalcLowers = false;
2493 continue;
2494 }
2495
2496 // First try to split the table. Condition:
2497 // 1. We have at least one non headline row
2498 // 2. If this row wants to keep, we need an additional row
2499 // 3. The table is allowed to split or we do not have a pIndPrev:
2500 SwFrame* pIndPrev = GetIndPrev();
2501 const SwRowFrame* pFirstNonHeadlineRow = GetFirstNonHeadlineRow();
2502 // #i120016# if this row wants to keep, allow split in case that all rows want to keep with next,
2503 // the table can not move forward as it is the first one and a split is in general allowed.
2504 const bool bAllowSplitOfRow = bTableRowKeep && !pIndPrev && AreAllRowsKeepWithNext(pFirstNonHeadlineRow);
2505 // tdf91083 MSCompat: this extends bAllowSplitOfRow (and perhaps should just replace it).
2506 // If the kept-together items cannot move to a new page, a table split is in general allowed.
2507 const bool bEmulateTableKeepSplitAllowed = bEmulateTableKeep && !IsKeepFwdMoveAllowed(/*IgnoreMyOwnKeepValue=*/true);
2508
2509 if ( pFirstNonHeadlineRow && nUnSplitted > 0 &&
2510 ( bEmulateTableKeepSplitAllowed || bAllowSplitOfRow ||
2511 ( ( !bTableRowKeep || pFirstNonHeadlineRow->GetNext() ||
2512 !pFirstNonHeadlineRow->ShouldRowKeepWithNext()
2513 ) && ( !bDontSplit || !pIndPrev )
2514 ) ) )
2515 {
2516 // #i29438#
2517 // Special DoNotSplit cases:
2518 // We better avoid splitting of a row frame if we are inside a columned
2519 // section which has a height of 0, because this is not growable and thus
2520 // all kinds of unexpected things could happen.
2521 if ( IsInSct() && FindSctFrame()->Lower()->IsColumnFrame() &&
2522 0 == aRectFnSet.GetHeight(GetUpper()->getFrameArea())
2523 )
2524 {
2525 bTryToSplit = false;
2526 }
2527
2528 // 1. Try: bTryToSplit = true => Try to split the row.
2529 // 2. Try: bTryToSplit = false => Split the table between the rows.
2530 if ( pFirstNonHeadlineRow->GetNext() || bTryToSplit )
2531 {
2532 SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
2533 bool bFlySplit = false;
2534 if (GetUpper()->IsFlyFrame())
2535 {
2536 // See if this is a split fly that can also grow.
2537 auto pUpperFly = static_cast<SwFlyFrame*>(GetUpper());
2538 bFlySplit = pUpperFly->IsFlySplitAllowed();
2539 }
2540 if( IsInSct() || GetUpper()->IsInTab() || bFlySplit )
2541 nDeadLine = aRectFnSet.YInc( nDeadLine,
2542 GetUpper()->Grow( LONG_MAX, true ) );
2543
2544 {
2545 SwFrameDeleteGuard g(Lower()); // tdf#134965 prevent RemoveFollowFlowLine()
2546 SetInRecalcLowerRow( true );
2547 ::lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), nDeadLine);
2548 SetInRecalcLowerRow( false );
2549 }
2550 m_bLowersFormatted = true;
2551 aNotify.SetLowersComplete( true );
2552
2553 // One more check if it's really necessary to split the table.
2554 // 1. The table either has to exceed the deadline or
2555 // 2. We explicitly want to cut off the last row.
2556 if( aRectFnSet.BottomDist( getFrameArea(), nDeadLine ) > 0 && !bLastRowHasToMoveToFollow )
2557 {
2558 continue;
2559 }
2560
2561 // Set to false again as early as possible.
2562 bLastRowHasToMoveToFollow = false;
2563
2564 // #i52781#
2565 // YaSC - Yet another special case:
2566 // If our upper is inside a table cell which is not allowed
2567 // to split, we do not try to split:
2568 if ( GetUpper()->IsInTab() )
2569 {
2570 const SwFrame* pTmpRow = GetUpper();
2571 while ( pTmpRow && !pTmpRow->IsRowFrame() )
2572 pTmpRow = pTmpRow->GetUpper();
2573 if ( pTmpRow && !static_cast<const SwRowFrame*>(pTmpRow)->IsRowSplitAllowed() )
2574 continue;
2575 }
2576
2577 sal_uInt16 nMinNumOfLines = nRepeat;
2578
2579 if ( bTableRowKeep )
2580 {
2581 const SwRowFrame* pTmpRow = GetFirstNonHeadlineRow();
2582 while ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() )
2583 {
2584 ++nMinNumOfLines;
2585 pTmpRow = static_cast<const SwRowFrame*>(pTmpRow->GetNext());
2586 }
2587 }
2588
2589 if ( !bTryToSplit )
2590 ++nMinNumOfLines;
2591
2592 const SwTwips nBreakLine = aRectFnSet.YInc(
2593 aRectFnSet.GetTop(getFrameArea()),
2594 aRectFnSet.GetTopMargin(*this) +
2595 lcl_GetHeightOfRows( GetLower(), nMinNumOfLines ) );
2596
2597 // Some more checks if we want to call the split algorithm or not:
2598 // The repeating lines / keeping lines still fit into the upper or
2599 // if we do not have an (in)direct Prev, we split anyway.
2600 if( aRectFnSet.YDiff(nDeadLine, nBreakLine) >=0
2601 || !pIndPrev || bEmulateTableKeepSplitAllowed )
2602 {
2603 aNotify.SetLowersComplete( false );
2604 bSplit = true;
2605
2606 // An existing follow flow line has to be removed.
2607 if ( HasFollowFlowLine() )
2608 {
2609 if (!nThrowAwayValidLayoutLimit)
2610 continue;
2611 const bool bInitialLoopEndCondition(isFrameAreaDefinitionValid());
2613 const bool bFinalLoopEndCondition(isFrameAreaDefinitionValid());
2614
2615 if (bInitialLoopEndCondition && !bFinalLoopEndCondition)
2616 {
2617 --nThrowAwayValidLayoutLimit;
2618 }
2619 }
2620
2621 oAccess.reset();
2622 const bool bSplitError = !Split( nDeadLine, bTryToSplit, ( bTableRowKeep && !(bAllowSplitOfRow || bEmulateTableKeepSplitAllowed) ) );
2623
2624 // tdf#130639 don't start table on a new page after the fallback "switch off repeating header"
2625 if (bSplitError && nRepeat > GetTable()->GetRowsToRepeat())
2626 {
2628 break;
2629 }
2630
2631 if (!bTryToSplit && !bSplitError)
2632 {
2633 --nUnSplitted;
2634 }
2635
2636 // #i29771# Two tries to split the table
2637 // If an error occurred during splitting. We start a second
2638 // try, this time without splitting of table rows.
2639 if ( bSplitError && HasFollowFlowLine() )
2641
2642 // If splitting the table was successful or not,
2643 // we do not want to have 'empty' follow tables.
2645 Join();
2646
2647 // We want to restore the situation before the failed
2648 // split operation as good as possible. Therefore we
2649 // do some more calculations. Note: Restricting this
2650 // to nDeadLine may not be enough.
2651 if ( bSplitError && bTryToSplit ) // no restart if we did not try to split: i72847, i79426
2652 {
2653 lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), LONG_MAX);
2655 bTryToSplit = false;
2656 continue;
2657 }
2658
2659 bTryToSplit = !bSplitError;
2660
2661 //To avoid oscillations the Follow must become valid now
2662 if ( GetFollow() )
2663 {
2664 // #i80924#
2665 // After a successful split assure that the first row
2666 // is invalid. When graphics are present, this isn't hold.
2667 // Note: defect i80924 could also be fixed, if it is
2668 // assured, that <SwLayNotify::bLowersComplete> is only
2669 // set, if all lower are valid *and* are correct laid out.
2670 if ( !bSplitError && GetFollow()->GetLower() )
2671 {
2673 }
2674 SwRectFnSet fnRectX(GetFollow());
2675
2676 static sal_uInt8 nStack = 0;
2677 if ( !StackHack::IsLocked() && nStack < 4 )
2678 {
2679 ++nStack;
2680 StackHack aHack;
2681 oAccess.reset();
2682
2683 GetFollow()->MakeAll(pRenderContext);
2684
2685 GetFollow()->SetLowersFormatted(false);
2686 // #i43913# - lock follow table
2687 // to avoid its formatting during the format of
2688 // its content.
2689 const bool bOldJoinLock = GetFollow()->IsJoinLocked();
2690 GetFollow()->LockJoin();
2691 ::lcl_RecalcRow(*static_cast<SwRowFrame*>(GetFollow()->Lower()),
2692 fnRectX.GetBottom(GetFollow()->GetUpper()->getFrameArea()) );
2693 // #i43913#
2694 // #i63632# Do not unlock the
2695 // follow if it wasn't locked before.
2696 if ( !bOldJoinLock )
2697 GetFollow()->UnlockJoin();
2698
2699 if ( !GetFollow()->GetFollow() )
2700 {
2701 SwFrame* pNxt = static_cast<SwFrame*>(GetFollow())->FindNext();
2702 if ( pNxt )
2703 {
2704 // #i18103# - no formatting of found next
2705 // frame, if it's a follow section of the
2706 // 'ColLocked' section, the follow table is
2707 // in.
2708 bool bCalcNxt = true;
2709 if ( GetFollow()->IsInSct() && pNxt->IsSctFrame() )
2710 {
2712 if ( pSct->IsColLocked() &&
2713 pSct->GetFollow() == pNxt )
2714 {
2715 bCalcNxt = false;
2716 }
2717 }
2718 if ( bCalcNxt )
2719 {
2720 // tdf#119109 follow was just formatted,
2721 // don't do it again now
2723 pNxt->Calc(pRenderContext);
2724 }
2725 }
2726 }
2727
2728 --nStack;
2729 }
2730 else if ( GetFollow() == GetNext() )
2731 GetFollow()->MoveFwd( true, false );
2732 }
2733 continue;
2734 }
2735 }
2736 }
2737
2738 // Set to false again as early as possible.
2739 bLastRowHasToMoveToFollow = false;
2740
2741 if( IsInSct() && bMovedFwd && bMakePage && GetUpper()->IsColBodyFrame() &&
2742 GetUpper()->GetUpper()->GetUpper()->IsSctFrame() &&
2743 ( GetUpper()->GetUpper()->GetPrev() || GetIndPrev() ) &&
2744 static_cast<SwSectionFrame*>(GetUpper()->GetUpper()->GetUpper())->MoveAllowed(this) )
2745 {
2746 bMovedFwd = false;
2747 }
2748
2749 // #i29771# Reset bTryToSplit flag on change of upper
2750 const SwFrame* pOldUpper = GetUpper();
2751
2752 //Let's see if we find some place anywhere...
2753 if (!bMovedFwd)
2754 {
2755 // don't make the effort to move fwd if its known
2756 // conditions that are known not to work
2757 if (IsInFootnote() && ForbiddenForFootnoteCntFwd())
2758 bMakePage = false;
2759 else if (!MoveFwd(bMakePage, false))
2760 bMakePage = false;
2761 }
2762
2763 // #i29771# Reset bSplitError flag on change of upper
2764 if ( GetUpper() != pOldUpper )
2765 {
2766 bTryToSplit = true;
2767 nUnSplitted = 5;
2768 }
2769
2770 aRectFnSet.Refresh(this);
2771 m_bCalcLowers = true;
2772 bMovedFwd = true;
2773 aNotify.SetLowersComplete( false );
2774 if ( IsFollow() )
2775 {
2776 // To avoid oscillations, master should not remain invalid
2777 SwTabFrame *pTab = FindMaster();
2778 if ( pTab->GetUpper() )
2779 pTab->GetUpper()->Calc(pRenderContext);
2780 pTab->Calc(pRenderContext);
2781 pTab->SetLowersFormatted( false );
2782 }
2783
2784 //If my neighbour is my Follow at the same time, I'll swallow it up.
2785 if ( ( GetNext() && GetNext() == GetFollow() ) || !GetLower() )
2786 {
2787 if ( HasFollowFlowLine() )
2789 if ( GetFollow() )
2790 Join();
2791 }
2792
2793 if ( bMovedBwd && GetUpper() )
2794 {
2795 //During flowing back the upper was animated to do a full repaint,
2796 //we can now skip this after the whole flowing back and forth.
2798 }
2799
2801 {
2802 // #i44910# - format of lower frames unnecessary
2803 // and can cause layout loops, if table doesn't fit and isn't
2804 // allowed to split.
2805 SwTwips nDistToUpperPrtBottom =
2806 aRectFnSet.BottomDist( getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
2807 if ( nDistToUpperPrtBottom >= 0 || bTryToSplit )
2808 {
2809 lcl_RecalcTable( *this, nullptr, aNotify );
2810 m_bLowersFormatted = true;
2811 m_bCalcLowers = false;
2812 if (!isFramePrintAreaValid())
2814 }
2815#if OSL_DEBUG_LEVEL > 0
2816 else
2817 {
2818 OSL_FAIL( "debug assertion: <SwTabFrame::MakeAll()> - format of table lowers suppressed by fix i44910" );
2819 }
2820#endif
2821 }
2822
2823 } //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
2824
2825 //If my direct predecessor is my master now, it can destroy me during the
2826 //next best opportunity.
2827 if ( IsFollow() )
2828 {
2829 SwFrame *pPre = GetPrev();
2830 if ( pPre && pPre->IsTabFrame() && static_cast<SwTabFrame*>(pPre)->GetFollow() == this)
2831 pPre->InvalidatePos();
2832 }
2833
2835 oAccess.reset();
2836 UnlockJoin();
2837 if ( bMovedFwd || bMovedBwd || !bOldValidPos )
2838 aNotify.SetInvaKeep();
2839}
2840
2841static bool IsNextOnSamePage(SwPageFrame const& rPage,
2842 SwTabFrame const& rTabFrame, SwTextFrame const& rAnchorFrame)
2843{
2844 for (SwContentFrame const* pContentFrame = rTabFrame.FindNextCnt();
2845 pContentFrame && pContentFrame->FindPageFrame() == &rPage;
2846 pContentFrame = pContentFrame->FindNextCnt())
2847 {
2848 if (pContentFrame == &rAnchorFrame)
2849 {
2850 return true;
2851 }
2852 }
2853 return false;
2854}
2855
2858 tools::Long& rLeftOffset,
2859 tools::Long& rRightOffset,
2860 SwTwips *const pSpaceBelowBottom) const
2861{
2862 bool bInvalidatePrtArea = false;
2863 const SwPageFrame *pPage = FindPageFrame();
2864 const SwFlyFrame* pMyFly = FindFlyFrame();
2865
2866 // --> #108724# Page header/footer content doesn't have to wrap around
2867 // floating screen objects
2868
2870 const bool bWrapAllowed = rIDSA.get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) ||
2871 ( !IsInFootnote() && nullptr == FindFooterOrHeader() );
2872
2873 if (!bWrapAllowed || !pPage->GetSortedObjs())
2874 return bInvalidatePrtArea;
2875
2876 SwRectFnSet aRectFnSet(this);
2877 const bool bConsiderWrapOnObjPos
2879 tools::Long nPrtPos = aRectFnSet.GetTop(getFrameArea());
2880 nPrtPos = aRectFnSet.YInc(nPrtPos, rUpper);
2881 SwRect aRect(getFrameArea());
2882 if (pSpaceBelowBottom)
2883 {
2884 // set to space below table frame
2885 aRectFnSet.SetTopAndHeight(aRect, aRectFnSet.GetBottom(aRect), *pSpaceBelowBottom);
2886 }
2887 else
2888 {
2889 tools::Long nYDiff = aRectFnSet.YDiff(aRectFnSet.GetTop(getFramePrintArea()), rUpper);
2890 if (nYDiff > 0)
2891 aRectFnSet.AddBottom(aRect, -nYDiff);
2892 }
2893
2894 bool bAddVerticalFlyOffsets = rIDSA.get(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS);
2895
2896 for (size_t i = 0; i < pPage->GetSortedObjs()->size(); ++i)
2897 {
2898 SwAnchoredObject* pAnchoredObj = (*pPage->GetSortedObjs())[i];
2899 auto pFly = pAnchoredObj->DynCastFlyFrame();
2900 if (!pFly)
2901 continue;
2902
2903 const SwRect aFlyRect = pFly->GetObjRectWithSpaces();
2904 // #i26945# - correction of conditions,
2905 // if Writer fly frame has to be considered:
2906 // - no need to check, if top of Writer fly frame differs
2907 // from FAR_AWAY, because it's also checked, if the Writer
2908 // fly frame rectangle overlaps with <aRect>
2909 // - no check, if bottom of anchor frame is prior the top of
2910 // the table, because Writer fly frames can be negative positioned.
2911 // - correct check, if the Writer fly frame is a lower of the
2912 // table, because table lines/rows can split and an at-character
2913 // anchored Writer fly frame could be positioned in the follow
2914 // flow line.
2915 // - add condition, that an existing anchor character text frame
2916 // has to be on the same page as the table.
2917 // E.g., it could happen, that the fly frame is still registered
2918 // at the page frame, the table is on, but it's anchor character
2919 // text frame has already changed its page.
2920 const SwTextFrame* pAnchorCharFrame = pFly->FindAnchorCharFrame();
2921 const SwFormatHoriOrient& rHori= pFly->GetFormat()->GetHoriOrient();
2922 // Only consider invalid Writer fly frames if they'll be shifted down.
2923 bool bIgnoreFlyValidity
2924 = bAddVerticalFlyOffsets && rHori.GetHoriOrient() == text::HoriOrientation::NONE;
2925 bool bConsiderFly =
2926 // #i46807# - do not consider invalid
2927 // Writer fly frames.
2928 (pFly->isFrameAreaDefinitionValid() || bIgnoreFlyValidity)
2929 // fly anchored at character or at paragraph
2930 && pFly->IsFlyAtContentFrame()
2931 // fly overlaps with corresponding table rectangle
2932 && aFlyRect.Overlaps(aRect)
2933 // fly isn't lower of table and
2934 // anchor character frame of fly isn't lower of table
2935 && (pSpaceBelowBottom // not if in ShouldBwdMoved
2936 || (!IsAnLower(pFly) && (!pAnchorCharFrame || !IsAnLower(pAnchorCharFrame))))
2937 // table isn't lower of fly
2938 && !pFly->IsAnLower(this)
2939 // fly is lower of fly, the table is in
2940 // #123274# - correction
2941 // assure that fly isn't a lower of a fly, the table isn't in.
2942 // E.g., a table in the body doesn't wrap around a graphic,
2943 // which is inside a frame.
2944 && (!pMyFly || pMyFly->IsAnLower(pFly))
2945 && pMyFly == pFly->GetAnchorFrameContainingAnchPos()->FindFlyFrame()
2946 // anchor frame not on following page
2947 && pPage->GetPhyPageNum() >= pFly->GetAnchorFrame()->FindPageFrame()->GetPhyPageNum()
2948 // anchor character text frame on same page
2949 && (!pAnchorCharFrame ||
2950 pAnchorCharFrame->FindPageFrame()->GetPhyPageNum() == pPage->GetPhyPageNum());
2951
2952 if (!bConsiderFly)
2953 continue;
2954
2955 const SwFrame* pFlyHeaderFooterFrame = pFly->GetAnchorFrame()->FindFooterOrHeader();
2956 const SwFrame* pThisHeaderFooterFrame = FindFooterOrHeader();
2957 if (pFlyHeaderFooterFrame != pThisHeaderFooterFrame
2958 // #148493# If bConsiderWrapOnObjPos is set,
2959 // we want to consider the fly if it is located in the header and
2960 // the table is located in the body:
2961 && (!bConsiderWrapOnObjPos || nullptr != pThisHeaderFooterFrame
2962 || !pFlyHeaderFooterFrame->IsHeaderFrame()))
2963 {
2964 continue;
2965 }
2966
2967 text::WrapTextMode nSurround = pFly->GetFormat()->GetSurround().GetSurround();
2968 // If the frame format is a TextBox of a draw shape,
2969 // then use the surround of the original shape.
2970 bool bWrapThrough = nSurround == text::WrapTextMode_THROUGH;
2971 SwTextBoxHelper::getShapeWrapThrough(pFly->GetFormat(), bWrapThrough);
2972 if (bWrapThrough)
2973 continue;
2974 if (!bWrapThrough && nSurround == text::WrapTextMode_THROUGH)
2975 nSurround = text::WrapTextMode_PARALLEL;
2976
2977 bool bShiftDown = css::text::WrapTextMode_NONE == nSurround;
2978 if (!bShiftDown && bAddVerticalFlyOffsets)
2979 {
2980 if (nSurround == text::WrapTextMode_PARALLEL
2982 {
2983 // We know that wrapping was requested and the table frame overlaps with
2984 // the fly frame. Check if the print area overlaps with the fly frame as
2985 // well (in case the table does not use all the available width).
2986 basegfx::B1DRange aTabRange(
2987 aRectFnSet.GetLeft(aRect) + aRectFnSet.GetLeft(getFramePrintArea()),
2988 aRectFnSet.GetLeft(aRect) + aRectFnSet.GetLeft(getFramePrintArea())
2989 + aRectFnSet.GetWidth(getFramePrintArea()));
2990
2991 // Ignore spacing when determining the left/right edge of the fly, like
2992 // Word does.
2993 const SwRect aFlyRectWithoutSpaces = pFly->GetObjRect();
2994 basegfx::B1DRange aFlyRange(aRectFnSet.GetLeft(aFlyRectWithoutSpaces),
2995 aRectFnSet.GetRight(aFlyRectWithoutSpaces));
2996
2997 // If it does, shift the table down. Do this only in the compat case,
2998 // normally an SwFlyPortion is created instead that increases the height
2999 // of the first table row.
3000 bShiftDown = aTabRange.overlaps(aFlyRange);
3001 }
3002 }
3003
3004 if (bShiftDown)
3005 {
3006 // possible cases:
3007 // both in body
3008 // both in same fly
3009 // any comb. of body, footnote, header/footer
3010 // to keep it safe, check only in doc body vs page margin for now
3011 tools::Long nBottom = aRectFnSet.GetBottom(aFlyRect);
3012 // tdf#138039 don't grow beyond the page body
3013 // if the fly is anchored below the table; the fly
3014 // must move with its anchor frame to the next page
3015 SwRectFnSet fnPage(pPage);
3016 if (!IsInDocBody() // TODO
3017 || fnPage.YDiff(fnPage.GetBottom(aFlyRect), fnPage.GetPrtBottom(*pPage)) <= 0
3018 || !IsNextOnSamePage(
3019 *pPage, *this,
3020 *static_cast<SwTextFrame*>(pFly->GetAnchorFrameContainingAnchPos())))
3021 {
3022 if (aRectFnSet.YDiff(nPrtPos, nBottom) < 0)
3023 nPrtPos = nBottom;
3024 // tdf#116501 subtract flys blocking space from below
3025 // TODO this may not work ideally for multiple flys
3026 if (pSpaceBelowBottom && aRectFnSet.YDiff(aRectFnSet.GetBottom(aRect), nBottom) < 0)
3027 {
3028 if (aRectFnSet.YDiff(aRectFnSet.GetTop(aRect), aRectFnSet.GetTop(aFlyRect)) < 0)
3029 {
3030 aRectFnSet.SetBottom(aRect, aRectFnSet.GetTop(aFlyRect));
3031 }
3032 else
3033 {
3034 aRectFnSet.SetHeight(aRect, 0);
3035 }
3036 }
3037 bInvalidatePrtArea = true;
3038 }
3039 }
3040
3041 if ((css::text::WrapTextMode_RIGHT == nSurround
3042 || css::text::WrapTextMode_PARALLEL == nSurround)
3043 && text::HoriOrientation::LEFT == rHori.GetHoriOrient())
3044 {
3045 const tools::Long nWidth
3046 = aRectFnSet.XDiff(aRectFnSet.GetRight(aFlyRect),
3047 aRectFnSet.GetLeft(pFly->GetAnchorFrame()->getFrameArea()));
3048 rLeftOffset = std::max(rLeftOffset, nWidth);
3049 bInvalidatePrtArea = true;
3050 }
3051 if ((css::text::WrapTextMode_LEFT == nSurround
3052 || css::text::WrapTextMode_PARALLEL == nSurround)
3053 && text::HoriOrientation::RIGHT == rHori.GetHoriOrient())
3054 {
3055 const tools::Long nWidth
3056 = aRectFnSet.XDiff(aRectFnSet.GetRight(pFly->GetAnchorFrame()->getFrameArea()),
3057 aRectFnSet.GetLeft(aFlyRect));
3058 rRightOffset = std::max(rRightOffset, nWidth);
3059 bInvalidatePrtArea = true;
3060 }
3061 }
3062 rUpper = aRectFnSet.YDiff( nPrtPos, aRectFnSet.GetTop(getFrameArea()) );
3063 if (pSpaceBelowBottom)
3064 {
3065 *pSpaceBelowBottom = aRectFnSet.GetHeight(aRect);
3066 }
3067
3068 return bInvalidatePrtArea;
3069}
3070
3073void SwTabFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
3074{
3075 OSL_ENSURE( pAttrs, "TabFrame::Format, pAttrs is 0." );
3076
3077 SwRectFnSet aRectFnSet(this);
3078 if ( !isFrameAreaSizeValid() )
3079 {
3080 tools::Long nDiff = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()) -
3081 aRectFnSet.GetWidth(getFrameArea());
3082 if( nDiff )
3083 {
3085 aRectFnSet.AddRight( aFrm, nDiff );
3086 }
3087 }
3088
3089 //VarSize is always the height.
3090 //For the upper/lower margins the same rules apply as for ContentFrames (see
3091 //MakePrtArea() of those).
3092
3093 SwTwips nUpper = CalcUpperSpace( pAttrs );
3094
3095 // We want to dodge the flys. Two possibilities:
3096 // 1. There are flys with SurroundNone, dodge them completely
3097 // 2. There are flys which only wrap on the right or the left side and
3098 // those are right or left aligned, those set the minimum for the margins
3099 tools::Long nTmpRight = -1000000,
3100 nLeftOffset = 0;
3101 if (CalcFlyOffsets(nUpper, nLeftOffset, nTmpRight, nullptr))
3102 {
3104 }
3105
3106 tools::Long nRightOffset = std::max( tools::Long(0), nTmpRight );
3107
3108 SwTwips nLower = pAttrs->CalcBottomLine();
3109 // #i29550#
3110 if ( IsCollapsingBorders() )
3111 nLower += GetBottomLineSize();
3112
3113 if ( !isFramePrintAreaValid() )
3114 {
3116
3117 // The width of the PrintArea is given by the FrameFormat, the margins
3118 // have to be set accordingly.
3119 // Minimum margins are determined depending on borders and shadows.
3120 // The margins are set so that the PrintArea is aligned into the
3121 // Frame according to the adjustment.
3122 // If the adjustment is 0, the margins are set according to the border
3123 // attributes.
3124
3125 const SwTwips nOldHeight = aRectFnSet.GetHeight(getFramePrintArea());
3126 const SwTwips nMax = aRectFnSet.GetWidth(getFrameArea());
3127
3128 // OD 14.03.2003 #i9040# - adjust variable names.
3129 const SwTwips nLeftLine = pAttrs->CalcLeftLine();
3130 const SwTwips nRightLine = pAttrs->CalcRightLine();
3131
3132 // The width possibly is a percentage value. If the table is inside
3133 // something else, the value refers to the environment. If it's in the
3134 // body then in the BrowseView the value refers to the screen width.
3135 const SwFormatFrameSize &rSz = GetFormat()->GetFrameSize();
3136 // OD 14.03.2003 #i9040# - adjust variable name.
3137 const SwTwips nWishedTableWidth = CalcRel( rSz );
3138
3139 bool bCheckBrowseWidth = false;
3140
3141 // OD 14.03.2003 #i9040# - insert new variables for left/right spacing.
3142 SwTwips nLeftSpacing = 0;
3143 SwTwips nRightSpacing = 0;
3144 switch ( GetFormat()->GetHoriOrient().GetHoriOrient() )
3145 {
3146 case text::HoriOrientation::LEFT:
3147 {
3148 // left indent:
3149 nLeftSpacing = nLeftLine + nLeftOffset;
3150 // OD 06.03.2003 #i9040# - correct calculation of right indent:
3151 // - Consider right indent given by right line attributes.
3152 // - Consider negative right indent.
3153 // wished right indent determined by wished table width and
3154 // left offset given by surround fly frames on the left:
3155 const SwTwips nWishRight = nMax - nWishedTableWidth - nLeftOffset;
3156 if ( nRightOffset > 0 )
3157 {
3158 // surrounding fly frames on the right
3159 // -> right indent is maximum of given right offset
3160 // and wished right offset.
3161 nRightSpacing = nRightLine + std::max( SwTwips(nRightOffset), nWishRight );
3162 }
3163 else
3164 {
3165 // no surrounding fly frames on the right
3166 // If intrinsic right indent (intrinsic means not considering
3167 // determined left indent) is negative,
3168 // then hold this intrinsic indent,
3169 // otherwise non negative wished right indent is hold.
3170 nRightSpacing = nRightLine +
3171 ( ( (nWishRight+nLeftOffset) < 0 ) ?
3172 (nWishRight+nLeftOffset) :
3173 std::max( SwTwips(0), nWishRight ) );
3174 }
3175 }
3176 break;
3177 case text::HoriOrientation::RIGHT:
3178 {
3179 // right indent:
3180 nRightSpacing = nRightLine + nRightOffset;
3181 // OD 06.03.2003 #i9040# - correct calculation of left indent:
3182 // - Consider left indent given by left line attributes.
3183 // - Consider negative left indent.
3184 // wished left indent determined by wished table width and
3185 // right offset given by surrounding fly frames on the right:
3186 const SwTwips nWishLeft = nMax - nWishedTableWidth - nRightOffset;
3187 if ( nLeftOffset > 0 )
3188 {
3189 // surrounding fly frames on the left
3190 // -> right indent is maximum of given left offset
3191 // and wished left offset.
3192 nLeftSpacing = nLeftLine + std::max( SwTwips(nLeftOffset), nWishLeft );
3193 }
3194 else
3195 {
3196 // no surrounding fly frames on the left
3197 // If intrinsic left indent (intrinsic = not considering
3198 // determined right indent) is negative,
3199 // then hold this intrinsic indent,
3200 // otherwise non negative wished left indent is hold.
3201 nLeftSpacing = nLeftLine +
3202 ( ( (nWishLeft+nRightOffset) < 0 ) ?
3203 (nWishLeft+nRightOffset) :
3204 std::max( SwTwips(0), nWishLeft ) );
3205 }
3206 }
3207 break;
3208 case text::HoriOrientation::CENTER:
3209 {
3210 // OD 07.03.2003 #i9040# - consider left/right line attribute.
3211 const SwTwips nCenterSpacing = ( nMax - nWishedTableWidth ) / 2;
3212 nLeftSpacing = nLeftLine +
3213 ( (nLeftOffset > 0) ?
3214 std::max( nCenterSpacing, SwTwips(nLeftOffset) ) :
3215 nCenterSpacing );
3216 nRightSpacing = nRightLine +
3217 ( (nRightOffset > 0) ?
3218 std::max( nCenterSpacing, SwTwips(nRightOffset) ) :
3219 nCenterSpacing );
3220 }
3221 break;
3222 case text::HoriOrientation::FULL:
3223 //This things grows over the whole width.
3224 //Only the free space needed for the border is taken into
3225 //account. The attribute values of LRSpace are ignored
3226 //intentionally.
3227 bCheckBrowseWidth = true;
3228 nLeftSpacing = nLeftLine + nLeftOffset;
3229 nRightSpacing = nRightLine + nRightOffset;
3230 break;
3232 {
3233 // The margins are defined by the LRSpace attribute.
3234 nLeftSpacing = pAttrs->CalcLeft( this );
3235 if( nLeftOffset )
3236 {
3237 // OD 07.03.2003 #i9040# - surround fly frames only, if
3238 // they overlap with the table.
3239 // Thus, take maximum of left spacing and left offset.
3240 // OD 10.03.2003 #i9040# - consider left line attribute.
3241 nLeftSpacing = std::max( nLeftSpacing, SwTwips( nLeftOffset + nLeftLine ) );
3242 }
3243 // OD 23.01.2003 #106895# - add 1st param to <SwBorderAttrs::CalcRight(..)>
3244 nRightSpacing = pAttrs->CalcRight( this );
3245 if( nRightOffset )
3246 {
3247 // OD 07.03.2003 #i9040# - surround fly frames only, if
3248 // they overlap with the table.
3249 // Thus, take maximum of right spacing and right offset.
3250 // OD 10.03.2003 #i9040# - consider right line attribute.
3251 nRightSpacing = std::max( nRightSpacing, SwTwips( nRightOffset + nRightLine ) );
3252 }
3253 }
3254 break;
3255 case text::HoriOrientation::LEFT_AND_WIDTH:
3256 {
3257 // count left border and width (Word specialty)
3258 // OD 10.03.2003 #i9040# - no width alignment in online mode.
3259 //bCheckBrowseWidth = true;
3260 nLeftSpacing = pAttrs->CalcLeft( this );
3261 if( nLeftOffset )
3262 {
3263 // OD 10.03.2003 #i9040# - surround fly frames only, if
3264 // they overlap with the table.
3265 // Thus, take maximum of right spacing and right offset.
3266 // OD 10.03.2003 #i9040# - consider left line attribute.
3267 nLeftSpacing = std::max( nLeftSpacing, SwTwips( pAttrs->CalcLeftLine() + nLeftOffset ) );
3268 }
3269 // OD 10.03.2003 #i9040# - consider right and left line attribute.
3270 const SwTwips nWishRight =
3271 nMax - (nLeftSpacing-pAttrs->CalcLeftLine()) - nWishedTableWidth;
3272 nRightSpacing = nRightLine +
3273 ( (nRightOffset > 0) ?
3274 std::max( nWishRight, SwTwips(nRightOffset) ) :
3275 nWishRight );
3276 }
3277 break;
3278 default:
3279 OSL_FAIL( "Invalid orientation for table." );
3280 }
3281
3282 // #i26250# - extend bottom printing area, if table
3283 // is last content inside a table cell.
3284 if ( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS) &&
3285 GetUpper()->IsInTab() && !GetIndNext() )
3286 {
3287 nLower += pAttrs->GetULSpace().GetLower();
3288 }
3289 aRectFnSet.SetYMargins( *this, nUpper, nLower );
3290 if( (nMax - MINLAY) < (nLeftSpacing + nRightSpacing) )
3291 aRectFnSet.SetXMargins( *this, 0, 0 );
3292 else
3293 aRectFnSet.SetXMargins( *this, nLeftSpacing, nRightSpacing );
3294
3296 if ( bCheckBrowseWidth &&
3297 pSh && pSh->GetViewOptions()->getBrowseMode() &&
3298 GetUpper()->IsPageBodyFrame() && // only PageBodyFrames and not ColBodyFrames
3299 pSh->VisArea().Width() )
3300 {
3301 //Don't go beyond the edge of the visible area.
3302 //The page width can be bigger because objects with
3303 //"over-size" are possible (RootFrame::ImplCalcBrowseWidth())
3304 tools::Long nWidth = pSh->GetBrowseWidth();
3305 nWidth -= getFramePrintArea().Left();
3306 nWidth -= pAttrs->CalcRightLine();
3307
3309 aPrt.Width( std::min( nWidth, aPrt.Width() ) );
3310 }
3311
3312 if ( nOldHeight != aRectFnSet.GetHeight(getFramePrintArea()) )
3313 {
3314 setFrameAreaSizeValid(false);
3315 }
3316 }
3317
3318 if ( isFrameAreaSizeValid() )
3319 return;
3320
3322
3323 // The size is defined by the content plus the margins.
3324 SwTwips nRemaining = 0, nDiff;
3325 SwFrame *pFrame = m_pLower;
3326 while ( pFrame )
3327 {
3328 nRemaining += aRectFnSet.GetHeight(pFrame->getFrameArea());
3329 pFrame = pFrame->GetNext();
3330 }
3331 // And now add the margins
3332 nRemaining += nUpper + nLower;
3333
3334 nDiff = aRectFnSet.GetHeight(getFrameArea()) - nRemaining;
3335 if ( nDiff > 0 )
3336 Shrink( nDiff );
3337 else if ( nDiff < 0 )
3338 Grow( -nDiff );
3339}
3340
3341SwTwips SwTabFrame::GrowFrame( SwTwips nDist, bool bTst, bool bInfo )
3342{
3343 SwRectFnSet aRectFnSet(this);
3344 SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
3345 if( nHeight > 0 && nDist > ( LONG_MAX - nHeight ) )
3346 nDist = LONG_MAX - nHeight;
3347
3348 if ( bTst && !IsRestrictTableGrowth() )
3349 return nDist;
3350
3351 if ( GetUpper() )
3352 {
3353 //The upper only grows as far as needed. nReal provides the distance
3354 //which is already available.
3355 SwTwips nReal = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea());
3356 SwFrame *pFrame = GetUpper()->Lower();
3357 while ( pFrame && GetFollow() != pFrame )
3358 {
3359 nReal -= aRectFnSet.GetHeight(pFrame->getFrameArea());
3360 pFrame = pFrame->GetNext();
3361 }
3362
3363 if ( nReal < nDist )
3364 {
3365 tools::Long nTmp = GetUpper()->Grow( nDist - std::max<tools::Long>(nReal, 0), bTst, bInfo );
3366
3367 if ( IsRestrictTableGrowth() )
3368 {
3369 nTmp = std::min( tools::Long(nDist), nReal + nTmp );
3370 nDist = nTmp < 0 ? 0 : nTmp;
3371 }
3372 }
3373
3374 if ( !bTst )
3375 {
3376 {
3378 aRectFnSet.AddBottom( aFrm, nDist );
3379 }
3380
3381#if !ENABLE_WASM_STRIP_ACCESSIBILITY
3382 SwRootFrame *pRootFrame = getRootFrame();
3383 if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
3384 pRootFrame->GetCurrShell() )
3385 {
3386 SwRect aOldFrame( getFrameArea() );
3387 pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( this, aOldFrame );
3388 }
3389#endif
3390 }
3391 }
3392
3393 if ( !bTst && ( nDist || IsRestrictTableGrowth() ) )
3394 {
3395 SwPageFrame *pPage = FindPageFrame();
3396 if ( GetNext() )
3397 {
3399 if ( GetNext()->IsContentFrame() )
3400 GetNext()->InvalidatePage( pPage );
3401 }
3402 // #i28701# - Due to the new object positioning the
3403 // frame on the next page/column can flow backward (e.g. it was moved
3404 // forward due to the positioning of its objects ). Thus, invalivate this
3405 // next frame, if document compatibility option 'Consider wrapping style
3406 // influence on object positioning' is ON.
3407 else if ( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
3408 {
3410 }
3412 InvalidatePage( pPage );
3413 SetComplete();
3414
3415 std::unique_ptr<SvxBrushItem> aBack = GetFormat()->makeBackgroundBrushItem();
3416 const SvxGraphicPosition ePos = aBack->GetGraphicPos();
3417 if ( GPOS_NONE != ePos && GPOS_TILED != ePos )
3419 }
3420
3421 return nDist;
3422}
3424{
3425 if(eInvFlags == SwTabFrameInvFlags::NONE)
3426 return;
3427 SwPageFrame* pPage = FindPageFrame();
3428 InvalidatePage(pPage);
3429 if(eInvFlags & SwTabFrameInvFlags::InvalidatePrt)
3431 if(eInvFlags & SwTabFrameInvFlags::InvalidatePos)
3433 SwFrame* pTmp = GetIndNext();
3434 if(nullptr != pTmp)
3435 {
3437 {
3438 pTmp->InvalidatePrt_();
3439 if(pTmp->IsContentFrame())
3440 pTmp->InvalidatePage(pPage);
3441 }
3443 pTmp->SetCompletePaint();
3444 }
3445 if(eInvFlags & SwTabFrameInvFlags::InvalidatePrevPrt && nullptr != (pTmp = GetPrev()))
3446 {
3447 pTmp->InvalidatePrt_();
3448 if(pTmp->IsContentFrame())
3449 pTmp->InvalidatePage(pPage);
3450 }
3452 {
3453 if(pPage && pPage->GetUpper() && !IsFollow())
3454 static_cast<SwRootFrame*>(pPage->GetUpper())->InvalidateBrowseWidth();
3455 }
3458}
3459
3460void SwTabFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
3461{
3462 if(rHint.GetId() == SfxHintId::SwTableHeadingChange)
3463 {
3465 return;
3466 }
3467 else if (rHint.GetId() != SfxHintId::SwLegacyModify)
3468 return;
3469 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
3471 bool bAttrSetChg = pLegacy->m_pNew && RES_ATTRSET_CHG == pLegacy->m_pNew->Which();
3472
3473 if(bAttrSetChg)
3474 {
3475 auto& rOldSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pOld);
3476 auto& rNewSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew);
3477 SfxItemIter aOIter(*rOldSetChg.GetChgSet());
3478 SfxItemIter aNIter(*rNewSetChg.GetChgSet());
3479 const SfxPoolItem* pOItem = aOIter.GetCurItem();
3480 const SfxPoolItem* pNItem = aNIter.GetCurItem();
3481 SwAttrSetChg aOldSet(rOldSetChg);
3482 SwAttrSetChg aNewSet(rNewSetChg);
3483 do
3484 {
3485 UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
3486 pNItem = aNIter.NextItem();
3487 pOItem = aOIter.NextItem();
3488 } while(pNItem);
3489 if(aOldSet.Count() || aNewSet.Count())
3490 SwLayoutFrame::SwClientNotify(rMod, sw::LegacyModifyHint(&aOldSet, &aNewSet));
3491 }
3492 else
3493 UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
3494 Invalidate(eInvFlags);
3495}
3496
3498{
3499 if(!IsFollow())
3500 return;
3501 // Delete remaining headlines:
3502 SwRowFrame* pLowerRow = nullptr;
3503 while(nullptr != (pLowerRow = static_cast<SwRowFrame*>(Lower())) && pLowerRow->IsRepeatedHeadline())
3504 {
3505 pLowerRow->Cut();
3506 SwFrame::DestroyFrame(pLowerRow);
3507 }
3508
3509 // insert new headlines
3510 const sal_uInt16 nNewRepeat = GetTable()->GetRowsToRepeat();
3511 auto& rLines = GetTable()->GetTabLines();
3512 for(sal_uInt16 nIdx = 0; nIdx < nNewRepeat; ++nIdx)
3513 {
3514 SwRowFrame* pHeadline = new SwRowFrame(*rLines[nIdx], this);
3515 {
3516 sw::FlyCreationSuppressor aSuppressor;
3517 pHeadline->SetRepeatedHeadline(true);
3518 }
3519 pHeadline->Paste(this, pLowerRow);
3520 }
3522}
3523
3524void SwTabFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
3525 SwTabFrameInvFlags &rInvFlags,
3526 SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
3527{
3528 bool bClear = true;
3529 const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
3530 switch( nWhich )
3531 {
3532 case RES_FRM_SIZE:
3533 case RES_HORI_ORIENT:
3535 break;
3536
3537 case RES_PAGEDESC: //Attribute changes (on/off)
3538 if ( IsInDocBody() )
3539 {
3541 SwPageFrame *pPage = FindPageFrame();
3542 if (pPage)
3543 {
3544 if ( !GetPrev() )
3545 CheckPageDescs( pPage );
3546 if (GetFormat()->GetPageDesc().GetNumOffset())
3547 static_cast<SwRootFrame*>(pPage->GetUpper())->SetVirtPageNum( true );
3549 }
3550 }
3551 break;
3552
3553 case RES_BREAK:
3555 break;
3556
3557 case RES_LAYOUT_SPLIT:
3558 if ( !IsFollow() )
3560 break;
3561 case RES_FRAMEDIR :
3562 SetDerivedR2L( false );
3564 break;
3568 break;
3569 case RES_UL_SPACE:
3571 [[fallthrough]];
3572
3573 default:
3574 bClear = false;
3575 }
3576 if ( !bClear )
3577 return;
3578
3579 if ( pOldSet || pNewSet )
3580 {
3581 if ( pOldSet )
3582 pOldSet->ClearItem( nWhich );
3583 if ( pNewSet )
3584 pNewSet->ClearItem( nWhich );
3585 }
3586 else
3587 {
3588 SwModify aMod;
3590 }
3591}
3592
3594{
3595 if ( RES_VIRTPAGENUM_INFO == rHint.Which() && IsInDocBody() && !IsFollow() )
3596 {
3597 SwVirtPageNumInfo &rInfo = static_cast<SwVirtPageNumInfo&>(rHint);
3598 const SwPageFrame *pPage = FindPageFrame();
3599 if ( pPage )
3600 {
3601 if ( pPage == rInfo.GetOrigPage() && !GetPrev() )
3602 {
3603 // Should be the one (can temporarily be different, should we be
3604 // concerned about this possibility?)
3605 rInfo.SetInfo( pPage, this );
3606 return false;
3607 }
3608 if ( pPage->GetPhyPageNum() < rInfo.GetOrigPage()->GetPhyPageNum() &&
3609 (!rInfo.GetPage() || pPage->GetPhyPageNum() > rInfo.GetPage()->GetPhyPageNum()))
3610 {
3611 //This could be the one.
3612 rInfo.SetInfo( pPage, this );
3613 }
3614 }
3615 }
3616 return true;
3617}
3618
3620{
3621 SwFrame *pRet = m_pLower;
3622
3623 while ( pRet && !pRet->IsContentFrame() )
3624 {
3625 SwFrame *pOld = pRet;
3626
3627 SwFrame *pTmp = pRet; // To skip empty section frames
3628 while ( pRet->GetNext() )
3629 {
3630 pRet = pRet->GetNext();
3631 if( !pRet->IsSctFrame() || static_cast<SwSectionFrame*>(pRet)->GetSection() )
3632 pTmp = pRet;
3633 }
3634 pRet = pTmp;
3635
3636 if ( pRet->GetLower() )
3637 pRet = pRet->GetLower();
3638 if ( pRet == pOld )
3639 {
3640 // Check all other columns if there is a column based section with
3641 // an empty last column at the end of the last cell - this is done
3642 // by SwSectionFrame::FindLastContent
3643 if( pRet->IsColBodyFrame() )
3644 {
3645#if OSL_DEBUG_LEVEL > 0
3646 SwSectionFrame* pSect = pRet->FindSctFrame();
3647 OSL_ENSURE( pSect, "Where does this column come from?");
3648 OSL_ENSURE( IsAnLower( pSect ), "Split cell?" );
3649#endif
3650 return pRet->FindSctFrame()->FindLastContent();
3651 }
3652
3653 // pRet may be a cell frame without a lower (cell has been split).
3654 // We have to find the last content the hard way:
3655
3656 OSL_ENSURE( pRet->IsCellFrame(), "SwTabFrame::FindLastContent failed" );
3657 const SwFrame* pRow = pRet->GetUpper();
3658 while ( pRow && !pRow->GetUpper()->IsTabFrame() )
3659 pRow = pRow->GetUpper();
3660 const SwContentFrame* pContentFrame = pRow ? static_cast<const SwLayoutFrame*>(pRow)->ContainsContent() : nullptr;
3661 pRet = nullptr;
3662
3663 while ( pContentFrame && static_cast<const SwLayoutFrame*>(pRow)->IsAnLower( pContentFrame ) )
3664 {
3665 pRet = const_cast<SwContentFrame*>(pContentFrame);
3666 pContentFrame = pContentFrame->GetNextContentFrame();
3667 }
3668 }
3669 }
3670
3671 // #112929# There actually is a situation, which results in pRet = 0:
3672 // Insert frame, insert table via text <-> table. This gives you a frame
3673 // containing a table without any other content frames. Split the table
3674 // and undo the splitting. This operation gives us a table frame without
3675 // a lower.
3676 if ( pRet )
3677 {
3678 while ( pRet->GetNext() )
3679 pRet = pRet->GetNext();
3680
3681 if (pRet->IsSctFrame())
3682 pRet = static_cast<SwSectionFrame*>(pRet)->FindLastContent();
3683 }
3684
3685 assert(pRet == nullptr || dynamic_cast<SwContentFrame*>(pRet) || dynamic_cast<SwTabFrame*>(pRet));
3686 return pRet;
3687}
3688
3690{
3692
3693 while (pRet && pRet->IsTabFrame()) // possibly there's only tables here!
3694 { // tdf#126138 skip table, don't look inside
3695 pRet = pRet->GetPrev();
3696 }
3697
3698 assert(pRet == nullptr || dynamic_cast<SwContentFrame*>(pRet));
3699 return static_cast<SwContentFrame*>(pRet);
3700}
3701
3703bool SwTabFrame::ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool &rReformat )
3704{
3705 rReformat = false;
3706 if ( SwFlowFrame::IsMoveBwdJump() || !IsPrevObjMove() )
3707 {
3708 //Flowing back Frames is quite time consuming unfortunately.
3709 //Most often the location where the Frame wants to flow to has the same
3710 //FixSize as the Frame itself. In such a situation it's easy to check if
3711 //the Frame will find enough space for its VarSize, if this is not the
3712 //case, the relocation can be skipped.
3713 //Checking if the Frame will find enough space is done by the Frame itself,
3714 //this also takes the possibility of splitting the Frame into account.
3715 //If the FixSize is different or Flys are involved (at the old or the
3716 //new position) the checks are pointless, the Frame then
3717 //needs to be relocated tentatively (if a bit of space is available).
3718
3719 //The FixSize of the environments which contain tables is always the
3720 //width.
3721
3722 SwPageFrame *pOldPage = FindPageFrame(),
3723 *pNewPage = pNewUpper->FindPageFrame();
3724 bool bMoveAnyway = false;
3725 SwTwips nSpace = 0;
3726
3727 SwRectFnSet aRectFnSet(this);
3728 if ( !SwFlowFrame::IsMoveBwdJump() )
3729 {
3730
3731 tools::Long nOldWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
3732 SwRectFnSet fnRectX(pNewUpper);
3733 tools::Long nNewWidth = fnRectX.GetWidth(pNewUpper->getFramePrintArea());
3734 if( std::abs( nNewWidth - nOldWidth ) < 2 )
3735 {
3736 bMoveAnyway = BwdMoveNecessary( pOldPage, getFrameArea() ) > 1;
3737 if( !bMoveAnyway )
3738 {
3739 SwRect aRect( pNewUpper->getFramePrintArea() );
3740 aRect.Pos() += pNewUpper->getFrameArea().Pos();
3741 const SwFrame *pPrevFrame = pNewUpper->Lower();
3742 while ( pPrevFrame && pPrevFrame != this )
3743 {
3744 fnRectX.SetTop( aRect, fnRectX.GetBottom(pPrevFrame->getFrameArea()) );
3745 pPrevFrame = pPrevFrame->GetNext();
3746 }
3747 bMoveAnyway = BwdMoveNecessary( pNewPage, aRect) > 1;
3748
3749 // #i54861# Due to changes made in PrepareMake,
3750 // the tabfrm may not have a correct position. Therefore
3751 // it is possible that pNewUpper->getFramePrintArea().Height == 0. In this
3752 // case the above calculation of nSpace might give wrong
3753 // results and we really do not want to MoveBackward into a
3754 // 0 height frame. If nTmpSpace is already <= 0, we take this
3755 // value:
3756 const SwTwips nTmpSpace = fnRectX.GetHeight(aRect);
3757 if ( fnRectX.GetHeight(pNewUpper->getFramePrintArea()) > 0 || nTmpSpace <= 0 )
3758 nSpace = nTmpSpace;
3759
3760 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
3761 if( pSh && pSh->GetViewOptions()->getBrowseMode() )
3762 nSpace += pNewUpper->Grow( LONG_MAX, true );
3763 if (0 < nSpace && GetPrecede())
3764 {
3765 SwTwips nUpperDummy(0);
3766 tools::Long nLeftOffsetDummy(0), nRightOffsetDummy(0);
3767 // tdf#116501 check for no-wrap fly overlap
3768 static_cast<const SwTabFrame*>(GetPrecede())->CalcFlyOffsets(
3769 nUpperDummy, nLeftOffsetDummy, nRightOffsetDummy, &nSpace);
3770 }
3771 }
3772 }
3773 else if (!m_bLockBackMove)
3774 bMoveAnyway = true;
3775 }
3776 else if (!m_bLockBackMove)
3777 bMoveAnyway = true;
3778
3779 if ( bMoveAnyway )
3780 {
3781 rReformat = true;
3782 return true;
3783 }
3784
3785 bool bFits = nSpace > 0;
3786 if (!bFits && aRectFnSet.GetHeight(getFrameArea()) == 0)
3787 // This frame fits into pNewUpper in case it has no space, but this
3788 // frame is empty.
3789 bFits = nSpace >= 0;
3790 if (!m_bLockBackMove && bFits)
3791 {
3792 // #i26945# - check, if follow flow line
3793 // contains frame, which are moved forward due to its object
3794 // positioning.
3795 const SwRowFrame* pFirstRow = GetFirstNonHeadlineRow();
3796 if ( pFirstRow && pFirstRow->IsInFollowFlowRow() &&
3798 *(pFirstRow->GetFormat()->GetDoc()),
3799 *pFirstRow ) )
3800 {
3801 return false;
3802 }
3804
3805 // For some mysterious reason, I changed the good old
3806 // 'return nHeight <= nSpace' to 'return nTmpHeight < nSpace'.
3807 // This obviously results in problems with table frames in
3808 // sections. Remember: Every twip is sacred.
3809 return nTmpHeight <= nSpace;
3810 }
3811 }
3812 return false;
3813}
3814
3816{
3817 OSL_ENSURE( GetUpper(), "Cut without Upper()." );
3818
3819 SwPageFrame *pPage = FindPageFrame();
3820 InvalidatePage( pPage );
3821 SwFrame *pFrame = GetNext();
3822 if( pFrame )
3823 {
3824 // Possibly the old follow calculated a spacing to the predecessor
3825 // which is obsolete now when it becomes the first frame
3826 pFrame->InvalidatePrt_();
3827 pFrame->InvalidatePos_();
3828 if ( pFrame->IsContentFrame() )
3829 pFrame->InvalidatePage( pPage );
3830 if( IsInSct() && !GetPrev() )
3831 {
3832 SwSectionFrame* pSct = FindSctFrame();
3833 if( !pSct->IsFollow() )
3834 {
3835 pSct->InvalidatePrt_();
3836 pSct->InvalidatePage( pPage );
3837 }
3838 }
3839 }
3840 else
3841 {
3843 //Someone has to do the retouch: predecessor or upper
3844 pFrame = GetPrev();
3845 if ( nullptr != pFrame )
3846 {
3847 pFrame->SetRetouche();
3849 pFrame->InvalidatePos_();
3850 if ( pFrame->IsContentFrame() )
3851 pFrame->InvalidatePage( pPage );
3852 }
3853 //If I am (was) the only FlowFrame in my own upper, it has to do
3854 //the retouch. Moreover a new empty page might be created.
3855 else
3856 { SwRootFrame *pRoot = static_cast<SwRootFrame*>(pPage->GetUpper());
3857 pRoot->SetSuperfluous();
3859 if( IsInSct() )
3860 {
3861 SwSectionFrame* pSct = FindSctFrame();
3862 if( !pSct->IsFollow() )
3863 {
3864 pSct->InvalidatePrt_();
3865 pSct->InvalidatePage( pPage );
3866 }
3867 }
3868 }
3869 }
3870
3871 //First remove, then shrink the upper.
3872 SwLayoutFrame *pUp = GetUpper();
3873 SwRectFnSet aRectFnSet(this);
3875 if ( pUp )
3876 {
3877 OSL_ENSURE( !pUp->IsFootnoteFrame(), "Table in Footnote." );
3878 SwSectionFrame *pSct = nullptr;
3879 SwFlyFrame *pFly = nullptr;
3880 // #126020# - adjust check for empty section
3881 // #130797# - correct fix #126020#
3882 if ( !pUp->Lower() && pUp->IsInSct() &&
3883 !(pSct = pUp->FindSctFrame())->ContainsContent() &&
3884 !pSct->ContainsAny( true ) )
3885 {
3886 if ( pUp->GetUpper() )
3887 {
3888 pSct->DelEmpty( false );
3889 pSct->InvalidateSize_();
3890 }
3891 }
3892 else if (!pUp->Lower() && pUp->IsInFly() &&
3893 !(pFly = pUp->FindFlyFrame())->ContainsContent() &&
3894 !pFly->ContainsAny())
3895 {
3896 bool bSplitFly = pFly->IsFlySplitAllowed();
3897 if (!bSplitFly && pFly->IsFlyAtContentFrame())
3898 {
3899 // If the fly is not allowed to split, it's still possible that it was allowed to
3900 // split. That is definitely the case when the fly is a follow.
3901 auto pFlyAtContent = static_cast<SwFlyAtContentFrame*>(pFly);
3902 bSplitFly = pFlyAtContent->IsFollow();
3903 }
3904 if (pUp == pFly && bSplitFly)
3905 {
3906 auto pFlyAtContent = static_cast<SwFlyAtContentFrame*>(pFly);
3907 pFlyAtContent->DelEmpty();
3908 }
3909 }
3910 // table-in-footnote: delete empty footnote frames (like SwContentFrame::Cut)
3911 else if (!pUp->Lower() && pUp->IsFootnoteFrame() && !pUp->IsColLocked())
3912 {
3913 if (pUp->GetNext() && !pUp->GetPrev())
3914 {
3915 if (SwFrame *const pTmp = static_cast<SwLayoutFrame*>(pUp->GetNext())->ContainsAny())
3916 {
3917 pTmp->InvalidatePrt_();
3918 }
3919 }
3920 if (!pUp->IsDeleteForbidden())
3921 {
3922 pUp->Cut();
3924 }
3925 }
3926 else if( aRectFnSet.GetHeight(getFrameArea()) )
3927 {
3928 // OD 26.08.2003 #i18103# - *no* 'ColUnlock' of section -
3929 // undo changes of fix for #104992#
3930 pUp->Shrink( getFrameArea().Height() );
3931 }
3932 }
3933
3934
3935 if ( pPage && !IsFollow() && pPage->GetUpper() )
3936 static_cast<SwRootFrame*>(pPage->GetUpper())->InvalidateBrowseWidth();
3937}
3938
3939void SwTabFrame::Paste( SwFrame* pParent, SwFrame* pSibling )
3940{
3941 OSL_ENSURE( pParent, "No parent for pasting." );
3942 OSL_ENSURE( pParent->IsLayoutFrame(), "Parent is ContentFrame." );
3943 OSL_ENSURE( pParent != this, "I'm the parent myself." );
3944 OSL_ENSURE( pSibling != this, "I'm my own neighbour." );
3945 OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(),
3946 "I'm still registered somewhere." );
3947
3948 //Insert in the tree.
3949 InsertBefore( static_cast<SwLayoutFrame*>(pParent), pSibling );
3950
3952 SwPageFrame *pPage = FindPageFrame();
3953 InvalidatePage( pPage );
3954
3955 if ( GetNext() )
3956 {
3959 if ( GetNext()->IsContentFrame() )
3960 GetNext()->InvalidatePage( pPage );
3961 }
3962
3963 SwRectFnSet aRectFnSet(this);
3964 if( aRectFnSet.GetHeight(getFrameArea()) )
3965 pParent->Grow( aRectFnSet.GetHeight(getFrameArea()) );
3966
3967 if( aRectFnSet.GetWidth(getFrameArea()) != aRectFnSet.GetWidth(pParent->getFramePrintArea()) )
3969 if ( GetPrev() )
3970 {
3971 if ( !IsFollow() )
3972 {
3974 if ( GetPrev()->IsContentFrame() )
3975 GetPrev()->InvalidatePage( pPage );
3976 }
3977 }
3978 else if ( GetNext() )
3979 // Take the spacing into account when dealing with ContentFrames.
3980 // There are two situations (both always happen at the same time):
3981 // a) The Content becomes the first in a chain
3982 // b) The new follower was previously the first in a chain
3984
3985 if ( !pPage || IsFollow() )
3986 return;
3987
3988 if ( pPage->GetUpper() )
3989 static_cast<SwRootFrame*>(pPage->GetUpper())->InvalidateBrowseWidth();
3990
3991 if ( !GetPrev() )//At least needed for HTML with a table at the beginning.
3992 {
3993 const SwPageDesc *pDesc = GetFormat()->GetPageDesc().GetPageDesc();
3994 if ( (pDesc && pDesc != pPage->GetPageDesc()) ||
3995 (!pDesc && pPage->GetPageDesc() != &GetFormat()->GetDoc()->GetPageDesc(0)) )
3996 CheckPageDescs( pPage );
3997 }
3998}
3999
4000bool SwTabFrame::Prepare( const PrepareHint eHint, const void *, bool )
4001{
4002 if( PrepareHint::BossChanged == eHint )
4004 return false;
4005}
4006
4007SwRowFrame::SwRowFrame(const SwTableLine &rLine, SwFrame* pSib, bool bInsertContent)
4008 : SwLayoutFrame( rLine.GetFrameFormat(), pSib )
4009 , m_pTabLine( &rLine )
4010 , m_pFollowRow( nullptr )
4011 // #i29550#
4012 , mnTopMarginForLowers( 0 )
4013 , mnBottomMarginForLowers( 0 )
4014 , mnBottomLineSize( 0 )
4015 // --> split table rows
4016 , m_bIsFollowFlowRow( false )
4017 // <-- split table rows
4018 , m_bIsRepeatedHeadline( false )
4019 , m_bIsRowSpanLine( false )
4020 , m_bForceRowSplitAllowed( false )
4021 , m_bIsInSplit( false )
4022{
4024
4025 //Create the boxes and insert them.
4026 const SwTableBoxes &rBoxes = rLine.GetTabBoxes();
4027 SwFrame *pTmpPrev = nullptr;
4028
4029 bool bHiddenRedlines = getRootFrame()->IsHideRedlines() &&
4031 for ( size_t i = 0; i < rBoxes.size(); ++i )
4032 {
4033 // skip cells deleted with track changes
4034 if ( bHiddenRedlines && RedlineType::Delete == rBoxes[i]->GetRedlineType() )
4035 continue;
4036
4037 SwCellFrame *pNew = new SwCellFrame( *rBoxes[i], this, bInsertContent );
4038 pNew->InsertBehind( this, pTmpPrev );
4039 pTmpPrev = pNew;
4040 }
4041}
4042
4044{
4045 sw::BroadcastingModify* pMod = GetFormat();
4046 if( pMod )
4047 {
4048 pMod->Remove( this );
4049 if( !pMod->HasWriterListeners() )
4050 delete pMod;
4051 }
4052
4054}
4055
4057{
4058}
4059
4061{
4062 ::RegistFlys( pPage ? pPage : FindPageFrame(), this );
4063}
4064
4066{
4067 SwTabFrame* pTab = FindTabFrame();
4068 if(pTab)
4069 {
4070 const bool bInFirstNonHeadlineRow = pTab->IsFollow() && this == pTab->GetFirstNonHeadlineRow();
4071 // #i35063#
4072 // Invalidation required is pRow is last row
4073 if(bInFirstNonHeadlineRow)
4074 pTab = pTab->FindMaster();
4075 if(bInFirstNonHeadlineRow || !GetNext())
4076 pTab->InvalidatePos();
4077 }
4078 const sw::BroadcastingModify aMod;
4080}
4081
4082void SwRowFrame::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
4083{
4084 if(auto pNewFormatHint = dynamic_cast<const sw::TableLineFormatChanged*>(&rHint))
4085 {
4086 if(GetTabLine() != &pNewFormatHint->m_rTabLine)
4087 return;
4088 RegisterToFormat(const_cast<SwTableLineFormat&>(pNewFormatHint->m_rNewFormat));
4093
4094 // #i35063#
4095 // consider 'split row allowed' attribute
4096 SwTabFrame* pTab = FindTabFrame();
4097 bool bInFollowFlowRow = false;
4098 const bool bInFirstNonHeadlineRow = pTab->IsFollow() && this == pTab->GetFirstNonHeadlineRow();
4099 if(bInFirstNonHeadlineRow ||
4100 !GetNext() ||
4101 (bInFollowFlowRow = IsInFollowFlowRow()) ||
4102 nullptr != IsInSplitTableRow() )
4103 {
4104 if(bInFirstNonHeadlineRow || bInFollowFlowRow)
4105 pTab = pTab->FindMaster();
4106
4108 pTab->InvalidatePos();
4109 }
4110 }
4111 else if(auto pMoveTableLineHint = dynamic_cast<const sw::MoveTableLineHint*>(&rHint))
4112 {
4113
4114 if(GetTabLine() != &pMoveTableLineHint->m_rTableLine)
4115 return;
4116 const_cast<SwFrameFormat*>(&pMoveTableLineHint->m_rNewFormat)->Add(this);
4117 InvalidateAll();
4119 return;
4120 }
4121 if (rHint.GetId() != SfxHintId::SwLegacyModify)
4122 return;
4123 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
4124 if(!pLegacy->m_pNew)
4125 {
4126 // possibly not needed?
4127 SwLayoutFrame::SwClientNotify(rModify, rHint);
4128 return;
4129 }
4130 switch(pLegacy->m_pNew->Which())
4131 {
4132 case RES_ATTRSET_CHG:
4133 {
4134 const SwAttrSet* pChgSet = static_cast<const SwAttrSetChg*>(pLegacy->m_pNew)->GetChgSet();
4135 const SfxPoolItem* pItem = nullptr;
4136 pChgSet->GetItemState(RES_FRM_SIZE, false, &pItem);
4137 if(!pItem)
4138 pChgSet->GetItemState(RES_ROW_SPLIT, false, &pItem);
4139 if(pItem)
4140 OnFrameSize(*pItem);
4141 else
4142 SwLayoutFrame::SwClientNotify(rModify, rHint); // possibly not needed?
4143 return;
4144 }
4145 case RES_FRM_SIZE:
4146 case RES_ROW_SPLIT:
4147 OnFrameSize(*static_cast<const SwFormatFrameSize*>(pLegacy->m_pNew));
4148 return;
4149 }
4150}
4151
4153{
4154 if ( !GetNext() )
4155 {
4156 setFrameAreaSizeValid(false);
4157 }
4158
4159 SwLayoutFrame::MakeAll(pRenderContext);
4160}
4161
4163{
4164 (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("row"));
4165 dumpAsXmlAttributes(writer);
4166
4167 (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
4168 dumpInfosAsXml(writer);
4169 (void)xmlTextWriterEndElement(writer);
4170 dumpChildrenAsXml(writer);
4171
4172 (void)xmlTextWriterEndElement(writer);
4173}
4174
4176{
4177 SwRectFnSet aRectFnSet(pFrame);
4178 tools::Long nHeight = 0;
4179 const SwFrame* pTmp = pFrame->IsSctFrame() ?
4180 static_cast<const SwSectionFrame*>(pFrame)->ContainsContent() : pFrame;
4181 while( pTmp )
4182 {
4183 // #i26945# - consider follow text frames
4184 const SwSortedObjs* pObjs( nullptr );
4185 bool bIsFollow( false );
4186 if ( pTmp->IsTextFrame() && static_cast<const SwTextFrame*>(pTmp)->IsFollow() )
4187 {
4188 const SwFrame* pMaster;
4189 // #i46450# Master does not necessarily have
4190 // to exist if this function is called from JoinFrame() ->
4191 // Cut() -> Shrink()
4192 const SwTextFrame* pTmpFrame = static_cast<const SwTextFrame*>(pTmp);
4193 if ( pTmpFrame->GetPrev() && pTmpFrame->GetPrev()->IsTextFrame() &&
4194 static_cast<const SwTextFrame*>(pTmpFrame->GetPrev())->GetFollow() &&
4195 static_cast<const SwTextFrame*>(pTmpFrame->GetPrev())->GetFollow() != pTmp )
4196 pMaster = nullptr;
4197 else
4198 pMaster = pTmpFrame->FindMaster();
4199
4200 if ( pMaster )
4201 {
4202 pObjs = static_cast<const SwTextFrame*>(pTmp)->FindMaster()->GetDrawObjs();
4203 bIsFollow = true;
4204 }
4205 }
4206 else
4207 {
4208 pObjs = pTmp->GetDrawObjs();
4209 }
4210 if ( pObjs )
4211 {
4212 for (SwAnchoredObject* pAnchoredObj : *pObjs)
4213 {
4214 // #i26945# - if <pTmp> is follow, the
4215 // anchor character frame has to be <pTmp>.
4216 if ( bIsFollow &&
4217 pAnchoredObj->FindAnchorCharFrame() != pTmp )
4218 {
4219 continue;
4220 }
4221 // #i26945# - consider also drawing objects
4222 {
4223 // OD 30.09.2003 #i18732# - only objects, which follow
4224 // the text flow have to be considered.
4225 const SwFrameFormat& rFrameFormat = pAnchoredObj->GetFrameFormat();
4226 bool bFollowTextFlow = rFrameFormat.GetFollowTextFlow().GetValue();
4227 bool bIsFarAway = pAnchoredObj->GetObjRect().Top() != FAR_AWAY;
4228 const SwPageFrame* pPageFrm = pTmp->FindPageFrame();
4229 bool bIsAnchoredToTmpFrm = false;
4230 if ( pPageFrm && pPageFrm->IsPageFrame() && pAnchoredObj->GetPageFrame())
4231 bIsAnchoredToTmpFrm = pAnchoredObj->GetPageFrame() == pPageFrm ||
4232 (pPageFrm->GetFormatPage().GetPhyPageNum() == pAnchoredObj->GetPageFrame()->GetFormatPage().GetPhyPageNum() + 1);
4233 const bool bConsiderObj =
4234 (rFrameFormat.GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) &&
4235 bIsFarAway &&
4236 bFollowTextFlow && bIsAnchoredToTmpFrm;
4237 bool bWrapThrough = rFrameFormat.GetSurround().GetValue() == text::WrapTextMode_THROUGH;
4238 bool bInBackground = !rFrameFormat.GetOpaque().GetValue();
4239 // Legacy render requires in-background setting, the new mode does not.
4240 bool bConsiderFollowTextFlow = bInBackground
4241 || !rFrameFormat.getIDocumentSettingAccess().get(
4243 if (pFrame->IsInTab() && bFollowTextFlow && bWrapThrough && bConsiderFollowTextFlow)
4244 {
4245 // Ignore wrap-through objects when determining the cell height.
4246 // Normally FollowTextFlow requires a resize of the cell, but not in case of
4247 // wrap-through.
4248 continue;
4249 }
4250
4251 if ( bConsiderObj )
4252 {
4253 const SwFormatFrameSize &rSz = rFrameFormat.GetFrameSize();
4254 if( !rSz.GetHeightPercent() )
4255 {
4256 const SwTwips nDistOfFlyBottomToAnchorTop =
4257 aRectFnSet.GetHeight(pAnchoredObj->GetObjRect()) +
4258 ( aRectFnSet.IsVert() ?
4259 pAnchoredObj->GetCurrRelPos().X() :
4260 pAnchoredObj->GetCurrRelPos().Y() );
4261
4262 const SwTwips nFrameDiff =
4263 aRectFnSet.YDiff(
4264 aRectFnSet.GetTop(pTmp->getFrameArea()),
4265 aRectFnSet.GetTop(pFrame->getFrameArea()) );
4266
4267 nHeight = std::max( nHeight, nDistOfFlyBottomToAnchorTop + nFrameDiff -
4268 aRectFnSet.GetHeight(pFrame->getFrameArea()) );
4269
4270 // #i56115# The first height calculation
4271 // gives wrong results if pFrame->getFramePrintArea().Y() > 0. We do
4272 // a second calculation based on the actual rectangles of
4273 // pFrame and pAnchoredObj, and use the maximum of the results.
4274 // I do not want to remove the first calculation because
4275 // if clipping has been applied, using the GetCurrRelPos
4276 // might be the better option to calculate nHeight.
4277 const SwTwips nDistOfFlyBottomToAnchorTop2 = aRectFnSet.YDiff(
4278 aRectFnSet.GetBottom(pAnchoredObj->GetObjRect()),
4279 aRectFnSet.GetBottom(pFrame->getFrameArea()) );
4280
4281 nHeight = std::max( nHeight, tools::Long(nDistOfFlyBottomToAnchorTop2 ));
4282 }
4283 }
4284 }
4285 }
4286 }
4287 if( !pFrame->IsSctFrame() )
4288 break;
4289 pTmp = pTmp->FindNextCnt();
4290 if( !static_cast<const SwSectionFrame*>(pFrame)->IsAnLower( pTmp ) )
4291 break;
4292 }
4293 return nHeight;
4294}
4295
4297{
4298 const SwTabFrame* pTab = rCell.FindTabFrame();
4299 SwTwips nTopSpace = 0;
4300 SwTwips nBottomSpace = 0;
4301
4302 // #i29550#
4303 if ( pTab->IsCollapsingBorders() && rCell.Lower() && !rCell.Lower()->IsRowFrame() )
4304 {
4305 nTopSpace = static_cast<const SwRowFrame*>(rCell.GetUpper())->GetTopMarginForLowers();
4306 nBottomSpace = static_cast<const SwRowFrame*>(rCell.GetUpper())->GetBottomMarginForLowers();
4307 }
4308 else
4309 {
4310 if ( pTab->IsVertical() != rCell.IsVertical() )
4311 {
4312 nTopSpace = rAttrs.CalcLeft( &rCell );
4313 nBottomSpace = rAttrs.CalcRight( &rCell );
4314 }
4315 else
4316 {
4317 nTopSpace = rAttrs.CalcTop();
4318 nBottomSpace = rAttrs.CalcBottom();
4319 }
4320 }
4321
4322 return nTopSpace + nBottomSpace;
4323}
4324
4325// #i26945# - add parameter <_bConsiderObjs> in order to
4326// control, if floating screen objects have to be considered for the minimal
4327// cell height.
4329 const bool _bConsiderObjs,
4330 const SwBorderAttrs *pAttrs = nullptr )
4331{
4332 SwRectFnSet aRectFnSet(_pCell);
4333 SwTwips nHeight = 0;
4334 const SwFrame* pLow = _pCell->Lower();
4335 if ( pLow )
4336 {
4337 tools::Long nFlyAdd = 0;
4338 while ( pLow )
4339 {
4340 if ( pLow->IsRowFrame() )
4341 {
4342 // #i26945#
4343 nHeight += ::lcl_CalcMinRowHeight( static_cast<const SwRowFrame*>(pLow),
4344 _bConsiderObjs );
4345 }
4346 else
4347 {
4348 tools::Long nLowHeight = aRectFnSet.GetHeight(pLow->getFrameArea());
4349 nHeight += nLowHeight;
4350 // #i26945#
4351 if ( _bConsiderObjs )
4352 {
4353 nFlyAdd = std::max( tools::Long(0), nFlyAdd - nLowHeight );
4354 nFlyAdd = std::max( nFlyAdd, ::CalcHeightWithFlys( pLow ) );
4355 }
4356 }
4357
4358 pLow = pLow->GetNext();
4359 }
4360 if ( nFlyAdd )
4361 nHeight += nFlyAdd;
4362 }
4363 // The border/margin needs to be considered too, unfortunately it can't be
4364 // calculated using PrintArea and FrameArea because any or all of those
4365 // may be invalid.
4366 if ( _pCell->Lower() )
4367 {
4368 if ( pAttrs )
4369 nHeight += lcl_CalcTopAndBottomMargin( *_pCell, *pAttrs );
4370 else
4371 {
4372 SwBorderAttrAccess aAccess( SwFrame::GetCache(), _pCell );
4373 const SwBorderAttrs &rAttrs = *aAccess.Get();
4374 nHeight += lcl_CalcTopAndBottomMargin( *_pCell, rAttrs );
4375 }
4376 }
4377 return nHeight;
4378}
4379
4380// #i26945# - add parameter <_bConsiderObjs> in order to control,
4381// if floating screen objects have to be considered for the minimal cell height
4383 const bool _bConsiderObjs )
4384{
4385 SwTwips nHeight = 0;
4386 if ( !_pRow->IsRowSpanLine() )
4387 {
4388 const SwFormatFrameSize &rSz = _pRow->GetFormat()->GetFrameSize();
4389 if ( _pRow->HasFixSize() )
4390 {
4391 OSL_ENSURE(SwFrameSize::Fixed == rSz.GetHeightSizeType(), "pRow claims to have fixed size");
4392 return rSz.GetHeight();
4393 }
4394 // If this row frame is being split, then row's minimal height shouldn't restrict
4395 // this frame's minimal height, because the rest will go to follow frame.
4396 else if ( !_pRow->IsInSplit() && rSz.GetHeightSizeType() == SwFrameSize::Minimum )
4397 {
4398 bool bSplitFly = false;
4399 if (_pRow->IsInFly())
4400 {
4401 // See if we're in a split fly that is anchored on a page that has enough space to
4402 // host this row with its minimum row height.
4403 const SwFlyFrame* pFly = _pRow->FindFlyFrame();
4404 if (pFly->IsFlySplitAllowed())
4405 {
4406 SwFrame* pAnchor = const_cast<SwFlyFrame*>(pFly)->FindAnchorCharFrame();
4407 if (pAnchor)
4408 {
4409 if (pAnchor->FindPageFrame()->getFramePrintArea().Height() > rSz.GetHeight())
4410 {
4411 bSplitFly = true;
4412 }
4413 }
4414 }
4415 }
4416
4417 if (bSplitFly)
4418 {
4419 // Split fly: enforce minimum row height for the master and follows.
4420 nHeight = rSz.GetHeight();
4421 }
4422 else
4423 {
4424 nHeight = rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*_pRow);
4425 }
4426 }
4427 }
4428
4429 SwRectFnSet aRectFnSet(_pRow);
4430 const SwCellFrame* pLow = static_cast<const SwCellFrame*>(_pRow->Lower());
4431 while ( pLow )
4432 {
4433 SwTwips nTmp = 0;
4434 const tools::Long nRowSpan = pLow->GetLayoutRowSpan();
4435 // --> NEW TABLES
4436 // Consider height of
4437 // 1. current cell if RowSpan == 1
4438 // 2. current cell if cell is "follow" cell of a cell with RowSpan == -1
4439 // 3. master cell if RowSpan == -1
4440 if ( 1 == nRowSpan )
4441 {
4442 nTmp = ::lcl_CalcMinCellHeight( pLow, _bConsiderObjs );
4443 }
4444 else if ( -1 == nRowSpan )
4445 {
4446 // Height of the last cell of a row span is height of master cell
4447 // minus the height of the other rows which are covered by the master
4448 // cell:
4449 const SwCellFrame& rMaster = pLow->FindStartEndOfRowSpanCell( true );
4450 nTmp = ::lcl_CalcMinCellHeight( &rMaster, _bConsiderObjs );
4451 const SwFrame* pMasterRow = rMaster.GetUpper();
4452 while ( pMasterRow && pMasterRow != _pRow )
4453 {
4454 nTmp -= aRectFnSet.GetHeight(pMasterRow->getFrameArea());
4455 pMasterRow = pMasterRow->GetNext();
4456 }
4457 }
4458 // <-- NEW TABLES
4459
4460 // Do not consider rotated cells:
4461 if ( pLow->IsVertical() == aRectFnSet.IsVert() && nTmp > nHeight )
4462 nHeight = nTmp;
4463
4464 pLow = static_cast<const SwCellFrame*>(pLow->GetNext());
4465 }
4466
4467 return nHeight;
4468}
4469
4470// #i29550#
4471
4472// Calculate the maximum of (TopLineSize + TopLineDist) over all lowers:
4473static sal_uInt16 lcl_GetTopSpace( const SwRowFrame& rRow )
4474{
4475 sal_uInt16 nTopSpace = 0;
4476 for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
4477 pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
4478 {
4479 sal_uInt16 nTmpTopSpace = 0;
4480 if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
4481 nTmpTopSpace = lcl_GetTopSpace( *static_cast<const SwRowFrame*>(pCurrLower->Lower()) );
4482 else
4483 {
4484 const SwAttrSet& rSet = const_cast<SwCellFrame*>(pCurrLower)->GetFormat()->GetAttrSet();
4485 const SvxBoxItem& rBoxItem = rSet.GetBox();
4486 nTmpTopSpace = rBoxItem.CalcLineSpace( SvxBoxItemLine::TOP, true );
4487 }
4488 nTopSpace = std::max( nTopSpace, nTmpTopSpace );
4489 }
4490 return nTopSpace;
4491}
4492
4493// Calculate the maximum of TopLineDist over all lowers:
4494static sal_uInt16 lcl_GetTopLineDist( const SwRowFrame& rRow )
4495{
4496 sal_uInt16 nTopLineDist = 0;
4497 for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
4498 pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
4499 {
4500 sal_uInt16 nTmpTopLineDist = 0;
4501 if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
4502 nTmpTopLineDist = lcl_GetTopLineDist( *static_cast<const SwRowFrame*>(pCurrLower->Lower()) );
4503 else
4504 {
4505 const SwAttrSet& rSet = const_cast<SwCellFrame*>(pCurrLower)->GetFormat()->GetAttrSet();
4506 const SvxBoxItem& rBoxItem = rSet.GetBox();
4507 nTmpTopLineDist = rBoxItem.GetDistance( SvxBoxItemLine::TOP );
4508 }
4509 nTopLineDist = std::max( nTopLineDist, nTmpTopLineDist );
4510 }
4511 return nTopLineDist;
4512}
4513
4514// Calculate the maximum of BottomLineSize over all lowers:
4515static sal_uInt16 lcl_GetBottomLineSize( const SwRowFrame& rRow )
4516{
4517 sal_uInt16 nBottomLineSize = 0;
4518 for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
4519 pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
4520 {
4521 sal_uInt16 nTmpBottomLineSize = 0;
4522 if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
4523 {
4524 const SwFrame* pRow = pCurrLower->GetLastLower();
4525 nTmpBottomLineSize = lcl_GetBottomLineSize( *static_cast<const SwRowFrame*>(pRow) );
4526 }
4527 else
4528 {
4529 const SwAttrSet& rSet = const_cast<SwCellFrame*>(pCurrLower)->GetFormat()->GetAttrSet();
4530 const SvxBoxItem& rBoxItem = rSet.GetBox();
4531 nTmpBottomLineSize = rBoxItem.CalcLineSpace( SvxBoxItemLine::BOTTOM, true ) -
4532 rBoxItem.GetDistance( SvxBoxItemLine::BOTTOM );
4533 }
4534 nBottomLineSize = std::max( nBottomLineSize, nTmpBottomLineSize );
4535 }
4536 return nBottomLineSize;
4537}
4538
4539// Calculate the maximum of BottomLineDist over all lowers:
4540static sal_uInt16 lcl_GetBottomLineDist( const SwRowFrame& rRow )
4541{
4542 sal_uInt16 nBottomLineDist = 0;
4543 for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
4544 pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
4545 {
4546 sal_uInt16 nTmpBottomLineDist = 0;
4547 if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
4548 {
4549 const SwFrame* pRow = pCurrLower->GetLastLower();
4550 nTmpBottomLineDist = lcl_GetBottomLineDist( *static_cast<const SwRowFrame*>(pRow) );
4551 }
4552 else
4553 {
4554 const SwAttrSet& rSet = const_cast<SwCellFrame*>(pCurrLower)->GetFormat()->GetAttrSet();
4555 const SvxBoxItem& rBoxItem = rSet.GetBox();
4556 nTmpBottomLineDist = rBoxItem.GetDistance( SvxBoxItemLine::BOTTOM );
4557 }
4558 nBottomLineDist = std::max( nBottomLineDist, nTmpBottomLineDist );
4559 }
4560 return nBottomLineDist;
4561}
4562
4563// tdf#104425: calculate the height of all row frames,
4564// for which this frame is a follow.
4565// When a row has fixed/minimum height, it may span over
4566// several pages. The minimal height on this page should
4567// take into account the sum of all the heights of previous
4568// frames that constitute the table row on previous pages.
4569// Otherwise, trying to split a too high row frame will
4570// result in loop trying to create that too high row
4571// on each following page
4573{
4574 // We don't need to account for previous instances of repeated headlines
4575 if (rRow.IsRepeatedHeadline())
4576 return 0;
4577 SwRectFnSet aRectFnSet(&rRow);
4578 const SwTableLine* pLine = rRow.GetTabLine();
4579 const SwTabFrame* pTab = rRow.FindTabFrame();
4580 if (!pLine || !pTab || !pTab->IsFollow())
4581 return 0;
4582 SwTwips nResult = 0;
4584 for (const SwRowFrame* pCurRow = aIter.First(); pCurRow; pCurRow = aIter.Next())
4585 {
4586 if (pCurRow != &rRow && pCurRow->GetTabLine() == pLine)
4587 {
4588 // We've found another row frame that is part of the same table row
4589 const SwTabFrame* pCurTab = pCurRow->FindTabFrame();
4590 // A row frame may not belong to a table frame, when it is being cut, e.g., in
4591 // lcl_PostprocessRowsInCells().
4592 // Its SwRowFrame::Cut() has been called; it in turn called SwLayoutFrame::Cut(),
4593 // which nullified row's upper in RemoveFromLayout(), and then called Shrink()
4594 // for its former upper.
4595 // Regardless of whether it will be pasted back, or destroyed, currently it's not
4596 // part of layout, and its height does not count
4597 if (pCurTab && pCurTab->IsAnFollow(pTab))
4598 {
4599 // The found row frame belongs to a table frame that precedes
4600 // (above) this one in chain. So, include it in the sum
4601 nResult += aRectFnSet.GetHeight(pCurRow->getFrameArea());
4602 }
4603 }
4604 }
4605 return nResult;
4606}
4607
4608void SwRowFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
4609{
4610 SwRectFnSet aRectFnSet(this);
4611 OSL_ENSURE( pAttrs, "SwRowFrame::Format without Attrs." );
4612
4613 const bool bFix = mbFixSize;
4614
4615 if ( !isFramePrintAreaValid() )
4616 {
4617 // RowFrames don't have borders/margins therefore the PrintArea always
4618 // matches the FrameArea.
4620
4621 {
4623 aPrt.Left( 0 );
4624 aPrt.Top( 0 );
4625 aPrt.Width ( getFrameArea().Width() );
4626 aPrt.Height( getFrameArea().Height() );
4627 }
4628
4629 // #i29550#
4630 // Here we calculate the top-printing area for the lower cell frames
4631 SwTabFrame* pTabFrame = FindTabFrame();
4632 if ( pTabFrame->IsCollapsingBorders() )
4633 {
4634 const sal_uInt16 nTopSpace = lcl_GetTopSpace( *this );
4635 const sal_uInt16 nTopLineDist = lcl_GetTopLineDist( *this );
4636 const sal_uInt16 nBottomLineSize = lcl_GetBottomLineSize( *this );
4637 const sal_uInt16 nBottomLineDist = lcl_GetBottomLineDist( *this );
4638
4639 const SwRowFrame* pPreviousRow = nullptr;
4640
4641 // #i32456#
4642 // In order to calculate the top printing area for the lower cell
4643 // frames, we have to find the 'previous' row frame and compare
4644 // the bottom values of the 'previous' row with the 'top' values
4645 // of this row. The best way to find the 'previous' row is to
4646 // use the table structure:
4647 const SwTable* pTable = pTabFrame->GetTable();
4648 const SwTableLine* pPrevTabLine = nullptr;
4649 const SwRowFrame* pTmpRow = this;
4650
4651 while ( pTmpRow && !pPrevTabLine )
4652 {
4653 size_t nIdx = 0;
4654 const SwTableLines& rLines = pTmpRow->GetTabLine()->GetUpper() ?
4655 pTmpRow->GetTabLine()->GetUpper()->GetTabLines() :
4656 pTable->GetTabLines();
4657
4658 while ( rLines[ nIdx ] != pTmpRow->GetTabLine() )
4659 ++nIdx;
4660
4661 if ( nIdx > 0 )
4662 {
4663 // pTmpRow has a 'previous' row in the table structure:
4664 pPrevTabLine = rLines[ nIdx - 1 ];
4665 }
4666 else
4667 {
4668 // pTmpRow is a first row in the table structure.
4669 // We go up in the table structure:
4670 pTmpRow = pTmpRow->GetUpper()->GetUpper() &&
4671 pTmpRow->GetUpper()->GetUpper()->IsRowFrame() ?
4672 static_cast<const SwRowFrame*>( pTmpRow->GetUpper()->GetUpper() ) :
4673 nullptr;
4674 }
4675 }
4676
4677 // If we found a 'previous' row, we look for the appropriate row frame:
4678 if ( pPrevTabLine )
4679 {
4680 SwIterator<SwRowFrame,SwFormat> aIter( *pPrevTabLine->GetFrameFormat() );
4681 for ( SwRowFrame* pRow = aIter.First(); pRow; pRow = aIter.Next() )
4682 {
4683 // #115759# - do *not* take repeated
4684 // headlines, because during split of table it can be
4685 // invalid and thus can't provide correct border values.
4686 if ( pRow->GetTabLine() == pPrevTabLine &&
4687 !pRow->IsRepeatedHeadline() )
4688 {
4689 pPreviousRow = pRow;
4690 break;
4691 }
4692 }
4693 }
4694
4695 sal_uInt16 nTopPrtMargin = nTopSpace;
4696 if ( pPreviousRow )
4697 {
4698 const sal_uInt16 nTmpPrtMargin = pPreviousRow->GetBottomLineSize() + nTopLineDist;
4699 if ( nTmpPrtMargin > nTopPrtMargin )
4700 nTopPrtMargin = nTmpPrtMargin;
4701 }
4702
4703 // table has to be notified if it has to change its lower
4704 // margin due to changes of nBottomLineSize:
4705 if ( !GetNext() && nBottomLineSize != GetBottomLineSize() )
4706 pTabFrame->InvalidatePrt_();
4707
4708 // If there are rows nested inside this row, the nested rows
4709 // may not have been calculated yet. Therefore the
4710 // ::lcl_CalcMinRowHeight( this ) operation later in this
4711 // function cannot consider the correct border values. We
4712 // have to trigger the invalidation of the outer row frame
4713 // manually:
4714 // Note: If any further invalidations should be necessary, we
4715 // should consider moving the invalidation stuff to the
4716 // appropriate SwNotify object.
4717 if ( GetUpper()->GetUpper()->IsRowFrame() &&
4718 ( nBottomLineDist != GetBottomMarginForLowers() ||
4719 nTopPrtMargin != GetTopMarginForLowers() ) )
4721
4722 SetBottomMarginForLowers( nBottomLineDist ); // 3.
4723 SetBottomLineSize( nBottomLineSize ); // 4.
4724 SetTopMarginForLowers( nTopPrtMargin ); // 5.
4725
4726 }
4727 }
4728
4729 while ( !isFrameAreaSizeValid() )
4730 {
4732
4733#if OSL_DEBUG_LEVEL > 0
4734 if ( HasFixSize() )
4735 {
4736 const SwFormatFrameSize &rFrameSize = GetFormat()->GetFrameSize();
4737 OSL_ENSURE( rFrameSize.GetSize().Height() > 0, "Has it" );
4738 }
4739#endif
4740 const SwTwips nDiff = aRectFnSet.GetHeight(getFrameArea()) -
4741 ( HasFixSize() && !IsRowSpanLine()
4742 ? pAttrs->GetSize().Height()
4743 // #i26945#
4744 : ::lcl_CalcMinRowHeight( this,
4745 FindTabFrame()->IsConsiderObjsForMinCellHeight() ) );
4746 if ( nDiff )
4747 {
4748 mbFixSize = false;
4749 if ( nDiff > 0 )
4750 Shrink( nDiff, false, true );
4751 else if ( nDiff < 0 )
4752 Grow( -nDiff );
4753 mbFixSize = bFix;
4754 }
4755 }
4756
4757 // last row will fill the space in its upper.
4758 if ( GetNext() )
4759 return;
4760
4761 //The last fills the remaining space in the upper.
4762 SwTwips nDiff = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea());
4763 SwFrame *pSibling = GetUpper()->Lower();
4764 do
4765 { nDiff -= aRectFnSet.GetHeight(pSibling->getFrameArea());
4766 pSibling = pSibling->GetNext();
4767 } while ( pSibling );
4768 if ( nDiff > 0 )
4769 {
4770 mbFixSize = false;
4771 Grow( nDiff );
4772 mbFixSize = bFix;
4774 }
4775}
4776
4777void SwRowFrame::AdjustCells( const SwTwips nHeight, const bool bHeight )
4778{
4779 SwFrame *pFrame = Lower();
4780 if ( bHeight )
4781 {
4782 SwRectFnSet aRectFnSet(this);
4783#if !ENABLE_WASM_STRIP_ACCESSIBILITY
4784 SwRect aOldFrame;
4785#endif
4786
4787 while ( pFrame )
4788 {
4789 SwFrame* pNotify = nullptr;
4790
4791 SwCellFrame* pCellFrame = static_cast<SwCellFrame*>(pFrame);
4792
4793 // NEW TABLES
4794 // Which cells need to be adjusted if the current row changes
4795 // its height?
4796
4797 // Current frame is a covered frame:
4798 // Set new height for covered cell and adjust master cell:
4799 if ( pCellFrame->GetTabBox()->getRowSpan() < 1 )
4800 {
4801 // Set height of current (covered) cell to new line height.
4802 const tools::Long nDiff = nHeight - aRectFnSet.GetHeight(pCellFrame->getFrameArea());
4803 if ( nDiff )
4804 {
4805 {
4807 aRectFnSet.AddBottom( aFrm, nDiff );
4808 }
4809
4810 pCellFrame->InvalidatePrt_();
4811 }
4812 }
4813
4814 SwCellFrame* pToAdjust = nullptr;
4815 SwFrame* pToAdjustRow = nullptr;
4816
4817 // If current frame is covered frame, we still want to adjust the
4818 // height of the cell starting the row span
4819 if ( pCellFrame->GetLayoutRowSpan() < 1 )
4820 {
4821 pToAdjust = const_cast< SwCellFrame*>(&pCellFrame->FindStartEndOfRowSpanCell( true ));
4822 pToAdjustRow = pToAdjust->GetUpper();
4823 }
4824 else
4825 {
4826 pToAdjust = pCellFrame;
4827 pToAdjustRow = this;
4828 }
4829
4830 // Set height of master cell to height of all lines spanned by this line.
4831 tools::Long nRowSpan = pToAdjust->GetLayoutRowSpan();
4832 SwTwips nSumRowHeight = 0;
4833 while ( pToAdjustRow )
4834 {
4835 // Use new height for the current row:
4836 nSumRowHeight += pToAdjustRow == this ?
4837 nHeight :
4838 aRectFnSet.GetHeight(pToAdjustRow->getFrameArea());
4839
4840 if ( nRowSpan-- == 1 )
4841 break;
4842
4843 pToAdjustRow = pToAdjustRow->GetNext();
4844 }
4845
4846 if ( pToAdjustRow && pToAdjustRow != this )
4847 pToAdjustRow->InvalidateSize_();
4848
4849 const tools::Long nDiff = nSumRowHeight - aRectFnSet.GetHeight(pToAdjust->getFrameArea());
4850 if ( nDiff )
4851 {
4852#if !ENABLE_WASM_STRIP_ACCESSIBILITY
4853 aOldFrame = pToAdjust->getFrameArea();
4854#endif
4856 aRectFnSet.AddBottom( aFrm, nDiff );
4857 pNotify = pToAdjust;
4858 }
4859
4860 if ( pNotify )
4861 {
4862#if !ENABLE_WASM_STRIP_ACCESSIBILITY
4863 SwRootFrame *pRootFrame = getRootFrame();
4864 if( pRootFrame && pRootFrame->IsAnyShellAccessible() && pRootFrame->GetCurrShell() )
4865 pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( pNotify, aOldFrame );
4866#endif
4867
4868 pNotify->InvalidatePrt_();
4869 }
4870
4871 pFrame = pFrame->GetNext();
4872 }
4873 }
4874 else
4875 { while ( pFrame )
4876 {
4877 pFrame->InvalidateAll_();
4878 pFrame = pFrame->GetNext();
4879 }
4880 }
4882}
4883
4885{
4886 SwTabFrame *pTab = FindTabFrame();
4887 if ( pTab && pTab->IsFollow() && this == pTab->GetFirstNonHeadlineRow() )
4888 {
4889 pTab->FindMaster()->InvalidatePos();
4890 }
4891
4893}
4894
4895SwTwips SwRowFrame::GrowFrame( SwTwips nDist, bool bTst, bool bInfo )
4896{
4897 SwTwips nReal = 0;
4898
4899 SwTabFrame* pTab = FindTabFrame();
4900 SwRectFnSet aRectFnSet(pTab);
4901
4902 bool bRestrictTableGrowth;
4903 bool bHasFollowFlowLine = pTab->HasFollowFlowLine();
4904
4905 if ( GetUpper()->IsTabFrame() )
4906 {
4907 const SwRowFrame* pFollowFlowRow = IsInSplitTableRow();
4908 bRestrictTableGrowth = pFollowFlowRow && !pFollowFlowRow->IsRowSpanLine();
4909 }
4910 else
4911 {
4912 OSL_ENSURE( GetUpper()->IsCellFrame(), "RowFrame->GetUpper neither table nor cell" );
4913 bRestrictTableGrowth = GetFollowRow() && bHasFollowFlowLine;
4914 OSL_ENSURE( !bRestrictTableGrowth || !GetNext(),
4915 "GetFollowRow for row frame that has a Next" );
4916
4917 // There may still be some space left in my direct upper:
4918 const SwTwips nAdditionalSpace =
4919 aRectFnSet.BottomDist( getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()->GetUpper()) );
4920 if ( bRestrictTableGrowth && nAdditionalSpace > 0 )
4921 {
4922 nReal = std::min( nAdditionalSpace, nDist );
4923 nDist -= nReal;
4924 if ( !bTst )
4925 {
4927 aRectFnSet.AddBottom( aFrm, nReal );
4928 }
4929 }
4930 }
4931
4932 if ( bRestrictTableGrowth )
4933 pTab->SetRestrictTableGrowth( true );
4934 else
4935 {
4936 // Ok, this looks like a hack, indeed, it is a hack.
4937 // If the current row frame is inside another cell frame,
4938 // and the current row frame has no follow, it should not
4939 // be allowed to grow. In fact, setting bRestrictTableGrowth
4940 // to 'false' does not work, because the surrounding RowFrame
4941 // would set this to 'true'.
4942 pTab->SetFollowFlowLine( false );
4943 }
4944
4945 nReal += SwLayoutFrame::GrowFrame( nDist, bTst, bInfo);
4946
4947 pTab->SetRestrictTableGrowth( false );
4948 pTab->SetFollowFlowLine( bHasFollowFlowLine );
4949
4950 //Update the height of the cells to the newest value.
4951 if ( !bTst )
4952 {
4953 SwRectFnSet fnRectX(this);
4954 AdjustCells( fnRectX.GetHeight(getFramePrintArea()) + nReal, true );
4955 if ( nReal )
4957 }
4958
4959 return nReal;
4960}
4961
4962SwTwips SwRowFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool bInfo )
4963{
4964 SwRectFnSet aRectFnSet(this);
4965 if( HasFixSize() )
4966 {
4967 AdjustCells( aRectFnSet.GetHeight(getFramePrintArea()), true );
4968 return 0;
4969 }
4970
4971 // bInfo may be set to true by SwRowFrame::Format; we need to handle this
4972 // here accordingly
4973 const bool bShrinkAnyway = bInfo;
4974
4975 //Only shrink as much as the content of the biggest cell allows.
4976 SwTwips nRealDist = nDist;
4977 SwFormat* pMod = GetFormat();
4978 if (pMod)
4979 {
4980 const SwFormatFrameSize &rSz = pMod->GetFrameSize();
4981 SwTwips nMinHeight = 0;
4983 nMinHeight = std::max(rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*this),
4984 tools::Long(0));
4985
4986 // Only necessary to calculate minimal row height if height
4987 // of pRow is at least nMinHeight. Otherwise nMinHeight is the
4988 // minimum height.
4989 if( nMinHeight < aRectFnSet.GetHeight(getFrameArea()) )
4990 {
4991 // #i26945#
4992 OSL_ENSURE( FindTabFrame(), "<SwRowFrame::ShrinkFrame(..)> - no table frame -> crash." );
4993 const bool bConsiderObjs( FindTabFrame()->IsConsiderObjsForMinCellHeight() );
4994 nMinHeight = lcl_CalcMinRowHeight( this, bConsiderObjs );
4995 }
4996
4997 if ( (aRectFnSet.GetHeight(getFrameArea()) - nRealDist) < nMinHeight )
4998 nRealDist = aRectFnSet.GetHeight(getFrameArea()) - nMinHeight;
4999 }
5000 if ( nRealDist < 0 )
5001 nRealDist = 0;
5002
5003 SwTwips nReal = nRealDist;
5004 if ( nReal )
5005 {
5006 if ( !bTst )
5007 {
5008 SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
5010 aRectFnSet.SetHeight( aFrm, nHeight - nReal );
5011
5012 if( IsVertical() && !IsVertLR() )
5013 {
5014 aFrm.Pos().AdjustX(nReal );
5015 }
5016 }
5017
5018 SwLayoutFrame* pFrame = GetUpper();
5019 SwTwips nTmp = pFrame ? pFrame->Shrink(nReal, bTst) : 0;
5020 if ( !bShrinkAnyway && !GetNext() && nTmp != nReal )
5021 {
5022 //The last one gets the leftover in the upper and therefore takes
5023 //care (otherwise: endless loop)
5024 if ( !bTst )
5025 {
5026 nReal -= nTmp;
5027 SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
5029 aRectFnSet.SetHeight( aFrm, nHeight + nReal );
5030
5031 if( IsVertical() && !IsVertLR() )
5032 {
5033 aFrm.Pos().AdjustX( -nReal );
5034 }
5035 }
5036 nReal = nTmp;
5037 }
5038 }
5039
5040 // Invalidate appropriately and update the height to the newest value.
5041 if ( !bTst )
5042 {
5043 if ( nReal )
5044 {
5045 if ( GetNext() )
5049
5050 SwTabFrame *pTab = FindTabFrame();
5051 if ( !pTab->IsRebuildLastLine()
5052 && pTab->IsFollow()
5053 && this == pTab->GetFirstNonHeadlineRow()
5054 && !pTab->IsInRecalcLowerRow() )
5055 {
5056 SwTabFrame* pMasterTab = pTab->FindMaster();
5057 pMasterTab->InvalidatePos();
5058 }
5059 }
5060 AdjustCells( aRectFnSet.GetHeight(getFramePrintArea()) - nReal, true );
5061 }
5062 return nReal;
5063}
5064
5066{
5067 // Fixed size rows are never allowed to split:
5068 if ( HasFixSize() )
5069 {
5070 OSL_ENSURE( SwFrameSize::Fixed == GetFormat()->GetFrameSize().GetHeightSizeType(), "pRow claims to have fixed size" );
5071 return false;
5072 }
5073
5074 // Repeated headlines are never allowed to split:
5075 const SwTabFrame* pTabFrame = FindTabFrame();
5076 if ( pTabFrame->GetTable()->GetRowsToRepeat() > 0 &&
5077 pTabFrame->IsInHeadline( *this ) )
5078 return false;
5079
5080 if ( IsForceRowSplitAllowed() )
5081 return true;
5082
5083 const SwTableLineFormat* pFrameFormat = static_cast<SwTableLineFormat*>(GetTabLine()->GetFrameFormat());
5084 const SwFormatRowSplit& rLP = pFrameFormat->GetRowSplit();
5085 return rLP.GetValue();
5086}
5087
5088bool SwRowFrame::ShouldRowKeepWithNext( const bool bCheckParents ) const
5089{
5090 // No KeepWithNext if nested in another table
5091 if ( GetUpper()->GetUpper()->IsCellFrame() )
5092 return false;
5093
5094 const SwCellFrame* pCell = static_cast<const SwCellFrame*>(Lower());
5095 const SwFrame* pText = pCell->Lower();
5096
5097 return pText && pText->IsTextFrame() &&
5098 static_cast<const SwTextFrame*>(pText)->GetTextNodeForParaProps()->GetSwAttrSet().GetKeep(bCheckParents).GetValue();
5099}
5100
5101SwCellFrame::SwCellFrame(const SwTableBox &rBox, SwFrame* pSib, bool bInsertContent)
5102 : SwLayoutFrame( rBox.GetFrameFormat(), pSib )
5103 , m_pTabBox( &rBox )
5104{
5106
5107 if ( !bInsertContent )
5108 return;
5109
5110 //If a StartIdx is available, ContentFrames are added in the cell, otherwise
5111 //Rows have to be present and those are added.
5112 if ( SwNodeOffset nIndex = rBox.GetSttIdx() )
5113 {
5114 ::InsertCnt_( this, rBox.GetFrameFormat()->GetDoc(), ++nIndex );
5115 }
5116 else
5117 {
5118 const SwTableLines &rLines = rBox.GetTabLines();
5119 SwFrame *pTmpPrev = nullptr;
5120 for ( size_t i = 0; i < rLines.size(); ++i )
5121 {
5122 SwRowFrame *pNew = new SwRowFrame( *rLines[i], this, bInsertContent );
5123 pNew->InsertBehind( this, pTmpPrev );
5124 pTmpPrev = pNew;
5125 }
5126 }
5127}
5128
5130{
5131 sw::BroadcastingModify* pMod = GetFormat();
5132 if( pMod )
5133 {
5134 // At this stage the lower frames aren't destroyed already,
5135 // therefore we have to do a recursive dispose.
5136#if !ENABLE_WASM_STRIP_ACCESSIBILITY
5137 SwRootFrame *pRootFrame = getRootFrame();
5138 if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
5139 pRootFrame->GetCurrShell() )
5140 {
5141 pRootFrame->GetCurrShell()->Imp()->DisposeAccessibleFrame( this, true );
5142 }
5143#endif
5144
5145 pMod->Remove( this );
5146 if( !pMod->HasWriterListeners() )
5147 delete pMod;
5148 }
5149
5151}
5152
5154{
5155}
5156
5157static bool lcl_ArrangeLowers( SwLayoutFrame *pLay, tools::Long lYStart, bool bInva )
5158{
5159 bool bRet = false;
5160 SwFrame *pFrame = pLay->Lower();
5161 SwRectFnSet aRectFnSet(pLay);
5162 while ( pFrame )
5163 {
5164 tools::Long nFrameTop = aRectFnSet.GetTop(pFrame->getFrameArea());
5165 if( nFrameTop != lYStart )
5166 {
5167 bRet = true;
5168 const tools::Long lDiff = aRectFnSet.YDiff( lYStart, nFrameTop );
5169 const tools::Long lDiffX = lYStart - nFrameTop;
5170
5171 {
5173 aRectFnSet.SubTop( aFrm, -lDiff );
5174 aRectFnSet.AddBottom( aFrm, lDiff );
5175 }
5176
5177 pFrame->SetCompletePaint();
5178
5179 if ( !pFrame->GetNext() )
5180 pFrame->SetRetouche();
5181 if( bInva )
5183 if ( pFrame->IsLayoutFrame() && static_cast<SwLayoutFrame*>(pFrame)->Lower() )
5184 lcl_ArrangeLowers( static_cast<SwLayoutFrame*>(pFrame),
5185 aRectFnSet.GetTop(static_cast<SwLayoutFrame*>(pFrame)->Lower()->getFrameArea())
5186 + lDiffX, bInva );
5187 if ( pFrame->GetDrawObjs() )
5188 {
5189 for ( size_t i = 0; i < pFrame->GetDrawObjs()->size(); ++i )