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