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 // #i29771#
1091 // To avoid loops, we do some checks before actually trying to split
1092 // the row. Maybe we should keep the next row in this table.
1093 // Note: This is only done if we are at the beginning of our upper
1094 bool bKeepNextRow = false;
1095 if ( nRowCount < nRepeat )
1096 {
1097 // First case: One of the repeated headline does not fit to the page anymore.
1098 // tdf#88496 Disable repeated headline (like for #i44910#) to avoid loops and
1099 // to fix interoperability problems (very long tables only with headline)
1100 OSL_ENSURE( !GetIndPrev(), "Table is supposed to be at beginning" );
1102 return false;
1103 }
1104 else if ( !GetIndPrev() && nRepeat == nRowCount )
1105 {
1106 // Second case: The first non-headline row does not fit to the page.
1107 // If it is not allowed to be split, or it contains a sub-row that
1108 // is not allowed to be split, we keep the row in this table:
1109 if ( bTryToSplit && bSplitRowAllowed )
1110 {
1111 // Check if there are (first) rows inside this row,
1112 // which are not allowed to be split.
1113 SwCellFrame* pLowerCell = static_cast<SwCellFrame*>(pRow->Lower());
1114 while ( pLowerCell )
1115 {
1116 if ( pLowerCell->Lower() && pLowerCell->Lower()->IsRowFrame() )
1117 {
1118 const SwRowFrame* pLowerRow = static_cast<SwRowFrame*>(pLowerCell->Lower());
1119 if ( !pLowerRow->IsRowSplitAllowed() &&
1120 aRectFnSet.GetHeight(pLowerRow->getFrameArea()) > nRemainingSpaceForLastRow )
1121 {
1122 bKeepNextRow = true;
1123 break;
1124 }
1125 }
1126 pLowerCell = static_cast<SwCellFrame*>(pLowerCell->GetNext());
1127 }
1128 }
1129 else
1130 bKeepNextRow = true;
1131 }
1132
1133 // Better keep the next row in this table:
1134 if ( bKeepNextRow )
1135 {
1136 pRow = GetFirstNonHeadlineRow();
1137 if ( pRow && pRow->IsRowSpanLine() && 0 == aRectFnSet.GetHeight(pRow->getFrameArea()) )
1138 pRow = static_cast<SwRowFrame*>(pRow->GetNext());
1139 if ( pRow )
1140 {
1141 pRow = static_cast<SwRowFrame*>(pRow->GetNext());
1142 ++nRowCount;
1143 }
1144 }
1145
1146 // No more row to split or to move to follow table:
1147 if ( !pRow )
1148 return bRet;
1149
1150 // We try to split the row if
1151 // - the attributes of the row are set accordingly and
1152 // - we are allowed to do so
1153 // - it should not be kept with the next row
1154 bSplitRowAllowed = bSplitRowAllowed && bTryToSplit &&
1155 ( !bTableRowKeep ||
1156 !pRow->ShouldRowKeepWithNext() );
1157
1158 // Adjust pRow according to the keep-with-next attribute:
1159 if ( !bSplitRowAllowed && bTableRowKeep )
1160 {
1161 SwRowFrame* pTmpRow = static_cast<SwRowFrame*>(pRow->GetPrev());
1162 SwRowFrame* pOldRow = pRow;
1163 while ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() &&
1164 nRowCount > nRepeat )
1165 {
1166 pRow = pTmpRow;
1167 --nRowCount;
1168 pTmpRow = static_cast<SwRowFrame*>(pTmpRow->GetPrev());
1169 }
1170
1171 // loop prevention
1172 if ( nRowCount == nRepeat && !GetIndPrev())
1173 {
1174 pRow = pOldRow;
1175 }
1176 }
1177
1178 // If we do not intend to split pRow, we check if we are
1179 // allowed to move pRow to a follow. Otherwise we return
1180 // false, indicating an error
1181 if ( !bSplitRowAllowed )
1182 {
1183 SwRowFrame* pFirstNonHeadlineRow = GetFirstNonHeadlineRow();
1184 if ( pRow == pFirstNonHeadlineRow )
1185 return false;
1186
1187 // #i91764#
1188 // Ignore row span lines
1189 SwRowFrame* pTmpRow = pFirstNonHeadlineRow;
1190 while ( pTmpRow && pTmpRow->IsRowSpanLine() )
1191 {
1192 pTmpRow = static_cast<SwRowFrame*>(pTmpRow->GetNext());
1193 }
1194 if ( !pTmpRow || pRow == pTmpRow )
1195 {
1196 return false;
1197 }
1198 }
1199
1200 // Build follow table if not already done:
1201 bool bNewFollow;
1202 SwTabFrame *pFoll;
1203 if ( GetFollow() )
1204 {
1205 pFoll = GetFollow();
1206 bNewFollow = false;
1207 }
1208 else
1209 {
1210 bNewFollow = true;
1211 pFoll = new SwTabFrame( *this );
1212
1213 // We give the follow table an initial width.
1214 {
1216 aRectFnSet.AddWidth(aFrm, aRectFnSet.GetWidth(getFrameArea()));
1217 aRectFnSet.SetLeft(aFrm, aRectFnSet.GetLeft(getFrameArea()));
1218 }
1219
1220 {
1222 aRectFnSet.AddWidth(aPrt, aRectFnSet.GetWidth(getFramePrintArea()));
1223 }
1224
1225 // Insert the new follow table
1226 pFoll->InsertBehind( GetUpper(), this );
1227
1228 // Repeat the headlines.
1229 auto& rLines = GetTable()->GetTabLines();
1230 for ( nRowCount = 0; nRowCount < nRepeat; ++nRowCount )
1231 {
1232 // Insert new headlines:
1233 SwRowFrame* pHeadline = new SwRowFrame(*rLines[nRowCount], this);
1234 {
1235 sw::FlyCreationSuppressor aSuppressor;
1236 pHeadline->SetRepeatedHeadline(true);
1237 }
1238 pHeadline->InsertBefore( pFoll, nullptr );
1239
1240 SwPageFrame *pPage = pHeadline->FindPageFrame();
1241 const SwFrameFormats *pTable = GetFormat()->GetDoc()->GetSpzFrameFormats();
1242 if( !pTable->empty() )
1243 {
1245 SwContentFrame* pFrame = pHeadline->ContainsContent();
1246 while( pFrame )
1247 {
1248 // sw_redlinehide: the implementation of AppendObjs
1249 // takes care of iterating merged SwTextFrame
1250 nIndex = pFrame->IsTextFrame()
1251 ? static_cast<SwTextFrame*>(pFrame)->GetTextNodeFirst()->GetIndex()
1252 : static_cast<SwNoTextFrame*>(pFrame)->GetNode()->GetIndex();
1253 AppendObjs( pTable, nIndex, pFrame, pPage, GetFormat()->GetDoc());
1254 pFrame = pFrame->GetNextContentFrame();
1255 if( !pHeadline->IsAnLower( pFrame ) )
1256 break;
1257 }
1258 }
1259 }
1260 }
1261
1262 SwRowFrame* pLastRow = nullptr; // points to the last remaining line in master
1263 SwRowFrame* pFollowRow = nullptr; // points to either the follow flow line or the
1264 // first regular line in the follow
1265
1266 if ( bSplitRowAllowed )
1267 {
1268 // If the row that does not fit anymore is allowed
1269 // to be split, the next row has to be moved to the follow table.
1270 pLastRow = pRow;
1271 pRow = static_cast<SwRowFrame*>(pRow->GetNext());
1272
1273 // new follow flow line for last row of master table
1274 pFollowRow = lcl_InsertNewFollowFlowLine( *this, *pLastRow, false );
1275 }
1276 else
1277 {
1278 pFollowRow = pRow;
1279
1280 // NEW TABLES
1281 // check if we will break a row span by moving pFollowRow to the follow:
1282 // In this case we want to reformat the last line.
1283 const SwCellFrame* pCellFrame = static_cast<const SwCellFrame*>(pFollowRow->GetLower());
1284 while ( pCellFrame )
1285 {
1286 if ( pCellFrame->GetTabBox()->getRowSpan() < 1 )
1287 {
1288 pLastRow = static_cast<SwRowFrame*>(pRow->GetPrev());
1289 break;
1290 }
1291
1292 pCellFrame = static_cast<const SwCellFrame*>(pCellFrame->GetNext());
1293 }
1294
1295 // new follow flow line for last row of master table
1296 if ( pLastRow )
1297 pFollowRow = lcl_InsertNewFollowFlowLine( *this, *pLastRow, true );
1298 }
1299
1300 SwTwips nShrink = 0;
1301
1302 //Optimization: There is no paste needed for the new Follow and the
1303 //optimized insert can be used (large numbers of rows luckily only occur in
1304 //such situations).
1305 if ( bNewFollow )
1306 {
1307 SwFrame* pInsertBehind = pFoll->GetLastLower();
1308
1309 while ( pRow )
1310 {
1311 SwFrame* pNxt = pRow->GetNext();
1312 nShrink += aRectFnSet.GetHeight(pRow->getFrameArea());
1313 // The footnotes do not have to be moved, this is done in the
1314 // MoveFwd of the follow table!!!
1315 pRow->RemoveFromLayout();
1316 pRow->InsertBehind( pFoll, pInsertBehind );
1317 pRow->InvalidateAll_();
1318 pInsertBehind = pRow;
1319 pRow = static_cast<SwRowFrame*>(pNxt);
1320 }
1321 }
1322 else
1323 {
1324 SwFrame* pPasteBefore = HasFollowFlowLine() ?
1325 pFollowRow->GetNext() :
1326 pFoll->GetFirstNonHeadlineRow();
1327
1328 while ( pRow )
1329 {
1330 SwFrame* pNxt = pRow->GetNext();
1331 nShrink += aRectFnSet.GetHeight(pRow->getFrameArea());
1332
1333 // The footnotes have to be moved:
1334 lcl_MoveFootnotes( *this, *GetFollow(), *pRow );
1335
1336 pRow->RemoveFromLayout();
1337 pRow->Paste( pFoll, pPasteBefore );
1338
1339 pRow->CheckDirChange();
1340 pRow = static_cast<SwRowFrame*>(pNxt);
1341 }
1342 }
1343
1344 if ( !pLastRow )
1345 Shrink( nShrink );
1346 else
1347 {
1348 // we rebuild the last line to assure that it will be fully formatted
1349 // we also don't shrink here, because we will be doing that in lcl_RecalcSplitLine
1350
1351 // recalculate the split line
1352 bRet = lcl_RecalcSplitLine( *pLastRow, *pFollowRow, nRemainingSpaceForLastRow, nShrink );
1353
1354 // RecalcSplitLine did not work. In this case we conceal the split error:
1355 if (!bRet && !bSplitRowAllowed)
1356 {
1357 bRet = true;
1358 }
1359
1360 // NEW TABLES
1361 // check if each cell in the row span line has a good height
1362 if ( bRet && pFollowRow->IsRowSpanLine() )
1363 lcl_AdjustRowSpanCells( pFollowRow );
1364 }
1365
1366 return bRet;
1367}
1368
1369namespace
1370{
1371 bool CanDeleteFollow(SwTabFrame *pFoll)
1372 {
1373 if (pFoll->IsJoinLocked())
1374 return false;
1375
1376 if (pFoll->IsDeleteForbidden())
1377 {
1378 SAL_WARN("sw.layout", "Delete Forbidden");
1379 return false;
1380 }
1381
1382 return true;
1383 }
1384}
1385
1387{
1388 OSL_ENSURE( !HasFollowFlowLine(), "Joining follow flow line" );
1389
1390 SwTabFrame *pFoll = GetFollow();
1391
1392 if (!pFoll || !CanDeleteFollow(pFoll))
1393 return;
1394
1395 SwRectFnSet aRectFnSet(this);
1396 pFoll->Cut(); //Cut out first to avoid unnecessary notifications.
1397
1398 SwFrame *pRow = pFoll->GetFirstNonHeadlineRow(),
1399 *pNxt;
1400
1401 SwFrame* pPrv = GetLastLower();
1402
1403 SwTwips nHeight = 0; //Total height of the inserted rows as return value.
1404
1405 while ( pRow )
1406 {
1407 pNxt = pRow->GetNext();
1408 nHeight += aRectFnSet.GetHeight(pRow->getFrameArea());
1409 pRow->RemoveFromLayout();
1410 pRow->InvalidateAll_();
1411 pRow->InsertBehind( this, pPrv );
1412 pRow->CheckDirChange();
1413 pPrv = pRow;
1414 pRow = pNxt;
1415 }
1416
1417 SetFollow( pFoll->GetFollow() );
1419 SwFrame::DestroyFrame(pFoll);
1420
1421 Grow( nHeight );
1422}
1423
1424static void SwInvalidatePositions( SwFrame *pFrame, tools::Long nBottom )
1425{
1426 // LONG_MAX == nBottom means we have to calculate all
1427 bool bAll = LONG_MAX == nBottom;
1428 SwRectFnSet aRectFnSet(pFrame);
1429 do
1430 { pFrame->InvalidatePos_();
1431 pFrame->InvalidateSize_();
1432 if( pFrame->IsLayoutFrame() )
1433 {
1434 if ( static_cast<SwLayoutFrame*>(pFrame)->Lower() )
1435 {
1436 ::SwInvalidatePositions( static_cast<SwLayoutFrame*>(pFrame)->Lower(), nBottom);
1437 // #i26945#
1438 ::lcl_InvalidateLowerObjs( *static_cast<SwLayoutFrame*>(pFrame) );
1439 }
1440 }
1441 else
1443 pFrame = pFrame->GetNext();
1444 } while ( pFrame &&
1445 ( bAll ||
1446 aRectFnSet.YDiff( aRectFnSet.GetTop(pFrame->getFrameArea()), nBottom ) < 0 ) );
1447}
1448
1449void SwInvalidateAll( SwFrame *pFrame, tools::Long nBottom )
1450{
1451 // LONG_MAX == nBottom means we have to calculate all
1452 bool bAll = LONG_MAX == nBottom;
1453 SwRectFnSet aRectFnSet(pFrame);
1454 do
1455 {
1456 pFrame->InvalidatePos_();
1457 pFrame->InvalidateSize_();
1458 pFrame->InvalidatePrt_();
1459 if( pFrame->IsLayoutFrame() )
1460 {
1461 // NEW TABLES
1462 SwLayoutFrame* pToInvalidate = static_cast<SwLayoutFrame*>(pFrame);
1463 if (pFrame->IsCellFrame())
1464 {
1465 SwCellFrame* pThisCell = static_cast<SwCellFrame*>(pFrame);
1466 if ( pThisCell->GetTabBox()->getRowSpan() < 1 )
1467 {
1468 pToInvalidate = & const_cast<SwCellFrame&>(pThisCell->FindStartEndOfRowSpanCell( true ));
1469 pToInvalidate->InvalidatePos_();
1470 pToInvalidate->InvalidateSize_();
1471 pToInvalidate->InvalidatePrt_();
1472 }
1473 }
1474 if ( pToInvalidate->Lower() )
1475 ::SwInvalidateAll( pToInvalidate->Lower(), nBottom);
1476 }
1477 else
1478 pFrame->Prepare();
1479
1480 pFrame = pFrame->GetNext();
1481 } while ( pFrame &&
1482 ( bAll ||
1483 aRectFnSet.YDiff( aRectFnSet.GetTop(pFrame->getFrameArea()), nBottom ) < 0 ) );
1484}
1485
1486// #i29550#
1488{
1489 pLayFrame->InvalidatePrt_();
1490 pLayFrame->InvalidateSize_();
1491 pLayFrame->SetCompletePaint();
1492
1493 SwFrame* pFrame = pLayFrame->Lower();
1494
1495 while ( pFrame )
1496 {
1497 if ( pFrame->IsLayoutFrame() )
1498 lcl_InvalidateAllLowersPrt( static_cast<SwLayoutFrame*>(pFrame) );
1499 else
1500 {
1501 pFrame->InvalidatePrt_();
1502 pFrame->InvalidateSize_();
1503 pFrame->SetCompletePaint();
1504 }
1505
1506 pFrame = pFrame->GetNext();
1507 }
1508}
1509
1511 tools::Long nBottom, bool bSkipRowSpanCells )
1512{
1513 vcl::RenderContext* pRenderContext = rLay.getRootFrame()->GetCurrShell()->GetOut();
1514 // LONG_MAX == nBottom means we have to calculate all
1515 bool bAll = LONG_MAX == nBottom;
1516 bool bRet = false;
1517 SwContentFrame *pCnt = rLay.ContainsContent();
1518 SwRectFnSet aRectFnSet(&rLay);
1519
1520 // FME 2007-08-30 #i81146# new loop control
1521 int nLoopControlRuns = 0;
1522 const int nLoopControlMax = 10;
1523 const sw::BroadcastingModify* pLoopControlCond = nullptr;
1524
1525 while (pCnt && rDontLeave.IsAnLower(pCnt))
1526 {
1527 // #115759# - check, if a format of content frame is
1528 // possible. Thus, 'copy' conditions, found at the beginning of
1529 // <SwContentFrame::MakeAll(..)>, and check these.
1530 const bool bFormatPossible = !pCnt->IsJoinLocked() &&
1531 ( !pCnt->IsTextFrame() ||
1532 !static_cast<SwTextFrame*>(pCnt)->IsLocked() ) &&
1533 ( pCnt->IsFollow() || !StackHack::IsLocked() );
1534
1535 // NEW TABLES
1536 bool bSkipContent = false;
1537 if ( bSkipRowSpanCells && pCnt->IsInTab() )
1538 {
1539 const SwFrame* pCell = pCnt->GetUpper();
1540 while ( pCell && !pCell->IsCellFrame() )
1541 pCell = pCell->GetUpper();
1542 if ( pCell && 1 != static_cast<const SwCellFrame*>( pCell )->GetLayoutRowSpan() )
1543 bSkipContent = true;
1544 }
1545
1546 if ( bFormatPossible && !bSkipContent )
1547 {
1548 bRet |= !pCnt->isFrameAreaDefinitionValid();
1549 // #i26945# - no extra invalidation of floating
1550 // screen objects needed.
1551 // Thus, delete call of method <SwFrame::InvalidateObjs( true )>
1552 pCnt->Calc(pRenderContext);
1553 // #i46941# - frame has to be valid
1554 // Note: frame could be invalid after calling its format, if it's locked.
1555 OSL_ENSURE( !pCnt->IsTextFrame() ||
1557 static_cast<SwTextFrame*>(pCnt)->IsJoinLocked(),
1558 "<SwContentFrame::CalcLowers(..)> - text frame invalid and not locked." );
1559 if ( pCnt->IsTextFrame() && pCnt->isFrameAreaDefinitionValid() )
1560 {
1561 // #i23129#, #i36347# - pass correct page frame to
1562 // the object formatter
1564 *(pCnt->FindPageFrame()) ) )
1565 {
1566 SwTextNode const*const pTextNode(
1567 static_cast<SwTextFrame*>(pCnt)->GetTextNodeFirst());
1568 if (pTextNode == pLoopControlCond)
1569 ++nLoopControlRuns;
1570 else
1571 {
1572 nLoopControlRuns = 0;
1573 pLoopControlCond = pTextNode;
1574 }
1575
1576 if ( nLoopControlRuns < nLoopControlMax )
1577 {
1578 // restart format with first content
1579 pCnt = rLay.ContainsContent();
1580 continue;
1581 }
1582
1583#if OSL_DEBUG_LEVEL > 1
1584 OSL_FAIL( "LoopControl in SwContentFrame::CalcLowers" );
1585#endif
1586 }
1587 }
1588 if (!rDontLeave.IsAnLower(pCnt)) // moved backward?
1589 {
1590 pCnt = rLay.ContainsContent();
1591 continue; // avoid formatting new upper on different page
1592 }
1593 pCnt->GetUpper()->Calc(pRenderContext);
1594 }
1595 if( ! bAll && aRectFnSet.YDiff(aRectFnSet.GetTop(pCnt->getFrameArea()), nBottom) > 0 )
1596 break;
1597 pCnt = pCnt->GetNextContentFrame();
1598 }
1599 return bRet;
1600}
1601
1602// #i26945# - add parameter <_bOnlyRowsAndCells> to control
1603// that only row and cell frames are formatted.
1604static bool lcl_InnerCalcLayout( SwFrame *pFrame,
1605 tools::Long nBottom,
1606 bool _bOnlyRowsAndCells )
1607{
1608 vcl::RenderContext* pRenderContext = pFrame->getRootFrame()->GetCurrShell() ? pFrame->getRootFrame()->GetCurrShell()->GetOut() : nullptr;
1609 // LONG_MAX == nBottom means we have to calculate all
1610 bool bAll = LONG_MAX == nBottom;
1611 bool bRet = false;
1612 const SwFrame* pOldUp = pFrame->GetUpper();
1613 SwRectFnSet aRectFnSet(pFrame);
1614 do
1615 {
1616 // #i26945# - parameter <_bOnlyRowsAndCells> controls,
1617 // if only row and cell frames are formatted.
1618 if ( pFrame->IsLayoutFrame() &&
1619 ( !_bOnlyRowsAndCells || pFrame->IsRowFrame() || pFrame->IsCellFrame() ) )
1620 {
1621 SwFrameDeleteGuard aDeleteGuard(pFrame);
1622
1623 // #130744# An invalid locked table frame will
1624 // not be calculated => It will not become valid =>
1625 // Loop in lcl_RecalcRow(). Therefore we do not consider them for bRet.
1626 bRet |= !pFrame->isFrameAreaDefinitionValid() && ( !pFrame->IsTabFrame() || !static_cast<SwTabFrame*>(pFrame)->IsJoinLocked() );
1627 pFrame->Calc(pRenderContext);
1628 if( static_cast<SwLayoutFrame*>(pFrame)->Lower() )
1629 bRet |= lcl_InnerCalcLayout( static_cast<SwLayoutFrame*>(pFrame)->Lower(), nBottom);
1630
1631 // NEW TABLES
1632 if (pFrame->IsCellFrame())
1633 {
1634 SwCellFrame* pThisCell = static_cast<SwCellFrame*>(pFrame);
1635 if ( pThisCell->GetTabBox()->getRowSpan() < 1 )
1636 {
1637 SwCellFrame& rToCalc = const_cast<SwCellFrame&>(pThisCell->FindStartEndOfRowSpanCell( true ));
1638 bRet |= !rToCalc.isFrameAreaDefinitionValid();
1639 rToCalc.Calc(pRenderContext);
1640 if ( rToCalc.Lower() )
1641 bRet |= lcl_InnerCalcLayout( rToCalc.Lower(), nBottom);
1642 }
1643 }
1644 }
1645 pFrame = pFrame->GetNext();
1646 } while( pFrame &&
1647 ( bAll ||
1648 aRectFnSet.YDiff(aRectFnSet.GetTop(pFrame->getFrameArea()), nBottom) < 0 )
1649 && pFrame->GetUpper() == pOldUp );
1650 return bRet;
1651}
1652
1653static void lcl_RecalcRow(SwRowFrame & rRow, tools::Long const nBottom)
1654{
1655 // FME 2007-08-30 #i81146# new loop control
1656 int nLoopControlRuns_1 = 0;
1657 sal_uInt16 nLoopControlStage_1 = 0;
1658 const int nLoopControlMax = 10;
1659
1660 bool bCheck = true;
1661 do
1662 {
1663 // FME 2007-08-30 #i81146# new loop control
1664 int nLoopControlRuns_2 = 0;
1665 sal_uInt16 nLoopControlStage_2 = 0;
1666
1667 while (lcl_InnerCalcLayout(&rRow, nBottom))
1668 {
1669 if ( ++nLoopControlRuns_2 > nLoopControlMax )
1670 {
1671 SAL_WARN_IF(nLoopControlStage_2 == 0, "sw.layout", "LoopControl_2 in lcl_RecalcRow: Stage 1!");
1672 SAL_WARN_IF(nLoopControlStage_2 == 1, "sw.layout", "LoopControl_2 in lcl_RecalcRow: Stage 2!!");
1673 SAL_WARN_IF(nLoopControlStage_2 >= 2, "sw.layout", "LoopControl_2 in lcl_RecalcRow: Stage 3!!!");
1674 rRow.ValidateThisAndAllLowers( nLoopControlStage_2++ );
1675 nLoopControlRuns_2 = 0;
1676 if( nLoopControlStage_2 > 2 )
1677 break;
1678 }
1679
1680 bCheck = true;
1681 }
1682
1683 if( bCheck )
1684 {
1685 // #115759# - force another format of the
1686 // lowers, if at least one of it was invalid.
1687 bCheck = SwContentFrame::CalcLowers(rRow, *rRow.GetUpper(), nBottom, true);
1688
1689 // NEW TABLES
1690 // First we calculate the cells with row span of < 1, afterwards
1691 // all cells with row span of > 1:
1692 for ( int i = 0; i < 2; ++i )
1693 {
1694 SwCellFrame* pCellFrame = static_cast<SwCellFrame*>(rRow.Lower());
1695 while ( pCellFrame )
1696 {
1697 const bool bCalc = 0 == i ?
1698 pCellFrame->GetLayoutRowSpan() < 1 :
1699 pCellFrame->GetLayoutRowSpan() > 1;
1700
1701 if ( bCalc )
1702 {
1703 SwCellFrame& rToRecalc = 0 == i ?
1704 const_cast<SwCellFrame&>(pCellFrame->FindStartEndOfRowSpanCell( true )) :
1705 *pCellFrame;
1706 bCheck |= SwContentFrame::CalcLowers(rToRecalc, rToRecalc, nBottom, false);
1707 }
1708
1709 pCellFrame = static_cast<SwCellFrame*>(pCellFrame->GetNext());
1710 }
1711 }
1712
1713 if ( bCheck )
1714 {
1715 if ( ++nLoopControlRuns_1 > nLoopControlMax )
1716 {
1717 SAL_WARN_IF(nLoopControlStage_1 == 0, "sw.layout", "LoopControl_1 in lcl_RecalcRow: Stage 1!");
1718 SAL_WARN_IF(nLoopControlStage_1 == 1, "sw.layout", "LoopControl_1 in lcl_RecalcRow: Stage 2!!");
1719 SAL_WARN_IF(nLoopControlStage_1 >= 2, "sw.layout", "LoopControl_1 in lcl_RecalcRow: Stage 3!!!");
1720 rRow.ValidateThisAndAllLowers( nLoopControlStage_1++ );
1721 nLoopControlRuns_1 = 0;
1722 if( nLoopControlStage_1 > 2 )
1723 break;
1724 }
1725
1726 continue;
1727 }
1728 }
1729 break;
1730 } while( true );
1731}
1732
1733static void lcl_RecalcTable( SwTabFrame& rTab,
1734 SwLayoutFrame *pFirstRow,
1735 SwLayNotify &rNotify )
1736{
1737 if ( rTab.Lower() )
1738 {
1739 if ( !pFirstRow )
1740 {
1741 pFirstRow = static_cast<SwLayoutFrame*>(rTab.Lower());
1742 rNotify.SetLowersComplete( true );
1743 }
1744 ::SwInvalidatePositions( pFirstRow, LONG_MAX );
1745 lcl_RecalcRow( *static_cast<SwRowFrame*>(pFirstRow), LONG_MAX );
1746 }
1747}
1748
1749// This is a new function to check the first condition whether
1750// a tab frame may move backward. It replaces the formerly used
1751// GetIndPrev(), which did not work correctly for #i5947#
1752static bool lcl_NoPrev( const SwFrame& rFrame )
1753{
1754 // #i79774#
1755 // skip empty sections on investigation of direct previous frame.
1756 // use information, that at least one empty section is skipped in the following code.
1757 bool bSkippedDirectPrevEmptySection( false );
1758 if ( rFrame.GetPrev() )
1759 {
1760 const SwFrame* pPrev( rFrame.GetPrev() );
1761 while ( pPrev &&
1762 pPrev->IsSctFrame() &&
1763 !dynamic_cast<const SwSectionFrame&>(*pPrev).GetSection() )
1764 {
1765 pPrev = pPrev->GetPrev();
1766 bSkippedDirectPrevEmptySection = true;
1767 }
1768 if ( pPrev )
1769 {
1770 return false;
1771 }
1772 }
1773
1774 if ( ( !bSkippedDirectPrevEmptySection && !rFrame.GetIndPrev() ) ||
1775 ( bSkippedDirectPrevEmptySection &&
1776 ( !rFrame.IsInSct() || !rFrame.GetIndPrev_() ) ) )
1777 {
1778 return true;
1779 }
1780
1781 // I do not have a direct prev, but I have an indirect prev.
1782 // In section frames I have to check if I'm located inside
1783 // the first column:
1784 if ( rFrame.IsInSct() )
1785 {
1786 const SwFrame* pSct = rFrame.GetUpper();
1787 if ( pSct && pSct->IsColBodyFrame() &&
1788 pSct->GetUpper()->GetUpper()->IsSctFrame() )
1789 {
1790 const SwFrame* pPrevCol = rFrame.GetUpper()->GetUpper()->GetPrev();
1791 if ( pPrevCol )
1792 // I'm not inside the first column and do not have a direct
1793 // prev. I can try to go backward.
1794 return true;
1795 }
1796 }
1797
1798 return false;
1799}
1800
1801#define KEEPTAB ( !GetFollow() && !IsFollow() )
1802
1803// - helper method to find next content frame of
1804// a table frame and format it to assure keep attribute.
1805// method return true, if a next content frame is formatted.
1806// Precondition: The given table frame hasn't a follow and isn't a follow.
1808{
1809 vcl::RenderContext* pRenderContext = pTabFrame->getRootFrame()->GetCurrShell()->GetOut();
1810 // find next content, table or section
1811 SwFrame* pNxt = pTabFrame->FindNext();
1812
1813 // skip empty sections
1814 while ( pNxt && pNxt->IsSctFrame() &&
1815 !static_cast<SwSectionFrame*>(pNxt)->GetSection() )
1816 {
1817 pNxt = pNxt->FindNext();
1818 }
1819
1820 // if found next frame is a section, get its first content.
1821 if ( pNxt && pNxt->IsSctFrame() )
1822 {
1823 pNxt = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
1824 }
1825
1826 // format found next frame.
1827 // if table frame is inside another table, method <SwFrame::MakeAll()> is
1828 // called to avoid that the superior table frame is formatted.
1829 if ( pNxt )
1830 {
1831 if ( pTabFrame->GetUpper()->IsInTab() )
1832 pNxt->MakeAll(pNxt->getRootFrame()->GetCurrShell()->GetOut());
1833 else
1834 pNxt->Calc(pRenderContext);
1835 }
1836
1837 return pNxt;
1838}
1839
1840namespace {
1841 bool AreAllRowsKeepWithNext( const SwRowFrame* pFirstRowFrame, const bool bCheckParents = true )
1842 {
1843 bool bRet = pFirstRowFrame != nullptr &&
1844 pFirstRowFrame->ShouldRowKeepWithNext( bCheckParents );
1845
1846 while ( bRet && pFirstRowFrame->GetNext() != nullptr )
1847 {
1848 pFirstRowFrame = dynamic_cast<const SwRowFrame*>(pFirstRowFrame->GetNext());
1849 bRet = pFirstRowFrame != nullptr &&
1850 pFirstRowFrame->ShouldRowKeepWithNext( bCheckParents );
1851 }
1852
1853 return bRet;
1854 }
1855}
1856
1857// extern because static can't be friend
1859{
1860 // hilariously static_cast<SwTabFrame*>(GetLower()) would not require friend declaration, but it's UB...
1861 rRowFrame.setFrameAreaPositionValid(false);
1862}
1863
1865{
1866 while (pFrame)
1867 {
1868 if (pFrame->IsLayoutFrame())
1869 {
1871 }
1872 else if (pFrame->IsTextFrame())
1873 {
1875 }
1876 pFrame = pFrame->GetNext();
1877 }
1878}
1879
1881{
1882 if ( IsJoinLocked() || StackHack::IsLocked() || StackHack::Count() > 50 )
1883 return;
1884
1885 if ( HasFollow() )
1886 {
1887 SwTabFrame* pFollowFrame = GetFollow();
1888 OSL_ENSURE( !pFollowFrame->IsJoinLocked() || !pFollowFrame->IsRebuildLastLine(),
1889 "SwTabFrame::MakeAll for master while follow is in RebuildLastLine()" );
1890 if ( pFollowFrame->IsJoinLocked() && pFollowFrame->IsRebuildLastLine() )
1891 return;
1892 }
1893
1895
1896 LockJoin(); //I don't want to be destroyed on the way.
1897 SwLayNotify aNotify( this ); //does the notification in the DTor
1898 // If pos is invalid, we have to call a SetInvaKeep at aNotify.
1899 // Otherwise the keep attribute would not work in front of a table.
1900 const bool bOldValidPos = isFrameAreaPositionValid();
1901
1902 //If my neighbour is my Follow at the same time, I'll swallow it up.
1903 // OD 09.04.2003 #108698# - join all follows, which are placed on the
1904 // same page/column.
1905 // OD 29.04.2003 #109213# - join follow, only if join for the follow
1906 // is not locked. Otherwise, join will not be performed and this loop
1907 // will be endless.
1908 while ( GetNext() && GetNext() == GetFollow() &&
1909 CanDeleteFollow(GetFollow())
1910 )
1911 {
1912 if ( HasFollowFlowLine() )
1914 Join();
1915 }
1916
1917 // The bRemoveFollowFlowLinePending is set if the split attribute of the
1918 // last line is set:
1920 {
1921 if ( RemoveFollowFlowLine() )
1922 Join();
1924 }
1925
1926 if (m_bResizeHTMLTable) //Optimized interplay with grow/shrink of the content
1927 {
1928 m_bResizeHTMLTable = false;
1930 if ( pLayout )
1931 m_bCalcLowers = pLayout->Resize(
1932 pLayout->GetBrowseWidthByTabFrame( *this ) );
1933 }
1934
1935 // as long as bMakePage is true, a new page can be created (exactly once)
1936 bool bMakePage = true;
1937 // bMovedBwd gets set to true when the frame flows backwards
1938 bool bMovedBwd = false;
1939 // as long as bMovedFwd is false, the Frame may flow backwards (until
1940 // it has been moved forward once)
1941 bool bMovedFwd = false;
1942 // gets set to true when the Frame is split
1943 bool bSplit = false;
1944 const bool bFootnotesInDoc = !GetFormat()->GetDoc()->GetFootnoteIdxs().empty();
1945 const bool bFly = IsInFly();
1946
1947 std::optional<SwBorderAttrAccess> oAccess(std::in_place, SwFrame::GetCache(), this);
1948 const SwBorderAttrs *pAttrs = oAccess->Get();
1949
1950 // All rows should keep together
1951 const bool bDontSplit = !IsFollow() &&
1952 ( !GetFormat()->GetLayoutSplit().GetValue() );
1953
1954 // The number of repeated headlines
1955 const sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat();
1956
1957 // This flag indicates that we are allowed to try to split the
1958 // table rows.
1959 bool bTryToSplit = true;
1960
1961 // Indicates that two individual rows may keep together, based on the keep
1962 // attribute set at the first paragraph in the first cell.
1963 const bool bTableRowKeep = !bDontSplit && GetFormat()->GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::TABLE_ROW_KEEP);
1964
1965 // The Magic Move: Used for the table row keep feature.
1966 // If only the last row of the table wants to keep (implicitly by setting
1967 // keep for the first paragraph in the first cell), and this table does
1968 // not have a next, the last line will be cut. Loop prevention: Only
1969 // one try.
1970 // WHAT IS THIS??? It "magically" hides last line (paragraph) in a table,
1971 // if first is set to keep with next???
1972 bool bLastRowHasToMoveToFollow = false;
1973 bool bLastRowMoveNoMoreTries = false;
1974
1975 const bool bLargeTable = GetTable()->GetTabLines().size() > 64; //arbitrary value, virtually guaranteed to be larger than one page.
1976 const bool bEmulateTableKeep = !bLargeTable && bTableRowKeep
1977 && !pAttrs->GetAttrSet().GetKeep().GetValue()
1978 && AreAllRowsKeepWithNext(GetFirstNonHeadlineRow(), /*bCheckParents=*/false);
1979 // The beloved keep attribute
1980 const bool bKeep = IsKeep(pAttrs->GetAttrSet().GetKeep(), GetBreakItem(), bEmulateTableKeep);
1981
1982 // Join follow table, if this table is not allowed to split:
1983 if ( bDontSplit )
1984 {
1985 while ( GetFollow() && !GetFollow()->IsJoinLocked() )
1986 {
1987 if ( HasFollowFlowLine() )
1989 Join();
1990 }
1991 }
1992
1993 // Join follow table, if this does not have enough (repeated) lines:
1994 if ( nRepeat )
1995 {
1996 if( GetFollow() && !GetFollow()->IsJoinLocked() &&
1997 nullptr == GetFirstNonHeadlineRow() )
1998 {
1999 if ( HasFollowFlowLine() )
2001 Join();
2002 }
2003 }
2004
2005 // Join follow table, if last row of this table should keep:
2006 if ( bTableRowKeep && GetFollow() && !GetFollow()->IsJoinLocked() )
2007 {
2008 const SwRowFrame* pTmpRow = static_cast<const SwRowFrame*>(GetLastLower());
2009 if ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() )
2010 {
2011 if ( HasFollowFlowLine() )
2013 Join();
2014 }
2015 }
2016
2017 // a new one is moved forwards immediately
2018 if ( !getFrameArea().Top() && IsFollow() )
2019 {
2020 SwFrame *pPre = GetPrev();
2021 if ( pPre && pPre->IsTabFrame() && static_cast<SwTabFrame*>(pPre)->GetFollow() == this)
2022 {
2023 // don't make the effort to move fwd if its known
2024 // conditions that are known not to work
2025 if (IsInFootnote() && ForbiddenForFootnoteCntFwd())
2026 bMakePage = false;
2027 else if (!MoveFwd(bMakePage, false))
2028 bMakePage = false;
2029 bMovedFwd = true;
2030 }
2031 }
2032
2033 int nUnSplitted = 5; // Just another loop control :-(
2034 int nThrowAwayValidLayoutLimit = 5; // And another one :-(
2035 SwRectFnSet aRectFnSet(this);
2037 {
2038 const bool bMoveable = IsMoveable();
2039 if (bMoveable &&
2040 !(bMovedFwd && bEmulateTableKeep) )
2041 if ( CheckMoveFwd( bMakePage, bKeep && KEEPTAB, bEmulateTableKeep ) )
2042 {
2043 bMovedFwd = true;
2044 m_bCalcLowers = true;
2045 // #i99267#
2046 // reset <bSplit> after forward move to assure that follows
2047 // can be joined, if further space is available.
2048 bSplit = false;
2049 }
2050
2051 Point aOldPos( aRectFnSet.GetPos(getFrameArea()) );
2052 MakePos();
2053
2054 if ( aOldPos != aRectFnSet.GetPos(getFrameArea()) )
2055 {
2056 if ( aOldPos.Y() != aRectFnSet.GetTop(getFrameArea()) )
2057 {
2059 if( pLayout )
2060 {
2061 oAccess.reset();
2062 m_bCalcLowers |= pLayout->Resize(
2063 pLayout->GetBrowseWidthByTabFrame( *this ) );
2064 }
2065
2067 aNotify.SetLowersComplete( false );
2068 }
2069 SwFrame *pPre;
2070 if ( bKeep || (nullptr != (pPre = FindPrev()) &&
2071 pPre->GetAttrSet()->GetKeep().GetValue()) )
2072 {
2073 m_bCalcLowers = true;
2074 }
2075 if (GetLower())
2076 { // it's possible that the rows already have valid pos - but it is surely wrong if the table's pos changed!
2078 // invalidate text frames to get rid of their SwFlyPortions
2080 }
2081 }
2082
2083 //We need to know the height of the first row, because the master needs
2084 //to be invalidated if it shrinks and then absorb the row if possible.
2085 tools::Long n1StLineHeight = 0;
2086 if ( IsFollow() )
2087 {
2088 SwFrame* pFrame = GetFirstNonHeadlineRow();
2089 if ( pFrame )
2090 n1StLineHeight = aRectFnSet.GetHeight(pFrame->getFrameArea());
2091 }
2092
2094 {
2095 const tools::Long nOldPrtWidth = aRectFnSet.GetWidth(getFramePrintArea());
2096 const tools::Long nOldFrameWidth = aRectFnSet.GetWidth(getFrameArea());
2097 const Point aOldPrtPos = aRectFnSet.GetPos(getFramePrintArea());
2098
2099 if (!oAccess)
2100 {
2101 oAccess.emplace(SwFrame::GetCache(), this);
2102 pAttrs = oAccess->Get();
2103 }
2104 Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs );
2105
2107 if ( pLayout &&
2108 (aRectFnSet.GetWidth(getFramePrintArea()) != nOldPrtWidth ||
2109 aRectFnSet.GetWidth(getFrameArea()) != nOldFrameWidth) )
2110 {
2111 oAccess.reset();
2112 m_bCalcLowers |= pLayout->Resize(
2113 pLayout->GetBrowseWidthByTabFrame( *this ) );
2114 }
2115 if ( aOldPrtPos != aRectFnSet.GetPos(getFramePrintArea()) )
2116 aNotify.SetLowersComplete( false );
2117 }
2118
2119 // If this is the first one in a chain, check if this can flow
2120 // backwards (if this is movable at all).
2121 // To prevent oscillations/loops, check that this has not just
2122 // flowed forwards.
2123 if ( !bMovedFwd && (bMoveable || bFly) && lcl_NoPrev( *this ) )
2124 {
2125 // for Follows notify Master.
2126 // only move Follow if it has to skip empty pages.
2127 if ( IsFollow() )
2128 {
2129 // Only if the height of the first line got smaller.
2130 SwFrame *pFrame = GetFirstNonHeadlineRow();
2131 if( pFrame && n1StLineHeight >aRectFnSet.GetHeight(pFrame->getFrameArea()) )
2132 {
2133 SwTabFrame *pMaster = FindMaster();
2134 bool bDummy;
2135 if ( ShouldBwdMoved( pMaster->GetUpper(), bDummy ) )
2136 pMaster->InvalidatePos();
2137 }
2138 }
2139 SwFootnoteBossFrame *pOldBoss = bFootnotesInDoc ? FindFootnoteBossFrame( true ) : nullptr;
2140 bool bReformat;
2141 std::optional<SfxDeleteListener> oDeleteListener;
2142 if (pOldBoss)
2143 oDeleteListener.emplace(*pOldBoss);
2144 SwFrameDeleteGuard g(this);
2145 if ( MoveBwd( bReformat ) )
2146 {
2147 SAL_WARN_IF(oDeleteListener && oDeleteListener->WasDeleted(), "sw.layout", "SwFootnoteBossFrame unexpectedly deleted");
2148
2149 aRectFnSet.Refresh(this);
2150 bMovedBwd = true;
2151 aNotify.SetLowersComplete( false );
2152 if (bFootnotesInDoc && !oDeleteListener->WasDeleted())
2153 MoveLowerFootnotes( nullptr, pOldBoss, nullptr, true );
2154 if ( bReformat || bKeep )
2155 {
2156 tools::Long nOldTop = aRectFnSet.GetTop(getFrameArea());
2157 MakePos();
2158 if( nOldTop != aRectFnSet.GetTop(getFrameArea()) )
2159 {
2160 SwHTMLTableLayout *pHTMLLayout =
2162 if( pHTMLLayout )
2163 {
2164 oAccess.reset();
2165 m_bCalcLowers |= pHTMLLayout->Resize(
2166 pHTMLLayout->GetBrowseWidthByTabFrame( *this ) );
2167 }
2168
2170
2171 if (!oAccess)
2172 {
2173 oAccess.emplace(SwFrame::GetCache(), this);
2174 pAttrs = oAccess->Get();
2175 }
2176 Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs );
2177 }
2178
2179 oAccess.reset();
2180
2181 lcl_RecalcTable( *this, nullptr, aNotify );
2182
2183 m_bLowersFormatted = true;
2184 if ( bKeep && KEEPTAB )
2185 {
2186
2187 // Consider case that table is inside another table,
2188 // because it has to be avoided, that superior table
2189 // is formatted.
2190 // Thus, find next content, table or section
2191 // and, if a section is found, get its first
2192 // content.
2193 if ( nullptr != sw_FormatNextContentForKeep( this ) && !GetNext() )
2194 {
2196 }
2197 }
2198 }
2199 }
2200 }
2201
2202 //Again an invalid value? - do it again...
2204 continue;
2205
2206 // check, if calculation of table frame is ready.
2207
2208 // Local variable <nDistanceToUpperPrtBottom>
2209 // Introduce local variable and init it with the distance from the
2210 // table frame bottom to the bottom of the upper printing area.
2211 // Note: negative values denotes the situation that table frame doesn't fit in its upper.
2212 SwTwips nDistanceToUpperPrtBottom =
2213 aRectFnSet.BottomDist(getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
2214
2216 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
2217 const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
2218 if ( nDistanceToUpperPrtBottom < 0 && bBrowseMode )
2219 {
2220 if ( GetUpper()->Grow( -nDistanceToUpperPrtBottom ) )
2221 {
2222 // upper is grown --> recalculate <nDistanceToUpperPrtBottom>
2223 nDistanceToUpperPrtBottom = aRectFnSet.BottomDist(getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
2224 }
2225 }
2226
2227 // If there is still some space left in the upper, we check if we
2228 // can join some rows of the follow.
2229 // Setting bLastRowHasToMoveToFollow to true means we want to force
2230 // the table to be split! Only skip this if condition once.
2231 if( nDistanceToUpperPrtBottom >= 0 && !bLastRowHasToMoveToFollow )
2232 {
2233 // If there is space left in the upper printing area, join as for trial
2234 // at least one further row of an existing follow.
2235 if ( !bSplit && GetFollow() )
2236 {
2237 bool bDummy;
2238 if ( GetFollow()->ShouldBwdMoved( GetUpper(), bDummy ) )
2239 {
2240 SwFrame *pTmp = GetUpper();
2241 SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*pTmp);
2242 if ( bBrowseMode )
2243 nDeadLine += pTmp->Grow( LONG_MAX, true );
2244 bool bFits = aRectFnSet.BottomDist(getFrameArea(), nDeadLine) > 0;
2245 if (!bFits && aRectFnSet.GetHeight(GetFollow()->getFrameArea()) == 0)
2246 // The follow should move backwards, so allow the case
2247 // when the upper has no space, but the follow is
2248 // empty.
2249 bFits = aRectFnSet.BottomDist(getFrameArea(), nDeadLine) >= 0;
2250 if (bFits)
2251 {
2252 // First, we remove an existing follow flow line.
2253 if ( HasFollowFlowLine() )
2254 {
2255 SwFrame* pLastLine = GetLastLower();
2257 // invalidate and rebuild last row
2258 if ( pLastLine )
2259 {
2260 ::SwInvalidateAll( pLastLine, LONG_MAX );
2261 SetRebuildLastLine( true );
2262 lcl_RecalcRow(*static_cast<SwRowFrame*>(pLastLine), LONG_MAX);
2263 SetRebuildLastLine( false );
2264 }
2265
2267
2268 if ( !pRow || !pRow->GetNext() )
2269 // The follow became empty and hence useless
2270 Join();
2271
2272 continue;
2273 }
2274
2275 // If there is no follow flow line, we move the first
2276 // row in the follow table to the master table.
2278
2279 // The follow became empty and hence useless
2280 if ( !pRow )
2281 {
2282 Join();
2283 continue;
2284 }
2285
2286 const SwTwips nOld = aRectFnSet.GetHeight(getFrameArea());
2287 tools::Long nRowsToMove = lcl_GetMaximumLayoutRowSpan( *pRow );
2288 SwFrame* pRowToMove = pRow;
2289
2290 while ( pRowToMove && nRowsToMove-- > 0 )
2291 {
2292 const bool bMoveFootnotes = bFootnotesInDoc && !GetFollow()->IsJoinLocked();
2293
2294 SwFootnoteBossFrame *pOldBoss = nullptr;
2295 if ( bMoveFootnotes )
2296 pOldBoss = pRowToMove->FindFootnoteBossFrame( true );
2297
2298 SwFrame* pNextRow = pRowToMove->GetNext();
2299
2300 if ( !pNextRow )
2301 {
2302 // The follow became empty and hence useless
2303 Join();
2304 }
2305 else
2306 {
2307 pRowToMove->Cut();
2308 pRowToMove->Paste( this );
2309 }
2310
2311 // Move the footnotes!
2312 if ( bMoveFootnotes )
2313 if ( static_cast<SwLayoutFrame*>(pRowToMove)->MoveLowerFootnotes( nullptr, pOldBoss, FindFootnoteBossFrame( true ), true ) )
2314 GetUpper()->Calc(pRenderContext);
2315
2316 pRowToMove = pNextRow;
2317 }
2318
2319 if ( nOld != aRectFnSet.GetHeight(getFrameArea()) )
2320 lcl_RecalcTable( *this, static_cast<SwLayoutFrame*>(pRow), aNotify );
2321
2322 continue;
2323 }
2324 }
2325 }
2326 else if ( KEEPTAB )
2327 {
2328 bool bFormat = false;
2329 if ( bKeep )
2330 bFormat = true;
2331 else if ( bTableRowKeep && !bLastRowMoveNoMoreTries )
2332 {
2333 // We only want to give the last row one chance to move
2334 // to the follow table. Set the flag as early as possible:
2335 bLastRowMoveNoMoreTries = true;
2336
2337 // The last line of the table has to be cut off if:
2338 // 1. The table does not want to keep with its next
2339 // 2. The compatibility option is set and the table is allowed to split
2340 // 3. We did not already cut off the last row
2341 // 4. There is not break after attribute set at the table
2342 // 5. There is no break before attribute set behind the table
2343 // 6. There is no section change behind the table (see IsKeep)
2344 // 7. The last table row wants to keep with its next.
2345 const SwRowFrame* pLastRow = static_cast<const SwRowFrame*>(GetLastLower());
2346 if (pLastRow)
2347 {
2348 if (!oAccess)
2349 {
2350 oAccess.emplace(SwFrame::GetCache(), this);
2351 pAttrs = oAccess->Get();
2352 }
2353 if (IsKeep(pAttrs->GetAttrSet().GetKeep(), GetBreakItem(), true)
2354 && pLastRow->ShouldRowKeepWithNext())
2355 {
2356 bFormat = true;
2357 }
2358 }
2359 }
2360
2361 if ( bFormat )
2362 {
2363 oAccess.reset();
2364
2365 // Consider case that table is inside another table, because
2366 // it has to be avoided, that superior table is formatted.
2367 // Thus, find next content, table or section and, if a section
2368 // is found, get its first content.
2369 const SwFrame* pTmpNxt = sw_FormatNextContentForKeep( this );
2370
2371 // The last row wants to keep with the frame behind the table.
2372 // Check if the next frame is on a different page and valid.
2373 // In this case we do a magic trick:
2374 if ( !bKeep && !GetNext() && pTmpNxt && pTmpNxt->isFrameAreaDefinitionValid() )
2375 {
2377 bLastRowHasToMoveToFollow = true;
2378 }
2379 }
2380 }
2381
2383 {
2384 if (m_bCalcLowers)
2385 {
2386 lcl_RecalcTable( *this, nullptr, aNotify );
2387 m_bLowersFormatted = true;
2388 m_bCalcLowers = false;
2389 }
2390 else if (m_bONECalcLowers)
2391 {
2392 lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), LONG_MAX);
2393 m_bONECalcLowers = false;
2394 }
2395 }
2396 continue;
2397 }
2398
2399 // I don't fit in the upper Frame anymore, therefore it's the
2400 // right moment to do some preferably constructive changes.
2401
2402 // If I'm NOT allowed to leave the upper Frame, I've got a problem.
2403 // Following Arthur Dent, we do the only thing that you can do with
2404 // an unsolvable problem: We ignore it with all our power.
2405 if ( !bMoveable )
2406 {
2408 {
2409 lcl_RecalcTable( *this, nullptr, aNotify );
2410 m_bLowersFormatted = true;
2411 m_bCalcLowers = false;
2412 }
2413 else if (m_bONECalcLowers)
2414 {
2415 lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), LONG_MAX);
2416 m_bONECalcLowers = false;
2417 }
2418
2419 // It does not make sense to cut off the last line if we are
2420 // not moveable:
2421 bLastRowHasToMoveToFollow = false;
2422
2423 continue;
2424 }
2425
2427 {
2428 lcl_RecalcTable( *this, nullptr, aNotify );
2429 m_bLowersFormatted = true;
2430 m_bCalcLowers = false;
2432 continue;
2433 }
2434
2435 // First try to split the table. Condition:
2436 // 1. We have at least one non headline row
2437 // 2. If this row wants to keep, we need an additional row
2438 // 3. The table is allowed to split or we do not have a pIndPrev:
2439 SwFrame* pIndPrev = GetIndPrev();
2440 const SwRowFrame* pFirstNonHeadlineRow = GetFirstNonHeadlineRow();
2441 // #i120016# if this row wants to keep, allow split in case that all rows want to keep with next,
2442 // the table can not move forward as it is the first one and a split is in general allowed.
2443 const bool bAllowSplitOfRow = bTableRowKeep && !pIndPrev && AreAllRowsKeepWithNext(pFirstNonHeadlineRow);
2444 // tdf91083 MSCompat: this extends bAllowSplitOfRow (and perhaps should just replace it).
2445 // If the kept-together items cannot move to a new page, a table split is in general allowed.
2446 const bool bEmulateTableKeepSplitAllowed = bEmulateTableKeep && !IsKeepFwdMoveAllowed(/*IgnoreMyOwnKeepValue=*/true);
2447
2448 if ( pFirstNonHeadlineRow && nUnSplitted > 0 &&
2449 ( bEmulateTableKeepSplitAllowed || bAllowSplitOfRow ||
2450 ( ( !bTableRowKeep || pFirstNonHeadlineRow->GetNext() ||
2451 !pFirstNonHeadlineRow->ShouldRowKeepWithNext()
2452 ) && ( !bDontSplit || !pIndPrev )
2453 ) ) )
2454 {
2455 // #i29438#
2456 // Special DoNotSplit cases:
2457 // We better avoid splitting of a row frame if we are inside a columned
2458 // section which has a height of 0, because this is not growable and thus
2459 // all kinds of unexpected things could happen.
2460 if ( IsInSct() && FindSctFrame()->Lower()->IsColumnFrame() &&
2461 0 == aRectFnSet.GetHeight(GetUpper()->getFrameArea())
2462 )
2463 {
2464 bTryToSplit = false;
2465 }
2466
2467 // 1. Try: bTryToSplit = true => Try to split the row.
2468 // 2. Try: bTryToSplit = false => Split the table between the rows.
2469 if ( pFirstNonHeadlineRow->GetNext() || bTryToSplit )
2470 {
2471 SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
2472 if( IsInSct() || GetUpper()->IsInTab() ) // TABLE IN TABLE)
2473 nDeadLine = aRectFnSet.YInc( nDeadLine,
2474 GetUpper()->Grow( LONG_MAX, true ) );
2475
2476 {
2477 SwFrameDeleteGuard g(Lower()); // tdf#134965 prevent RemoveFollowFlowLine()
2478 SetInRecalcLowerRow( true );
2479 ::lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), nDeadLine);
2480 SetInRecalcLowerRow( false );
2481 }
2482 m_bLowersFormatted = true;
2483 aNotify.SetLowersComplete( true );
2484
2485 // One more check if it's really necessary to split the table.
2486 // 1. The table either has to exceed the deadline or
2487 // 2. We explicitly want to cut off the last row.
2488 if( aRectFnSet.BottomDist( getFrameArea(), nDeadLine ) > 0 && !bLastRowHasToMoveToFollow )
2489 {
2490 continue;
2491 }
2492
2493 // Set to false again as early as possible.
2494 bLastRowHasToMoveToFollow = false;
2495
2496 // #i52781#
2497 // YaSC - Yet another special case:
2498 // If our upper is inside a table cell which is not allowed
2499 // to split, we do not try to split:
2500 if ( GetUpper()->IsInTab() )
2501 {
2502 const SwFrame* pTmpRow = GetUpper();
2503 while ( pTmpRow && !pTmpRow->IsRowFrame() )
2504 pTmpRow = pTmpRow->GetUpper();
2505 if ( pTmpRow && !static_cast<const SwRowFrame*>(pTmpRow)->IsRowSplitAllowed() )
2506 continue;
2507 }
2508
2509 sal_uInt16 nMinNumOfLines = nRepeat;
2510
2511 if ( bTableRowKeep )
2512 {
2513 const SwRowFrame* pTmpRow = GetFirstNonHeadlineRow();
2514 while ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() )
2515 {
2516 ++nMinNumOfLines;
2517 pTmpRow = static_cast<const SwRowFrame*>(pTmpRow->GetNext());
2518 }
2519 }
2520
2521 if ( !bTryToSplit )
2522 ++nMinNumOfLines;
2523
2524 const SwTwips nBreakLine = aRectFnSet.YInc(
2525 aRectFnSet.GetTop(getFrameArea()),
2526 aRectFnSet.GetTopMargin(*this) +
2527 lcl_GetHeightOfRows( GetLower(), nMinNumOfLines ) );
2528
2529 // Some more checks if we want to call the split algorithm or not:
2530 // The repeating lines / keeping lines still fit into the upper or
2531 // if we do not have an (in)direct Prev, we split anyway.
2532 if( aRectFnSet.YDiff(nDeadLine, nBreakLine) >=0
2533 || !pIndPrev || bEmulateTableKeepSplitAllowed )
2534 {
2535 aNotify.SetLowersComplete( false );
2536 bSplit = true;
2537
2538 // An existing follow flow line has to be removed.
2539 if ( HasFollowFlowLine() )
2540 {
2541 if (!nThrowAwayValidLayoutLimit)
2542 continue;
2543 const bool bInitialLoopEndCondition(isFrameAreaDefinitionValid());
2545 const bool bFinalLoopEndCondition(isFrameAreaDefinitionValid());
2546
2547 if (bInitialLoopEndCondition && !bFinalLoopEndCondition)
2548 {
2549 --nThrowAwayValidLayoutLimit;
2550 }
2551 }
2552
2553 oAccess.reset();
2554 const bool bSplitError = !Split( nDeadLine, bTryToSplit, ( bTableRowKeep && !(bAllowSplitOfRow || bEmulateTableKeepSplitAllowed) ) );
2555
2556 // tdf#130639 don't start table on a new page after the fallback "switch off repeating header"
2557 if (bSplitError && nRepeat > GetTable()->GetRowsToRepeat())
2558 {
2560 break;
2561 }
2562
2563 if (!bTryToSplit && !bSplitError)
2564 {
2565 --nUnSplitted;
2566 }
2567
2568 // #i29771# Two tries to split the table
2569 // If an error occurred during splitting. We start a second
2570 // try, this time without splitting of table rows.
2571 if ( bSplitError && HasFollowFlowLine() )
2573
2574 // If splitting the table was successful or not,
2575 // we do not want to have 'empty' follow tables.
2577 Join();
2578
2579 // We want to restore the situation before the failed
2580 // split operation as good as possible. Therefore we
2581 // do some more calculations. Note: Restricting this
2582 // to nDeadLine may not be enough.
2583 if ( bSplitError && bTryToSplit ) // no restart if we did not try to split: i72847, i79426
2584 {
2585 lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), LONG_MAX);
2587 bTryToSplit = false;
2588 continue;
2589 }
2590
2591 bTryToSplit = !bSplitError;
2592
2593 //To avoid oscillations the Follow must become valid now
2594 if ( GetFollow() )
2595 {
2596 // #i80924#
2597 // After a successful split assure that the first row
2598 // is invalid. When graphics are present, this isn't hold.
2599 // Note: defect i80924 could also be fixed, if it is
2600 // assured, that <SwLayNotify::bLowersComplete> is only
2601 // set, if all lower are valid *and* are correct laid out.
2602 if ( !bSplitError && GetFollow()->GetLower() )
2603 {
2605 }
2606 SwRectFnSet fnRectX(GetFollow());
2607
2608 static sal_uInt8 nStack = 0;
2609 if ( !StackHack::IsLocked() && nStack < 4 )
2610 {
2611 ++nStack;
2612 StackHack aHack;
2613 oAccess.reset();
2614
2615 GetFollow()->MakeAll(pRenderContext);
2616
2617 GetFollow()->SetLowersFormatted(false);
2618 // #i43913# - lock follow table
2619 // to avoid its formatting during the format of
2620 // its content.
2621 const bool bOldJoinLock = GetFollow()->IsJoinLocked();
2622 GetFollow()->LockJoin();
2623 ::lcl_RecalcRow(*static_cast<SwRowFrame*>(GetFollow()->Lower()),
2624 fnRectX.GetBottom(GetFollow()->GetUpper()->getFrameArea()) );
2625 // #i43913#
2626 // #i63632# Do not unlock the
2627 // follow if it wasn't locked before.
2628 if ( !bOldJoinLock )
2629 GetFollow()->UnlockJoin();
2630
2631 if ( !GetFollow()->GetFollow() )
2632 {
2633 SwFrame* pNxt = static_cast<SwFrame*>(GetFollow())->FindNext();
2634 if ( pNxt )
2635 {
2636 // #i18103# - no formatting of found next
2637 // frame, if it's a follow section of the
2638 // 'ColLocked' section, the follow table is
2639 // in.
2640 bool bCalcNxt = true;
2641 if ( GetFollow()->IsInSct() && pNxt->IsSctFrame() )
2642 {
2644 if ( pSct->IsColLocked() &&
2645 pSct->GetFollow() == pNxt )
2646 {
2647 bCalcNxt = false;
2648 }
2649 }
2650 if ( bCalcNxt )
2651 {
2652 // tdf#119109 follow was just formatted,
2653 // don't do it again now
2655 pNxt->Calc(pRenderContext);
2656 }
2657 }
2658 }
2659
2660 --nStack;
2661 }
2662 else if ( GetFollow() == GetNext() )
2663 GetFollow()->MoveFwd( true, false );
2664 }
2665 continue;
2666 }
2667 }
2668 }
2669
2670 // Set to false again as early as possible.
2671 bLastRowHasToMoveToFollow = false;
2672
2673 if( IsInSct() && bMovedFwd && bMakePage && GetUpper()->IsColBodyFrame() &&
2674 GetUpper()->GetUpper()->GetUpper()->IsSctFrame() &&
2675 ( GetUpper()->GetUpper()->GetPrev() || GetIndPrev() ) &&
2676 static_cast<SwSectionFrame*>(GetUpper()->GetUpper()->GetUpper())->MoveAllowed(this) )
2677 {
2678 bMovedFwd = false;
2679 }
2680
2681 // #i29771# Reset bTryToSplit flag on change of upper
2682 const SwFrame* pOldUpper = GetUpper();
2683
2684 //Let's see if we find some place anywhere...
2685 if (!bMovedFwd)
2686 {
2687 // don't make the effort to move fwd if its known
2688 // conditions that are known not to work
2689 if (IsInFootnote() && ForbiddenForFootnoteCntFwd())
2690 bMakePage = false;
2691 else if (!MoveFwd(bMakePage, false))
2692 bMakePage = false;
2693 }
2694
2695 // #i29771# Reset bSplitError flag on change of upper
2696 if ( GetUpper() != pOldUpper )
2697 {
2698 bTryToSplit = true;
2699 nUnSplitted = 5;
2700 }
2701
2702 aRectFnSet.Refresh(this);
2703 m_bCalcLowers = true;
2704 bMovedFwd = true;
2705 aNotify.SetLowersComplete( false );
2706 if ( IsFollow() )
2707 {
2708 // To avoid oscillations, master should not remain invalid
2709 SwTabFrame *pTab = FindMaster();
2710 if ( pTab->GetUpper() )
2711 pTab->GetUpper()->Calc(pRenderContext);
2712 pTab->Calc(pRenderContext);
2713 pTab->SetLowersFormatted( false );
2714 }
2715
2716 //If my neighbour is my Follow at the same time, I'll swallow it up.
2717 if ( ( GetNext() && GetNext() == GetFollow() ) || !GetLower() )
2718 {
2719 if ( HasFollowFlowLine() )
2721 if ( GetFollow() )
2722 Join();
2723 }
2724
2725 if ( bMovedBwd && GetUpper() )
2726 {
2727 //During flowing back the upper was animated to do a full repaint,
2728 //we can now skip this after the whole flowing back and forth.
2730 }
2731
2733 {
2734 // #i44910# - format of lower frames unnecessary
2735 // and can cause layout loops, if table doesn't fit and isn't
2736 // allowed to split.
2737 SwTwips nDistToUpperPrtBottom =
2738 aRectFnSet.BottomDist( getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
2739 if ( nDistToUpperPrtBottom >= 0 || bTryToSplit )
2740 {
2741 lcl_RecalcTable( *this, nullptr, aNotify );
2742 m_bLowersFormatted = true;
2743 m_bCalcLowers = false;
2744 if (!isFramePrintAreaValid())
2746 }
2747#if OSL_DEBUG_LEVEL > 0
2748 else
2749 {
2750 OSL_FAIL( "debug assertion: <SwTabFrame::MakeAll()> - format of table lowers suppressed by fix i44910" );
2751 }
2752#endif
2753 }
2754
2755 } //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
2756
2757 //If my direct predecessor is my master now, it can destroy me during the
2758 //next best opportunity.
2759 if ( IsFollow() )
2760 {
2761 SwFrame *pPre = GetPrev();
2762 if ( pPre && pPre->IsTabFrame() && static_cast<SwTabFrame*>(pPre)->GetFollow() == this)
2763 pPre->InvalidatePos();
2764 }
2765
2767 oAccess.reset();
2768 UnlockJoin();
2769 if ( bMovedFwd || bMovedBwd || !bOldValidPos )
2770 aNotify.SetInvaKeep();
2771}
2772
2773static bool IsNextOnSamePage(SwPageFrame const& rPage,
2774 SwTabFrame const& rTabFrame, SwTextFrame const& rAnchorFrame)
2775{
2776 for (SwContentFrame const* pContentFrame = rTabFrame.FindNextCnt();
2777 pContentFrame && pContentFrame->FindPageFrame() == &rPage;
2778 pContentFrame = pContentFrame->FindNextCnt())
2779 {
2780 if (pContentFrame == &rAnchorFrame)
2781 {
2782 return true;
2783 }
2784 }
2785 return false;
2786}
2787
2790 tools::Long& rLeftOffset,
2791 tools::Long& rRightOffset,
2792 SwTwips *const pSpaceBelowBottom) const
2793{
2794 bool bInvalidatePrtArea = false;
2795 const SwPageFrame *pPage = FindPageFrame();
2796 const SwFlyFrame* pMyFly = FindFlyFrame();
2797
2798 // --> #108724# Page header/footer content doesn't have to wrap around
2799 // floating screen objects
2800
2802 const bool bWrapAllowed = rIDSA.get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) ||
2803 ( !IsInFootnote() && nullptr == FindFooterOrHeader() );
2804
2805 if ( pPage->GetSortedObjs() && bWrapAllowed )
2806 {
2807 SwRectFnSet aRectFnSet(this);
2808 const bool bConsiderWrapOnObjPos = rIDSA.get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION);
2809 tools::Long nPrtPos = aRectFnSet.GetTop(getFrameArea());
2810 nPrtPos = aRectFnSet.YInc( nPrtPos, rUpper );
2811 SwRect aRect( getFrameArea() );
2812 if (pSpaceBelowBottom)
2813 { // set to space below table frame
2814 aRectFnSet.SetTopAndHeight(aRect, aRectFnSet.GetBottom(aRect), *pSpaceBelowBottom);
2815 }
2816 else
2817 {
2818 tools::Long nYDiff = aRectFnSet.YDiff( aRectFnSet.GetTop(getFramePrintArea()), rUpper );
2819 if (nYDiff > 0)
2820 aRectFnSet.AddBottom( aRect, -nYDiff );
2821 }
2822
2823 bool bAddVerticalFlyOffsets = rIDSA.get(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS);
2824
2825 for ( size_t i = 0; i < pPage->GetSortedObjs()->size(); ++i )
2826 {
2827 SwAnchoredObject* pAnchoredObj = (*pPage->GetSortedObjs())[i];
2828 if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
2829 {
2830 const SwRect aFlyRect = pFly->GetObjRectWithSpaces();
2831 // #i26945# - correction of conditions,
2832 // if Writer fly frame has to be considered:
2833 // - no need to check, if top of Writer fly frame differs
2834 // from FAR_AWAY, because it's also checked, if the Writer
2835 // fly frame rectangle overlaps with <aRect>
2836 // - no check, if bottom of anchor frame is prior the top of
2837 // the table, because Writer fly frames can be negative positioned.
2838 // - correct check, if the Writer fly frame is a lower of the
2839 // table, because table lines/rows can split and an at-character
2840 // anchored Writer fly frame could be positioned in the follow
2841 // flow line.
2842 // - add condition, that an existing anchor character text frame
2843 // has to be on the same page as the table.
2844 // E.g., it could happen, that the fly frame is still registered
2845 // at the page frame, the table is on, but it's anchor character
2846 // text frame has already changed its page.
2847 const SwTextFrame* pAnchorCharFrame = pFly->FindAnchorCharFrame();
2848 bool bConsiderFly =
2849 // #i46807# - do not consider invalid
2850 // Writer fly frames.
2851 (pFly->isFrameAreaDefinitionValid() || bAddVerticalFlyOffsets) &&
2852 // fly anchored at character or at paragraph
2853 pFly->IsFlyAtContentFrame() &&
2854 // fly overlaps with corresponding table rectangle
2855 aFlyRect.Overlaps( aRect ) &&
2856 // fly isn't lower of table and
2857 // anchor character frame of fly isn't lower of table
2858 (pSpaceBelowBottom // not if in ShouldBwdMoved
2859 || (!IsAnLower( pFly ) &&
2860 (!pAnchorCharFrame || !IsAnLower(pAnchorCharFrame)))) &&
2861 // table isn't lower of fly
2862 !pFly->IsAnLower( this ) &&
2863 // fly is lower of fly, the table is in
2864 // #123274# - correction
2865 // assure that fly isn't a lower of a fly, the table isn't in.
2866 // E.g., a table in the body doesn't wrap around a graphic,
2867 // which is inside a frame.
2868 ( ( !pMyFly ||
2869 pMyFly->IsAnLower( pFly ) ) &&
2870 pMyFly == pFly->GetAnchorFrameContainingAnchPos()->FindFlyFrame() ) &&
2871 // anchor frame not on following page
2872 pPage->GetPhyPageNum() >=
2873 pFly->GetAnchorFrame()->FindPageFrame()->GetPhyPageNum() &&
2874 // anchor character text frame on same page
2875 ( !pAnchorCharFrame ||
2876 pAnchorCharFrame->FindPageFrame()->GetPhyPageNum() ==
2877 pPage->GetPhyPageNum() );
2878
2879 if ( bConsiderFly )
2880 {
2881 const SwFrame* pFlyHeaderFooterFrame = pFly->GetAnchorFrame()->FindFooterOrHeader();
2882 const SwFrame* pThisHeaderFooterFrame = FindFooterOrHeader();
2883
2884 if ( pFlyHeaderFooterFrame != pThisHeaderFooterFrame &&
2885 // #148493# If bConsiderWrapOnObjPos is set,
2886 // we want to consider the fly if it is located in the header and
2887 // the table is located in the body:
2888 ( !bConsiderWrapOnObjPos || nullptr != pThisHeaderFooterFrame || !pFlyHeaderFooterFrame->IsHeaderFrame() ) )
2889 bConsiderFly = false;
2890 }
2891
2892 if ( bConsiderFly )
2893 {
2894 const SwFormatSurround &rSur = pFly->GetFormat()->GetSurround();
2895 const SwFormatHoriOrient &rHori= pFly->GetFormat()->GetHoriOrient();
2896 bool bShiftDown = css::text::WrapTextMode_NONE == rSur.GetSurround();
2897 if (!bShiftDown && bAddVerticalFlyOffsets)
2898 {
2899 if (rSur.GetSurround() == text::WrapTextMode_PARALLEL
2901 {
2902 // We know that wrapping was requested and the table frame overlaps with
2903 // the fly frame. Check if the print area overlaps with the fly frame as
2904 // well (in case the table does not use all the available width).
2905 basegfx::B1DRange aTabRange(
2906 aRectFnSet.GetLeft(aRect) + aRectFnSet.GetLeft(getFramePrintArea()),
2907 aRectFnSet.GetLeft(aRect) + aRectFnSet.GetLeft(getFramePrintArea())
2908 + aRectFnSet.GetWidth(getFramePrintArea()));
2909
2910 // Ignore spacing when determining the left/right edge of the fly, like
2911 // Word does.
2912 const SwRect aFlyRectWithoutSpaces = pFly->GetObjRect();
2913 basegfx::B1DRange aFlyRange(aRectFnSet.GetLeft(aFlyRectWithoutSpaces),
2914 aRectFnSet.GetRight(aFlyRectWithoutSpaces));
2915
2916 // If it does, shift the table down. Do this only in the compat case,
2917 // normally an SwFlyPortion is created instead that increases the height
2918 // of the first table row.
2919 bShiftDown = aTabRange.overlaps(aFlyRange);
2920 }
2921 }
2922
2923 if (bShiftDown)
2924 {
2925 // possible cases:
2926 // both in body
2927 // both in same fly
2928 // any comb. of body, footnote, header/footer
2929 // to keep it safe, check only in doc body vs page margin for now
2930 tools::Long nBottom = aRectFnSet.GetBottom(aFlyRect);
2931 // tdf#138039 don't grow beyond the page body
2932 // if the fly is anchored below the table; the fly
2933 // must move with its anchor frame to the next page
2934 SwRectFnSet fnPage(pPage);
2935 if (!IsInDocBody() // TODO
2936 || fnPage.YDiff(fnPage.GetBottom(aFlyRect), fnPage.GetPrtBottom(*pPage)) <= 0
2937 || !IsNextOnSamePage(*pPage, *this,
2938 *static_cast<SwTextFrame*>(pFly->GetAnchorFrameContainingAnchPos())))
2939 {
2940 if (aRectFnSet.YDiff( nPrtPos, nBottom ) < 0)
2941 nPrtPos = nBottom;
2942 // tdf#116501 subtract flys blocking space from below
2943 // TODO this may not work ideally for multiple flys
2944 if (pSpaceBelowBottom
2945 && aRectFnSet.YDiff(aRectFnSet.GetBottom(aRect), nBottom) < 0)
2946 {
2947 if (aRectFnSet.YDiff(aRectFnSet.GetTop(aRect), aRectFnSet.GetTop(aFlyRect)) < 0)
2948 {
2949 aRectFnSet.SetBottom(aRect, aRectFnSet.GetTop(aFlyRect));
2950 }
2951 else
2952 {
2953 aRectFnSet.SetHeight(aRect, 0);
2954 }
2955 }
2956 bInvalidatePrtArea = true;
2957 }
2958 }
2959 if ( (css::text::WrapTextMode_RIGHT == rSur.GetSurround() ||
2960 css::text::WrapTextMode_PARALLEL == rSur.GetSurround())&&
2961 text::HoriOrientation::LEFT == rHori.GetHoriOrient() )
2962 {
2963 const tools::Long nWidth = aRectFnSet.XDiff(
2964 aRectFnSet.GetRight(aFlyRect),
2965 aRectFnSet.GetLeft(pFly->GetAnchorFrame()->getFrameArea()) );
2966 rLeftOffset = std::max( rLeftOffset, nWidth );
2967 bInvalidatePrtArea = true;
2968 }
2969 if ( (css::text::WrapTextMode_LEFT == rSur.GetSurround() ||
2970 css::text::WrapTextMode_PARALLEL == rSur.GetSurround())&&
2971 text::HoriOrientation::RIGHT == rHori.GetHoriOrient() )
2972 {
2973 const tools::Long nWidth = aRectFnSet.XDiff(
2974 aRectFnSet.GetRight(pFly->GetAnchorFrame()->getFrameArea()),
2975 aRectFnSet.GetLeft(aFlyRect) );
2976 rRightOffset = std::max( rRightOffset, nWidth );
2977 bInvalidatePrtArea = true;
2978 }
2979 }
2980 }
2981 }
2982 rUpper = aRectFnSet.YDiff( nPrtPos, aRectFnSet.GetTop(getFrameArea()) );
2983 if (pSpaceBelowBottom)
2984 {
2985 *pSpaceBelowBottom = aRectFnSet.GetHeight(aRect);
2986 }
2987 }
2988
2989 return bInvalidatePrtArea;
2990}
2991
2994void SwTabFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
2995{
2996 OSL_ENSURE( pAttrs, "TabFrame::Format, pAttrs is 0." );
2997
2998 SwRectFnSet aRectFnSet(this);
2999 if ( !isFrameAreaSizeValid() )
3000 {
3001 tools::Long nDiff = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()) -
3002 aRectFnSet.GetWidth(getFrameArea());
3003 if( nDiff )
3004 {
3006 aRectFnSet.AddRight( aFrm, nDiff );
3007 }
3008 }
3009
3010 //VarSize is always the height.
3011 //For the upper/lower margins the same rules apply as for ContentFrames (see
3012 //MakePrtArea() of those).
3013
3014 SwTwips nUpper = CalcUpperSpace( pAttrs );
3015
3016 // We want to dodge the flys. Two possibilities:
3017 // 1. There are flys with SurroundNone, dodge them completely
3018 // 2. There are flys which only wrap on the right or the left side and
3019 // those are right or left aligned, those set the minimum for the margins
3020 tools::Long nTmpRight = -1000000,
3021 nLeftOffset = 0;
3022 if (CalcFlyOffsets(nUpper, nLeftOffset, nTmpRight, nullptr))
3023 {
3025 }
3026
3027 tools::Long nRightOffset = std::max( tools::Long(0), nTmpRight );
3028
3029 SwTwips nLower = pAttrs->CalcBottomLine();
3030 // #i29550#
3031 if ( IsCollapsingBorders() )
3032 nLower += GetBottomLineSize();
3033
3034 if ( !isFramePrintAreaValid() )
3035 {
3037
3038 // The width of the PrintArea is given by the FrameFormat, the margins
3039 // have to be set accordingly.
3040 // Minimum margins are determined depending on borders and shadows.
3041 // The margins are set so that the PrintArea is aligned into the
3042 // Frame according to the adjustment.
3043 // If the adjustment is 0, the margins are set according to the border
3044 // attributes.
3045
3046 const SwTwips nOldHeight = aRectFnSet.GetHeight(getFramePrintArea());
3047 const SwTwips nMax = aRectFnSet.GetWidth(getFrameArea());
3048
3049 // OD 14.03.2003 #i9040# - adjust variable names.
3050 const SwTwips nLeftLine = pAttrs->CalcLeftLine();
3051 const SwTwips nRightLine = pAttrs->CalcRightLine();
3052
3053 // The width possibly is a percentage value. If the table is inside
3054 // something else, the value refers to the environment. If it's in the
3055 // body then in the BrowseView the value refers to the screen width.
3056 const SwFormatFrameSize &rSz = GetFormat()->GetFrameSize();
3057 // OD 14.03.2003 #i9040# - adjust variable name.
3058 const SwTwips nWishedTableWidth = CalcRel( rSz );
3059
3060 bool bCheckBrowseWidth = false;
3061
3062 // OD 14.03.2003 #i9040# - insert new variables for left/right spacing.
3063 SwTwips nLeftSpacing = 0;
3064 SwTwips nRightSpacing = 0;
3065 switch ( GetFormat()->GetHoriOrient().GetHoriOrient() )
3066 {
3067 case text::HoriOrientation::LEFT:
3068 {
3069 // left indent:
3070 nLeftSpacing = nLeftLine + nLeftOffset;
3071 // OD 06.03.2003 #i9040# - correct calculation of right indent:
3072 // - Consider right indent given by right line attributes.
3073 // - Consider negative right indent.
3074 // wished right indent determined by wished table width and
3075 // left offset given by surround fly frames on the left:
3076 const SwTwips nWishRight = nMax - nWishedTableWidth - nLeftOffset;
3077 if ( nRightOffset > 0 )
3078 {
3079 // surrounding fly frames on the right
3080 // -> right indent is maximum of given right offset
3081 // and wished right offset.
3082 nRightSpacing = nRightLine + std::max( SwTwips(nRightOffset), nWishRight );
3083 }
3084 else
3085 {
3086 // no surrounding fly frames on the right
3087 // If intrinsic right indent (intrinsic means not considering
3088 // determined left indent) is negative,
3089 // then hold this intrinsic indent,
3090 // otherwise non negative wished right indent is hold.
3091 nRightSpacing = nRightLine +
3092 ( ( (nWishRight+nLeftOffset) < 0 ) ?
3093 (nWishRight+nLeftOffset) :
3094 std::max( SwTwips(0), nWishRight ) );
3095 }
3096 }
3097 break;
3098 case text::HoriOrientation::RIGHT:
3099 {
3100 // right indent:
3101 nRightSpacing = nRightLine + nRightOffset;
3102 // OD 06.03.2003 #i9040# - correct calculation of left indent:
3103 // - Consider left indent given by left line attributes.
3104 // - Consider negative left indent.
3105 // wished left indent determined by wished table width and
3106 // right offset given by surrounding fly frames on the right:
3107 const SwTwips nWishLeft = nMax - nWishedTableWidth - nRightOffset;
3108 if ( nLeftOffset > 0 )
3109 {
3110 // surrounding fly frames on the left
3111 // -> right indent is maximum of given left offset
3112 // and wished left offset.
3113 nLeftSpacing = nLeftLine + std::max( SwTwips(nLeftOffset), nWishLeft );
3114 }
3115 else
3116 {
3117 // no surrounding fly frames on the left
3118 // If intrinsic left indent (intrinsic = not considering
3119 // determined right indent) is negative,
3120 // then hold this intrinsic indent,
3121 // otherwise non negative wished left indent is hold.
3122 nLeftSpacing = nLeftLine +
3123 ( ( (nWishLeft+nRightOffset) < 0 ) ?
3124 (nWishLeft+nRightOffset) :
3125 std::max( SwTwips(0), nWishLeft ) );
3126 }
3127 }
3128 break;
3129 case text::HoriOrientation::CENTER:
3130 {
3131 // OD 07.03.2003 #i9040# - consider left/right line attribute.
3132 const SwTwips nCenterSpacing = ( nMax - nWishedTableWidth ) / 2;
3133 nLeftSpacing = nLeftLine +
3134 ( (nLeftOffset > 0) ?
3135 std::max( nCenterSpacing, SwTwips(nLeftOffset) ) :
3136 nCenterSpacing );
3137 nRightSpacing = nRightLine +
3138 ( (nRightOffset > 0) ?
3139 std::max( nCenterSpacing, SwTwips(nRightOffset) ) :
3140 nCenterSpacing );
3141 }
3142 break;
3143 case text::HoriOrientation::FULL:
3144 //This things grows over the whole width.
3145 //Only the free space needed for the border is taken into
3146 //account. The attribute values of LRSpace are ignored
3147 //intentionally.
3148 bCheckBrowseWidth = true;
3149 nLeftSpacing = nLeftLine + nLeftOffset;
3150 nRightSpacing = nRightLine + nRightOffset;
3151 break;
3153 {
3154 // The margins are defined by the LRSpace attribute.
3155 nLeftSpacing = pAttrs->CalcLeft( this );
3156 if( nLeftOffset )
3157 {
3158 // OD 07.03.2003 #i9040# - surround fly frames only, if
3159 // they overlap with the table.
3160 // Thus, take maximum of left spacing and left offset.
3161 // OD 10.03.2003 #i9040# - consider left line attribute.
3162 nLeftSpacing = std::max( nLeftSpacing, SwTwips( nLeftOffset + nLeftLine ) );
3163 }
3164 // OD 23.01.2003 #106895# - add 1st param to <SwBorderAttrs::CalcRight(..)>
3165 nRightSpacing = pAttrs->CalcRight( this );
3166 if( nRightOffset )
3167 {
3168 // OD 07.03.2003 #i9040# - surround fly frames only, if
3169 // they overlap with the table.
3170 // Thus, take maximum of right spacing and right offset.
3171 // OD 10.03.2003 #i9040# - consider right line attribute.
3172 nRightSpacing = std::max( nRightSpacing, SwTwips( nRightOffset + nRightLine ) );
3173 }
3174 }
3175 break;
3176 case text::HoriOrientation::LEFT_AND_WIDTH:
3177 {
3178 // count left border and width (Word specialty)
3179 // OD 10.03.2003 #i9040# - no width alignment in online mode.
3180 //bCheckBrowseWidth = true;
3181 nLeftSpacing = pAttrs->CalcLeft( this );
3182 if( nLeftOffset )
3183 {
3184 // OD 10.03.2003 #i9040# - surround fly frames only, if
3185 // they overlap with the table.
3186 // Thus, take maximum of right spacing and right offset.
3187 // OD 10.03.2003 #i9040# - consider left line attribute.
3188 nLeftSpacing = std::max( nLeftSpacing, SwTwips( pAttrs->CalcLeftLine() + nLeftOffset ) );
3189 }
3190 // OD 10.03.2003 #i9040# - consider right and left line attribute.
3191 const SwTwips nWishRight =
3192 nMax - (nLeftSpacing-pAttrs->CalcLeftLine()) - nWishedTableWidth;
3193 nRightSpacing = nRightLine +
3194 ( (nRightOffset > 0) ?
3195 std::max( nWishRight, SwTwips(nRightOffset) ) :
3196 nWishRight );
3197 }
3198 break;
3199 default:
3200 OSL_FAIL( "Invalid orientation for table." );
3201 }
3202
3203 // #i26250# - extend bottom printing area, if table
3204 // is last content inside a table cell.
3205 if ( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS) &&
3206 GetUpper()->IsInTab() && !GetIndNext() )
3207 {
3208 nLower += pAttrs->GetULSpace().GetLower();
3209 }
3210 aRectFnSet.SetYMargins( *this, nUpper, nLower );
3211 if( (nMax - MINLAY) < (nLeftSpacing + nRightSpacing) )
3212 aRectFnSet.SetXMargins( *this, 0, 0 );
3213 else
3214 aRectFnSet.SetXMargins( *this, nLeftSpacing, nRightSpacing );
3215
3217 if ( bCheckBrowseWidth &&
3218 pSh && pSh->GetViewOptions()->getBrowseMode() &&
3219 GetUpper()->IsPageBodyFrame() && // only PageBodyFrames and not ColBodyFrames
3220 pSh->VisArea().Width() )
3221 {
3222 //Don't go beyond the edge of the visible area.
3223 //The page width can be bigger because objects with
3224 //"over-size" are possible (RootFrame::ImplCalcBrowseWidth())
3225 tools::Long nWidth = pSh->GetBrowseWidth();
3226 nWidth -= getFramePrintArea().Left();
3227 nWidth -= pAttrs->CalcRightLine();
3228
3230 aPrt.Width( std::min( nWidth, aPrt.Width() ) );
3231 }
3232
3233 if ( nOldHeight != aRectFnSet.GetHeight(getFramePrintArea()) )
3234 {
3235 setFrameAreaSizeValid(false);
3236 }
3237 }
3238
3239 if ( isFrameAreaSizeValid() )
3240 return;
3241
3243
3244 // The size is defined by the content plus the margins.
3245 SwTwips nRemaining = 0, nDiff;
3246 SwFrame *pFrame = m_pLower;
3247 while ( pFrame )
3248 {
3249 nRemaining += aRectFnSet.GetHeight(pFrame->getFrameArea());
3250 pFrame = pFrame->GetNext();
3251 }
3252 // And now add the margins
3253 nRemaining += nUpper + nLower;
3254
3255 nDiff = aRectFnSet.GetHeight(getFrameArea()) - nRemaining;
3256 if ( nDiff > 0 )
3257 Shrink( nDiff );
3258 else if ( nDiff < 0 )
3259 Grow( -nDiff );
3260}
3261
3262SwTwips SwTabFrame::GrowFrame( SwTwips nDist, bool bTst, bool bInfo )
3263{
3264 SwRectFnSet aRectFnSet(this);
3265 SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
3266 if( nHeight > 0 && nDist > ( LONG_MAX - nHeight ) )
3267 nDist = LONG_MAX - nHeight;
3268
3269 if ( bTst && !IsRestrictTableGrowth() )
3270 return nDist;
3271
3272 if ( GetUpper() )
3273 {
3274 //The upper only grows as far as needed. nReal provides the distance
3275 //which is already available.
3276 SwTwips nReal = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea());
3277 SwFrame *pFrame = GetUpper()->Lower();
3278 while ( pFrame && GetFollow() != pFrame )
3279 {
3280 nReal -= aRectFnSet.GetHeight(pFrame->getFrameArea());
3281 pFrame = pFrame->GetNext();
3282 }
3283
3284 if ( nReal < nDist )
3285 {
3286 tools::Long nTmp = GetUpper()->Grow( nDist - std::max<tools::Long>(nReal, 0), bTst, bInfo );
3287
3288 if ( IsRestrictTableGrowth() )
3289 {
3290 nTmp = std::min( tools::Long(nDist), nReal + nTmp );
3291 nDist = nTmp < 0 ? 0 : nTmp;
3292 }
3293 }
3294
3295 if ( !bTst )
3296 {
3297 {
3299 aRectFnSet.AddBottom( aFrm, nDist );
3300 }
3301
3302#if !ENABLE_WASM_STRIP_ACCESSIBILITY
3303 SwRootFrame *pRootFrame = getRootFrame();
3304 if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
3305 pRootFrame->GetCurrShell() )
3306 {
3307 SwRect aOldFrame( getFrameArea() );
3308 pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( this, aOldFrame );
3309 }
3310#endif
3311 }
3312 }
3313
3314 if ( !bTst && ( nDist || IsRestrictTableGrowth() ) )
3315 {
3316 SwPageFrame *pPage = FindPageFrame();
3317 if ( GetNext() )
3318 {
3320 if ( GetNext()->IsContentFrame() )
3321 GetNext()->InvalidatePage( pPage );
3322 }
3323 // #i28701# - Due to the new object positioning the
3324 // frame on the next page/column can flow backward (e.g. it was moved
3325 // forward due to the positioning of its objects ). Thus, invalivate this
3326 // next frame, if document compatibility option 'Consider wrapping style
3327 // influence on object positioning' is ON.
3328 else if ( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
3329 {
3331 }
3333 InvalidatePage( pPage );
3334 SetComplete();
3335
3336 std::unique_ptr<SvxBrushItem> aBack = GetFormat()->makeBackgroundBrushItem();
3337 const SvxGraphicPosition ePos = aBack->GetGraphicPos();
3338 if ( GPOS_NONE != ePos && GPOS_TILED != ePos )
3340 }
3341
3342 return nDist;
3343}
3344
3345void SwTabFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
3346{
3347 if (rHint.GetId() != SfxHintId::SwLegacyModify)
3348 return;
3349 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
3351 bool bAttrSetChg = pLegacy->m_pNew && RES_ATTRSET_CHG == pLegacy->m_pNew->Which();
3352
3353 if(bAttrSetChg)
3354 {
3355 auto& rOldSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pOld);
3356 auto& rNewSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew);
3357 SfxItemIter aOIter(*rOldSetChg.GetChgSet());
3358 SfxItemIter aNIter(*rNewSetChg.GetChgSet());
3359 const SfxPoolItem* pOItem = aOIter.GetCurItem();
3360 const SfxPoolItem* pNItem = aNIter.GetCurItem();
3361 SwAttrSetChg aOldSet(rOldSetChg);
3362 SwAttrSetChg aNewSet(rNewSetChg);
3363 do
3364 {
3365 UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
3366 pNItem = aNIter.NextItem();
3367 pOItem = aOIter.NextItem();
3368 } while(pNItem);
3369 if(aOldSet.Count() || aNewSet.Count())
3370 SwLayoutFrame::SwClientNotify(rMod, sw::LegacyModifyHint(&aOldSet, &aNewSet));
3371 }
3372 else
3373 UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
3374
3375 if(eInvFlags == SwTabFrameInvFlags::NONE)
3376 return;
3377
3378 SwPageFrame* pPage = FindPageFrame();
3379 InvalidatePage(pPage);
3380 if(eInvFlags & SwTabFrameInvFlags::InvalidatePrt)
3382 if(eInvFlags & SwTabFrameInvFlags::InvalidatePos)
3384 SwFrame* pTmp = GetIndNext();
3385 if(nullptr != pTmp)
3386 {
3388 {
3389 pTmp->InvalidatePrt_();
3390 if(pTmp->IsContentFrame())
3391 pTmp->InvalidatePage(pPage);
3392 }
3394 pTmp->SetCompletePaint();
3395 }
3396 if(eInvFlags & SwTabFrameInvFlags::InvalidatePrevPrt && nullptr != (pTmp = GetPrev()))
3397 {
3398 pTmp->InvalidatePrt_();
3399 if(pTmp->IsContentFrame())
3400 pTmp->InvalidatePage( pPage );
3401 }
3403 {
3404 if(pPage && pPage->GetUpper() && !IsFollow())
3405 static_cast<SwRootFrame*>(pPage->GetUpper())->InvalidateBrowseWidth();
3406 }
3409}
3410
3411void SwTabFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
3412 SwTabFrameInvFlags &rInvFlags,
3413 SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
3414{
3415 bool bClear = true;
3416 const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
3417 switch( nWhich )
3418 {
3419 case RES_TBLHEADLINECHG:
3420 if ( IsFollow() )
3421 {
3422 // Delete remaining headlines:
3423 SwRowFrame* pLowerRow = nullptr;
3424 while ( nullptr != ( pLowerRow = static_cast<SwRowFrame*>(Lower()) ) && pLowerRow->IsRepeatedHeadline() )
3425 {
3426 pLowerRow->Cut();
3427 SwFrame::DestroyFrame(pLowerRow);
3428 }
3429
3430 // insert new headlines
3431 const sal_uInt16 nNewRepeat = GetTable()->GetRowsToRepeat();
3432 auto& rLines = GetTable()->GetTabLines();
3433 for ( sal_uInt16 nIdx = 0; nIdx < nNewRepeat; ++nIdx )
3434 {
3435 SwRowFrame* pHeadline = new SwRowFrame(*rLines[nIdx], this);
3436 {
3437 sw::FlyCreationSuppressor aSuppressor;
3438 pHeadline->SetRepeatedHeadline(true);
3439 }
3440 pHeadline->Paste( this, pLowerRow );
3441 }
3442 }
3444 break;
3445
3446 case RES_FRM_SIZE:
3447 case RES_HORI_ORIENT:
3449 break;
3450
3451 case RES_PAGEDESC: //Attribute changes (on/off)
3452 if ( IsInDocBody() )
3453 {
3455 SwPageFrame *pPage = FindPageFrame();
3456 if (pPage)
3457 {
3458 if ( !GetPrev() )
3459 CheckPageDescs( pPage );
3460 if (GetFormat()->GetPageDesc().GetNumOffset())
3461 static_cast<SwRootFrame*>(pPage->GetUpper())->SetVirtPageNum( true );
3462 SwDocPosUpdate aMsgHint( pPage->getFrameArea().Top() );
3464 }
3465 }
3466 break;
3467
3468 case RES_BREAK:
3470 break;
3471
3472 case RES_LAYOUT_SPLIT:
3473 if ( !IsFollow() )
3475 break;
3476 case RES_FRAMEDIR :
3477 SetDerivedR2L( false );
3479 break;
3483 break;
3484 case RES_UL_SPACE:
3486 [[fallthrough]];
3487
3488 default:
3489 bClear = false;
3490 }
3491 if ( !bClear )
3492 return;
3493
3494 if ( pOldSet || pNewSet )
3495 {
3496 if ( pOldSet )
3497 pOldSet->ClearItem( nWhich );
3498 if ( pNewSet )
3499 pNewSet->ClearItem( nWhich );
3500 }
3501 else
3502 {
3503 SwModify aMod;
3505 }
3506}
3507
3509{
3510 if ( RES_VIRTPAGENUM_INFO == rHint.Which() && IsInDocBody() && !IsFollow() )
3511 {
3512 SwVirtPageNumInfo &rInfo = static_cast<SwVirtPageNumInfo&>(rHint);
3513 const SwPageFrame *pPage = FindPageFrame();
3514 if ( pPage )
3515 {
3516 if ( pPage == rInfo.GetOrigPage() && !GetPrev() )
3517 {
3518 // Should be the one (can temporarily be different, should we be
3519 // concerned about this possibility?)
3520 rInfo.SetInfo( pPage, this );
3521 return false;
3522 }
3523 if ( pPage->GetPhyPageNum() < rInfo.GetOrigPage()->GetPhyPageNum() &&
3524 (!rInfo.GetPage() || pPage->GetPhyPageNum() > rInfo.GetPage()->GetPhyPageNum()))
3525 {
3526 //This could be the one.
3527 rInfo.SetInfo( pPage, this );
3528 }
3529 }
3530 }
3531 return true;
3532}
3533
3535{
3536 SwFrame *pRet = m_pLower;
3537
3538 while ( pRet && !pRet->IsContentFrame() )
3539 {
3540 SwFrame *pOld = pRet;
3541
3542 SwFrame *pTmp = pRet; // To skip empty section frames
3543 while ( pRet->GetNext() )
3544 {
3545 pRet = pRet->GetNext();
3546 if( !pRet->IsSctFrame() || static_cast<SwSectionFrame*>(pRet)->GetSection() )
3547 pTmp = pRet;
3548 }
3549 pRet = pTmp;
3550
3551 if ( pRet->GetLower() )
3552 pRet = pRet->GetLower();
3553 if ( pRet == pOld )
3554 {
3555 // Check all other columns if there is a column based section with
3556 // an empty last column at the end of the last cell - this is done
3557 // by SwSectionFrame::FindLastContent
3558 if( pRet->IsColBodyFrame() )
3559 {
3560#if OSL_DEBUG_LEVEL > 0
3561 SwSectionFrame* pSect = pRet->FindSctFrame();
3562 OSL_ENSURE( pSect, "Where does this column come from?");
3563 OSL_ENSURE( IsAnLower( pSect ), "Split cell?" );
3564#endif
3565 return pRet->FindSctFrame()->FindLastContent();
3566 }
3567
3568 // pRet may be a cell frame without a lower (cell has been split).
3569 // We have to find the last content the hard way:
3570
3571 OSL_ENSURE( pRet->IsCellFrame(), "SwTabFrame::FindLastContent failed" );
3572 const SwFrame* pRow = pRet->GetUpper();
3573 while ( pRow && !pRow->GetUpper()->IsTabFrame() )
3574 pRow = pRow->GetUpper();
3575 const SwContentFrame* pContentFrame = pRow ? static_cast<const SwLayoutFrame*>(pRow)->ContainsContent() : nullptr;
3576 pRet = nullptr;
3577
3578 while ( pContentFrame && static_cast<const SwLayoutFrame*>(pRow)->IsAnLower( pContentFrame ) )
3579 {
3580 pRet = const_cast<SwContentFrame*>(pContentFrame);
3581 pContentFrame = pContentFrame->GetNextContentFrame();
3582 }
3583 }
3584 }
3585
3586 // #112929# There actually is a situation, which results in pRet = 0:
3587 // Insert frame, insert table via text <-> table. This gives you a frame
3588 // containing a table without any other content frames. Split the table
3589 // and undo the splitting. This operation gives us a table frame without
3590 // a lower.
3591 if ( pRet )
3592 {
3593 while ( pRet->GetNext() )
3594 pRet = pRet->GetNext();
3595
3596 if (pRet->IsSctFrame())
3597 pRet = static_cast<SwSectionFrame*>(pRet)->FindLastContent();
3598 }
3599
3600 assert(pRet == nullptr || dynamic_cast<SwContentFrame*>(pRet) || dynamic_cast<SwTabFrame*>(pRet));
3601 return pRet;
3602}
3603
3605{
3607
3608 while (pRet && pRet->IsTabFrame()) // possibly there's only tables here!
3609 { // tdf#126138 skip table, don't look inside
3610 pRet = pRet->GetPrev();
3611 }
3612
3613 assert(pRet == nullptr || dynamic_cast<SwContentFrame*>(pRet));
3614 return static_cast<SwContentFrame*>(pRet);
3615}
3616
3618bool SwTabFrame::ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool &rReformat )
3619{
3620 rReformat = false;
3621 if ( SwFlowFrame::IsMoveBwdJump() || !IsPrevObjMove() )
3622 {
3623 //Flowing back Frames is quite time consuming unfortunately.
3624 //Most often the location where the Frame wants to flow to has the same
3625 //FixSize as the Frame itself. In such a situation it's easy to check if
3626 //the Frame will find enough space for its VarSize, if this is not the
3627 //case, the relocation can be skipped.
3628 //Checking if the Frame will find enough space is done by the Frame itself,
3629 //this also takes the possibility of splitting the Frame into account.
3630 //If the FixSize is different or Flys are involved (at the old or the
3631 //new position) the checks are pointless, the Frame then
3632 //needs to be relocated tentatively (if a bit of space is available).
3633
3634 //The FixSize of the environments which contain tables is always the
3635 //width.
3636
3637 SwPageFrame *pOldPage = FindPageFrame(),
3638 *pNewPage = pNewUpper->FindPageFrame();
3639 bool bMoveAnyway = false;
3640 SwTwips nSpace = 0;
3641
3642 SwRectFnSet aRectFnSet(this);
3643 if ( !SwFlowFrame::IsMoveBwdJump() )
3644 {
3645
3646 tools::Long nOldWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
3647 SwRectFnSet fnRectX(pNewUpper);
3648 tools::Long nNewWidth = fnRectX.GetWidth(pNewUpper->getFramePrintArea());
3649 if( std::abs( nNewWidth - nOldWidth ) < 2 )
3650 {
3651 bMoveAnyway = BwdMoveNecessary( pOldPage, getFrameArea() ) > 1;
3652 if( !bMoveAnyway )
3653 {
3654 SwRect aRect( pNewUpper->getFramePrintArea() );
3655 aRect.Pos() += pNewUpper->getFrameArea().Pos();
3656 const SwFrame *pPrevFrame = pNewUpper->Lower();
3657 while ( pPrevFrame && pPrevFrame != this )
3658 {
3659 fnRectX.SetTop( aRect, fnRectX.GetBottom(pPrevFrame->getFrameArea()) );
3660 pPrevFrame = pPrevFrame->GetNext();
3661 }
3662 bMoveAnyway = BwdMoveNecessary( pNewPage, aRect) > 1;
3663
3664 // #i54861# Due to changes made in PrepareMake,
3665 // the tabfrm may not have a correct position. Therefore
3666 // it is possible that pNewUpper->getFramePrintArea().Height == 0. In this
3667 // case the above calculation of nSpace might give wrong
3668 // results and we really do not want to MoveBackward into a
3669 // 0 height frame. If nTmpSpace is already <= 0, we take this
3670 // value:
3671 const SwTwips nTmpSpace = fnRectX.GetHeight(aRect);
3672 if ( fnRectX.GetHeight(pNewUpper->getFramePrintArea()) > 0 || nTmpSpace <= 0 )
3673 nSpace = nTmpSpace;
3674
3675 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
3676 if( pSh && pSh->GetViewOptions()->getBrowseMode() )
3677 nSpace += pNewUpper->Grow( LONG_MAX, true );
3678 if (0 < nSpace && GetPrecede())
3679 {
3680 SwTwips nUpperDummy(0);
3681 tools::Long nLeftOffsetDummy(0), nRightOffsetDummy(0);
3682 // tdf#116501 check for no-wrap fly overlap
3683 static_cast<const SwTabFrame*>(GetPrecede())->CalcFlyOffsets(
3684 nUpperDummy, nLeftOffsetDummy, nRightOffsetDummy, &nSpace);
3685 }
3686 }
3687 }
3688 else if (!m_bLockBackMove)
3689 bMoveAnyway = true;
3690 }
3691 else if (!m_bLockBackMove)
3692 bMoveAnyway = true;
3693
3694 if ( bMoveAnyway )
3695 {
3696 rReformat = true;
3697 return true;
3698 }
3699
3700 bool bFits = nSpace > 0;
3701 if (!bFits && aRectFnSet.GetHeight(getFrameArea()) == 0)
3702 // This frame fits into pNewUpper in case it has no space, but this
3703 // frame is empty.
3704 bFits = nSpace >= 0;
3705 if (!m_bLockBackMove && bFits)
3706 {
3707 // #i26945# - check, if follow flow line
3708 // contains frame, which are moved forward due to its object
3709 // positioning.
3710 const SwRowFrame* pFirstRow = GetFirstNonHeadlineRow();
3711 if ( pFirstRow && pFirstRow->IsInFollowFlowRow() &&
3713 *(pFirstRow->GetFormat()->GetDoc()),
3714 *pFirstRow ) )
3715 {
3716 return false;
3717 }
3719
3720 // For some mysterious reason, I changed the good old
3721 // 'return nHeight <= nSpace' to 'return nTmpHeight < nSpace'.
3722 // This obviously results in problems with table frames in
3723 // sections. Remember: Every twip is sacred.
3724 return nTmpHeight <= nSpace;
3725 }
3726 }
3727 return false;
3728}
3729
3731{
3732 OSL_ENSURE( GetUpper(), "Cut without Upper()." );
3733
3734 SwPageFrame *pPage = FindPageFrame();
3735 InvalidatePage( pPage );
3736 SwFrame *pFrame = GetNext();
3737 if( pFrame )
3738 {
3739 // Possibly the old follow calculated a spacing to the predecessor
3740 // which is obsolete now when it becomes the first frame
3741 pFrame->InvalidatePrt_();
3742 pFrame->InvalidatePos_();
3743 if ( pFrame->IsContentFrame() )
3744 pFrame->InvalidatePage( pPage );
3745 if( IsInSct() && !GetPrev() )
3746 {
3747 SwSectionFrame* pSct = FindSctFrame();
3748 if( !pSct->IsFollow() )
3749 {
3750 pSct->InvalidatePrt_();
3751 pSct->InvalidatePage( pPage );
3752 }
3753 }
3754 }
3755 else
3756 {
3758 //Someone has to do the retouch: predecessor or upper
3759 pFrame = GetPrev();
3760 if ( nullptr != pFrame )
3761 {
3762 pFrame->SetRetouche();
3764 pFrame->InvalidatePos_();
3765 if ( pFrame->IsContentFrame() )
3766 pFrame->InvalidatePage( pPage );
3767 }
3768 //If I am (was) the only FlowFrame in my own upper, it has to do
3769 //the retouch. Moreover a new empty page might be created.
3770 else
3771 { SwRootFrame *pRoot = static_cast<SwRootFrame*>(pPage->GetUpper());
3772 pRoot->SetSuperfluous();
3774 if( IsInSct() )
3775 {
3776 SwSectionFrame* pSct = FindSctFrame();
3777 if( !pSct->IsFollow() )
3778 {
3779 pSct->InvalidatePrt_();
3780 pSct->InvalidatePage( pPage );
3781 }
3782 }
3783 }
3784 }
3785
3786 //First remove, then shrink the upper.
3787 SwLayoutFrame *pUp = GetUpper();
3788 SwRectFnSet aRectFnSet(this);
3790 if ( pUp )
3791 {
3792 OSL_ENSURE( !pUp->IsFootnoteFrame(), "Table in Footnote." );
3793 SwSectionFrame *pSct = nullptr;
3794 // #126020# - adjust check for empty section
3795 // #130797# - correct fix #126020#
3796 if ( !pUp->Lower() && pUp->IsInSct() &&
3797 !(pSct = pUp->FindSctFrame())->ContainsContent() &&
3798 !pSct->ContainsAny( true ) )
3799 {
3800 if ( pUp->GetUpper() )
3801 {
3802 pSct->DelEmpty( false );
3803 pSct->InvalidateSize_();
3804 }
3805 }
3806 // table-in-footnote: delete empty footnote frames (like SwContentFrame::Cut)
3807 else if (!pUp->Lower() && pUp->IsFootnoteFrame() && !pUp->IsColLocked())
3808 {
3809 if (pUp->GetNext() && !pUp->GetPrev())
3810 {
3811 if (SwFrame *const pTmp = static_cast<SwLayoutFrame*>(pUp->GetNext())->ContainsAny())
3812 {
3813 pTmp->InvalidatePrt_();
3814 }
3815 }
3816 if (!pUp->IsDeleteForbidden())
3817 {
3818 pUp->Cut();
3820 }
3821 }
3822 else if( aRectFnSet.GetHeight(getFrameArea()) )
3823 {
3824 // OD 26.08.2003 #i18103# - *no* 'ColUnlock' of section -
3825 // undo changes of fix for #104992#
3826 pUp->Shrink( getFrameArea().Height() );
3827 }
3828 }
3829
3830
3831 if ( pPage && !IsFollow() && pPage->GetUpper() )
3832 static_cast<SwRootFrame*>(pPage->GetUpper())->InvalidateBrowseWidth();
3833}
3834
3835void SwTabFrame::Paste( SwFrame* pParent, SwFrame* pSibling )
3836{
3837 OSL_ENSURE( pParent, "No parent for pasting." );
3838 OSL_ENSURE( pParent->IsLayoutFrame(), "Parent is ContentFrame." );
3839 OSL_ENSURE( pParent != this, "I'm the parent myself." );
3840 OSL_ENSURE( pSibling != this, "I'm my own neighbour." );
3841 OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(),
3842 "I'm still registered somewhere." );
3843
3844 //Insert in the tree.
3845 InsertBefore( static_cast<SwLayoutFrame*>(pParent), pSibling );
3846
3848 SwPageFrame *pPage = FindPageFrame();
3849 InvalidatePage( pPage );
3850
3851 if ( GetNext() )
3852 {
3855 if ( GetNext()->IsContentFrame() )
3856 GetNext()->InvalidatePage( pPage );
3857 }
3858
3859 SwRectFnSet aRectFnSet(this);
3860 if( aRectFnSet.GetHeight(getFrameArea()) )
3861 pParent->Grow( aRectFnSet.GetHeight(getFrameArea()) );
3862
3863 if( aRectFnSet.GetWidth(getFrameArea()) != aRectFnSet.GetWidth(pParent->getFramePrintArea()) )
3865 if ( GetPrev() )
3866 {
3867 if ( !IsFollow() )
3868 {
3870 if ( GetPrev()->IsContentFrame() )
3871 GetPrev()->InvalidatePage( pPage );
3872 }
3873 }
3874 else if ( GetNext() )
3875 // Take the spacing into account when dealing with ContentFrames.
3876 // There are two situations (both always happen at the same time):
3877 // a) The Content becomes the first in a chain
3878 // b) The new follower was previously the first in a chain
3880
3881 if ( !pPage || IsFollow() )
3882 return;
3883
3884 if ( pPage->GetUpper() )
3885 static_cast<SwRootFrame*>(pPage->GetUpper())->InvalidateBrowseWidth();
3886
3887 if ( !GetPrev() )//At least needed for HTML with a table at the beginning.
3888 {
3889 const SwPageDesc *pDesc = GetFormat()->GetPageDesc().GetPageDesc();
3890 if ( (pDesc && pDesc != pPage->GetPageDesc()) ||
3891 (!pDesc && pPage->GetPageDesc() != &GetFormat()->GetDoc()->GetPageDesc(0)) )
3892 CheckPageDescs( pPage );
3893 }
3894}
3895
3896bool SwTabFrame::Prepare( const PrepareHint eHint, const void *, bool )
3897{
3898 if( PrepareHint::BossChanged == eHint )
3900 return false;
3901}
3902
3903SwRowFrame::SwRowFrame(const SwTableLine &rLine, SwFrame* pSib, bool bInsertContent)
3904 : SwLayoutFrame( rLine.GetFrameFormat(), pSib )
3905 , m_pTabLine( &rLine )
3906 , m_pFollowRow( nullptr )
3907 // #i29550#
3908 , mnTopMarginForLowers( 0 )
3909 , mnBottomMarginForLowers( 0 )
3910 , mnBottomLineSize( 0 )
3911 // --> split table rows
3912 , m_bIsFollowFlowRow( false )
3913 // <-- split table rows
3914 , m_bIsRepeatedHeadline( false )
3915 , m_bIsRowSpanLine( false )
3916 , m_bForceRowSplitAllowed( false )
3917 , m_bIsInSplit( false )
3918{
3920
3921 //Create the boxes and insert them.
3922 const SwTableBoxes &rBoxes = rLine.GetTabBoxes();
3923 SwFrame *pTmpPrev = nullptr;
3924 for ( size_t i = 0; i < rBoxes.size(); ++i )
3925 {
3926 SwCellFrame *pNew = new SwCellFrame( *rBoxes[i], this, bInsertContent );
3927 pNew->InsertBehind( this, pTmpPrev );
3928 pTmpPrev = pNew;
3929 }
3930}
3931
3933{
3934 sw::BroadcastingModify* pMod = GetFormat();
3935 if( pMod )
3936 {
3937 pMod->Remove( this );
3938 if( !pMod->HasWriterListeners() )
3939 delete pMod;
3940 }
3941
3943}
3944
3946{
3947}
3948
3950{
3951 ::RegistFlys( pPage ? pPage : FindPageFrame(), this );
3952}
3953
3955{
3956 SwTabFrame* pTab = FindTabFrame();
3957 if(pTab)
3958 {
3959 const bool bInFirstNonHeadlineRow = pTab->IsFollow() && this == pTab->GetFirstNonHeadlineRow();
3960 // #i35063#
3961 // Invalidation required is pRow is last row
3962 if(bInFirstNonHeadlineRow)
3963 pTab = pTab->FindMaster();
3964 if(bInFirstNonHeadlineRow || !GetNext())
3965 pTab->InvalidatePos();
3966 }
3967 const sw::BroadcastingModify aMod;
3969}
3970
3971void SwRowFrame::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
3972{
3973 if(auto pNewFormatHint = dynamic_cast<const sw::TableLineFormatChanged*>(&rHint))
3974 {
3975 if(GetTabLine() != &pNewFormatHint->m_rTabLine)
3976 return;
3977 RegisterToFormat(const_cast<SwTableLineFormat&>(pNewFormatHint->m_rNewFormat));
3982
3983 // #i35063#
3984 // consider 'split row allowed' attribute
3985 SwTabFrame* pTab = FindTabFrame();
3986 bool bInFollowFlowRow = false;
3987 const bool bInFirstNonHeadlineRow = pTab->IsFollow() && this == pTab->GetFirstNonHeadlineRow();
3988 if(bInFirstNonHeadlineRow ||
3989 !GetNext() ||
3990 (bInFollowFlowRow = IsInFollowFlowRow()) ||
3991 nullptr != IsInSplitTableRow() )
3992 {
3993 if(bInFirstNonHeadlineRow || bInFollowFlowRow)
3994 pTab = pTab->FindMaster();
3995
3997 pTab->InvalidatePos();
3998 }
3999 }
4000 else if(auto pMoveTableLineHint = dynamic_cast<const sw::MoveTableLineHint*>(&rHint))
4001 {
4002
4003 if(GetTabLine() != &pMoveTableLineHint->m_rTableLine)
4004 return;
4005 const_cast<SwFrameFormat*>(&pMoveTableLineHint->m_rNewFormat)->Add(this);
4006 InvalidateAll();
4008 return;
4009 }
4010 if (rHint.GetId() != SfxHintId::SwLegacyModify)
4011 return;
4012 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
4013 if(!pLegacy->m_pNew)
4014 {
4015 // possibly not needed?
4016 SwLayoutFrame::SwClientNotify(rModify, rHint);
4017 return;
4018 }
4019 switch(pLegacy->m_pNew->Which())
4020 {
4021 case RES_ATTRSET_CHG:
4022 {
4023 const SwAttrSet* pChgSet = static_cast<const SwAttrSetChg*>(pLegacy->m_pNew)->GetChgSet();
4024 const SfxPoolItem* pItem = nullptr;
4025 pChgSet->GetItemState(RES_FRM_SIZE, false, &pItem);
4026 if(!pItem)
4027 pChgSet->GetItemState(RES_ROW_SPLIT, false, &pItem);
4028 if(pItem)
4029 OnFrameSize(*pItem);
4030 else
4031 SwLayoutFrame::SwClientNotify(rModify, rHint); // possibly not needed?
4032 return;
4033 }
4034 case RES_FRM_SIZE:
4035 case RES_ROW_SPLIT:
4036 OnFrameSize(*static_cast<const SwFormatFrameSize*>(pLegacy->m_pNew));
4037 return;
4038 }
4039}
4040
4042{
4043 if ( !GetNext() )
4044 {
4045 setFrameAreaSizeValid(false);
4046 }
4047
4048 SwLayoutFrame::MakeAll(pRenderContext);
4049}
4050
4052{
4053 SwRectFnSet aRectFnSet(pFrame);
4054 tools::Long nHeight = 0;
4055 const SwFrame* pTmp = pFrame->IsSctFrame() ?
4056 static_cast<const SwSectionFrame*>(pFrame)->ContainsContent() : pFrame;
4057 while( pTmp )
4058 {
4059 // #i26945# - consider follow text frames
4060 const SwSortedObjs* pObjs( nullptr );
4061 bool bIsFollow( false );
4062 if ( pTmp->IsTextFrame() && static_cast<const SwTextFrame*>(pTmp)->IsFollow() )
4063 {
4064 const SwFrame* pMaster;
4065 // #i46450# Master does not necessarily have
4066 // to exist if this function is called from JoinFrame() ->
4067 // Cut() -> Shrink()
4068 const SwTextFrame* pTmpFrame = static_cast<const SwTextFrame*>(pTmp);
4069 if ( pTmpFrame->GetPrev() && pTmpFrame->GetPrev()->IsTextFrame() &&
4070 static_cast<const SwTextFrame*>(pTmpFrame->GetPrev())->GetFollow() &&
4071 static_cast<const SwTextFrame*>(pTmpFrame->GetPrev())->GetFollow() != pTmp )
4072 pMaster = nullptr;
4073 else
4074 pMaster = pTmpFrame->FindMaster();
4075
4076 if ( pMaster )
4077 {
4078 pObjs = static_cast<const SwTextFrame*>(pTmp)->FindMaster()->GetDrawObjs();
4079 bIsFollow = true;
4080 }
4081 }
4082 else
4083 {
4084 pObjs = pTmp->GetDrawObjs();
4085 }
4086 if ( pObjs )
4087 {
4088 for (SwAnchoredObject* pAnchoredObj : *pObjs)
4089 {
4090 // #i26945# - if <pTmp> is follow, the
4091 // anchor character frame has to be <pTmp>.
4092 if ( bIsFollow &&
4093 pAnchoredObj->FindAnchorCharFrame() != pTmp )
4094 {
4095 continue;
4096 }
4097 // #i26945# - consider also drawing objects
4098 {
4099 // OD 30.09.2003 #i18732# - only objects, which follow
4100 // the text flow have to be considered.
4101 const SwFrameFormat& rFrameFormat = pAnchoredObj->GetFrameFormat();
4102 bool bFollowTextFlow = rFrameFormat.GetFollowTextFlow().GetValue();
4103 bool bIsFarAway = pAnchoredObj->GetObjRect().Top() != FAR_AWAY;
4104 const SwPageFrame* pPageFrm = pTmp->FindPageFrame();
4105 bool bIsAnchoredToTmpFrm = false;
4106 if ( pPageFrm && pPageFrm->IsPageFrame() && pAnchoredObj->GetPageFrame())
4107 bIsAnchoredToTmpFrm = pAnchoredObj->GetPageFrame() == pPageFrm ||
4108 (pPageFrm->GetFormatPage().GetPhyPageNum() == pAnchoredObj->GetPageFrame()->GetFormatPage().GetPhyPageNum() + 1);
4109 const bool bConsiderObj =
4110 (rFrameFormat.GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) &&
4111 bIsFarAway &&
4112 bFollowTextFlow && bIsAnchoredToTmpFrm;
4113 bool bWrapThrough = rFrameFormat.GetSurround().GetValue() == text::WrapTextMode_THROUGH;
4114 bool bInBackground = !rFrameFormat.GetOpaque().GetValue();
4115 if (pFrame->IsInTab() && bFollowTextFlow && bWrapThrough && bInBackground)
4116 {
4117 // Ignore wrap-through objects when determining the cell height.
4118 // Normally FollowTextFlow requires a resize of the cell, but not in case of
4119 // wrap-through.
4120 continue;
4121 }
4122
4123 if ( bConsiderObj )
4124 {
4125 const SwFormatFrameSize &rSz = rFrameFormat.GetFrameSize();
4126 if( !rSz.GetHeightPercent() )
4127 {
4128 const SwTwips nDistOfFlyBottomToAnchorTop =
4129 aRectFnSet.GetHeight(pAnchoredObj->GetObjRect()) +
4130 ( aRectFnSet.IsVert() ?
4131 pAnchoredObj->GetCurrRelPos().X() :
4132 pAnchoredObj->GetCurrRelPos().Y() );
4133
4134 const SwTwips nFrameDiff =
4135 aRectFnSet.YDiff(
4136 aRectFnSet.GetTop(pTmp->getFrameArea()),
4137 aRectFnSet.GetTop(pFrame->getFrameArea()) );
4138
4139 nHeight = std::max( nHeight, nDistOfFlyBottomToAnchorTop + nFrameDiff -
4140 aRectFnSet.GetHeight(pFrame->getFrameArea()) );
4141
4142 // #i56115# The first height calculation
4143 // gives wrong results if pFrame->getFramePrintArea().Y() > 0. We do
4144 // a second calculation based on the actual rectangles of
4145 // pFrame and pAnchoredObj, and use the maximum of the results.
4146 // I do not want to remove the first calculation because
4147 // if clipping has been applied, using the GetCurrRelPos
4148 // might be the better option to calculate nHeight.
4149 const SwTwips nDistOfFlyBottomToAnchorTop2 = aRectFnSet.YDiff(
4150 aRectFnSet.GetBottom(pAnchoredObj->GetObjRect()),
4151 aRectFnSet.GetBottom(pFrame->getFrameArea()) );
4152
4153 nHeight = std::max( nHeight, tools::Long(nDistOfFlyBottomToAnchorTop2 ));
4154 }
4155 }
4156 }
4157 }
4158 }
4159 if( !pFrame->IsSctFrame() )
4160 break;
4161 pTmp = pTmp->FindNextCnt();
4162 if( !static_cast<const SwSectionFrame*>(pFrame)->IsAnLower( pTmp ) )
4163 break;
4164 }
4165 return nHeight;
4166}
4167
4169{
4170 const SwTabFrame* pTab = rCell.FindTabFrame();
4171 SwTwips nTopSpace = 0;
4172 SwTwips nBottomSpace = 0;
4173
4174 // #i29550#
4175 if ( pTab->IsCollapsingBorders() && rCell.Lower() && !rCell.Lower()->IsRowFrame() )
4176 {
4177 nTopSpace = static_cast<const SwRowFrame*>(rCell.GetUpper())->GetTopMarginForLowers();
4178 nBottomSpace = static_cast<const SwRowFrame*>(rCell.GetUpper())->GetBottomMarginForLowers();
4179 }
4180 else
4181 {
4182 if ( pTab->IsVertical() != rCell.IsVertical() )
4183 {
4184 nTopSpace = rAttrs.CalcLeft( &rCell );
4185 nBottomSpace = rAttrs.CalcRight( &rCell );
4186 }
4187 else
4188 {
4189 nTopSpace = rAttrs.CalcTop();
4190 nBottomSpace = rAttrs.CalcBottom();
4191 }
4192 }
4193
4194 return nTopSpace + nBottomSpace;
4195}
4196
4197// #i26945# - add parameter <_bConsiderObjs> in order to
4198// control, if floating screen objects have to be considered for the minimal
4199// cell height.
4201 const bool _bConsiderObjs,
4202 const SwBorderAttrs *pAttrs = nullptr )
4203{
4204 SwRectFnSet aRectFnSet(_pCell);
4205 SwTwips nHeight = 0;
4206 const SwFrame* pLow = _pCell->Lower();
4207 if ( pLow )
4208 {
4209 tools::Long nFlyAdd = 0;
4210 while ( pLow )
4211 {
4212 if ( pLow->IsRowFrame() )
4213 {
4214 // #i26945#
4215 nHeight += ::lcl_CalcMinRowHeight( static_cast<const SwRowFrame*>(pLow),
4216 _bConsiderObjs );
4217 }
4218 else
4219 {
4220 tools::Long nLowHeight = aRectFnSet.GetHeight(pLow->getFrameArea());
4221 nHeight += nLowHeight;
4222 // #i26945#
4223 if ( _bConsiderObjs )
4224 {
4225 nFlyAdd = std::max( tools::Long(0), nFlyAdd - nLowHeight );
4226 nFlyAdd = std::max( nFlyAdd, ::CalcHeightWithFlys( pLow ) );
4227 }
4228 }
4229
4230 pLow = pLow->GetNext();
4231 }
4232 if ( nFlyAdd )
4233 nHeight += nFlyAdd;
4234 }
4235 // The border/margin needs to be considered too, unfortunately it can't be
4236 // calculated using PrintArea and FrameArea because any or all of those
4237 // may be invalid.
4238 if ( _pCell->Lower() )
4239 {
4240 if ( pAttrs )
4241 nHeight += lcl_CalcTopAndBottomMargin( *_pCell, *pAttrs );
4242 else
4243 {
4244 SwBorderAttrAccess aAccess( SwFrame::GetCache(), _pCell );
4245 const SwBorderAttrs &rAttrs = *aAccess.Get();
4246 nHeight += lcl_CalcTopAndBottomMargin( *_pCell, rAttrs );
4247 }
4248 }
4249 return nHeight;
4250}
4251
4252// #i26945# - add parameter <_bConsiderObjs> in order to control,
4253// if floating screen objects have to be considered for the minimal cell height
4255 const bool _bConsiderObjs )
4256{
4257 SwTwips nHeight = 0;
4258 if ( !_pRow->IsRowSpanLine() )
4259 {
4260 const SwFormatFrameSize &rSz = _pRow->GetFormat()->GetFrameSize();
4261 if ( _pRow->HasFixSize() )
4262 {
4263 OSL_ENSURE(SwFrameSize::Fixed == rSz.GetHeightSizeType(), "pRow claims to have fixed size");
4264 return rSz.GetHeight();
4265 }
4266 // If this row frame is being split, then row's minimal height shouldn't restrict
4267 // this frame's minimal height, because the rest will go to follow frame.
4268 else if ( !_pRow->IsInSplit() && rSz.GetHeightSizeType() == SwFrameSize::Minimum )
4269 {
4270 nHeight = rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*_pRow);
4271 }
4272 }
4273
4274 SwRectFnSet aRectFnSet(_pRow);
4275 const SwCellFrame* pLow = static_cast<const SwCellFrame*>(_pRow->Lower());
4276 while ( pLow )
4277 {
4278 SwTwips nTmp = 0;
4279 const tools::Long nRowSpan = pLow->GetLayoutRowSpan();
4280 // --> NEW TABLES
4281 // Consider height of
4282 // 1. current cell if RowSpan == 1
4283 // 2. current cell if cell is "follow" cell of a cell with RowSpan == -1
4284 // 3. master cell if RowSpan == -1
4285 if ( 1 == nRowSpan )
4286 {
4287 nTmp = ::lcl_CalcMinCellHeight( pLow, _bConsiderObjs );
4288 }
4289 else if ( -1 == nRowSpan )
4290 {
4291 // Height of the last cell of a row span is height of master cell
4292 // minus the height of the other rows which are covered by the master
4293 // cell:
4294 const SwCellFrame& rMaster = pLow->FindStartEndOfRowSpanCell( true );
4295 nTmp = ::lcl_CalcMinCellHeight( &rMaster, _bConsiderObjs );
4296 const SwFrame* pMasterRow = rMaster.GetUpper();
4297 while ( pMasterRow && pMasterRow != _pRow )
4298 {
4299 nTmp -= aRectFnSet.GetHeight(pMasterRow->getFrameArea());
4300 pMasterRow = pMasterRow->GetNext();
4301 }
4302 }
4303 // <-- NEW TABLES
4304
4305 // Do not consider rotated cells:
4306 if ( pLow->IsVertical() == aRectFnSet.IsVert() && nTmp > nHeight )
4307 nHeight = nTmp;
4308
4309 pLow = static_cast<const SwCellFrame*>(pLow->GetNext());
4310 }
4311
4312 return nHeight;
4313}
4314
4315// #i29550#
4316
4317// Calculate the maximum of (TopLineSize + TopLineDist) over all lowers:
4318static sal_uInt16 lcl_GetTopSpace( const SwRowFrame& rRow )
4319{
4320 sal_uInt16 nTopSpace = 0;
4321 for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
4322 pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
4323 {
4324 sal_uInt16 nTmpTopSpace = 0;
4325 if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
4326 nTmpTopSpace = lcl_GetTopSpace( *static_cast<const SwRowFrame*>(pCurrLower->Lower()) );
4327 else
4328 {
4329 const SwAttrSet& rSet = const_cast<SwCellFrame*>(pCurrLower)->GetFormat()->GetAttrSet();
4330 const SvxBoxItem& rBoxItem = rSet.GetBox();
4331 nTmpTopSpace = rBoxItem.CalcLineSpace( SvxBoxItemLine::TOP, true );
4332 }
4333 nTopSpace = std::max( nTopSpace, nTmpTopSpace );
4334 }
4335 return nTopSpace;
4336}
4337
4338// Calculate the maximum of TopLineDist over all lowers:
4339static sal_uInt16 lcl_GetTopLineDist( const SwRowFrame& rRow )
4340{
4341 sal_uInt16 nTopLineDist = 0;
4342 for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
4343 pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
4344 {
4345 sal_uInt16 nTmpTopLineDist = 0;
4346 if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
4347 nTmpTopLineDist = lcl_GetTopLineDist( *static_cast<const SwRowFrame*>(pCurrLower->Lower()) );
4348 else
4349 {
4350 const SwAttrSet& rSet = const_cast<SwCellFrame*>(pCurrLower)->GetFormat()->GetAttrSet();
4351 const SvxBoxItem& rBoxItem = rSet.GetBox();
4352 nTmpTopLineDist = rBoxItem.GetDistance( SvxBoxItemLine::TOP );
4353 }
4354 nTopLineDist = std::max( nTopLineDist, nTmpTopLineDist );
4355 }
4356 return nTopLineDist;
4357}
4358
4359// Calculate the maximum of BottomLineSize over all lowers:
4360static sal_uInt16 lcl_GetBottomLineSize( const SwRowFrame& rRow )
4361{
4362 sal_uInt16 nBottomLineSize = 0;
4363 for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
4364 pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
4365 {
4366 sal_uInt16 nTmpBottomLineSize = 0;
4367 if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
4368 {
4369 const SwFrame* pRow = pCurrLower->GetLastLower();
4370 nTmpBottomLineSize = lcl_GetBottomLineSize( *static_cast<const SwRowFrame*>(pRow) );
4371 }
4372 else
4373 {
4374 const SwAttrSet& rSet = const_cast<SwCellFrame*>(pCurrLower)->GetFormat()->GetAttrSet();
4375 const SvxBoxItem& rBoxItem = rSet.GetBox();
4376 nTmpBottomLineSize = rBoxItem.CalcLineSpace( SvxBoxItemLine::BOTTOM, true ) -
4377 rBoxItem.GetDistance( SvxBoxItemLine::BOTTOM );
4378 }
4379 nBottomLineSize = std::max( nBottomLineSize, nTmpBottomLineSize );
4380 }
4381 return nBottomLineSize;
4382}
4383
4384// Calculate the maximum of BottomLineDist over all lowers:
4385static sal_uInt16 lcl_GetBottomLineDist( const SwRowFrame& rRow )
4386{
4387 sal_uInt16 nBottomLineDist = 0;
4388 for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
4389 pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
4390 {
4391 sal_uInt16 nTmpBottomLineDist = 0;
4392 if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
4393 {
4394 const SwFrame* pRow = pCurrLower->GetLastLower();
4395 nTmpBottomLineDist = lcl_GetBottomLineDist( *static_cast<const SwRowFrame*>(pRow) );
4396 }
4397 else
4398 {
4399 const SwAttrSet& rSet = const_cast<SwCellFrame*>(pCurrLower)->GetFormat()->GetAttrSet();
4400 const SvxBoxItem& rBoxItem = rSet.GetBox();
4401 nTmpBottomLineDist = rBoxItem.GetDistance( SvxBoxItemLine::BOTTOM );
4402 }
4403 nBottomLineDist = std::max( nBottomLineDist, nTmpBottomLineDist );
4404 }
4405 return nBottomLineDist;
4406}
4407
4408// tdf#104425: calculate the height of all row frames,
4409// for which this frame is a follow.
4410// When a row has fixed/minimum height, it may span over
4411// several pages. The minimal height on this page should
4412// take into account the sum of all the heights of previous
4413// frames that constitute the table row on previous pages.
4414// Otherwise, trying to split a too high row frame will
4415// result in loop trying to create that too high row
4416// on each following page
4418{
4419 // We don't need to account for previous instances of repeated headlines
4420 if (rRow.IsRepeatedHeadline())
4421 return 0;
4422 SwRectFnSet aRectFnSet(&rRow);
4423 const SwTableLine* pLine = rRow.GetTabLine();
4424 const SwTabFrame* pTab = rRow.FindTabFrame();
4425 if (!pLine || !pTab || !pTab->IsFollow())
4426 return 0;
4427 SwTwips nResult = 0;
4429 for (const SwRowFrame* pCurRow = aIter.First(); pCurRow; pCurRow = aIter.Next())
4430 {
4431 if (pCurRow != &rRow && pCurRow->GetTabLine() == pLine)
4432 {
4433 // We've found another row frame that is part of the same table row
4434 const SwTabFrame* pCurTab = pCurRow->FindTabFrame();
4435 // A row frame may not belong to a table frame, when it is being cut, e.g., in
4436 // lcl_PostprocessRowsInCells().
4437 // Its SwRowFrame::Cut() has been called; it in turn called SwLayoutFrame::Cut(),
4438 // which nullified row's upper in RemoveFromLayout(), and then called Shrink()
4439 // for its former upper.
4440 // Regardless of whether it will be pasted back, or destroyed, currently it's not
4441 // part of layout, and its height does not count
4442 if (pCurTab && pCurTab->IsAnFollow(pTab))
4443 {
4444 // The found row frame belongs to a table frame that precedes
4445 // (above) this one in chain. So, include it in the sum
4446 nResult += aRectFnSet.GetHeight(pCurRow->getFrameArea());
4447 }
4448 }
4449 }
4450 return nResult;
4451}
4452
4453void SwRowFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
4454{
4455 SwRectFnSet aRectFnSet(this);
4456 OSL_ENSURE( pAttrs, "SwRowFrame::Format without Attrs." );
4457
4458 const bool bFix = mbFixSize;
4459
4460 if ( !isFramePrintAreaValid() )
4461 {
4462 // RowFrames don't have borders/margins therefore the PrintArea always
4463 // matches the FrameArea.
4465
4466 {
4468 aPrt.Left( 0 );
4469 aPrt.Top( 0 );
4470 aPrt.Width ( getFrameArea().Width() );
4471 aPrt.Height( getFrameArea().Height() );
4472 }
4473
4474 // #i29550#
4475 // Here we calculate the top-printing area for the lower cell frames
4476 SwTabFrame* pTabFrame = FindTabFrame();
4477 if ( pTabFrame->IsCollapsingBorders() )
4478 {
4479 const sal_uInt16 nTopSpace = lcl_GetTopSpace( *this );
4480 const sal_uInt16 nTopLineDist = lcl_GetTopLineDist( *this );
4481 const sal_uInt16 nBottomLineSize = lcl_GetBottomLineSize( *this );
4482 const sal_uInt16 nBottomLineDist = lcl_GetBottomLineDist( *this );
4483
4484 const SwRowFrame* pPreviousRow = nullptr;
4485
4486 // #i32456#
4487 // In order to calculate the top printing area for the lower cell
4488 // frames, we have to find the 'previous' row frame and compare
4489 // the bottom values of the 'previous' row with the 'top' values
4490 // of this row. The best way to find the 'previous' row is to
4491 // use the table structure:
4492 const SwTable* pTable = pTabFrame->GetTable();
4493 const SwTableLine* pPrevTabLine = nullptr;
4494 const SwRowFrame* pTmpRow = this;
4495
4496 while ( pTmpRow && !pPrevTabLine )
4497 {
4498 size_t nIdx = 0;
4499 const SwTableLines& rLines = pTmpRow->GetTabLine()->GetUpper() ?
4500 pTmpRow->GetTabLine()->GetUpper()->GetTabLines() :
4501 pTable->GetTabLines();
4502
4503 while ( rLines[ nIdx ] != pTmpRow->GetTabLine() )
4504 ++nIdx;
4505
4506 if ( nIdx > 0 )
4507 {
4508 // pTmpRow has a 'previous' row in the table structure:
4509 pPrevTabLine = rLines[ nIdx - 1 ];
4510 }
4511 else
4512 {
4513 // pTmpRow is a first row in the table structure.
4514 // We go up in the table structure:
4515 pTmpRow = pTmpRow->GetUpper()->GetUpper() &&
4516 pTmpRow->GetUpper()->GetUpper()->IsRowFrame() ?
4517 static_cast<const SwRowFrame*>( pTmpRow->GetUpper()->GetUpper() ) :
4518 nullptr;
4519 }
4520 }
4521
4522 // If we found a 'previous' row, we look for the appropriate row frame:
4523 if ( pPrevTabLine )
4524 {
4525 SwIterator<SwRowFrame,SwFormat> aIter( *pPrevTabLine->GetFrameFormat() );
4526 for ( SwRowFrame* pRow = aIter.First(); pRow; pRow = aIter.Next() )
4527 {
4528 // #115759# - do *not* take repeated
4529 // headlines, because during split of table it can be
4530 // invalid and thus can't provide correct border values.
4531 if ( pRow->GetTabLine() == pPrevTabLine &&
4532 !pRow->IsRepeatedHeadline() )
4533 {
4534 pPreviousRow = pRow;
4535 break;
4536 }
4537 }
4538 }
4539
4540 sal_uInt16 nTopPrtMargin = nTopSpace;
4541 if ( pPreviousRow )
4542 {
4543 const sal_uInt16 nTmpPrtMargin = pPreviousRow->GetBottomLineSize() + nTopLineDist;
4544 if ( nTmpPrtMargin > nTopPrtMargin )
4545 nTopPrtMargin = nTmpPrtMargin;
4546 }
4547
4548 // table has to be notified if it has to change its lower
4549 // margin due to changes of nBottomLineSize:
4550 if ( !GetNext() && nBottomLineSize != GetBottomLineSize() )
4551 pTabFrame->InvalidatePrt_();
4552
4553 // If there are rows nested inside this row, the nested rows
4554 // may not have been calculated yet. Therefore the
4555 // ::lcl_CalcMinRowHeight( this ) operation later in this
4556 // function cannot consider the correct border values. We
4557 // have to trigger the invalidation of the outer row frame
4558 // manually:
4559 // Note: If any further invalidations should be necessary, we
4560 // should consider moving the invalidation stuff to the
4561 // appropriate SwNotify object.
4562 if ( GetUpper()->GetUpper()->IsRowFrame() &&
4563 ( nBottomLineDist != GetBottomMarginForLowers() ||
4564 nTopPrtMargin != GetTopMarginForLowers() ) )
4566
4567 SetBottomMarginForLowers( nBottomLineDist ); // 3.
4568 SetBottomLineSize( nBottomLineSize ); // 4.
4569 SetTopMarginForLowers( nTopPrtMargin ); // 5.
4570
4571 }
4572 }
4573
4574 while ( !isFrameAreaSizeValid() )
4575 {
4577
4578#if OSL_DEBUG_LEVEL > 0
4579 if ( HasFixSize() )
4580 {
4581 const SwFormatFrameSize &rFrameSize = GetFormat()->GetFrameSize();
4582 OSL_ENSURE( rFrameSize.GetSize().Height() > 0, "Has it" );
4583 }
4584#endif
4585 const SwTwips nDiff = aRectFnSet.GetHeight(getFrameArea()) -
4586 ( HasFixSize() && !IsRowSpanLine()
4587 ? pAttrs->GetSize().Height()
4588 // #i26945#
4589 : ::lcl_CalcMinRowHeight( this,
4590 FindTabFrame()->IsConsiderObjsForMinCellHeight() ) );
4591 if ( nDiff )
4592 {
4593 mbFixSize = false;
4594 if ( nDiff > 0 )
4595 Shrink( nDiff, false, true );
4596 else if ( nDiff < 0 )
4597 Grow( -nDiff );
4598 mbFixSize = bFix;
4599 }
4600 }
4601
4602 // last row will fill the space in its upper.
4603 if ( GetNext() )
4604 return;
4605
4606 //The last fills the remaining space in the upper.
4607 SwTwips nDiff = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea());
4608 SwFrame *pSibling = GetUpper()->Lower();
4609 do
4610 { nDiff -= aRectFnSet.GetHeight(pSibling->getFrameArea());
4611 pSibling = pSibling->GetNext();
4612 } while ( pSibling );
4613 if ( nDiff > 0 )
4614 {
4615 mbFixSize = false;
4616 Grow( nDiff );
4617 mbFixSize = bFix;
4619 }
4620}
4621
4622void SwRowFrame::AdjustCells( const SwTwips nHeight, const bool bHeight )
4623{
4624 SwFrame *pFrame = Lower();
4625 if ( bHeight )
4626 {
4627 SwRectFnSet aRectFnSet(this);
4628#if !ENABLE_WASM_STRIP_ACCESSIBILITY
4629 SwRect aOldFrame;
4630#endif
4631
4632 while ( pFrame )
4633 {
4634 SwFrame* pNotify = nullptr;
4635
4636 SwCellFrame* pCellFrame = static_cast<SwCellFrame*>(pFrame);
4637
4638 // NEW TABLES
4639 // Which cells need to be adjusted if the current row changes
4640 // its height?
4641
4642 // Current frame is a covered frame:
4643 // Set new height for covered cell and adjust master cell:
4644 if ( pCellFrame->GetTabBox()->getRowSpan() < 1 )
4645 {
4646 // Set height of current (covered) cell to new line height.
4647 const tools::Long nDiff = nHeight - aRectFnSet.GetHeight(pCellFrame->getFrameArea());
4648 if ( nDiff )
4649 {
4650 {
4652 aRectFnSet.AddBottom( aFrm, nDiff );
4653 }
4654
4655 pCellFrame->InvalidatePrt_();
4656 }
4657 }
4658
4659 SwCellFrame* pToAdjust = nullptr;
4660 SwFrame* pToAdjustRow = nullptr;
4661
4662 // If current frame is covered frame, we still want to adjust the
4663 // height of the cell starting the row span
4664 if ( pCellFrame->GetLayoutRowSpan() < 1 )
4665 {
4666 pToAdjust = const_cast< SwCellFrame*>(&pCellFrame->FindStartEndOfRowSpanCell( true ));
4667 pToAdjustRow = pToAdjust->GetUpper();
4668 }
4669 else
4670 {
4671 pToAdjust = pCellFrame;
4672 pToAdjustRow = this;
4673 }
4674
4675 // Set height of master cell to height of all lines spanned by this line.
4676 tools::Long nRowSpan = pToAdjust->GetLayoutRowSpan();
4677 SwTwips nSumRowHeight = 0;
4678 while ( pToAdjustRow )
4679 {
4680 // Use new height for the current row:
4681 nSumRowHeight += pToAdjustRow == this ?
4682 nHeight :
4683 aRectFnSet.GetHeight(pToAdjustRow->getFrameArea());
4684
4685 if ( nRowSpan-- == 1 )
4686 break;
4687
4688 pToAdjustRow = pToAdjustRow->GetNext();
4689 }
4690
4691 if ( pToAdjustRow && pToAdjustRow != this )
4692 pToAdjustRow->InvalidateSize_();
4693
4694 const tools::Long nDiff = nSumRowHeight - aRectFnSet.GetHeight(pToAdjust->getFrameArea());
4695 if ( nDiff )
4696 {
4697#if !ENABLE_WASM_STRIP_ACCESSIBILITY
4698 aOldFrame = pToAdjust->getFrameArea();
4699#endif
4701 aRectFnSet.AddBottom( aFrm, nDiff );
4702 pNotify = pToAdjust;
4703 }
4704
4705 if ( pNotify )
4706 {
4707#if !ENABLE_WASM_STRIP_ACCESSIBILITY
4708 SwRootFrame *pRootFrame = getRootFrame();
4709 if( pRootFrame && pRootFrame->IsAnyShellAccessible() && pRootFrame->GetCurrShell() )
4710 pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( pNotify, aOldFrame );
4711#endif
4712
4713 pNotify->InvalidatePrt_();
4714 }
4715
4716 pFrame = pFrame->GetNext();
4717 }
4718 }
4719 else
4720 { while ( pFrame )
4721 {
4722 pFrame->InvalidateAll_();
4723 pFrame = pFrame->GetNext();
4724 }
4725 }
4727}
4728
4730{
4731 SwTabFrame *pTab = FindTabFrame();
4732 if ( pTab && pTab->IsFollow() && this == pTab->GetFirstNonHeadlineRow() )
4733 {
4734 pTab->FindMaster()->InvalidatePos();
4735 }
4736
4738}
4739
4740SwTwips SwRowFrame::GrowFrame( SwTwips nDist, bool bTst, bool bInfo )
4741{
4742 SwTwips nReal = 0;
4743
4744 SwTabFrame* pTab = FindTabFrame();
4745 SwRectFnSet aRectFnSet(pTab);
4746
4747 bool bRestrictTableGrowth;
4748 bool bHasFollowFlowLine = pTab->HasFollowFlowLine();
4749
4750 if ( GetUpper()->IsTabFrame() )
4751 {
4752 const SwRowFrame* pFollowFlowRow = IsInSplitTableRow();
4753 bRestrictTableGrowth = pFollowFlowRow && !pFollowFlowRow->IsRowSpanLine();
4754 }
4755 else
4756 {
4757 OSL_ENSURE( GetUpper()->IsCellFrame(), "RowFrame->GetUpper neither table nor cell" );
4758 bRestrictTableGrowth = GetFollowRow() && bHasFollowFlowLine;
4759 OSL_ENSURE( !bRestrictTableGrowth || !GetNext(),
4760 "GetFollowRow for row frame that has a Next" );
4761
4762 // There may still be some space left in my direct upper:
4763 const SwTwips nAdditionalSpace =
4764 aRectFnSet.BottomDist( getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()->GetUpper()) );
4765 if ( bRestrictTableGrowth && nAdditionalSpace > 0 )
4766 {
4767 nReal = std::min( nAdditionalSpace, nDist );
4768 nDist -= nReal;
4769 if ( !bTst )
4770 {
4772 aRectFnSet.AddBottom( aFrm, nReal );
4773 }
4774 }
4775 }
4776
4777 if ( bRestrictTableGrowth )
4778 pTab->SetRestrictTableGrowth( true );
4779 else
4780 {
4781 // Ok, this looks like a hack, indeed, it is a hack.
4782 // If the current row frame is inside another cell frame,
4783 // and the current row frame has no follow, it should not
4784 // be allowed to grow. In fact, setting bRestrictTableGrowth
4785 // to 'false' does not work, because the surrounding RowFrame
4786 // would set this to 'true'.
4787 pTab->SetFollowFlowLine( false );
4788 }
4789
4790 nReal += SwLayoutFrame::GrowFrame( nDist, bTst, bInfo);
4791
4792 pTab->SetRestrictTableGrowth( false );
4793 pTab->SetFollowFlowLine( bHasFollowFlowLine );
4794
4795 //Update the height of the cells to the newest value.
4796 if ( !bTst )
4797 {
4798 SwRectFnSet fnRectX(this);
4799 AdjustCells( fnRectX.GetHeight(getFramePrintArea()) + nReal, true );
4800 if ( nReal )
4802 }
4803
4804 return nReal;
4805}
4806
4807SwTwips SwRowFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool bInfo )
4808{
4809 SwRectFnSet aRectFnSet(this);
4810 if( HasFixSize() )
4811 {
4812 AdjustCells( aRectFnSet.GetHeight(getFramePrintArea()), true );
4813 return 0;
4814 }
4815
4816 // bInfo may be set to true by SwRowFrame::Format; we need to handle this
4817 // here accordingly
4818 const bool bShrinkAnyway = bInfo;
4819
4820 //Only shrink as much as the content of the biggest cell allows.
4821 SwTwips nRealDist = nDist;
4822 SwFormat* pMod = GetFormat();
4823 if (pMod)
4824 {
4825 const SwFormatFrameSize &rSz = pMod->GetFrameSize();
4826 SwTwips nMinHeight = 0;
4828 nMinHeight = std::max(rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*this),
4829 tools::Long(0));
4830
4831 // Only necessary to calculate minimal row height if height
4832 // of pRow is at least nMinHeight. Otherwise nMinHeight is the
4833 // minimum height.
4834 if( nMinHeight < aRectFnSet.GetHeight(getFrameArea()) )
4835 {
4836 // #i26945#
4837 OSL_ENSURE( FindTabFrame(), "<SwRowFrame::ShrinkFrame(..)> - no table frame -> crash." );
4838 const bool bConsiderObjs( FindTabFrame()->IsConsiderObjsForMinCellHeight() );
4839 nMinHeight = lcl_CalcMinRowHeight( this, bConsiderObjs );
4840 }
4841
4842 if ( (aRectFnSet.GetHeight(getFrameArea()) - nRealDist) < nMinHeight )
4843 nRealDist = aRectFnSet.GetHeight(getFrameArea()) - nMinHeight;
4844 }
4845 if ( nRealDist < 0 )
4846 nRealDist = 0;
4847
4848 SwTwips nReal = nRealDist;
4849 if ( nReal )
4850 {
4851 if ( !bTst )
4852 {
4853 SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
4855 aRectFnSet.SetHeight( aFrm, nHeight - nReal );
4856
4857 if( IsVertical() && !IsVertLR() )
4858 {
4859 aFrm.Pos().AdjustX(nReal );
4860 }
4861 }
4862
4863 SwLayoutFrame* pFrame = GetUpper();
4864 SwTwips nTmp = pFrame ? pFrame->Shrink(nReal, bTst) : 0;
4865 if ( !bShrinkAnyway && !GetNext() && nTmp != nReal )
4866 {
4867 //The last one gets the leftover in the upper and therefore takes
4868 //care (otherwise: endless loop)
4869 if ( !bTst )
4870 {
4871 nReal -= nTmp;
4872 SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
4874 aRectFnSet.SetHeight( aFrm, nHeight + nReal );
4875
4876 if( IsVertical() && !IsVertLR() )
4877 {
4878 aFrm.Pos().AdjustX( -nReal );
4879 }
4880 }
4881 nReal = nTmp;
4882 }
4883 }
4884
4885 // Invalidate appropriately and update the height to the newest value.
4886 if ( !bTst )
4887 {
4888 if ( nReal )
4889 {
4890 if ( GetNext() )
4894
4895 SwTabFrame *pTab = FindTabFrame();
4896 if ( !pTab->IsRebuildLastLine()
4897 && pTab->IsFollow()
4898 && this == pTab->GetFirstNonHeadlineRow()
4899 && !pTab->IsInRecalcLowerRow() )
4900 {
4901 SwTabFrame* pMasterTab = pTab->FindMaster();
4902 pMasterTab->InvalidatePos();
4903 }
4904 }
4905 AdjustCells( aRectFnSet.GetHeight(getFramePrintArea()) - nReal, true );
4906 }
4907 return nReal;
4908}
4909
4911{
4912 // Fixed size rows are never allowed to split:
4913 if ( HasFixSize() )
4914 {
4915 OSL_ENSURE( SwFrameSize::Fixed == GetFormat()->GetFrameSize().GetHeightSizeType(), "pRow claims to have fixed size" );
4916 return false;
4917 }
4918
4919 // Repeated headlines are never allowed to split:
4920 const SwTabFrame* pTabFrame = FindTabFrame();
4921 if ( pTabFrame->GetTable()->GetRowsToRepeat() > 0 &&
4922 pTabFrame->IsInHeadline( *this ) )
4923 return false;
4924
4925 if ( IsForceRowSplitAllowed() )
4926 return true;
4927
4928 const SwTableLineFormat* pFrameFormat = static_cast<SwTableLineFormat*>(GetTabLine()->GetFrameFormat());
4929 const SwFormatRowSplit& rLP = pFrameFormat->GetRowSplit();
4930 return rLP.GetValue();
4931}
4932
4933bool SwRowFrame::ShouldRowKeepWithNext( const bool bCheckParents ) const
4934{
4935 // No KeepWithNext if nested in another table
4936 if ( GetUpper()->GetUpper()->IsCellFrame() )
4937 return false;
4938
4939 const SwCellFrame* pCell = static_cast<const SwCellFrame*>(Lower());
4940 const SwFrame* pText = pCell->Lower();
4941
4942 return pText && pText->IsTextFrame() &&
4943 static_cast<const SwTextFrame*>(pText)->GetTextNodeForParaProps()->GetSwAttrSet().GetKeep(bCheckParents).GetValue();
4944}
4945
4946SwCellFrame::SwCellFrame(const SwTableBox &rBox, SwFrame* pSib, bool bInsertContent)
4947 : SwLayoutFrame( rBox.GetFrameFormat(), pSib )
4948 , m_pTabBox( &rBox )
4949{
4951
4952 if ( !bInsertContent )
4953 return;
4954
4955 //If a StartIdx is available, ContentFrames are added in the cell, otherwise
4956 //Rows have to be present and those are added.
4957 if ( SwNodeOffset nIndex = rBox.GetSttIdx() )
4958 {
4959 ::InsertCnt_( this, rBox.GetFrameFormat()->GetDoc(), ++nIndex );
4960 }
4961 else
4962 {
4963 const SwTableLines &rLines = rBox.GetTabLines();
4964 SwFrame *pTmpPrev = nullptr;
4965 for ( size_t i = 0; i < rLines.size(); ++i )
4966 {
4967 SwRowFrame *pNew = new SwRowFrame( *rLines[i], this, bInsertContent );
4968 pNew->InsertBehind( this, pTmpPrev );
4969 pTmpPrev = pNew;
4970 }
4971 }
4972}
4973
4975{
4976 sw::BroadcastingModify* pMod = GetFormat();
4977 if( pMod )
4978 {
4979 // At this stage the lower frames aren't destroyed already,
4980 // therefore we have to do a recursive dispose.
4981#if !ENABLE_WASM_STRIP_ACCESSIBILITY
4982 SwRootFrame *pRootFrame = getRootFrame();
4983 if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
4984 pRootFrame->GetCurrShell() )
4985 {
4986 pRootFrame->GetCurrShell()->Imp()->DisposeAccessibleFrame( this, true );
4987 }
4988#endif
4989
4990 pMod->Remove( this );
4991 if( !pMod->HasWriterListeners() )
4992 delete pMod;
4993 }
4994
4996}
4997
4999{
5000}
5001
5002static bool lcl_ArrangeLowers( SwLayoutFrame *pLay, tools::Long lYStart, bool bInva )
5003{
5004 bool bRet = false;
5005 SwFrame *pFrame = pLay->Lower();
5006 SwRectFnSet aRectFnSet(pLay);
5007 while ( pFrame )
5008 {
5009 tools::Long nFrameTop = aRectFnSet.GetTop(pFrame->getFrameArea());
5010 if( nFrameTop != lYStart )
5011 {
5012 bRet = true;
5013 const tools::Long lDiff = aRectFnSet.YDiff( lYStart, nFrameTop );
5014 const tools::Long lDiffX = lYStart - nFrameTop;
5015
5016 {
5018 aRectFnSet.SubTop( aFrm, -lDiff );
5019 aRectFnSet.AddBottom( aFrm, lDiff );
5020 }
5021
5022 pFrame->SetCompletePaint();
5023
5024 if ( !pFrame->GetNext() )
5025 pFrame->SetRetouche();
5026 if( bInva )
5028 if ( pFrame->IsLayoutFrame() && static_cast<SwLayoutFrame*>(pFrame)->Lower() )
5029 lcl_ArrangeLowers( static_cast<SwLayoutFrame*>(pFrame),
5030 aRectFnSet.GetTop(static_cast<SwLayoutFrame*>(pFrame)->Lower()->getFrameArea())
5031 + lDiffX, bInva );
5032 if ( pFrame->GetDrawObjs() )
5033 {
5034 for ( size_t i = 0; i < pFrame->GetDrawObjs()->size(); ++i )
5035 {
5036 SwAnchoredObject* pAnchoredObj = (*pFrame->GetDrawObjs())[i];
5037 // #i26945# - check, if anchored object
5038 // is lower of layout frame by checking, if the anchor
5039 // frame, which contains the anchor position, is a lower
5040 // of the layout frame.
5041 if ( !pLay->IsAnLower( pAnchoredObj->GetAnchorFrameContainingAnchPos() ) )
5042 {
5043 continue;
5044 }
5045 // #i52904# - distinguish between anchored
5046 // objects, whose vertical position depends on its anchor
5047 // frame and whose vertical position is independent
5048 // from its anchor frame.
5049 bool bVertPosDepOnAnchor( true );
5050 {
5051 SwFormatVertOrient aVert( pAnchoredObj->GetFrameFormat().GetVertOrient() );
5052 switch ( aVert.GetRelationOrient() )
5053 {
5054 case text::RelOrientation::PAGE_FRAME:
5055 case text::RelOrientation::PAGE_PRINT_AREA:
5056 bVertPosDepOnAnchor = false;
5057 break;
5058 default: break;
5059 }
5060 }
5061 if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
5062 {
5063
5064 // OD 2004-05-18 #i28701# - no direct move of objects,
5065 // which are anchored to-paragraph/to-character, if
5066 // the wrapping style influence has to be considered
5067 // on the object positioning.
5068 // #i52904# - no direct move of objects,
5069 // whose vertical position doesn't depend on anchor frame.
5070 const bool bDirectMove =
5071 FAR_AWAY != pFly->getFrameArea().Top() &&
5072 bVertPosDepOnAnchor &&
5073 !pFly->ConsiderObjWrapInfluenceOnObjPos();
5074 if ( bDirectMove )
5075 {
5076 {
5078 aRectFnSet.SubTop( aFrm, -lDiff );
5079 aRectFnSet.AddBottom( aFrm, lDiff );
5080 }
5081
5082 pFly->GetVirtDrawObj()->SetBoundAndSnapRectsDirty();
5083 // --> OD 2004-08-17 - also notify view of <SdrObject>
5084 // instance, which represents the Writer fly frame in
5085 // the drawing layer
5086 pFly->GetVirtDrawObj()->SetChanged();
5087 // #i58280#
5088 pFly->InvalidateObjRectWithSpaces();
5089 }
5090
5091 if ( pFly->IsFlyInContentFrame() )
5092 {
5093 static_cast<SwFlyInContentFrame*>(pFly)->AddRefOfst( lDiff );
5094 // #115759# - reset current relative
5095 // position to get re-positioned, if not directly moved.
5096 if ( !bDirectMove )
5097 {
5098 pAnchoredObj->SetCurrRelPos( Point( 0, 0 ) );
5099 }
5100 }
5101 else if( pFly->IsAutoPos() )
5102 {
5103 pFly->AddLastCharY( lDiff );
5104 // OD 2004-05-18 #i28701# - follow-up of #i22341#
5105 // <mnLastTopOfLine> has also been adjusted.
5106 pFly->AddLastTopOfLineY( lDiff );
5107 }
5108 // #i26945# - re-registration at
5109 // page frame of anchor frame, if table frame isn't
5110 // a follow table and table frame isn't in its
5111 // rebuild of last line.
5112 const SwTabFrame* pTabFrame = pLay->FindTabFrame();
5113 // - save: check, if table frame is found.
5114 if ( pTabFrame &&
5115 !( pTabFrame->IsFollow() &&
5116 pTabFrame->FindMaster()->IsRebuildLastLine() ) &&
5117 pFly->IsFlyFreeFrame() )
5118 {
5119 SwPageFrame* pPageFrame = pFly->GetPageFrame();
5120 SwPageFrame* pPageOfAnchor = pFrame->FindPageFrame();
5121 if ( pPageFrame != pPageOfAnchor )
5122 {
5123 pFly->InvalidatePos();
5124 if ( pPageFrame )
5125 pPageFrame->MoveFly( pFly, pPageOfAnchor );
5126 else
5127 pPageOfAnchor->AppendFlyToPage( pFly );
5128 }
5129 }
5130 // OD 2004-05-11 #i28701# - Because of the introduction
5131 // of new positionings and alignments (e.g. aligned at
5132 // page area, but anchored at-character), the position
5133 // of the Writer fly frame has to be invalidated.
5134 pFly->InvalidatePos();
5135
5136 // #i26945# - follow-up of #i3317#
5137 // No arrangement of lowers, if Writer fly frame isn't
5138 // moved
5139 if ( bDirectMove &&
5140 ::lcl_ArrangeLowers( pFly,
5141 aRectFnSet.GetPrtTop(*pFly),
5142 bInva ) )
5143 {
5144 pFly->SetCompletePaint();
5145 }
5146 }
5147 else if ( dynamic_cast< const SwAnchoredDrawObject *>( pAnchoredObj ) != nullptr )
5148 {
5149 // #i26945#
5150 const SwTabFrame* pTabFrame = pLay->FindTabFrame();
5151 if ( pTabFrame &&
5152 !( pTabFrame->IsFollow() &&
5153 pTabFrame->FindMaster()->IsRebuildLastLine() ) &&
5154 (pAnchoredObj->GetFrameFormat().GetAnchor().GetAnchorId()
5155 != RndStdIds::FLY_AS_CHAR))
5156 {
5157 SwPageFrame* pPageFrame = pAnchoredObj->GetPageFrame();
5158 SwPageFrame* pPageOfAnchor = pFrame->FindPageFrame();
5159 if ( pPageFrame != pPageOfAnchor )
5160 {
5161 pAnchoredObj->InvalidateObjPos();
5162 if ( pPageFrame )
5163 {
5164 pPageFrame->RemoveDrawObjFromPage( *pAnchoredObj );
5165 }
5166 pPageOfAnchor->AppendDrawObjToPage( *pAnchoredObj );
5167 }
5168 }
5169 // #i28701# - adjust last character
5170 // rectangle and last top of line.
5171 pAnchoredObj->AddLastCharY( lDiff );
5172 pAnchoredObj->AddLastTopOfLineY( lDiff );
5173 // #i52904# - re-introduce direct move
5174 // of drawing objects
5175 const bool bDirectMove =
5176 static_cast<const SwDrawFrameFormat&>(pAnchoredObj->GetFrameFormat()).IsPosAttrSet() &&
5177 bVertPosDepOnAnchor &&
5178 !pAnchoredObj->ConsiderObjWrapInfluenceOnObjPos();
5179 if ( bDirectMove )
5180 {
5181 SwObjPositioningInProgress aObjPosInProgress( *pAnchoredObj );
5182 if ( aRectFnSet.IsVert() )
5183 {
5184 pAnchoredObj->DrawObj(