Add a comment to clarify what kind of inputs the class handles
[LibreOffice.git] / sw / source / core / layout / wsfrm.cxx
blob87ce9defeb8c71091d1288ee898bea9e111f8d7a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_wasm_strip.h>
22 #include <hints.hxx>
23 #include <osl/diagnose.h>
24 #include <o3tl/safeint.hxx>
25 #include <svl/itemiter.hxx>
26 #include <editeng/brushitem.hxx>
27 #include <sfx2/viewsh.hxx>
28 #include <fmtornt.hxx>
29 #include <pagefrm.hxx>
30 #include <section.hxx>
31 #include <rootfrm.hxx>
32 #include <anchoreddrawobject.hxx>
33 #include <fmtanchr.hxx>
34 #include <viewimp.hxx>
35 #include <viewopt.hxx>
36 #include <IDocumentSettingAccess.hxx>
37 #include <IDocumentFieldsAccess.hxx>
38 #include <IDocumentRedlineAccess.hxx>
39 #include <redline.hxx>
40 #include <docsh.hxx>
41 #include <ftninfo.hxx>
42 #include <ftnidx.hxx>
43 #include <fmtclbl.hxx>
44 #include <fmtfsize.hxx>
45 #include <fmtpdsc.hxx>
46 #include <txtftn.hxx>
47 #include <fmtftn.hxx>
48 #include <fmtsrnd.hxx>
49 #include <fmtcntnt.hxx>
50 #include <ftnfrm.hxx>
51 #include <tabfrm.hxx>
52 #include <rowfrm.hxx>
53 #include <flyfrm.hxx>
54 #include <sectfrm.hxx>
55 #include <fmtclds.hxx>
56 #include <txtfrm.hxx>
57 #include <bodyfrm.hxx>
58 #include <cellfrm.hxx>
59 #include <dbg_lay.hxx>
60 #include <editeng/frmdiritem.hxx>
61 #include <sortedobjs.hxx>
62 #include <frmatr.hxx>
63 #include <frmtool.hxx>
64 #include <layact.hxx>
65 #include <ndtxt.hxx>
66 #include <swtable.hxx>
67 #include <view.hxx>
69 // RotateFlyFrame3
70 #include <basegfx/matrix/b2dhommatrixtools.hxx>
72 using namespace ::com::sun::star;
74 SwFrameAreaDefinition::SwFrameAreaDefinition()
75 : mnFrameId(SwFrameAreaDefinition::snLastFrameId++),
76 mbFrameAreaPositionValid(false),
77 mbFrameAreaSizeValid(false),
78 mbFramePrintAreaValid(false)
82 SwFrameAreaDefinition::~SwFrameAreaDefinition()
86 void SwFrameAreaDefinition::setFrameAreaPositionValid(bool bNew)
88 if(mbFrameAreaPositionValid != bNew)
90 mbFrameAreaPositionValid = bNew;
94 void SwFrameAreaDefinition::setFrameAreaSizeValid(bool bNew)
96 if(mbFrameAreaSizeValid != bNew)
98 mbFrameAreaSizeValid = bNew;
102 void SwFrameAreaDefinition::setFramePrintAreaValid(bool bNew)
104 if(mbFramePrintAreaValid != bNew)
106 mbFramePrintAreaValid = bNew;
110 SwFrameAreaDefinition::FrameAreaWriteAccess::~FrameAreaWriteAccess()
112 if(mrTarget.maFrameArea != *this)
114 mrTarget.maFrameArea = *this;
118 SwFrameAreaDefinition::FramePrintAreaWriteAccess::~FramePrintAreaWriteAccess()
120 if(mrTarget.maFramePrintArea != *this)
122 mrTarget.maFramePrintArea = *this;
126 // RotateFlyFrame3 - Support for Transformations
127 basegfx::B2DHomMatrix SwFrameAreaDefinition::getFrameAreaTransformation() const
129 // default implementation hands out FrameArea (outer frame)
130 const SwRect& rFrameArea(getFrameArea());
132 return basegfx::utils::createScaleTranslateB2DHomMatrix(
133 rFrameArea.Width(), rFrameArea.Height(),
134 rFrameArea.Left(), rFrameArea.Top());
137 basegfx::B2DHomMatrix SwFrameAreaDefinition::getFramePrintAreaTransformation() const
139 // default implementation hands out FramePrintArea (outer frame)
140 // Take into account that FramePrintArea is relative to FrameArea
141 const SwRect& rFrameArea(getFrameArea());
142 const SwRect& rFramePrintArea(getFramePrintArea());
144 return basegfx::utils::createScaleTranslateB2DHomMatrix(
145 rFramePrintArea.Width(), rFramePrintArea.Height(),
146 rFramePrintArea.Left() + rFrameArea.Left(),
147 rFramePrintArea.Top() + rFrameArea.Top());
150 void SwFrameAreaDefinition::transform_translate(const Point& rOffset)
152 // RotateFlyFrame3: default is to change the FrameArea, FramePrintArea needs no
153 // change since it is relative to FrameArea
154 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
156 if (aFrm.Pos().X() != FAR_AWAY)
158 aFrm.Pos().AdjustX(rOffset.X() );
161 if (aFrm.Pos().Y() != FAR_AWAY)
163 aFrm.Pos().AdjustY(rOffset.Y() );
167 SwRect TransformableSwFrame::getUntransformedFrameArea() const
169 const basegfx::B2DHomMatrix& rSource(getLocalFrameAreaTransformation());
171 if(rSource.isIdentity())
173 return mrSwFrameAreaDefinition.getFrameArea();
175 else
177 basegfx::B2DVector aScale, aTranslate;
178 double fRotate, fShearX;
179 rSource.decompose(aScale, aTranslate, fRotate, fShearX);
180 const basegfx::B2DPoint aCenter(rSource * basegfx::B2DPoint(0.5, 0.5));
181 const basegfx::B2DVector aAbsScale(basegfx::absolute(aScale));
183 return SwRect(
184 basegfx::fround<tools::Long>(aCenter.getX() - (0.5 * aAbsScale.getX())),
185 basegfx::fround<tools::Long>(aCenter.getY() - (0.5 * aAbsScale.getY())),
186 basegfx::fround<tools::Long>(aAbsScale.getX()),
187 basegfx::fround<tools::Long>(aAbsScale.getY()));
191 SwRect TransformableSwFrame::getUntransformedFramePrintArea() const
193 const basegfx::B2DHomMatrix& rSource(getLocalFramePrintAreaTransformation());
195 if(rSource.isIdentity())
197 return mrSwFrameAreaDefinition.getFramePrintArea();
199 else
201 basegfx::B2DVector aScale, aTranslate;
202 double fRotate, fShearX;
203 rSource.decompose(aScale, aTranslate, fRotate, fShearX);
204 const basegfx::B2DPoint aCenter(rSource * basegfx::B2DPoint(0.5, 0.5));
205 const basegfx::B2DVector aAbsScale(basegfx::absolute(aScale));
206 const SwRect aUntransformedFrameArea(getUntransformedFrameArea());
208 return SwRect(
209 basegfx::fround<tools::Long>(aCenter.getX() - (0.5 * aAbsScale.getX())) - aUntransformedFrameArea.Left(),
210 basegfx::fround<tools::Long>(aCenter.getY() - (0.5 * aAbsScale.getY())) - aUntransformedFrameArea.Top(),
211 basegfx::fround<tools::Long>(aAbsScale.getX()),
212 basegfx::fround<tools::Long>(aAbsScale.getY()));
216 void TransformableSwFrame::createFrameAreaTransformations(
217 double fRotation,
218 const basegfx::B2DPoint& rCenter)
220 const basegfx::B2DHomMatrix aRotateAroundCenter(
221 basegfx::utils::createRotateAroundPoint(
222 rCenter.getX(),
223 rCenter.getY(),
224 fRotation));
225 const SwRect& rFrameArea(mrSwFrameAreaDefinition.getFrameArea());
226 const SwRect& rFramePrintArea(mrSwFrameAreaDefinition.getFramePrintArea());
228 maFrameAreaTransformation = aRotateAroundCenter * basegfx::utils::createScaleTranslateB2DHomMatrix(
229 rFrameArea.Width(), rFrameArea.Height(),
230 rFrameArea.Left(), rFrameArea.Top());
231 maFramePrintAreaTransformation = aRotateAroundCenter * basegfx::utils::createScaleTranslateB2DHomMatrix(
232 rFramePrintArea.Width(), rFramePrintArea.Height(),
233 rFramePrintArea.Left() + rFrameArea.Left(), rFramePrintArea.Top() + rFrameArea.Top());
236 void TransformableSwFrame::adaptFrameAreasToTransformations()
238 if(!getLocalFrameAreaTransformation().isIdentity())
240 basegfx::B2DRange aRangeFrameArea(0.0, 0.0, 1.0, 1.0);
241 aRangeFrameArea.transform(getLocalFrameAreaTransformation());
242 const SwRect aNewFrm(
243 basegfx::fround<tools::Long>(aRangeFrameArea.getMinX()), basegfx::fround<tools::Long>(aRangeFrameArea.getMinY()),
244 basegfx::fround<tools::Long>(aRangeFrameArea.getWidth()), basegfx::fround<tools::Long>(aRangeFrameArea.getHeight()));
246 if(aNewFrm != mrSwFrameAreaDefinition.getFrameArea())
248 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(mrSwFrameAreaDefinition);
249 aFrm.setSwRect(aNewFrm);
253 if(getLocalFramePrintAreaTransformation().isIdentity())
254 return;
256 basegfx::B2DRange aRangeFramePrintArea(0.0, 0.0, 1.0, 1.0);
257 aRangeFramePrintArea.transform(getLocalFramePrintAreaTransformation());
258 const SwRect aNewPrt(
259 basegfx::fround<tools::Long>(aRangeFramePrintArea.getMinX()) - mrSwFrameAreaDefinition.getFrameArea().Left(),
260 basegfx::fround<tools::Long>(aRangeFramePrintArea.getMinY()) - mrSwFrameAreaDefinition.getFrameArea().Top(),
261 basegfx::fround<tools::Long>(aRangeFramePrintArea.getWidth()),
262 basegfx::fround<tools::Long>(aRangeFramePrintArea.getHeight()));
264 if(aNewPrt != mrSwFrameAreaDefinition.getFramePrintArea())
266 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(mrSwFrameAreaDefinition);
267 aPrt.setSwRect(aNewPrt);
271 void TransformableSwFrame::restoreFrameAreas()
273 // This can be done fully based on the Transformations currently
274 // set, so use this. Only needed when transformation *is* used
275 if(!getLocalFrameAreaTransformation().isIdentity())
277 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(mrSwFrameAreaDefinition);
278 aFrm.setSwRect(getUntransformedFrameArea());
281 if(!getLocalFramePrintAreaTransformation().isIdentity())
283 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(mrSwFrameAreaDefinition);
284 aPrt.setSwRect(getUntransformedFramePrintArea());
288 // transform by given B2DHomMatrix
289 void TransformableSwFrame::transform(const basegfx::B2DHomMatrix& aTransform)
291 maFrameAreaTransformation *= aTransform;
292 maFramePrintAreaTransformation *= aTransform;
295 void SwFrame::dumpAsXml(xmlTextWriterPtr pWriter) const
297 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("infos"));
298 dumpInfosAsXml(pWriter);
299 (void)xmlTextWriterEndElement(pWriter);
301 if ( m_pDrawObjs && m_pDrawObjs->size() > 0 )
303 (void)xmlTextWriterStartElement( pWriter, BAD_CAST( "anchored" ) );
305 for (SwAnchoredObject* pObject : *m_pDrawObjs)
307 pObject->dumpAsXml( pWriter );
310 (void)xmlTextWriterEndElement( pWriter );
313 dumpChildrenAsXml(pWriter);
316 void SwFrame::dumpChildrenAsXml( xmlTextWriterPtr writer ) const
318 const SwFrame *pFrame = GetLower( );
319 for ( ; pFrame != nullptr; pFrame = pFrame->GetNext( ) )
321 pFrame->dumpAsXml( writer );
325 SwFrame::SwFrame( sw::BroadcastingModify *pMod, SwFrame* pSib )
326 : SwClient( pMod ),
327 mpRoot( pSib ? pSib->getRootFrame() : nullptr ),
328 mpUpper(nullptr),
329 mpNext(nullptr),
330 mpPrev(nullptr),
331 mnFrameType(SwFrameType::None),
332 mbInDtor(false),
333 mbInvalidR2L(true),
334 mbDerivedR2L(false),
335 mbRightToLeft(false),
336 mbInvalidVert(true),
337 mbDerivedVert(false),
338 mbVertical(false),
339 mbVertLR(false),
340 mbVertLRBT(false),
341 mbValidLineNum(false),
342 mbFixSize(false),
343 mbCompletePaint(true),
344 mbRetouche(false),
345 mbInfInvalid(true),
346 mbInfBody( false ),
347 mbInfTab ( false ),
348 mbInfFly ( false ),
349 mbInfFootnote ( false ),
350 mbInfSct ( false ),
351 mbColLocked(false),
352 m_isInDestroy(false),
353 mnForbidDelete(0)
355 OSL_ENSURE( pMod, "No frame format given." );
358 const IDocumentDrawModelAccess& SwFrame::getIDocumentDrawModelAccess()
360 return GetUpper()->GetFormat()->getIDocumentDrawModelAccess();
363 bool SwFrame::KnowsFormat( const SwFormat& rFormat ) const
365 return GetRegisteredIn() == &rFormat;
368 void SwFrame::RegisterToFormat( SwFormat& rFormat )
370 rFormat.Add(*this);
373 void SwFrame::CheckDir( SvxFrameDirection nDir, bool bVert, bool bOnlyBiDi, bool bBrowse )
375 if( SvxFrameDirection::Environment == nDir || ( bVert && bOnlyBiDi ) )
377 mbDerivedVert = true;
378 if( SvxFrameDirection::Environment == nDir )
379 mbDerivedR2L = true;
380 SetDirFlags( bVert );
382 else if( bVert )
384 mbInvalidVert = false;
385 if( SvxFrameDirection::Horizontal_LR_TB == nDir || SvxFrameDirection::Horizontal_RL_TB == nDir
386 || bBrowse )
388 mbVertical = false;
389 mbVertLR = false;
390 mbVertLRBT = false;
392 else
394 mbVertical = true;
395 if(SvxFrameDirection::Vertical_RL_TB == nDir)
397 mbVertLR = false;
398 mbVertLRBT = false;
400 else if(SvxFrameDirection::Vertical_LR_TB==nDir)
402 mbVertLR = true;
403 mbVertLRBT = false;
405 else if (nDir == SvxFrameDirection::Vertical_LR_BT)
407 mbVertLR = true;
408 mbVertLRBT = true;
410 else if (nDir == SvxFrameDirection::Vertical_RL_TB90)
412 // not yet implemented, render as RL_TB
413 mbVertLR = false;
414 mbVertLRBT = false;
418 else
420 mbInvalidR2L = false;
421 if( SvxFrameDirection::Horizontal_RL_TB == nDir )
422 mbRightToLeft = true;
423 else
424 mbRightToLeft = false;
428 void SwFrame::CheckDirection( bool bVert )
430 if( bVert )
432 if( !IsHeaderFrame() && !IsFooterFrame() )
434 mbDerivedVert = true;
435 SetDirFlags( bVert );
438 else
440 mbDerivedR2L = true;
441 SetDirFlags( bVert );
445 void SwSectionFrame::CheckDirection( bool bVert )
447 const SwFrameFormat* pFormat = GetFormat();
448 if( pFormat )
450 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
451 const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
452 CheckDir(pFormat->GetFormatAttr(RES_FRAMEDIR).GetValue(),
453 bVert, true, bBrowseMode );
455 else
456 SwFrame::CheckDirection( bVert );
459 void SwFlyFrame::CheckDirection( bool bVert )
461 const SwFrameFormat* pFormat = GetFormat();
462 if( pFormat )
464 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
465 const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
466 CheckDir(pFormat->GetFormatAttr(RES_FRAMEDIR).GetValue(),
467 bVert, false, bBrowseMode );
469 else
470 SwFrame::CheckDirection( bVert );
473 void SwTabFrame::CheckDirection( bool bVert )
475 const SwFrameFormat* pFormat = GetFormat();
476 if( pFormat )
478 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
479 const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
480 CheckDir(pFormat->GetFormatAttr(RES_FRAMEDIR).GetValue(),
481 bVert, true, bBrowseMode );
483 else
484 SwFrame::CheckDirection( bVert );
487 void SwCellFrame::CheckDirection( bool bVert )
489 const SwFrameFormat* pFormat = GetFormat();
490 const SvxFrameDirectionItem* pFrameDirItem;
491 // Check if the item is set, before actually
492 // using it. Otherwise the dynamic pool default is used, which may be set
493 // to LTR in case of OOo 1.0 documents.
494 if( pFormat && (pFrameDirItem = pFormat->GetItemIfSet( RES_FRAMEDIR ) ) )
496 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
497 const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
498 CheckDir( pFrameDirItem->GetValue(), bVert, false, bBrowseMode );
500 else
501 SwFrame::CheckDirection( bVert );
504 void SwTextFrame::CheckDirection( bool bVert )
506 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
507 const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
508 CheckDir(GetTextNodeForParaProps()->GetSwAttrSet().GetFrameDir().GetValue(),
509 bVert, true, bBrowseMode);
512 void SwFrame::SwClientNotify(const SwModify&, const SfxHint& rHint)
514 if (rHint.GetId() != SfxHintId::SwLegacyModify && rHint.GetId() != SfxHintId::SwFormatChange
515 && rHint.GetId() != SfxHintId::SwAttrSetChange)
516 return;
518 SwFrameInvFlags eInvFlags = SwFrameInvFlags::NONE;
519 if (rHint.GetId() == SfxHintId::SwLegacyModify)
521 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
522 UpdateAttrFrame(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
524 else if (rHint.GetId() == SfxHintId::SwAttrSetChange)
526 auto pChangeHint = static_cast<const sw::AttrSetChangeHint*>(&rHint);
527 if(pChangeHint->m_pOld && pChangeHint->m_pNew)
529 SfxItemIter aNIter(*pChangeHint->m_pNew->GetChgSet());
530 SfxItemIter aOIter(*pChangeHint->m_pOld->GetChgSet());
531 const SfxPoolItem* pNItem = aNIter.GetCurItem();
532 const SfxPoolItem* pOItem = aOIter.GetCurItem();
535 UpdateAttrFrame(pOItem, pNItem, eInvFlags);
536 pNItem = aNIter.NextItem();
537 pOItem = aOIter.NextItem();
538 } while (pNItem);
541 else // rHint.GetId() == SfxHintId::SwFormatChange
543 UpdateAttrFrameForFormatChange(eInvFlags);
546 if(eInvFlags == SwFrameInvFlags::NONE)
547 return;
549 SwPageFrame* pPage = FindPageFrame();
550 InvalidatePage(pPage);
551 if(eInvFlags & SwFrameInvFlags::InvalidatePrt)
553 InvalidatePrt_();
554 if(!GetPrev() && IsTabFrame() && IsInSct())
555 FindSctFrame()->InvalidatePrt_();
557 if(eInvFlags & SwFrameInvFlags::InvalidateSize)
558 InvalidateSize_();
559 if(eInvFlags & SwFrameInvFlags::InvalidatePos)
560 InvalidatePos_();
561 if(eInvFlags & SwFrameInvFlags::SetCompletePaint)
562 SetCompletePaint();
563 SwFrame *pNxt;
564 if (eInvFlags & (SwFrameInvFlags::NextInvalidatePos | SwFrameInvFlags::NextSetCompletePaint)
565 && nullptr != (pNxt = GetNext()))
567 pNxt->InvalidatePage(pPage);
568 if(eInvFlags & SwFrameInvFlags::NextInvalidatePos)
569 pNxt->InvalidatePos_();
570 if(eInvFlags & SwFrameInvFlags::NextSetCompletePaint)
571 pNxt->SetCompletePaint();
575 void SwFrame::UpdateAttrFrame( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
576 SwFrameInvFlags &rInvFlags )
578 sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
579 switch( nWhich )
581 case RES_BOX:
582 case RES_SHADOW:
583 Prepare( PrepareHint::FixSizeChanged );
584 [[fallthrough]];
585 case RES_MARGIN_FIRSTLINE:
586 case RES_MARGIN_TEXTLEFT:
587 case RES_MARGIN_RIGHT:
588 case RES_LR_SPACE:
589 case RES_UL_SPACE:
590 case RES_RTL_GUTTER:
591 rInvFlags |= SwFrameInvFlags::InvalidatePrt | SwFrameInvFlags::InvalidateSize
592 | SwFrameInvFlags::SetCompletePaint;
593 break;
595 case RES_HEADER_FOOTER_EAT_SPACING:
596 rInvFlags |= SwFrameInvFlags::InvalidatePrt | SwFrameInvFlags::InvalidateSize;
597 break;
599 case RES_BACKGROUND:
600 case RES_BACKGROUND_FULL_SIZE:
601 rInvFlags |= SwFrameInvFlags::SetCompletePaint | SwFrameInvFlags::NextSetCompletePaint;
602 break;
604 case RES_KEEP:
605 rInvFlags |= SwFrameInvFlags::InvalidatePos;
606 break;
608 case RES_FRM_SIZE:
609 ReinitializeFrameSizeAttrFlags();
610 rInvFlags |= SwFrameInvFlags::InvalidatePrt | SwFrameInvFlags::InvalidateSize
611 | SwFrameInvFlags::NextInvalidatePos;
612 break;
614 case RES_ROW_SPLIT:
616 if ( IsRowFrame() )
618 bool bInFollowFlowRow = nullptr != IsInFollowFlowRow();
619 if ( bInFollowFlowRow || nullptr != IsInSplitTableRow() )
621 SwTabFrame* pTab = FindTabFrame();
622 if ( bInFollowFlowRow )
623 pTab = pTab->FindMaster();
624 pTab->SetRemoveFollowFlowLinePending( true );
627 break;
629 case RES_COL:
630 OSL_FAIL( "Columns for new FrameType?" );
631 break;
633 default:
634 // the new FillStyle has to do the same as previous RES_BACKGROUND
635 if(nWhich >= XATTR_FILL_FIRST && nWhich <= XATTR_FILL_LAST)
637 rInvFlags
638 |= SwFrameInvFlags::SetCompletePaint | SwFrameInvFlags::NextSetCompletePaint;
640 /* do Nothing */;
644 // static
645 void SwFrame::UpdateAttrFrameForFormatChange( SwFrameInvFlags &rInvFlags )
647 rInvFlags |= SwFrameInvFlags::InvalidatePrt | SwFrameInvFlags::InvalidateSize
648 | SwFrameInvFlags::InvalidatePos | SwFrameInvFlags::SetCompletePaint;
651 bool SwFrame::Prepare( const PrepareHint, const void *, bool )
653 /* Do nothing */
654 return false;
658 * Invalidates the page in which the Frame is currently placed.
659 * The page is invalidated depending on the type (Layout, Content, FlyFrame)
661 void SwFrame::InvalidatePage( const SwPageFrame *pPage ) const
663 if ( !pPage )
665 pPage = FindPageFrame();
666 // #i28701# - for at-character and as-character
667 // anchored Writer fly frames additionally invalidate also page frame
668 // its 'anchor character' is on.
669 if ( pPage && pPage->GetUpper() && IsFlyFrame() )
671 const SwFlyFrame* pFlyFrame = static_cast<const SwFlyFrame*>(this);
672 if ( pFlyFrame->IsAutoPos() || pFlyFrame->IsFlyInContentFrame() )
674 // #i33751#, #i34060# - method <GetPageFrameOfAnchor()>
675 // is replaced by method <FindPageFrameOfAnchor()>. It's return value
676 // have to be checked.
677 SwPageFrame* pPageFrameOfAnchor =
678 const_cast<SwFlyFrame*>(pFlyFrame)->FindPageFrameOfAnchor();
679 if ( pPageFrameOfAnchor && pPageFrameOfAnchor != pPage )
681 InvalidatePage( pPageFrameOfAnchor );
687 if ( !(pPage && pPage->GetUpper()) )
688 return;
690 if ( pPage->GetFormat()->GetDoc()->IsInDtor() )
691 return;
693 SwRootFrame *pRoot = const_cast<SwRootFrame*>(static_cast<const SwRootFrame*>(pPage->GetUpper()));
694 const SwFlyFrame *pFly = FindFlyFrame();
695 if ( IsContentFrame() )
697 if ( pRoot->IsTurboAllowed() )
699 // If a ContentFrame wants to register for a second time, make it a TurboAction.
700 if ( !pRoot->GetTurbo() || this == pRoot->GetTurbo() )
701 pRoot->SetTurbo( static_cast<const SwContentFrame*>(this) );
702 else
704 pRoot->DisallowTurbo();
705 //The page of the Turbo could be a different one then mine,
706 //therefore we have to invalidate it.
707 const SwFrame *pTmp = pRoot->GetTurbo();
708 pRoot->ResetTurbo();
709 pTmp->InvalidatePage();
712 if ( !pRoot->GetTurbo() )
714 if ( pFly )
715 { if( !pFly->IsLocked() )
717 if ( pFly->IsFlyInContentFrame() )
718 { pPage->InvalidateFlyInCnt();
719 pFly->GetAnchorFrame()->InvalidatePage();
721 else
722 pPage->InvalidateFlyContent();
725 else
726 pPage->InvalidateContent();
729 else
731 pRoot->DisallowTurbo();
732 if ( pFly )
734 if ( !pFly->IsLocked() )
736 if ( pFly->IsFlyInContentFrame() )
738 pPage->InvalidateFlyInCnt();
739 pFly->GetAnchorFrame()->InvalidatePage();
741 else
742 pPage->InvalidateFlyLayout();
745 else
746 pPage->InvalidateLayout();
748 if ( pRoot->GetTurbo() )
749 { const SwFrame *pTmp = pRoot->GetTurbo();
750 pRoot->ResetTurbo();
751 pTmp->InvalidatePage();
754 pRoot->SetIdleFlags();
756 if (!IsTextFrame())
757 return;
759 SwTextFrame const*const pText(static_cast<SwTextFrame const*>(this));
760 if (sw::MergedPara const*const pMergedPara = pText->GetMergedPara())
762 SwTextNode const* pNode(nullptr);
763 for (auto const& e : pMergedPara->extents)
765 if (e.pNode != pNode)
767 pNode = e.pNode;
768 if (pNode->IsGrammarCheckDirty())
770 pRoot->SetNeedGrammarCheck( true );
771 break;
776 else
778 if (pText->GetTextNodeFirst()->IsGrammarCheckDirty())
780 pRoot->SetNeedGrammarCheck( true );
785 Size SwFrame::ChgSize( const Size& aNewSize )
787 mbFixSize = true;
788 const Size aOldSize( getFrameArea().SSize() );
789 if ( aNewSize == aOldSize )
790 return aOldSize;
792 if ( GetUpper() )
794 bool bNeighb = IsNeighbourFrame();
795 SwRectFnSet fnRect(IsVertical() != bNeighb, IsVertLR(), IsVertLRBT());
796 SwRect aNew( Point(0,0), aNewSize );
799 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
800 fnRect.SetWidth(aFrm, fnRect.GetWidth(aNew));
803 tools::Long nNew = fnRect.GetHeight(aNew);
804 tools::Long nDiff = nNew - fnRect.GetHeight(getFrameArea());
806 if( nDiff )
808 if ( GetUpper()->IsFootnoteBossFrame() && HasFixSize() &&
809 SwNeighbourAdjust::GrowShrink !=
810 static_cast<SwFootnoteBossFrame*>(GetUpper())->NeighbourhoodAdjustment() )
813 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
814 fnRect.SetHeight(aFrm, nNew);
817 SwTwips nReal = static_cast<SwLayoutFrame*>(this)->AdjustNeighbourhood(nDiff);
819 if ( nReal != nDiff )
821 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
822 fnRect.SetHeight(aFrm, nNew - nDiff + nReal);
825 else
827 // OD 24.10.2002 #97265# - grow/shrink not for neighbour frames
828 // NOTE: neighbour frames are cell and column frames.
829 if ( !bNeighb )
831 if ( nDiff > 0 )
832 Grow( nDiff );
833 else
834 Shrink( -nDiff );
836 if (GetUpper() && fnRect.GetHeight(getFrameArea()) != nNew)
838 GetUpper()->InvalidateSize_();
842 // Even if grow/shrink did not yet set the desired width, for
843 // example when called by ChgColumns to set the column width, we
844 // set the right width now.
845 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
846 fnRect.SetHeight(aFrm, nNew);
850 else
852 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
853 aFrm.SSize( aNewSize );
856 if ( getFrameArea().SSize() != aOldSize )
858 SwPageFrame *pPage = FindPageFrame();
859 if ( GetNext() )
861 GetNext()->InvalidatePos_();
862 GetNext()->InvalidatePage( pPage );
864 if( IsLayoutFrame() )
866 if( IsRightToLeft() )
867 InvalidatePos_();
868 if( static_cast<SwLayoutFrame*>(this)->Lower() )
869 static_cast<SwLayoutFrame*>(this)->Lower()->InvalidateSize_();
871 InvalidatePrt_();
872 InvalidateSize_();
873 InvalidatePage( pPage );
876 return getFrameArea().SSize();
879 /** Insert SwFrame into existing structure.
881 * Insertion is done below the parent either before pBehind or
882 * at the end of the chain if pBehind is empty.
884 void SwFrame::InsertBefore( SwLayoutFrame* pParent, SwFrame* pBehind )
886 OSL_ENSURE( pParent, "No parent for insert." );
887 OSL_ENSURE( (!pBehind || pParent == pBehind->GetUpper()),
888 "Frame tree is inconsistent." );
890 mpUpper = pParent;
891 mpNext = pBehind;
892 if( pBehind )
893 { //Insert before pBehind.
894 mpPrev = pBehind->mpPrev;
895 if( nullptr != mpPrev )
896 mpPrev->mpNext = this;
897 else
898 mpUpper->m_pLower = this;
899 pBehind->mpPrev = this;
901 else
902 { //Insert at the end, or as first node in the sub tree
903 mpPrev = mpUpper->Lower();
904 if ( mpPrev )
906 while( mpPrev->mpNext )
907 mpPrev = mpPrev->mpNext;
908 mpPrev->mpNext = this;
910 else
911 mpUpper->m_pLower = this;
915 /** Insert SwFrame into existing structure.
917 * Insertion is done below the parent either after pBehind or
918 * at the beginning of the chain if pBehind is empty.
920 void SwFrame::InsertBehind( SwLayoutFrame *pParent, SwFrame *pBefore )
922 OSL_ENSURE( (!pBefore || pParent == pBefore->GetUpper()),
923 "Frame tree is inconsistent." );
925 mpUpper = pParent;
926 mpPrev = pBefore;
927 if ( pBefore )
929 //Insert after pBefore
930 mpNext = pBefore->mpNext;
931 if ( nullptr != mpNext )
932 mpNext->mpPrev = this;
933 pBefore->mpNext = this;
935 else
937 assert(pParent && "No Parent for Insert.");
938 //Insert at the beginning of the chain
939 mpNext = pParent->Lower();
940 if ( mpNext )
941 mpNext->mpPrev = this;
942 pParent->m_pLower = this;
946 /** Insert a chain of SwFrames into an existing structure
948 * Currently, this method is used to insert a SectionFrame (which may have some siblings) into an
949 * existing structure. If the third parameter is NULL, this method is (besides handling the
950 * siblings) equal to SwFrame::InsertBefore(..).
952 * If the third parameter is passed, the following happens:
953 * - this becomes mpNext of pParent
954 * - pSct becomes mpNext of the last one in the this-chain
955 * - pBehind is reconnected from pParent to pSct
956 * The purpose is: a SectionFrame (this) won't become a child of another SectionFrame (pParent), but
957 * pParent gets split into two siblings (pParent+pSect) and this is inserted between.
959 bool SwFrame::InsertGroupBefore( SwFrame* pParent, SwFrame* pBehind, SwFrame* pSct )
961 OSL_ENSURE( pParent, "No parent for insert." );
962 OSL_ENSURE( (!pBehind || ( (pBehind && (pParent == pBehind->GetUpper()))
963 || ((pParent->IsSctFrame() && pBehind->GetUpper()->IsColBodyFrame())) ) ),
964 "Frame tree inconsistent." );
965 if( pSct )
967 mpUpper = pParent->GetUpper();
968 SwFrame *pLast = this;
969 while( pLast->GetNext() )
971 pLast = pLast->GetNext();
972 pLast->mpUpper = GetUpper();
974 if( pBehind )
976 pLast->mpNext = pSct;
977 pSct->mpPrev = pLast;
978 pSct->mpNext = pParent->GetNext();
980 else
982 pLast->mpNext = pParent->GetNext();
983 if( pLast->GetNext() )
984 pLast->GetNext()->mpPrev = pLast;
986 pParent->mpNext = this;
987 mpPrev = pParent;
988 if( pSct->GetNext() )
989 pSct->GetNext()->mpPrev = pSct;
990 while( pLast->GetNext() )
992 pLast = pLast->GetNext();
993 pLast->mpUpper = GetUpper();
995 if( pBehind )
996 { // Insert before pBehind.
997 if( pBehind->GetPrev() )
998 pBehind->GetPrev()->mpNext = nullptr;
999 else
1000 pBehind->GetUpper()->m_pLower = nullptr;
1001 pBehind->mpPrev = nullptr;
1002 SwLayoutFrame* pTmp = static_cast<SwLayoutFrame*>(pSct);
1003 SwFrame* pLower = pTmp->Lower();
1004 if( pLower )
1006 OSL_ENSURE( pLower->IsColumnFrame(), "InsertGrp: Used SectionFrame" );
1007 pTmp = static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(pLower)->Lower());
1008 OSL_ENSURE( pTmp, "InsertGrp: Missing ColBody" );
1010 pBehind->mpUpper = pTmp;
1011 pBehind->GetUpper()->m_pLower = pBehind;
1012 pLast = pBehind->GetNext();
1013 while ( pLast )
1015 pLast->mpUpper = pBehind->GetUpper();
1016 pLast = pLast->GetNext();
1019 else
1021 OSL_ENSURE( pSct->IsSctFrame(), "InsertGroup: For SectionFrames only" );
1022 SwFrame::DestroyFrame(pSct);
1023 return false;
1026 else
1028 mpUpper = static_cast<SwLayoutFrame*>(pParent);
1029 SwFrame *pLast = this;
1030 while( pLast->GetNext() )
1032 pLast = pLast->GetNext();
1033 pLast->mpUpper = GetUpper();
1035 pLast->mpNext = pBehind;
1036 if( pBehind )
1037 { // Insert before pBehind.
1038 mpPrev = pBehind->mpPrev;
1039 if( nullptr != mpPrev )
1040 mpPrev->mpNext = this;
1041 else
1042 mpUpper->m_pLower = this;
1043 pBehind->mpPrev = pLast;
1045 else
1047 //Insert at the end, or ... the first node in the subtree
1048 mpPrev = mpUpper->Lower();
1049 if ( mpPrev )
1051 while( mpPrev->mpNext )
1052 mpPrev = mpPrev->mpNext;
1053 mpPrev->mpNext = this;
1055 else
1056 mpUpper->m_pLower = this;
1059 return true;
1062 void SwFrame::RemoveFromLayout()
1064 OSL_ENSURE( mpUpper, "Remove without upper?" );
1066 if (mpPrev)
1067 // one out of the middle is removed
1068 mpPrev->mpNext = mpNext;
1069 else if (mpUpper)
1070 { // the first in a list is removed //TODO
1071 OSL_ENSURE( mpUpper->m_pLower == this, "Layout is inconsistent." );
1072 mpUpper->m_pLower = mpNext;
1074 if( mpNext )
1075 mpNext->mpPrev = mpPrev;
1077 // Remove link
1078 mpNext = mpPrev = nullptr;
1079 mpUpper = nullptr;
1082 void SwContentFrame::Paste( SwFrame* pParent, SwFrame* pSibling)
1084 OSL_ENSURE( pParent, "No parent for pasting." );
1085 OSL_ENSURE( pParent->IsLayoutFrame(), "Parent is ContentFrame." );
1086 OSL_ENSURE( pParent != this, "I'm the parent." );
1087 OSL_ENSURE( pSibling != this, "I'm my own neighbour." );
1088 OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(),
1089 "I'm still registered somewhere" );
1090 OSL_ENSURE( !pSibling || pSibling->IsFlowFrame(),
1091 "<SwContentFrame::Paste(..)> - sibling not of expected type." );
1093 //Insert in the tree.
1094 InsertBefore( static_cast<SwLayoutFrame*>(pParent), pSibling );
1096 SwPageFrame *pPage = FindPageFrame();
1097 InvalidateAll_();
1098 InvalidatePage( pPage );
1100 if( pPage )
1102 pPage->InvalidateSpelling();
1103 pPage->InvalidateSmartTags();
1104 pPage->InvalidateAutoCompleteWords();
1105 pPage->InvalidateWordCount();
1108 if ( GetNext() )
1110 SwFrame* pNxt = GetNext();
1111 pNxt->InvalidatePrt_();
1112 pNxt->InvalidatePos_();
1113 pNxt->InvalidatePage( pPage );
1114 if( pNxt->IsSctFrame() )
1115 pNxt = static_cast<SwSectionFrame*>(pNxt)->ContainsContent();
1116 if( pNxt && pNxt->IsTextFrame() && pNxt->IsInFootnote() )
1117 pNxt->Prepare( PrepareHint::FootnoteInvalidation, nullptr, false );
1120 if ( getFrameArea().Height() )
1121 pParent->Grow( getFrameArea().Height() );
1123 if ( getFrameArea().Width() != pParent->getFramePrintArea().Width() )
1124 Prepare( PrepareHint::FixSizeChanged );
1126 if ( GetPrev() )
1128 if ( IsFollow() )
1129 //I'm a direct follower of my master now
1130 static_cast<SwContentFrame*>(GetPrev())->Prepare( PrepareHint::FollowFollows );
1131 else
1133 if ( GetPrev()->getFrameArea().Height() !=
1134 GetPrev()->getFramePrintArea().Height() + GetPrev()->getFramePrintArea().Top() )
1136 // Take the border into account?
1137 GetPrev()->InvalidatePrt_();
1139 // OD 18.02.2003 #104989# - force complete paint of previous frame,
1140 // if frame is inserted at the end of a section frame, in order to
1141 // get subsidiary lines repainted for the section.
1142 if ( pParent->IsSctFrame() && !GetNext() )
1144 // force complete paint of previous frame, if new inserted frame
1145 // in the section is the last one.
1146 GetPrev()->SetCompletePaint();
1148 GetPrev()->InvalidatePage( pPage );
1151 if ( IsInFootnote() )
1153 SwFrame* pFrame = GetIndPrev();
1154 if( pFrame && pFrame->IsSctFrame() )
1155 pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
1156 if( pFrame )
1157 pFrame->Prepare( PrepareHint::QuoVadis, nullptr, false );
1158 if( !GetNext() )
1160 pFrame = FindFootnoteFrame()->GetNext();
1161 if( pFrame && nullptr != (pFrame=static_cast<SwLayoutFrame*>(pFrame)->ContainsAny()) )
1162 pFrame->InvalidatePrt_();
1166 InvalidateLineNum_();
1167 SwFrame *pNxt = FindNextCnt();
1168 if ( !pNxt )
1169 return;
1171 while ( pNxt && pNxt->IsInTab() )
1173 pNxt = pNxt->FindTabFrame();
1174 if( nullptr != pNxt )
1175 pNxt = pNxt->FindNextCnt();
1177 if ( pNxt )
1179 pNxt->InvalidateLineNum_();
1180 if ( pNxt != GetNext() )
1181 pNxt->InvalidatePage();
1185 void SwContentFrame::Cut()
1187 OSL_ENSURE( GetUpper(), "Cut without Upper()." );
1189 SwPageFrame *pPage = FindPageFrame();
1190 InvalidatePage( pPage );
1191 SwFrame *pFrame = GetIndPrev();
1192 if( pFrame )
1194 if( pFrame->IsSctFrame() )
1195 pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
1196 if ( pFrame && pFrame->IsContentFrame() )
1198 pFrame->InvalidatePrt_();
1199 if( IsInFootnote() )
1200 pFrame->Prepare( PrepareHint::QuoVadis, nullptr, false );
1202 // #i26250# - invalidate printing area of previous
1203 // table frame.
1204 else if ( pFrame && pFrame->IsTabFrame() )
1206 pFrame->InvalidatePrt();
1210 SwFrame *pNxt = FindNextCnt();
1211 if ( pNxt )
1213 while ( pNxt && pNxt->IsInTab() )
1215 pNxt = pNxt->FindTabFrame();
1216 if( nullptr != pNxt )
1217 pNxt = pNxt->FindNextCnt();
1219 if ( pNxt )
1221 pNxt->InvalidateLineNum_();
1222 if ( pNxt != GetNext() )
1223 pNxt->InvalidatePage();
1227 SwTabFrame* pMasterTab(nullptr);
1228 pFrame = GetIndNext();
1229 if( pFrame )
1231 // The old follow may have calculated a gap to the predecessor which
1232 // now becomes obsolete or different as it becomes the first one itself
1233 pFrame->InvalidatePrt_();
1234 pFrame->InvalidatePos_();
1235 pFrame->InvalidatePage( pPage );
1236 if( pFrame->IsSctFrame() )
1238 pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
1239 if( pFrame )
1241 pFrame->InvalidatePrt_();
1242 pFrame->InvalidatePos_();
1243 pFrame->InvalidatePage( pPage );
1246 if( pFrame && IsInFootnote() )
1247 pFrame->Prepare( PrepareHint::ErgoSum, nullptr, false );
1248 if( IsInSct() && !GetPrev() )
1250 SwSectionFrame* pSct = FindSctFrame();
1251 if( !pSct->IsFollow() )
1253 pSct->InvalidatePrt_();
1254 pSct->InvalidatePage( pPage );
1258 else
1260 InvalidateNextPos();
1261 //Someone needs to do the retouching: predecessor or upper
1262 pFrame = GetPrev();
1263 if ( nullptr != pFrame )
1264 { pFrame->SetRetouche();
1265 pFrame->Prepare( PrepareHint::WidowsOrphans );
1266 pFrame->InvalidatePos_();
1267 pFrame->InvalidatePage( pPage );
1269 // If I'm (was) the only ContentFrame in my upper, it has to do the
1270 // retouching. Also, perhaps a page became empty.
1271 else
1272 { SwRootFrame *pRoot = getRootFrame();
1273 if ( pRoot )
1275 pRoot->SetSuperfluous();
1276 // RemoveSuperfluous can only remove empty pages at the end;
1277 // find if there are pages without content following pPage
1278 // and if so request a call to CheckPageDescs()
1279 SwViewShell *pSh = pRoot->GetCurrShell();
1280 // tdf#152983 pPage is null when called from SwHeadFootFrame ctor
1281 if (pPage && pSh && pSh->Imp()->IsAction())
1283 SwPageFrame const* pNext(pPage);
1284 while ((pNext = static_cast<SwPageFrame const*>(pNext->GetNext())))
1286 if (!sw::IsPageFrameEmpty(*pNext) && !pNext->IsFootnotePage())
1288 pSh->Imp()->GetLayAction().SetCheckPageNum(pPage->GetPhyPageNum());
1289 break;
1293 GetUpper()->SetCompletePaint();
1294 GetUpper()->InvalidatePage( pPage );
1296 if( IsInSct() )
1298 SwSectionFrame* pSct = FindSctFrame();
1299 if( !pSct->IsFollow() )
1301 pSct->InvalidatePrt_();
1302 pSct->InvalidatePage( pPage );
1305 // #i52253# The master table should take care
1306 // of removing the follow flow line.
1307 if ( IsInTab() )
1309 SwTabFrame* pThisTab = FindTabFrame();
1310 if (pThisTab && pThisTab->IsFollow())
1312 pMasterTab = pThisTab->FindMaster();
1317 //Remove first, then shrink the upper.
1318 SwLayoutFrame *pUp = GetUpper();
1319 RemoveFromLayout();
1320 if ( !pUp )
1322 assert(!pMasterTab);
1323 return;
1326 if (pMasterTab
1327 && !pMasterTab->GetFollow()->GetFirstNonHeadlineRow()->ContainsContent())
1328 { // only do this if there's no content in other cells of the row!
1329 pMasterTab->InvalidatePos_();
1330 pMasterTab->SetRemoveFollowFlowLinePending(true);
1333 SwSectionFrame *pSct = nullptr;
1334 if ( !pUp->Lower() &&
1335 ( ( pUp->IsFootnoteFrame() && !pUp->IsColLocked() ) ||
1336 ( pUp->IsInSct() &&
1337 // #i29438#
1338 // We have to consider the case that the section may be "empty"
1339 // except from a temporary empty table frame.
1340 // This can happen due to the new cell split feature.
1341 !pUp->IsCellFrame() &&
1342 // #126020# - adjust check for empty section
1343 // #130797# - correct fix #126020#
1344 !(pSct = pUp->FindSctFrame())->ContainsContent() &&
1345 !pSct->ContainsAny( true ) ) ) )
1347 if ( pUp->GetUpper() )
1350 // prevent delete of <ColLocked> footnote frame
1351 if ( pUp->IsFootnoteFrame() && !pUp->IsColLocked())
1353 if( pUp->GetNext() && !pUp->GetPrev() )
1355 SwFrame* pTmp = static_cast<SwLayoutFrame*>(pUp->GetNext())->ContainsAny();
1356 if( pTmp )
1357 pTmp->InvalidatePrt_();
1359 if (!pUp->IsDeleteForbidden())
1361 pUp->Cut();
1362 SwFrame::DestroyFrame(pUp);
1365 else
1367 assert(pSct);
1368 if ( pSct->IsColLocked() || !pSct->IsInFootnote() ||
1369 ( pUp->IsFootnoteFrame() && pUp->IsColLocked() ) )
1371 pSct->DelEmpty( false );
1372 // If a locked section may not be deleted then at least
1373 // its size became invalid after removing its last
1374 // content.
1375 pSct->InvalidateSize_();
1377 else
1379 pSct->DelEmpty( true );
1380 SwFrame::DestroyFrame(pSct);
1385 else
1387 SwRectFnSet aRectFnSet(this);
1388 tools::Long nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
1389 if( nFrameHeight )
1390 pUp->Shrink( nFrameHeight );
1394 void SwLayoutFrame::Paste( SwFrame* pParent, SwFrame* pSibling)
1396 OSL_ENSURE( pParent, "No parent for pasting." );
1397 OSL_ENSURE( pParent->IsLayoutFrame(), "Parent is ContentFrame." );
1398 OSL_ENSURE( pParent != this, "I'm the parent oneself." );
1399 OSL_ENSURE( pSibling != this, "I'm my own neighbour." );
1400 OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(),
1401 "I'm still registered somewhere." );
1403 //Insert in the tree.
1404 InsertBefore( static_cast<SwLayoutFrame*>(pParent), pSibling );
1406 // OD 24.10.2002 #103517# - correct setting of variable <fnRect>
1407 // <fnRect> is used for the following:
1408 // (1) To invalidate the frame's size, if its size, which has to be the
1409 // same as its upper/parent, differs from its upper's/parent's.
1410 // (2) To adjust/grow the frame's upper/parent, if it has a dimension in its
1411 // size, which is not determined by its upper/parent.
1412 // Which size is which depends on the frame type and the layout direction
1413 // (vertical or horizontal).
1414 // There are the following cases:
1415 // (A) Header and footer frames both in vertical and in horizontal layout
1416 // have to size the width to the upper/parent. A dimension in the height
1417 // has to cause an adjustment/grow of the upper/parent.
1418 // --> <fnRect> = fnRectHori
1419 // (B) Cell and column frames in vertical layout, the width has to be the
1420 // same as upper/parent and a dimension in height causes adjustment/grow
1421 // of the upper/parent.
1422 // --> <fnRect> = fnRectHori
1423 // in horizontal layout the other way around
1424 // --> <fnRect> = fnRectVert
1425 // (C) Other frames in vertical layout, the height has to be the
1426 // same as upper/parent and a dimension in width causes adjustment/grow
1427 // of the upper/parent.
1428 // --> <fnRect> = fnRectVert
1429 // in horizontal layout the other way around
1430 // --> <fnRect> = fnRectHori
1431 //SwRectFn fnRect = IsVertical() ? fnRectHori : fnRectVert;
1432 bool bVert, bVertL2R, bVertL2RB2T;
1433 if ( IsHeaderFrame() || IsFooterFrame() )
1434 bVert = bVertL2R = bVertL2RB2T = false;
1435 else
1437 bVert = (IsCellFrame() || IsColumnFrame()) ? !GetUpper()->IsVertical() : GetUpper()->IsVertical();
1438 bVertL2R = GetUpper()->IsVertLR();
1439 bVertL2RB2T = GetUpper()->IsVertLRBT();
1441 SwRectFnSet fnRect(bVert, bVertL2R, bVertL2RB2T);
1443 if (fnRect.GetWidth(getFrameArea()) != fnRect.GetWidth(pParent->getFramePrintArea()))
1444 InvalidateSize_();
1445 InvalidatePos_();
1446 const SwPageFrame *pPage = FindPageFrame();
1447 InvalidatePage( pPage );
1448 if( !IsColumnFrame() )
1450 SwFrame *pFrame = GetIndNext();
1451 if( nullptr != pFrame )
1453 pFrame->InvalidatePos_();
1454 if( IsInFootnote() )
1456 if( pFrame->IsSctFrame() )
1457 pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
1458 if( pFrame )
1459 pFrame->Prepare( PrepareHint::ErgoSum, nullptr, false );
1462 if ( IsInFootnote() && nullptr != ( pFrame = GetIndPrev() ) )
1464 if( pFrame->IsSctFrame() )
1465 pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
1466 if( pFrame )
1467 pFrame->Prepare( PrepareHint::QuoVadis, nullptr, false );
1471 if (!fnRect.GetHeight(getFrameArea()))
1472 return;
1474 // AdjustNeighbourhood is now also called in columns which are not
1475 // placed inside a frame
1476 SwNeighbourAdjust nAdjust = GetUpper()->IsFootnoteBossFrame() ?
1477 static_cast<SwFootnoteBossFrame*>(GetUpper())->NeighbourhoodAdjustment()
1478 : SwNeighbourAdjust::GrowShrink;
1479 SwTwips nGrow = fnRect.GetHeight(getFrameArea());
1480 if( SwNeighbourAdjust::OnlyAdjust == nAdjust )
1481 AdjustNeighbourhood( nGrow );
1482 else
1484 SwTwips nReal = 0;
1485 if( SwNeighbourAdjust::AdjustGrow == nAdjust )
1486 nReal = AdjustNeighbourhood( nGrow );
1487 if( nReal < nGrow )
1488 nReal += pParent->Grow( nGrow - nReal );
1489 if( SwNeighbourAdjust::GrowAdjust == nAdjust && nReal < nGrow )
1490 AdjustNeighbourhood( nGrow - nReal );
1494 void SwLayoutFrame::Cut()
1496 if ( GetNext() )
1497 GetNext()->InvalidatePos_();
1499 SwRectFnSet aRectFnSet(this);
1500 SwTwips nShrink = aRectFnSet.GetHeight(getFrameArea());
1502 // Remove first, then shrink upper.
1503 SwLayoutFrame *pUp = GetUpper();
1505 // AdjustNeighbourhood is now also called in columns which are not
1506 // placed inside a frame.
1508 // Remove must not be called before an AdjustNeighbourhood, but it has to
1509 // be called before the upper-shrink-call, if the upper-shrink takes care
1510 // of its content.
1511 if ( pUp && nShrink )
1513 if( pUp->IsFootnoteBossFrame() )
1515 SwNeighbourAdjust nAdjust= static_cast<SwFootnoteBossFrame*>(pUp)->NeighbourhoodAdjustment();
1516 if( SwNeighbourAdjust::OnlyAdjust == nAdjust )
1517 AdjustNeighbourhood( -nShrink );
1518 else
1520 SwTwips nReal = 0;
1521 if( SwNeighbourAdjust::AdjustGrow == nAdjust )
1522 nReal = -AdjustNeighbourhood( -nShrink );
1523 if( nReal < nShrink )
1525 const SwTwips nOldHeight = aRectFnSet.GetHeight(getFrameArea());
1527 // seems as if this needs to be forwarded to the SwFrame already here,
1528 // changing to zero seems temporary anyways
1530 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1531 aRectFnSet.SetHeight( aFrm, 0 );
1534 nReal += pUp->Shrink( nShrink - nReal );
1537 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1538 aRectFnSet.SetHeight( aFrm, nOldHeight );
1542 if( SwNeighbourAdjust::GrowAdjust == nAdjust && nReal < nShrink )
1543 AdjustNeighbourhood( nReal - nShrink );
1545 RemoveFromLayout();
1547 else
1549 RemoveFromLayout();
1550 pUp->Shrink( nShrink );
1553 else
1554 RemoveFromLayout();
1556 if( pUp && !pUp->Lower() )
1558 pUp->SetCompletePaint();
1559 pUp->InvalidatePage();
1563 SwTwips SwFrame::Grow(SwTwips nDist, SwResizeLimitReason& reason, bool bTst, bool bInfo)
1565 OSL_ENSURE( nDist >= 0, "Negative growth?" );
1567 PROTOCOL_ENTER( this, bTst ? PROT::GrowTest : PROT::Grow, DbgAction::NONE, &nDist )
1569 if ( !nDist )
1571 reason = SwResizeLimitReason::Unspecified;
1572 return 0;
1574 if ( IsFlyFrame() )
1575 return static_cast<SwFlyFrame*>(this)->Grow_(nDist, reason, bTst);
1576 if ( IsSctFrame() )
1577 return static_cast<SwSectionFrame*>(this)->Grow_(nDist, reason, bTst);
1578 if (IsCellFrame())
1580 const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(this);
1581 const SwTabFrame* pTab = FindTabFrame();
1583 // NEW TABLES
1584 if ( pTab->IsVertical() != IsVertical() ||
1585 pThisCell->GetLayoutRowSpan() < 1 )
1587 reason = SwResizeLimitReason::FixedSizeFrame;
1588 return 0;
1592 SwRectFnSet aRectFnSet(this);
1594 SwTwips nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
1595 if( nPrtHeight > 0 && nDist > (LONG_MAX - nPrtHeight) )
1596 nDist = LONG_MAX - nPrtHeight;
1598 const SwTwips nReal = GrowFrame(nDist, reason, bTst, bInfo);
1599 if( !bTst )
1601 nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
1603 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
1604 aRectFnSet.SetHeight( aPrt, nPrtHeight + ( IsContentFrame() ? nDist : nReal ) );
1606 return nReal;
1609 SwTwips SwFrame::Shrink( SwTwips nDist, bool bTst, bool bInfo )
1611 OSL_ENSURE( nDist >= 0, "Negative reduction?" );
1613 PROTOCOL_ENTER( this, bTst ? PROT::ShrinkTest : PROT::Shrink, DbgAction::NONE, &nDist )
1615 if ( nDist )
1617 if ( IsFlyFrame() )
1618 return static_cast<SwFlyFrame*>(this)->Shrink_( nDist, bTst );
1619 else if( IsSctFrame() )
1620 return static_cast<SwSectionFrame*>(this)->Shrink_( nDist, bTst );
1621 else
1623 if (IsCellFrame())
1625 const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(this);
1626 const SwTabFrame* pTab = FindTabFrame();
1628 // NEW TABLES
1629 if ( (pTab && pTab->IsVertical() != IsVertical()) ||
1630 pThisCell->GetLayoutRowSpan() < 1 )
1631 return 0;
1633 SwRectFnSet aRectFnSet(this);
1634 SwTwips nReal = aRectFnSet.GetHeight(getFrameArea());
1635 ShrinkFrame( nDist, bTst, bInfo );
1636 nReal -= aRectFnSet.GetHeight(getFrameArea());
1637 if( !bTst )
1639 const SwTwips nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
1640 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
1641 aRectFnSet.SetHeight( aPrt, nPrtHeight - ( IsContentFrame() ? nDist : nReal ) );
1643 return nReal;
1646 return 0;
1649 /** Adjust surrounding neighbourhood after insertion
1651 * A Frame needs "normalization" if it is directly placed below a footnote boss (page/column) and its
1652 * size changes. There is always a frame that takes the maximum possible space (the frame that
1653 * contains the Body text) and zero or more frames which only take the space needed (header/footer
1654 * area, footnote container). If one of these frames changes, the body-text-frame has to grow or
1655 * shrink accordingly, even though it's fixed.
1657 * !! Is it possible to do this in a generic way and not restrict it to the page and a distinct
1658 * frame which takes the maximum space (controlled using the FrameSize attribute)?
1659 * Problems:
1660 * - What if multiple frames taking the maximum space are placed next to each other?
1661 * - How is the maximum space calculated?
1662 * - How small can those frames become?
1664 * In any case, only a certain amount of space is allowed, so we never go below a minimum value for
1665 * the height of the body.
1667 * @param nDiff the value around which the space has to be allocated
1669 SwTwips SwFrame::AdjustNeighbourhood( SwTwips nDiff, bool bTst )
1671 PROTOCOL_ENTER( this, PROT::AdjustN, DbgAction::NONE, &nDiff );
1673 if ( !nDiff || !GetUpper()->IsFootnoteBossFrame() ) // only inside pages/columns
1674 return 0;
1676 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
1677 const bool bBrowse = pSh && pSh->GetViewOptions()->getBrowseMode();
1679 //The (Page-)Body only changes in BrowseMode, but only if it does not
1680 //contain columns.
1681 if ( IsPageBodyFrame() && (!bBrowse ||
1682 (static_cast<SwLayoutFrame*>(this)->Lower() &&
1683 static_cast<SwLayoutFrame*>(this)->Lower()->IsColumnFrame())) )
1684 return 0;
1686 //In BrowseView mode the PageFrame can handle some of the requests.
1687 tools::Long nBrowseAdd = 0;
1688 if ( bBrowse && GetUpper()->IsPageFrame() ) // only (Page-)BodyFrames
1690 SwViewShell *pViewShell = getRootFrame()->GetCurrShell();
1691 SwLayoutFrame *pUp = GetUpper();
1692 tools::Long nChg;
1693 const tools::Long nUpPrtBottom = pUp->getFrameArea().Height() -
1694 pUp->getFramePrintArea().Height() - pUp->getFramePrintArea().Top();
1695 SwRect aInva( pUp->getFrameArea() );
1696 if ( pViewShell )
1698 aInva.Pos().setX( pViewShell->VisArea().Left() );
1699 aInva.Width( pViewShell->VisArea().Width() );
1701 if ( nDiff > 0 )
1703 nChg = BROWSE_HEIGHT - pUp->getFrameArea().Height();
1704 nChg = std::min( nDiff, SwTwips(nChg) );
1706 if ( !IsBodyFrame() )
1708 SetCompletePaint();
1709 if ( !pViewShell || pViewShell->VisArea().Height() >= pUp->getFrameArea().Height() )
1711 //First minimize Body, it will grow again later.
1712 SwFrame *pBody = static_cast<SwFootnoteBossFrame*>(pUp)->FindBodyCont();
1713 const tools::Long nTmp = nChg - pBody->getFramePrintArea().Height();
1714 if ( !bTst )
1717 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pBody);
1718 aFrm.Height(std::max( tools::Long(0), aFrm.Height() - nChg ));
1721 pBody->InvalidatePrt_();
1722 pBody->InvalidateSize_();
1723 if ( pBody->GetNext() )
1724 pBody->GetNext()->InvalidatePos_();
1725 if ( !IsHeaderFrame() )
1726 pBody->SetCompletePaint();
1728 nChg = nTmp <= 0 ? 0 : nTmp;
1732 const tools::Long nTmp = nUpPrtBottom + 20;
1733 aInva.Top( aInva.Bottom() - nTmp );
1734 aInva.Height( nChg + nTmp );
1736 else
1738 //The page can shrink to 0. The first page keeps the same size like
1739 //VisArea.
1740 nChg = nDiff;
1741 tools::Long nInvaAdd = 0;
1742 if ( pViewShell && !pUp->GetPrev() &&
1743 pUp->getFrameArea().Height() + nDiff < pViewShell->VisArea().Height() )
1745 // This means that we have to invalidate adequately.
1746 nChg = pViewShell->VisArea().Height() - pUp->getFrameArea().Height();
1747 nInvaAdd = -(nDiff - nChg);
1750 //Invalidate including bottom border.
1751 tools::Long nBorder = nUpPrtBottom + 20;
1752 nBorder -= nChg;
1753 aInva.Top( aInva.Bottom() - (nBorder+nInvaAdd) );
1754 if ( !IsBodyFrame() )
1756 SetCompletePaint();
1757 if ( !IsHeaderFrame() )
1758 static_cast<SwFootnoteBossFrame*>(pUp)->FindBodyCont()->SetCompletePaint();
1760 //Invalidate the page because of the frames. Thereby the page becomes
1761 //the right size again if a frame didn't fit. This only works
1762 //randomly for paragraph bound frames otherwise (NotifyFlys).
1763 pUp->InvalidateSize();
1765 if ( !bTst )
1767 //Independent from nChg
1768 if ( pViewShell && aInva.HasArea() && pUp->GetUpper() )
1769 pViewShell->InvalidateWindows( aInva );
1771 if ( !bTst && nChg )
1774 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pUp);
1775 aFrm.AddHeight(nChg );
1779 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pUp);
1780 aPrt.AddHeight(nChg );
1783 if ( pViewShell )
1784 pViewShell->Imp()->SetFirstVisPageInvalid();
1786 if ( GetNext() )
1787 GetNext()->InvalidatePos_();
1789 //Trigger a repaint if necessary.
1790 std::unique_ptr<SvxBrushItem> aBack(pUp->GetFormat()->makeBackgroundBrushItem());
1791 const SvxGraphicPosition ePos = aBack->GetGraphicPos();
1792 if ( ePos != GPOS_NONE && ePos != GPOS_TILED )
1793 pViewShell->InvalidateWindows( pUp->getFrameArea() );
1795 if ( pUp->GetUpper() )
1797 if ( pUp->GetNext() )
1798 pUp->GetNext()->InvalidatePos();
1800 //Sad but true: during notify on ViewImp a Calc on the page and
1801 //its Lower may be called. The values should not be changed
1802 //because the caller takes care of the adjustment of Frame and
1803 //Prt.
1804 const tools::Long nOldFrameHeight = getFrameArea().Height();
1805 const tools::Long nOldPrtHeight = getFramePrintArea().Height();
1806 const bool bOldComplete = IsCompletePaint();
1808 if ( IsBodyFrame() )
1810 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
1811 aPrt.Height( nOldFrameHeight );
1814 if ( pUp->GetUpper() )
1816 static_cast<SwRootFrame*>(pUp->GetUpper())->CheckViewLayout( nullptr, nullptr );
1819 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1820 aFrm.Height( nOldFrameHeight );
1822 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
1823 aPrt.Height( nOldPrtHeight );
1825 mbCompletePaint = bOldComplete;
1827 if ( !IsBodyFrame() )
1828 pUp->InvalidateSize_();
1829 InvalidatePage( static_cast<SwPageFrame*>(pUp) );
1831 nDiff -= nChg;
1832 if ( !nDiff )
1833 return nChg;
1834 else
1835 nBrowseAdd = nChg;
1838 const SwFootnoteBossFrame *pBoss = static_cast<SwFootnoteBossFrame*>(GetUpper());
1840 SwTwips nReal = 0,
1841 nAdd = 0;
1842 SwFrame *pFrame = nullptr;
1843 SwRectFnSet aRectFnSet(this);
1845 if( IsBodyFrame() )
1847 if( IsInSct() )
1849 SwSectionFrame *pSect = FindSctFrame();
1850 if( nDiff > 0 && pSect->IsEndnAtEnd() && GetNext() &&
1851 GetNext()->IsFootnoteContFrame() )
1853 SwFootnoteContFrame* pCont = static_cast<SwFootnoteContFrame*>(GetNext());
1854 SwTwips nMinH = 0;
1855 SwFootnoteFrame* pFootnote = static_cast<SwFootnoteFrame*>(pCont->Lower());
1856 bool bFootnote = false;
1857 while( pFootnote )
1859 if( !pFootnote->GetAttr()->GetFootnote().IsEndNote() )
1861 nMinH += aRectFnSet.GetHeight(pFootnote->getFrameArea());
1862 bFootnote = true;
1864 pFootnote = static_cast<SwFootnoteFrame*>(pFootnote->GetNext());
1866 if( bFootnote )
1867 nMinH += aRectFnSet.GetTop(pCont->getFramePrintArea());
1868 nReal = aRectFnSet.GetHeight(pCont->getFrameArea()) - nMinH;
1869 if( nReal > nDiff )
1870 nReal = nDiff;
1871 if( nReal > 0 )
1872 pFrame = GetNext();
1873 else
1874 nReal = 0;
1876 if( !bTst && !pSect->IsColLocked() )
1877 pSect->InvalidateSize();
1879 if( !pFrame )
1880 return nBrowseAdd;
1882 else
1884 const bool bFootnotePage = pBoss->IsPageFrame() && static_cast<const SwPageFrame*>(pBoss)->IsFootnotePage();
1885 if ( bFootnotePage && !IsFootnoteContFrame() )
1886 pFrame = const_cast<SwFrame*>(static_cast<SwFrame const *>(pBoss->FindFootnoteCont()));
1887 if ( !pFrame )
1888 pFrame = const_cast<SwFrame*>(static_cast<SwFrame const *>(pBoss->FindBodyCont()));
1890 if ( !pFrame )
1891 return 0;
1893 //If not one is found, everything else is solved.
1894 nReal = aRectFnSet.GetHeight(pFrame->getFrameArea());
1895 if( nReal > nDiff )
1896 nReal = nDiff;
1897 if( !bFootnotePage )
1899 //Respect the minimal boundary!
1900 if( nReal )
1902 const SwTwips nMax = pBoss->GetVarSpace();
1903 if ( nReal > nMax )
1904 nReal = nMax;
1906 if( !IsFootnoteContFrame() && nDiff > nReal &&
1907 pFrame->GetNext() && pFrame->GetNext()->IsFootnoteContFrame()
1908 && ( pFrame->GetNext()->IsVertical() == IsVertical() )
1911 //If the Body doesn't return enough, we look for a footnote, if
1912 //there is one, we steal there accordingly.
1913 const SwTwips nAddMax = aRectFnSet.GetHeight(pFrame->GetNext()->getFrameArea());
1914 nAdd = nDiff - nReal;
1915 if ( nAdd > nAddMax )
1916 nAdd = nAddMax;
1917 if ( !bTst )
1920 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFrame->GetNext());
1921 aRectFnSet.SetHeight(aFrm, nAddMax-nAdd);
1923 if( aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() )
1925 aFrm.Pos().AdjustX(nAdd );
1929 pFrame->GetNext()->InvalidatePrt();
1931 if ( pFrame->GetNext()->GetNext() )
1933 pFrame->GetNext()->GetNext()->InvalidatePos_();
1940 if ( !bTst && nReal )
1942 SwTwips nTmp = aRectFnSet.GetHeight(pFrame->getFrameArea());
1945 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFrame);
1946 aRectFnSet.SetHeight( aFrm, nTmp - nReal );
1948 if( aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() )
1950 aFrm.Pos().AdjustX(nReal );
1954 pFrame->InvalidatePrt();
1956 if ( pFrame->GetNext() )
1957 pFrame->GetNext()->InvalidatePos_();
1959 if( nReal < 0 && pFrame->IsInSct() )
1961 SwLayoutFrame* pUp = pFrame->GetUpper();
1962 if( pUp && nullptr != ( pUp = pUp->GetUpper() ) && pUp->IsSctFrame() &&
1963 !pUp->IsColLocked() )
1964 pUp->InvalidateSize();
1966 if( ( IsHeaderFrame() || IsFooterFrame() ) && pBoss->GetDrawObjs() )
1968 const SwSortedObjs &rObjs = *pBoss->GetDrawObjs();
1969 OSL_ENSURE( pBoss->IsPageFrame(), "Header/Footer out of page?" );
1970 for (SwAnchoredObject* pAnchoredObj : rObjs)
1972 if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
1974 OSL_ENSURE( !pFly->IsFlyInContentFrame(), "FlyInCnt at Page?" );
1975 const SwFormatVertOrient &rVert =
1976 pFly->GetFormat()->GetVertOrient();
1977 // When do we have to invalidate?
1978 // If a frame is aligned on a PageTextArea and the header
1979 // changes a TOP, MIDDLE or NONE aligned frame needs to
1980 // recalculate it's position; if the footer changes a BOTTOM
1981 // or MIDDLE aligned frame needs to recalculate it's
1982 // position.
1983 if( ( rVert.GetRelationOrient() == text::RelOrientation::PRINT_AREA ||
1984 rVert.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA ) &&
1985 ((IsHeaderFrame() && rVert.GetVertOrient()!=text::VertOrientation::BOTTOM) ||
1986 (IsFooterFrame() && rVert.GetVertOrient()!=text::VertOrientation::NONE &&
1987 rVert.GetVertOrient() != text::VertOrientation::TOP)) )
1989 pFly->InvalidatePos_();
1990 pFly->Invalidate_();
1996 return (nBrowseAdd + nReal + nAdd);
1999 /** method to perform additional actions on an invalidation (2004-05-19 #i28701#) */
2000 void SwFrame::ActionOnInvalidation( const InvalidationType )
2002 // default behaviour is to perform no additional action
2005 /** method to determine, if an invalidation is allowed (2004-05-19 #i28701#) */
2006 bool SwFrame::InvalidationAllowed( const InvalidationType ) const
2008 // default behaviour is to allow invalidation
2009 return true;
2012 void SwFrame::ImplInvalidateSize()
2014 if ( InvalidationAllowed( INVALID_SIZE ) )
2016 setFrameAreaSizeValid(false);
2018 if ( IsFlyFrame() )
2019 static_cast<SwFlyFrame*>(this)->Invalidate_();
2020 else
2021 InvalidatePage();
2023 // OD 2004-05-19 #i28701#
2024 ActionOnInvalidation( INVALID_SIZE );
2028 void SwFrame::ImplInvalidatePrt()
2030 if ( InvalidationAllowed( INVALID_PRTAREA ) )
2032 setFramePrintAreaValid(false);
2034 if ( IsFlyFrame() )
2035 static_cast<SwFlyFrame*>(this)->Invalidate_();
2036 else
2037 InvalidatePage();
2039 // OD 2004-05-19 #i28701#
2040 ActionOnInvalidation( INVALID_PRTAREA );
2044 void SwFrame::ImplInvalidatePos()
2046 if ( !InvalidationAllowed( INVALID_POS ) )
2047 return;
2049 setFrameAreaPositionValid(false);
2051 if ( IsFlyFrame() )
2053 static_cast<SwFlyFrame*>(this)->Invalidate_();
2055 else
2057 InvalidatePage();
2060 // OD 2004-05-19 #i28701#
2061 ActionOnInvalidation( INVALID_POS );
2064 void SwFrame::ImplInvalidateLineNum()
2066 if ( InvalidationAllowed( INVALID_LINENUM ) )
2068 mbValidLineNum = false;
2069 OSL_ENSURE( IsTextFrame(), "line numbers are implemented for text only" );
2070 InvalidatePage();
2072 // OD 2004-05-19 #i28701#
2073 ActionOnInvalidation( INVALID_LINENUM );
2077 void SwFrame::ReinitializeFrameSizeAttrFlags()
2079 const SwFormatFrameSize &rFormatSize = GetAttrSet()->GetFrameSize();
2080 if ( SwFrameSize::Variable == rFormatSize.GetHeightSizeType() ||
2081 SwFrameSize::Minimum == rFormatSize.GetHeightSizeType())
2083 mbFixSize = false;
2084 if ( GetType() & (SwFrameType::Header | SwFrameType::Footer | SwFrameType::Row) )
2086 SwFrame *pFrame = static_cast<SwLayoutFrame*>(this)->Lower();
2087 while ( pFrame )
2088 { pFrame->InvalidateSize_();
2089 pFrame->InvalidatePrt_();
2090 pFrame = pFrame->GetNext();
2092 SwContentFrame *pCnt = static_cast<SwLayoutFrame*>(this)->ContainsContent();
2093 // #i36991# - be save.
2094 // E.g., a row can contain *no* content.
2095 if ( pCnt )
2097 pCnt->InvalidatePage();
2100 pCnt->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
2101 pCnt->InvalidateSize_();
2102 pCnt = pCnt->GetNextContentFrame();
2103 } while ( static_cast<SwLayoutFrame*>(this)->IsAnLower( pCnt ) );
2107 else if ( rFormatSize.GetHeightSizeType() == SwFrameSize::Fixed )
2109 if( IsVertical() )
2110 ChgSize( Size( rFormatSize.GetWidth(), getFrameArea().Height()));
2111 else
2112 ChgSize( Size( getFrameArea().Width(), rFormatSize.GetHeight()));
2116 void SwFrame::ValidateThisAndAllLowers( const sal_uInt16 nStage )
2118 // Stage 0: Only validate frames. Do not process any objects.
2119 // Stage 1: Only validate fly frames and all of their contents.
2120 // Stage 2: Validate all.
2122 const bool bOnlyObject = 1 == nStage;
2123 const bool bIncludeObjects = 1 <= nStage;
2125 if ( !bOnlyObject || IsFlyFrame() )
2127 setFrameAreaSizeValid(true);
2128 setFramePrintAreaValid(true);
2129 setFrameAreaPositionValid(true);
2132 if ( bIncludeObjects )
2134 const SwSortedObjs* pObjs = GetDrawObjs();
2135 if ( pObjs )
2137 const size_t nCnt = pObjs->size();
2138 for ( size_t i = 0; i < nCnt; ++i )
2140 SwAnchoredObject* pAnchObj = (*pObjs)[i];
2141 if ( auto pFlyFrame = pAnchObj->DynCastFlyFrame() )
2142 pFlyFrame->ValidateThisAndAllLowers( 2 );
2143 else if ( auto pAnchoredDrawObj = dynamic_cast<SwAnchoredDrawObject *>( pAnchObj ) )
2144 pAnchoredDrawObj->ValidateThis();
2149 if ( IsLayoutFrame() )
2151 SwFrame* pLower = static_cast<SwLayoutFrame*>(this)->Lower();
2152 while ( pLower )
2154 pLower->ValidateThisAndAllLowers( nStage );
2155 pLower = pLower->GetNext();
2160 SwTwips SwContentFrame::GrowFrame(SwTwips nDist, SwResizeLimitReason& reason, bool bTst, bool bInfo)
2162 SwRectFnSet aRectFnSet(this);
2164 SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
2165 if( nFrameHeight > 0 &&
2166 nDist > (LONG_MAX - nFrameHeight ) )
2167 nDist = LONG_MAX - nFrameHeight;
2169 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
2170 const bool bBrowse = pSh && pSh->GetViewOptions()->getBrowseMode();
2171 SwFrameType nTmpType = SwFrameType::Cell | SwFrameType::Column;
2172 if (bBrowse)
2173 nTmpType |= SwFrameType::Body;
2174 if( !(GetUpper()->GetType() & nTmpType) && GetUpper()->HasFixSize() )
2176 if ( !bTst )
2179 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
2180 aRectFnSet.SetHeight( aFrm, nFrameHeight + nDist );
2182 if( IsVertical() && !IsVertLR() )
2184 aFrm.Pos().AdjustX( -nDist );
2188 if ( GetNext() )
2190 GetNext()->InvalidatePos();
2192 // #i28701# - Due to the new object positioning the
2193 // frame on the next page/column can flow backward (e.g. it was moved forward
2194 // due to the positioning of its objects ). Thus, invalivate this next frame,
2195 // if document compatibility option 'Consider wrapping style influence on
2196 // object positioning' is ON.
2197 else if ( GetUpper()->GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
2199 InvalidateNextPos();
2202 if (!nDist)
2203 reason = SwResizeLimitReason::Unspecified;
2204 else if (GetUpper()->IsBodyFrame() // Page / column body
2205 || (GetUpper()->IsFlyFrame()
2206 && static_cast<SwFlyFrame*>(GetUpper())->GetNextLink()))
2207 reason = SwResizeLimitReason::FlowToFollow;
2208 else
2209 reason = SwResizeLimitReason::FixedSizeFrame;
2210 return 0;
2213 reason = SwResizeLimitReason::Unspecified;
2214 SwTwips nReal = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea());
2215 for (SwFrame* pFrame = GetUpper()->Lower(); pFrame && nReal > 0; pFrame = pFrame->GetNext())
2216 nReal -= aRectFnSet.GetHeight(pFrame->getFrameArea());
2218 if ( !bTst )
2220 //Contents are always resized to the wished value.
2221 tools::Long nOld = aRectFnSet.GetHeight(getFrameArea());
2224 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
2226 aRectFnSet.SetHeight( aFrm, nOld + nDist );
2228 if( IsVertical()&& !IsVertLR() )
2230 aFrm.Pos().AdjustX( -nDist );
2234 SwTabFrame *pTab = (nOld && IsInTab()) ? FindTabFrame() : nullptr;
2235 if (pTab)
2237 SwDocShell* pShell = pTab->GetFormat()->GetDoc()->GetDocShell();
2238 if ( pTab->GetTable()->GetHTMLTableLayout() &&
2239 !pTab->IsJoinLocked() &&
2240 pShell && !pShell->IsReadOnly() )
2242 pTab->InvalidatePos();
2243 pTab->SetResizeHTMLTable();
2248 //Only grow Upper if necessary.
2249 if ( nReal < nDist )
2251 if( GetUpper() )
2253 if( bTst || !GetUpper()->IsFooterFrame() )
2254 nReal = GetUpper()->Grow(nDist - std::max(nReal, SwTwips(0)), reason, bTst, bInfo);
2255 else
2257 nReal = 0;
2258 GetUpper()->InvalidateSize();
2261 else
2262 nReal = 0;
2264 else
2265 nReal = nDist;
2267 // #i28701# - Due to the new object positioning the
2268 // frame on the next page/column can flow backward (e.g. it was moved forward
2269 // due to the positioning of its objects ). Thus, invalivate this next frame,
2270 // if document compatibility option 'Consider wrapping style influence on
2271 // object positioning' is ON.
2272 if ( !bTst )
2274 if ( GetNext() )
2276 GetNext()->InvalidatePos();
2278 else if ( GetUpper()->GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
2280 InvalidateNextPos();
2284 return nReal;
2287 SwTwips SwContentFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool bInfo )
2289 SwRectFnSet aRectFnSet(this);
2290 OSL_ENSURE( nDist >= 0, "nDist < 0" );
2291 OSL_ENSURE( nDist <= aRectFnSet.GetHeight(getFrameArea()),
2292 "nDist > than current size." );
2294 if ( !bTst )
2296 SwTwips nRstHeight;
2297 if( GetUpper() )
2298 nRstHeight = aRectFnSet.BottomDist( getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()) );
2299 else
2300 nRstHeight = 0;
2301 if( nRstHeight < 0 )
2303 SwTwips nNextHeight = 0;
2304 // i#94666 if WIDOW_MAGIC was set as height, nDist is wrong, need
2305 // to take into account all the frames in the section.
2306 if (GetUpper()->IsSctFrame()
2307 && sw::WIDOW_MAGIC - 20000 - getFrameArea().Top() < nDist)
2309 SwFrame *pNxt = GetNext();
2310 while( pNxt )
2312 nNextHeight += aRectFnSet.GetHeight(pNxt->getFrameArea());
2313 pNxt = pNxt->GetNext();
2316 nRstHeight = nDist + nRstHeight - nNextHeight;
2318 else
2320 nRstHeight = nDist;
2324 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
2325 aRectFnSet.SetHeight( aFrm, aRectFnSet.GetHeight(aFrm) - nDist );
2327 if( IsVertical() && !IsVertLR() )
2329 aFrm.Pos().AdjustX(nDist );
2333 nDist = nRstHeight;
2334 SwTabFrame *pTab = IsInTab() ? FindTabFrame() : nullptr;
2335 if (pTab)
2337 SwDocShell* pShell = pTab->GetFormat()->GetDoc()->GetDocShell();
2338 if ( pTab->GetTable()->GetHTMLTableLayout() &&
2339 !pTab->IsJoinLocked() &&
2340 pShell && !pShell->IsReadOnly() )
2342 pTab->InvalidatePos();
2343 pTab->SetResizeHTMLTable();
2348 SwTwips nReal;
2349 if( GetUpper() && nDist > 0 )
2351 if( bTst || !GetUpper()->IsFooterFrame() )
2352 nReal = GetUpper()->Shrink( nDist, bTst, bInfo );
2353 else
2355 nReal = 0;
2357 // #108745# Sorry, dear old footer friend, I'm not gonna invalidate you,
2358 // if there are any objects anchored inside your content, which
2359 // overlap with the shrinking frame.
2360 // This may lead to a footer frame that is too big, but this is better
2361 // than looping.
2362 // #109722# : The fix for #108745# was too strict.
2364 bool bInvalidate = true;
2365 const SwRect aRect( getFrameArea() );
2366 const SwPageFrame* pPage = FindPageFrame();
2367 const SwSortedObjs* pSorted = pPage ? pPage->GetSortedObjs() : nullptr;
2368 if( pSorted )
2370 for (SwAnchoredObject* pAnchoredObj : *pSorted)
2372 const SwRect aBound( pAnchoredObj->GetObjRectWithSpaces() );
2374 if( aBound.Left() > aRect.Right() )
2375 continue;
2377 if( aBound.Overlaps( aRect ) )
2379 const SwFrameFormat* pFormat = pAnchoredObj->GetFrameFormat();
2380 if( css::text::WrapTextMode_THROUGH != pFormat->GetSurround().GetSurround() )
2382 const SwFrame* pAnchor = pAnchoredObj->GetAnchorFrame();
2383 if ( pAnchor && pAnchor->FindFooterOrHeader() == GetUpper() )
2385 bInvalidate = false;
2386 break;
2393 if ( bInvalidate )
2394 GetUpper()->InvalidateSize();
2397 else
2398 nReal = 0;
2400 if ( !bTst )
2402 //The position of the next Frame changes for sure.
2403 InvalidateNextPos();
2405 //If I don't have a successor I have to do the retouch by myself.
2406 if ( !GetNext() )
2407 SetRetouche();
2409 return nReal;
2412 void SwContentFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
2414 if (rHint.GetId() != SfxHintId::SwLegacyModify && rHint.GetId() != SfxHintId::SwFormatChange
2415 && rHint.GetId() != SfxHintId::SwAttrSetChange)
2416 return;
2418 SwContentFrameInvFlags eInvFlags = SwContentFrameInvFlags::NONE;
2419 if (rHint.GetId() == SfxHintId::SwLegacyModify)
2421 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
2422 UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
2424 else if (rHint.GetId() == SfxHintId::SwAttrSetChange)
2426 auto pChangeHint = static_cast<const sw::AttrSetChangeHint*>(&rHint);
2427 if(pChangeHint->m_pNew && pChangeHint->m_pOld)
2429 const SwAttrSetChg& rOldSetChg = *pChangeHint->m_pOld;
2430 const SwAttrSetChg& rNewSetChg = *pChangeHint->m_pNew;
2431 SfxItemIter aOIter(*rOldSetChg.GetChgSet());
2432 SfxItemIter aNIter(*rNewSetChg.GetChgSet());
2433 const SfxPoolItem* pNItem = aNIter.GetCurItem();
2434 const SfxPoolItem* pOItem = aOIter.GetCurItem();
2435 SwAttrSetChg aOldSet(rOldSetChg);
2436 SwAttrSetChg aNewSet(rNewSetChg);
2439 UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
2440 pNItem = aNIter.NextItem();
2441 pOItem = aOIter.NextItem();
2442 } while(pNItem);
2443 if(aOldSet.Count() || aNewSet.Count())
2444 SwFrame::SwClientNotify(rMod, sw::AttrSetChangeHint(&aOldSet, &aNewSet));
2447 else // rHint.GetId() == SfxHintId::SwFormatChange
2449 auto pChangeHint = static_cast<const SwFormatChangeHint*>(&rHint);
2450 UpdateAttrForFormatChange(pChangeHint->m_pOldFormat, pChangeHint->m_pNewFormat, eInvFlags);
2453 if(eInvFlags == SwContentFrameInvFlags::NONE)
2454 return;
2456 SwPageFrame* pPage = FindPageFrame();
2457 InvalidatePage(pPage);
2458 if(eInvFlags & SwContentFrameInvFlags::SetCompletePaint)
2459 SetCompletePaint();
2460 if(eInvFlags & SwContentFrameInvFlags::InvalidatePos)
2461 InvalidatePos_();
2462 if(eInvFlags & SwContentFrameInvFlags::InvalidateSize)
2463 InvalidateSize_();
2464 if(eInvFlags & (SwContentFrameInvFlags::InvalidateSectPrt | SwContentFrameInvFlags::SetNextCompletePaint))
2466 if(IsInSct() && !GetPrev())
2468 SwSectionFrame* pSect = FindSctFrame();
2469 if(pSect->ContainsAny() == this)
2471 pSect->InvalidatePrt_();
2472 pSect->InvalidatePage(pPage);
2475 InvalidatePrt_();
2477 SwFrame* pNextFrame = GetIndNext();
2478 if(pNextFrame && eInvFlags & SwContentFrameInvFlags::InvalidateNextPrt)
2480 pNextFrame->InvalidatePrt_();
2481 pNextFrame->InvalidatePage(pPage);
2483 if(pNextFrame && eInvFlags & SwContentFrameInvFlags::SetNextCompletePaint)
2485 pNextFrame->SetCompletePaint();
2487 if(eInvFlags & SwContentFrameInvFlags::InvalidatePrevPrt)
2489 SwFrame* pPrevFrame = GetPrev();
2490 if(pPrevFrame)
2492 pPrevFrame->InvalidatePrt_();
2493 pPrevFrame->InvalidatePage(pPage);
2496 if(eInvFlags & SwContentFrameInvFlags::InvalidateNextPos)
2497 InvalidateNextPos();
2500 void SwContentFrame::UpdateAttr_( const SfxPoolItem* pOld, const SfxPoolItem* pNew,
2501 SwContentFrameInvFlags &rInvFlags,
2502 SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
2504 bool bClear = true;
2505 sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
2506 switch ( nWhich )
2508 case RES_PAGEDESC: //attribute changes (on/off)
2509 if ( IsInDocBody() && !IsInTab() )
2511 rInvFlags |= SwContentFrameInvFlags::InvalidatePos;
2512 SwPageFrame *pPage = FindPageFrame();
2513 if ( !GetPrev() )
2514 CheckPageDescs( pPage );
2515 if (GetPageDescItem().GetNumOffset())
2516 static_cast<SwRootFrame*>(pPage->GetUpper())->SetVirtPageNum( true );
2517 pPage->GetFormat()->GetDoc()->getIDocumentFieldsAccess().UpdatePageFields(pPage->getFrameArea().Top());
2519 break;
2521 case RES_UL_SPACE:
2523 // OD 2004-02-18 #106629# - correction
2524 // Invalidation of the printing area of next frame, not only
2525 // for footnote content.
2526 if ( !GetIndNext() )
2528 SwFrame* pNxt = FindNext();
2529 if ( pNxt )
2531 SwPageFrame* pPg = pNxt->FindPageFrame();
2532 pNxt->InvalidatePage( pPg );
2533 pNxt->InvalidatePrt_();
2534 if( pNxt->IsSctFrame() )
2536 SwFrame* pCnt = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
2537 if( pCnt )
2539 pCnt->InvalidatePrt_();
2540 pCnt->InvalidatePage( pPg );
2543 pNxt->SetCompletePaint();
2546 // OD 2004-03-17 #i11860#
2547 if ( GetIndNext() &&
2548 !GetUpper()->GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::USE_FORMER_OBJECT_POS) )
2550 // OD 2004-07-01 #i28701# - use new method <InvalidateObjs(..)>
2551 GetIndNext()->InvalidateObjs();
2553 Prepare( PrepareHint::ULSpaceChanged ); //TextFrame has to correct line spacing.
2554 rInvFlags |= SwContentFrameInvFlags::SetNextCompletePaint;
2555 [[fallthrough]];
2557 case RES_MARGIN_FIRSTLINE:
2558 case RES_MARGIN_TEXTLEFT:
2559 case RES_MARGIN_RIGHT:
2560 case RES_LR_SPACE:
2561 case RES_BOX:
2562 case RES_SHADOW:
2564 Prepare( PrepareHint::FixSizeChanged );
2565 SwModify aMod;
2566 SwFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
2567 rInvFlags |= SwContentFrameInvFlags::InvalidateNextPrt | SwContentFrameInvFlags::InvalidatePrevPrt;
2568 break;
2570 case RES_BREAK:
2572 rInvFlags |= SwContentFrameInvFlags::InvalidatePos | SwContentFrameInvFlags::InvalidateNextPos;
2573 const IDocumentSettingAccess& rIDSA = GetUpper()->GetFormat()->getIDocumentSettingAccess();
2574 if( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX) ||
2575 rIDSA.get(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES) )
2577 rInvFlags |= SwContentFrameInvFlags::SetCompletePaint;
2578 SwFrame* pNxt = FindNext();
2579 if( pNxt )
2581 SwPageFrame* pPg = pNxt->FindPageFrame();
2582 pNxt->InvalidatePage( pPg );
2583 pNxt->InvalidatePrt_();
2584 if( pNxt->IsSctFrame() )
2586 SwFrame* pCnt = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
2587 if( pCnt )
2589 pCnt->InvalidatePrt_();
2590 pCnt->InvalidatePage( pPg );
2593 pNxt->SetCompletePaint();
2597 break;
2599 // OD 2004-02-26 #i25029#
2600 case RES_PARATR_CONNECT_BORDER:
2602 rInvFlags |= SwContentFrameInvFlags::SetCompletePaint;
2603 if ( IsTextFrame() )
2605 InvalidateNextPrtArea();
2607 if ( !GetIndNext() && IsInTab() && IsInSplitTableRow() )
2609 FindTabFrame()->InvalidateSize();
2612 break;
2614 case RES_PARATR_TABSTOP:
2615 case RES_CHRATR_SHADOWED:
2616 case RES_CHRATR_AUTOKERN:
2617 case RES_CHRATR_UNDERLINE:
2618 case RES_CHRATR_OVERLINE:
2619 case RES_CHRATR_KERNING:
2620 case RES_CHRATR_FONT:
2621 case RES_CHRATR_FONTSIZE:
2622 case RES_CHRATR_ESCAPEMENT:
2623 case RES_CHRATR_CONTOUR:
2624 case RES_CHRATR_NOHYPHEN:
2625 case RES_PARATR_NUMRULE:
2626 rInvFlags |= SwContentFrameInvFlags::SetCompletePaint;
2627 break;
2629 case RES_FRM_SIZE:
2630 rInvFlags |= SwContentFrameInvFlags::SetCompletePaint;
2631 [[fallthrough]];
2633 default:
2634 bClear = false;
2636 if ( !bClear )
2637 return;
2639 if ( pOldSet || pNewSet )
2641 if ( pOldSet )
2642 pOldSet->ClearItem( nWhich );
2643 if ( pNewSet )
2644 pNewSet->ClearItem( nWhich );
2646 else
2648 SwModify aMod;
2649 SwFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
2653 void SwContentFrame::UpdateAttrForFormatChange( SwFormat* pOldFormat, SwFormat* pNewFormat,
2654 SwContentFrameInvFlags &rInvFlags )
2656 rInvFlags = SwContentFrameInvFlags::SetCompletePaint
2657 | SwContentFrameInvFlags::InvalidatePos
2658 | SwContentFrameInvFlags::InvalidateSize
2659 | SwContentFrameInvFlags::InvalidateSectPrt
2660 | SwContentFrameInvFlags::InvalidateNextPrt
2661 | SwContentFrameInvFlags::InvalidatePrevPrt
2662 | SwContentFrameInvFlags::InvalidateNextPos
2663 | SwContentFrameInvFlags::SetNextCompletePaint;
2665 if ( IsInDocBody() && !IsInTab() )
2667 rInvFlags |= SwContentFrameInvFlags::InvalidatePos;
2668 SwPageFrame *pPage = FindPageFrame();
2669 if ( !GetPrev() )
2670 CheckPageDescs( pPage );
2671 if (GetPageDescItem().GetNumOffset())
2672 static_cast<SwRootFrame*>(pPage->GetUpper())->SetVirtPageNum( true );
2673 pPage->GetFormat()->GetDoc()->getIDocumentFieldsAccess().UpdatePageFields(pPage->getFrameArea().Top());
2676 SwModify aMod;
2677 SwFrame::SwClientNotify(aMod, SwFormatChangeHint(pOldFormat, pNewFormat));
2680 SwLayoutFrame::SwLayoutFrame(SwFrameFormat *const pFormat, SwFrame *const pSib)
2681 : SwFrame(pFormat, pSib)
2682 , m_pLower(nullptr)
2684 const SwFormatFrameSize &rFormatSize = pFormat->GetFrameSize();
2685 if ( rFormatSize.GetHeightSizeType() == SwFrameSize::Fixed )
2686 mbFixSize = true;
2689 // #i28701#
2691 SwTwips SwLayoutFrame::InnerHeight() const
2693 const SwFrame* pCnt = Lower();
2694 if (!pCnt)
2695 return 0;
2697 SwRectFnSet aRectFnSet(this);
2698 SwTwips nRet = 0;
2699 if( pCnt->IsColumnFrame() || pCnt->IsCellFrame() )
2703 SwTwips nTmp = static_cast<const SwLayoutFrame*>(pCnt)->InnerHeight();
2704 if( pCnt->isFramePrintAreaValid() )
2705 nTmp += aRectFnSet.GetHeight(pCnt->getFrameArea()) -
2706 aRectFnSet.GetHeight(pCnt->getFramePrintArea());
2707 if( nRet < nTmp )
2708 nRet = nTmp;
2709 pCnt = pCnt->GetNext();
2710 } while ( pCnt );
2712 else
2716 nRet += aRectFnSet.GetHeight(pCnt->getFrameArea());
2717 if( pCnt->IsContentFrame() && static_cast<const SwTextFrame*>(pCnt)->IsUndersized() )
2718 nRet += static_cast<const SwTextFrame*>(pCnt)->GetParHeight() -
2719 aRectFnSet.GetHeight(pCnt->getFramePrintArea());
2720 if( pCnt->IsLayoutFrame() && !pCnt->IsTabFrame() )
2721 nRet += static_cast<const SwLayoutFrame*>(pCnt)->InnerHeight() -
2722 aRectFnSet.GetHeight(pCnt->getFramePrintArea());
2723 pCnt = pCnt->GetNext();
2724 } while( pCnt );
2727 return nRet;
2730 SwTwips SwLayoutFrame::GrowFrame(SwTwips nDist, SwResizeLimitReason& reason, bool bTst, bool bInfo)
2732 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
2733 const bool bBrowse = pSh && pSh->GetViewOptions()->getBrowseMode();
2734 SwFrameType nTmpType = SwFrameType::Cell | SwFrameType::Column;
2735 if (bBrowse)
2736 nTmpType |= SwFrameType::Body;
2737 if( !(GetType() & nTmpType) && HasFixSize() )
2739 if (nDist <= 0)
2740 reason = SwResizeLimitReason::Unspecified;
2741 else
2742 reason = IsBodyFrame() ? SwResizeLimitReason::FlowToFollow // Page / column body
2743 : SwResizeLimitReason::FixedSizeFrame;
2744 return 0;
2747 SwRectFnSet aRectFnSet(this);
2748 const SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
2749 const SwTwips nFramePos = getFrameArea().Pos().X();
2751 if ( nFrameHeight > 0 && nDist > (LONG_MAX - nFrameHeight) )
2752 nDist = LONG_MAX - nFrameHeight;
2754 SwTwips nMin = 0;
2755 if ( GetUpper() && !IsCellFrame() )
2757 for (SwFrame* pFrame = GetUpper()->Lower(); pFrame; pFrame = pFrame->GetNext())
2758 nMin += aRectFnSet.GetHeight(pFrame->getFrameArea());
2759 nMin = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea()) - nMin;
2760 if ( nMin < 0 )
2761 nMin = 0;
2764 SwRect aOldFrame( getFrameArea() );
2765 bool bMoveAccFrame = false;
2767 bool bChgPos = IsVertical();
2768 if ( !bTst )
2770 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
2771 aRectFnSet.SetHeight( aFrm, nFrameHeight + nDist );
2773 if( bChgPos && !IsVertLR() )
2775 aFrm.Pos().AdjustX( -nDist );
2778 bMoveAccFrame = true;
2781 reason = SwResizeLimitReason::Unspecified;
2782 SwTwips nReal = nDist - nMin;
2783 if ( nReal > 0 )
2785 if ( GetUpper() )
2786 { // AdjustNeighbourhood now only for the columns (but not in frames)
2787 SwNeighbourAdjust nAdjust = GetUpper()->IsFootnoteBossFrame() ?
2788 static_cast<SwFootnoteBossFrame*>(GetUpper())->NeighbourhoodAdjustment()
2789 : SwNeighbourAdjust::GrowShrink;
2790 if( SwNeighbourAdjust::OnlyAdjust == nAdjust )
2791 nReal = AdjustNeighbourhood( nReal, bTst );
2792 else
2794 if( SwNeighbourAdjust::AdjustGrow == nAdjust )
2795 nReal += AdjustNeighbourhood( nReal, bTst );
2797 SwTwips nGrow = 0;
2798 if( nReal > 0 )
2800 SwFrame* pToGrow = GetUpper();
2801 // NEW TABLES
2802 // A cell with a row span of > 1 is allowed to grow the
2803 // line containing the end of the row span if it is
2804 // located in the same table frame:
2805 if (IsCellFrame())
2807 const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(this);
2808 if ( pThisCell->GetLayoutRowSpan() > 1 )
2810 SwCellFrame& rEndCell = const_cast<SwCellFrame&>(pThisCell->FindStartEndOfRowSpanCell( false ));
2811 if ( -1 == rEndCell.GetTabBox()->getRowSpan() )
2812 pToGrow = rEndCell.GetUpper();
2813 else
2815 pToGrow = nullptr;
2816 reason = SwResizeLimitReason::FlowToFollow;
2820 nGrow = pToGrow ? pToGrow->Grow(nReal, reason, bTst, bInfo) : 0;
2823 if( SwNeighbourAdjust::GrowAdjust == nAdjust && nGrow < nReal )
2824 nReal = o3tl::saturating_add(nReal, AdjustNeighbourhood( nReal - nGrow, bTst ));
2826 if ( IsFootnoteFrame() && (nGrow != nReal) && GetNext() )
2828 //Footnotes can replace their successor.
2829 SwTwips nSpace = bTst ? 0 : -nDist;
2830 if (const SwFrame *pFrame = GetUpper()->Lower())
2833 { nSpace += aRectFnSet.GetHeight(pFrame->getFrameArea());
2834 pFrame = pFrame->GetNext();
2835 } while ( pFrame != GetNext() );
2837 nSpace = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea()) -nSpace;
2838 if ( nSpace < 0 )
2839 nSpace = 0;
2840 nSpace += nGrow;
2841 if ( nReal > nSpace )
2842 nReal = nSpace;
2843 if ( nReal && !bTst )
2844 static_cast<SwFootnoteFrame*>(this)->InvalidateNxtFootnoteCnts( FindPageFrame() );
2846 else
2847 nReal = nGrow;
2850 else
2851 nReal = 0;
2853 nReal += nMin;
2855 else
2856 nReal = nDist;
2858 if ( !bTst )
2860 if( nReal != nDist &&
2861 // NEW TABLES
2862 ( !IsCellFrame() || static_cast<SwCellFrame*>(this)->GetLayoutRowSpan() > 1 ) )
2864 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
2865 aRectFnSet.SetHeight( aFrm, nFrameHeight + nReal );
2867 if( bChgPos && !IsVertLR() )
2869 aFrm.Pos().setX( nFramePos - nReal );
2872 bMoveAccFrame = true;
2875 if ( nReal )
2877 SwPageFrame *pPage = FindPageFrame();
2878 if ( GetNext() )
2880 SwFrame * pNext = GetNext();
2883 pNext->InvalidatePos_();
2884 if (pNext->IsRowFrame())
2885 { // also invalidate first cell
2886 static_cast<SwLayoutFrame*>(pNext)->Lower()->InvalidatePos_();
2888 else if (pNext->IsContentFrame())
2890 pNext->InvalidatePage(pPage);
2892 if (pNext->HasFixSize())
2893 { // continue to invalidate because growing pNext won't do it!
2894 pNext = pNext->GetNext();
2896 else
2898 break;
2901 while (pNext);
2903 if ( !IsPageBodyFrame() )
2905 InvalidateAll_();
2906 InvalidatePage( pPage );
2908 if (!(GetType() & (SwFrameType::Row|SwFrameType::Tab|SwFrameType::FtnCont|SwFrameType::Page|SwFrameType::Root)))
2909 NotifyLowerObjs();
2911 if( IsCellFrame() )
2912 InvaPercentLowers( nReal );
2914 std::unique_ptr<SvxBrushItem> aBack(GetFormat()->makeBackgroundBrushItem());
2915 const SvxGraphicPosition ePos = aBack->GetGraphicPos();
2916 if ( GPOS_NONE != ePos && GPOS_TILED != ePos )
2917 SetCompletePaint();
2921 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
2922 if( bMoveAccFrame && IsAccessibleFrame() )
2924 SwRootFrame *pRootFrame = getRootFrame();
2925 if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
2926 pRootFrame->GetCurrShell() )
2928 pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( this, aOldFrame );
2931 #else
2932 (void)bMoveAccFrame;
2933 (void)aOldFrame;
2934 #endif
2936 if (reason == SwResizeLimitReason::Unspecified && nReal < nDist && IsBodyFrame()) // Page / column body
2937 reason = SwResizeLimitReason::FlowToFollow;
2939 return nReal;
2942 SwTwips SwLayoutFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool bInfo )
2944 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
2945 const bool bBrowse = pSh && pSh->GetViewOptions()->getBrowseMode();
2946 SwFrameType nTmpType = SwFrameType::Cell | SwFrameType::Column;
2947 if (bBrowse)
2948 nTmpType |= SwFrameType::Body;
2950 if (pSh && pSh->GetViewOptions()->IsWhitespaceHidden())
2952 if (IsBodyFrame())
2954 // Whitespace is hidden and this body frame will not shrink, as it
2955 // has a fix size.
2956 // Invalidate the page frame size, so in case the reason for the
2957 // shrink was that there is more whitespace on this page, the size
2958 // without whitespace will be recalculated correctly.
2959 SwPageFrame* pPageFrame = FindPageFrame();
2960 pPageFrame->InvalidateSize();
2964 if( !(GetType() & nTmpType) && HasFixSize() )
2965 return 0;
2967 OSL_ENSURE( nDist >= 0, "nDist < 0" );
2968 SwRectFnSet aRectFnSet(this);
2969 SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
2970 if ( nDist > nFrameHeight )
2971 nDist = nFrameHeight;
2973 SwTwips nMin = 0;
2974 bool bChgPos = IsVertical();
2975 if (const SwFrame *pFrame = Lower())
2977 if( !pFrame->IsNeighbourFrame() )
2979 const tools::Long nTmp = aRectFnSet.GetHeight(getFramePrintArea());
2980 while( pFrame && nMin < nTmp )
2981 { nMin += aRectFnSet.GetHeight(pFrame->getFrameArea());
2982 pFrame = pFrame->GetNext();
2986 SwTwips nReal = nDist;
2987 SwTwips nMinDiff = aRectFnSet.GetHeight(getFramePrintArea()) - nMin;
2988 if( nReal > nMinDiff )
2989 nReal = nMinDiff;
2990 if( nReal <= 0 )
2991 return nDist;
2993 SwRect aOldFrame( getFrameArea() );
2994 bool bMoveAccFrame = false;
2996 SwTwips nRealDist = nReal;
2997 if ( !bTst )
2999 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
3000 aRectFnSet.SetHeight( aFrm, nFrameHeight - nReal );
3002 if( bChgPos && !IsVertLR() )
3004 aFrm.Pos().AdjustX(nReal );
3007 bMoveAccFrame = true;
3010 SwNeighbourAdjust nAdjust = GetUpper() && GetUpper()->IsFootnoteBossFrame() ?
3011 static_cast<SwFootnoteBossFrame*>(GetUpper())->NeighbourhoodAdjustment()
3012 : SwNeighbourAdjust::GrowShrink;
3014 // AdjustNeighbourhood also in columns (but not in frames)
3015 if( SwNeighbourAdjust::OnlyAdjust == nAdjust )
3017 if ( IsPageBodyFrame() && !bBrowse )
3018 nReal = nDist;
3019 else
3020 { nReal = AdjustNeighbourhood( -nReal, bTst );
3021 nReal *= -1;
3022 if ( !bTst && IsBodyFrame() && nReal < nRealDist )
3024 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
3025 aRectFnSet.SetHeight( aFrm, aRectFnSet.GetHeight(aFrm) + nRealDist - nReal );
3027 if( bChgPos && !IsVertLR() )
3029 aFrm.Pos().AdjustX(nRealDist - nReal );
3032 OSL_ENSURE( !IsAccessibleFrame(), "bMoveAccFrame has to be set!" );
3036 else if( IsColumnFrame() || IsColBodyFrame() )
3038 SwTwips nTmp = GetUpper()->Shrink( nReal, bTst, bInfo );
3039 if ( nTmp != nReal )
3041 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
3042 aRectFnSet.SetHeight( aFrm, aRectFnSet.GetHeight(aFrm) + nReal - nTmp );
3044 if( bChgPos && !IsVertLR() )
3046 aFrm.Pos().AdjustX(nTmp - nReal );
3049 OSL_ENSURE( !IsAccessibleFrame(), "bMoveAccFrame has to be set!" );
3050 nReal = nTmp;
3053 else
3055 SwTwips nShrink = nReal;
3056 SwFrame* pToShrink = GetUpper();
3057 // NEW TABLES
3058 if ( IsCellFrame() )
3060 const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(this);
3061 if ( pThisCell->GetLayoutRowSpan() > 1 )
3063 SwCellFrame& rEndCell = const_cast<SwCellFrame&>(pThisCell->FindStartEndOfRowSpanCell( false ));
3064 pToShrink = rEndCell.GetUpper();
3068 // A table frame may have grown beyond its parent frame after
3069 // RemoveFollowFlowLine(), which is a problem in case the parent is a
3070 // section: prevent shrinking the section smaller than the contained
3071 // table.
3072 if (IsTabFrame()
3073 && static_cast<SwTabFrame*>(this)->IsRebuildLastLine()
3074 && pToShrink == GetUpper()
3075 && pToShrink->IsSctFrame()) // not required for page body, unsure about others
3077 SwTwips nUpperMin{0};
3078 for (SwFrame const* pFrame = pToShrink->GetLower();
3079 pFrame != GetNext(); pFrame = pFrame->GetNext())
3081 nUpperMin += aRectFnSet.GetHeight(pFrame->getFrameArea());
3083 if (aRectFnSet.GetHeight(pToShrink->getFramePrintArea()) - nShrink < nUpperMin)
3085 nShrink = aRectFnSet.GetHeight(pToShrink->getFramePrintArea()) - nUpperMin;
3086 if (nShrink <= 0)
3088 return 0; // nothing to do
3092 nReal = pToShrink ? pToShrink->Shrink( nShrink, bTst, bInfo ) : 0;
3093 if( ( SwNeighbourAdjust::GrowAdjust == nAdjust || SwNeighbourAdjust::AdjustGrow == nAdjust )
3094 && nReal < nShrink )
3095 AdjustNeighbourhood( nReal - nShrink );
3098 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
3099 if( bMoveAccFrame && IsAccessibleFrame() )
3101 SwRootFrame *pRootFrame = getRootFrame();
3102 if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
3103 pRootFrame->GetCurrShell() )
3105 pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( this, aOldFrame );
3108 #else
3109 (void)aOldFrame;
3110 (void)bMoveAccFrame;
3111 #endif
3113 if ( !bTst && (IsCellFrame() || IsColumnFrame() ? nReal : nRealDist) )
3115 SwPageFrame *pPage = FindPageFrame();
3116 if ( GetNext() )
3118 GetNext()->InvalidatePos_();
3119 if ( GetNext()->IsContentFrame() )
3120 GetNext()->InvalidatePage( pPage );
3121 if ( IsTabFrame() )
3122 static_cast<SwTabFrame*>(this)->SetComplete();
3124 else
3125 { if ( IsRetoucheFrame() )
3126 SetRetouche();
3127 if ( IsTabFrame() )
3129 static_cast<SwTabFrame*>(this)->SetComplete();
3130 if ( Lower() ) // Can also be in the Join and be empty!
3131 InvalidateNextPos();
3134 if ( !IsBodyFrame() )
3136 InvalidateAll_();
3137 InvalidatePage( pPage );
3138 bool bCompletePaint = true;
3139 const SwFrameFormat* pFormat = GetFormat();
3140 if (pFormat)
3142 std::unique_ptr<SvxBrushItem> aBack(pFormat->makeBackgroundBrushItem());
3143 const SvxGraphicPosition ePos = aBack->GetGraphicPos();
3144 if ( GPOS_NONE == ePos || GPOS_TILED == ePos )
3145 bCompletePaint = false;
3147 if (bCompletePaint)
3148 SetCompletePaint();
3151 if (!(GetType() & (SwFrameType::Row|SwFrameType::Tab|SwFrameType::FtnCont|SwFrameType::Page|SwFrameType::Root)))
3152 NotifyLowerObjs();
3154 if( IsCellFrame() )
3155 InvaPercentLowers( nReal );
3157 SwContentFrame *pCnt;
3158 if( IsFootnoteFrame() && !static_cast<SwFootnoteFrame*>(this)->GetAttr()->GetFootnote().IsEndNote() &&
3159 ( GetFormat()->GetDoc()->GetFootnoteInfo().m_ePos != FTNPOS_CHAPTER ||
3160 ( IsInSct() && FindSctFrame()->IsFootnoteAtEnd() ) ) &&
3161 nullptr != (pCnt = static_cast<SwFootnoteFrame*>(this)->GetRefFromAttr() ) )
3163 if ( pCnt->IsFollow() )
3164 { // If we are in another column/page than the frame with the
3165 // reference, we don't need to invalidate its master.
3166 SwFrame *pTmp = pCnt->FindFootnoteBossFrame(true) == FindFootnoteBossFrame(true)
3167 ? &pCnt->FindMaster()->GetFrame() : pCnt;
3168 pTmp->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
3169 pTmp->InvalidateSize();
3171 else
3173 if (pCnt->FindPageFrame() == FindPageFrame())
3175 pCnt->InvalidatePos();
3177 else
3179 SAL_WARN("sw.layout", "footnote frame on different page than ref frame?");
3184 return nReal;
3188 * Changes the size of the directly subsidiary Frame's that have a fixed size, proportionally to the
3189 * size change of the PrtArea of the Frame's.
3191 * The variable Frames are also proportionally adapted; they will grow/shrink again by themselves.
3193 void SwLayoutFrame::ChgLowersProp( const Size& rOldSize )
3195 // no change of lower properties for root frame or if no lower exists.
3196 if ( IsRootFrame() || !Lower() )
3197 return;
3199 // declare and init <SwFrame* pLowerFrame> with first lower
3200 SwFrame *pLowerFrame = Lower();
3202 // declare and init const booleans <bHeightChgd> and <bWidthChg>
3203 const bool bHeightChgd = rOldSize.Height() != getFramePrintArea().Height();
3204 const bool bWidthChgd = rOldSize.Width() != getFramePrintArea().Width();
3206 SwRectFnSet aRectFnSet(this);
3208 // This shortcut basically tries to handle only lower frames that
3209 // are affected by the size change. Otherwise much more lower frames
3210 // are invalidated.
3211 if ( !( aRectFnSet.IsVert() ? bHeightChgd : bWidthChgd ) &&
3212 ! Lower()->IsColumnFrame() &&
3213 ( ( IsBodyFrame() && IsInDocBody() && ( !IsInSct() || !FindSctFrame()->IsColLocked() ) ) ||
3214 // #i10826# Section frames without columns should not
3215 // invalidate all lowers!
3216 IsSctFrame() ) )
3218 // Determine page frame the body frame resp. the section frame belongs to.
3219 SwPageFrame *pPage = FindPageFrame();
3220 // Determine last lower by traveling through them using <GetNext()>.
3221 // During travel check each section frame, if it will be sized to
3222 // maximum. If Yes, invalidate size of section frame and set
3223 // corresponding flags at the page.
3226 if( pLowerFrame->IsSctFrame() && static_cast<SwSectionFrame*>(pLowerFrame)->ToMaximize_() )
3228 pLowerFrame->InvalidateSize_();
3229 pLowerFrame->InvalidatePage( pPage );
3231 if( pLowerFrame->GetNext() )
3232 pLowerFrame = pLowerFrame->GetNext();
3233 else
3234 break;
3235 } while( true );
3236 // If found last lower is a section frame containing no section
3237 // (section frame isn't valid and will be deleted in the future),
3238 // travel backwards.
3239 while( pLowerFrame->IsSctFrame() && !static_cast<SwSectionFrame*>(pLowerFrame)->GetSection() &&
3240 pLowerFrame->GetPrev() )
3241 pLowerFrame = pLowerFrame->GetPrev();
3242 // If found last lower is a section frame, set <pLowerFrame> to its last
3243 // content, if the section frame is valid and is not sized to maximum.
3244 // Otherwise set <pLowerFrame> to NULL - In this case body frame only
3245 // contains invalid section frames.
3246 if( pLowerFrame->IsSctFrame() )
3247 pLowerFrame = static_cast<SwSectionFrame*>(pLowerFrame)->GetSection() &&
3248 !static_cast<SwSectionFrame*>(pLowerFrame)->ToMaximize( false ) ?
3249 static_cast<SwSectionFrame*>(pLowerFrame)->FindLastContent() : nullptr;
3251 // continue with found last lower, probably the last content of a section
3252 if ( pLowerFrame )
3254 // If <pLowerFrame> is in a table frame, set <pLowerFrame> to this table
3255 // frame and continue.
3256 if ( pLowerFrame->IsInTab() )
3258 // OD 28.10.2002 #97265# - safeguard for setting <pLowerFrame> to
3259 // its table frame - check, if the table frame is also a lower
3260 // of the body frame, in order to assure that <pLowerFrame> is not
3261 // set to a frame, which is an *upper* of the body frame.
3262 SwFrame* pTableFrame = pLowerFrame->FindTabFrame();
3263 if ( IsAnLower( pTableFrame ) )
3265 pLowerFrame = pTableFrame;
3268 // Check, if variable size of body frame resp. section frame has grown
3269 // OD 28.10.2002 #97265# - correct check, if variable size has grown.
3270 SwTwips nOldHeight = aRectFnSet.IsVert() ? rOldSize.Width() : rOldSize.Height();
3271 if( nOldHeight < aRectFnSet.GetHeight(getFramePrintArea()) )
3273 // If variable size of body|section frame has grown, only found
3274 // last lower and the position of the its next have to be invalidated.
3275 pLowerFrame->InvalidateAll_();
3276 pLowerFrame->InvalidatePage( pPage );
3277 if( !pLowerFrame->IsFlowFrame() ||
3278 !SwFlowFrame::CastFlowFrame( pLowerFrame )->HasFollow() )
3279 pLowerFrame->InvalidateNextPos( true );
3280 if ( pLowerFrame->IsTextFrame() )
3281 static_cast<SwContentFrame*>(pLowerFrame)->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
3283 else
3285 SwFrame const* pFirstInvalid(nullptr);
3286 for (SwFrame const* pLow = Lower();
3287 pLow && pLow != pLowerFrame; pLow = pLow->GetNext())
3289 if (!pLow->isFrameAreaDefinitionValid())
3291 pFirstInvalid = pLow;
3292 break;
3295 // variable size of body|section frame has shrunk. Thus,
3296 // invalidate all lowers not matching the new body|section size
3297 // and the dedicated new last lower.
3298 if( aRectFnSet.IsVert() )
3300 SwTwips nBot = getFrameArea().Left() + getFramePrintArea().Left();
3301 while (pLowerFrame && pLowerFrame->GetPrev()
3302 && (pFirstInvalid != nullptr // tdf#152307 trust nothing after invalid frame
3303 || pLowerFrame->getFrameArea().Left() < nBot))
3305 pLowerFrame->InvalidateAll_();
3306 pLowerFrame->InvalidatePage( pPage );
3307 if (pLowerFrame == pFirstInvalid)
3309 pFirstInvalid = nullptr; // continue checking nBot
3311 pLowerFrame = pLowerFrame->GetPrev();
3314 else
3316 SwTwips nBot = getFrameArea().Top() + getFramePrintArea().Bottom();
3317 while (pLowerFrame && pLowerFrame->GetPrev()
3318 && (pFirstInvalid != nullptr // tdf#152307 trust nothing after invalid frame
3319 || nBot < pLowerFrame->getFrameArea().Top()))
3321 pLowerFrame->InvalidateAll_();
3322 pLowerFrame->InvalidatePage( pPage );
3323 if (pLowerFrame == pFirstInvalid)
3325 pFirstInvalid = nullptr; // continue checking nBot
3327 pLowerFrame = pLowerFrame->GetPrev();
3330 if ( pLowerFrame )
3332 pLowerFrame->InvalidateSize_();
3333 pLowerFrame->InvalidatePage( pPage );
3334 if ( pLowerFrame->IsTextFrame() )
3335 static_cast<SwContentFrame*>(pLowerFrame)->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
3338 // #i41694# - improvement by removing duplicates
3339 if ( pLowerFrame )
3341 if ( pLowerFrame->IsInSct() )
3343 // #i41694# - follow-up of issue #i10826#
3344 // No invalidation of section frame, if it's the this.
3345 SwFrame* pSectFrame = pLowerFrame->FindSctFrame();
3346 if( pSectFrame != this && IsAnLower( pSectFrame ) )
3348 pSectFrame->InvalidateSize_();
3349 pSectFrame->InvalidatePage( pPage );
3354 return;
3355 } // end of { special case }
3357 // Invalidate page for content only once.
3358 bool bInvaPageForContent = true;
3360 // Declare booleans <bFixChgd> and <bVarChgd>, indicating for text frame
3361 // adjustment, if fixed/variable size has changed.
3362 bool bFixChgd, bVarChgd;
3363 if( aRectFnSet.IsVert() == pLowerFrame->IsNeighbourFrame() )
3365 bFixChgd = bWidthChgd;
3366 bVarChgd = bHeightChgd;
3368 else
3370 bFixChgd = bHeightChgd;
3371 bVarChgd = bWidthChgd;
3374 // Declare const unsigned short <nFixWidth> and init it this frame types
3375 // which has fixed width in vertical respectively horizontal layout.
3376 // In vertical layout these are neighbour frames (cell and column frames),
3377 // header frames and footer frames.
3378 // In horizontal layout these are all frames, which aren't neighbour frames.
3379 const SwFrameType nFixWidth = aRectFnSet.IsVert() ? (FRM_NEIGHBOUR | FRM_HEADFOOT)
3380 : ~SwFrameType(FRM_NEIGHBOUR);
3382 // Declare const unsigned short <nFixHeight> and init it this frame types
3383 // which has fixed height in vertical respectively horizontal layout.
3384 // In vertical layout these are all frames, which aren't neighbour frames,
3385 // header frames, footer frames, body frames or foot note container frames.
3386 // In horizontal layout these are neighbour frames.
3387 const SwFrameType nFixHeight = aRectFnSet.IsVert() ? ~SwFrameType(FRM_NEIGHBOUR | FRM_HEADFOOT | FRM_BODYFTNC)
3388 : FRM_NEIGHBOUR;
3390 // Travel through all lowers using <GetNext()>
3391 while ( pLowerFrame )
3393 if ( pLowerFrame->IsTextFrame() )
3395 // Text frames will only be invalidated - prepare invalidation
3396 if ( bFixChgd )
3397 static_cast<SwContentFrame*>(pLowerFrame)->Prepare( PrepareHint::FixSizeChanged );
3398 if ( bVarChgd )
3399 static_cast<SwContentFrame*>(pLowerFrame)->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
3401 else
3403 // If lower isn't a table, row, cell or section frame, adjust its
3404 // frame size.
3405 const SwFrameType nLowerType = pLowerFrame->GetType();
3406 if ( !(nLowerType & (SwFrameType::Tab|SwFrameType::Row|SwFrameType::Cell|SwFrameType::Section)) )
3408 if ( bWidthChgd )
3410 if( nLowerType & nFixWidth )
3412 // Considering previous conditions:
3413 // In vertical layout set width of column, header and
3414 // footer frames to its upper width.
3415 // In horizontal layout set width of header, footer,
3416 // foot note container, foot note, body and no-text
3417 // frames to its upper width.
3418 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pLowerFrame);
3419 aFrm.Width( getFramePrintArea().Width() );
3421 else if( rOldSize.Width() && !pLowerFrame->IsFootnoteFrame() )
3423 // Adjust frame width proportional, if lower isn't a
3424 // foot note frame and condition <nLowerType & nFixWidth>
3425 // isn't true.
3426 // Considering previous conditions:
3427 // In vertical layout these are foot note container,
3428 // body and no-text frames.
3429 // In horizontal layout these are column and no-text frames.
3430 // OD 24.10.2002 #97265# - <double> calculation
3431 // Perform <double> calculation of new width, if
3432 // one of the coefficients is greater than 50000
3433 SwTwips nNewWidth;
3434 if ( (pLowerFrame->getFrameArea().Width() > 50000) ||
3435 (getFramePrintArea().Width() > 50000) )
3437 double nNewWidthTmp =
3438 ( double(pLowerFrame->getFrameArea().Width())
3439 * double(getFramePrintArea().Width()) )
3440 / double(rOldSize.Width());
3441 nNewWidth = SwTwips(nNewWidthTmp);
3443 else
3445 nNewWidth =
3446 (pLowerFrame->getFrameArea().Width() * getFramePrintArea().Width()) / rOldSize.Width();
3449 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pLowerFrame);
3450 aFrm.Width( nNewWidth );
3453 if ( bHeightChgd )
3455 if( nLowerType & nFixHeight )
3457 // Considering previous conditions:
3458 // In vertical layout set height of foot note and
3459 // no-text frames to its upper height.
3460 // In horizontal layout set height of column frames
3461 // to its upper height.
3462 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pLowerFrame);
3463 aFrm.Height( getFramePrintArea().Height() );
3465 // OD 01.10.2002 #102211#
3466 // add conditions <!pLowerFrame->IsHeaderFrame()> and
3467 // <!pLowerFrame->IsFooterFrame()> in order to avoid that
3468 // the <Grow> of header or footer are overwritten.
3469 // NOTE: Height of header/footer frame is determined by contents.
3470 else if ( rOldSize.Height() &&
3471 !pLowerFrame->IsFootnoteFrame() &&
3472 !pLowerFrame->IsHeaderFrame() &&
3473 !pLowerFrame->IsFooterFrame()
3476 // Adjust frame height proportional, if lower isn't a
3477 // foot note, a header or a footer frame and
3478 // condition <nLowerType & nFixHeight> isn't true.
3479 // Considering previous conditions:
3480 // In vertical layout these are column, foot note container,
3481 // body and no-text frames.
3482 // In horizontal layout these are column, foot note
3483 // container, body and no-text frames.
3485 // OD 29.10.2002 #97265# - special case for page lowers
3486 // The page lowers that have to be adjusted on page height
3487 // change are the body frame and the foot note container
3488 // frame.
3489 // In vertical layout the height of both is directly
3490 // adjusted to the page height change.
3491 // In horizontal layout the height of the body frame is
3492 // directly adjusted to the page height change and the
3493 // foot note frame height isn't touched, because its
3494 // determined by its content.
3495 // OD 31.03.2003 #108446# - apply special case for page
3496 // lowers - see description above - also for section columns.
3497 if ( IsPageFrame() ||
3498 ( IsColumnFrame() && IsInSct() )
3501 OSL_ENSURE( pLowerFrame->IsBodyFrame() || pLowerFrame->IsFootnoteContFrame(),
3502 "ChgLowersProp - only for body or foot note container" );
3503 if ( pLowerFrame->IsBodyFrame() || pLowerFrame->IsFootnoteContFrame() )
3505 if ( IsVertical() || pLowerFrame->IsBodyFrame() )
3507 SwTwips nNewHeight =
3508 pLowerFrame->getFrameArea().Height() +
3509 ( getFramePrintArea().Height() - rOldSize.Height() );
3510 if ( nNewHeight < 0)
3512 // OD 01.04.2003 #108446# - adjust assertion condition and text
3513 OSL_ENSURE( !( IsPageFrame() &&
3514 (pLowerFrame->getFrameArea().Height()>0) &&
3515 (pLowerFrame->isFrameAreaDefinitionValid()) ),
3516 "ChgLowersProg - negative height for lower.");
3517 nNewHeight = 0;
3520 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pLowerFrame);
3521 aFrm.Height( nNewHeight );
3525 else
3527 SwTwips nNewHeight;
3528 // OD 24.10.2002 #97265# - <double> calculation
3529 // Perform <double> calculation of new height, if
3530 // one of the coefficients is greater than 50000
3531 if ( (pLowerFrame->getFrameArea().Height() > 50000) ||
3532 (getFramePrintArea().Height() > 50000) )
3534 double nNewHeightTmp =
3535 ( double(pLowerFrame->getFrameArea().Height())
3536 * double(getFramePrintArea().Height()) )
3537 / double(rOldSize.Height());
3538 nNewHeight = SwTwips(nNewHeightTmp);
3540 else
3542 nNewHeight = ( pLowerFrame->getFrameArea().Height()
3543 * getFramePrintArea().Height() ) / rOldSize.Height();
3545 if( !pLowerFrame->GetNext() )
3547 SwTwips nSum = getFramePrintArea().Height();
3548 SwFrame* pTmp = Lower();
3549 while( pTmp->GetNext() )
3551 if( !pTmp->IsFootnoteContFrame() || !pTmp->IsVertical() )
3552 nSum -= pTmp->getFrameArea().Height();
3553 pTmp = pTmp->GetNext();
3555 if( nSum - nNewHeight == 1 &&
3556 nSum == pLowerFrame->getFrameArea().Height() )
3557 nNewHeight = nSum;
3560 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pLowerFrame);
3561 aFrm.Height( nNewHeight );
3566 } // end of else { NOT text frame }
3568 pLowerFrame->InvalidateAll_();
3569 if ( bInvaPageForContent && pLowerFrame->IsContentFrame() )
3571 pLowerFrame->InvalidatePage();
3572 bInvaPageForContent = false;
3575 if ( !pLowerFrame->GetNext() && pLowerFrame->IsRetoucheFrame() )
3577 //If a growth took place and the subordinate elements can retouch
3578 //itself (currently Tabs, Sections and Content) we trigger it.
3579 if ( rOldSize.Height() < getFramePrintArea().SSize().Height() ||
3580 rOldSize.Width() < getFramePrintArea().SSize().Width() )
3581 pLowerFrame->SetRetouche();
3583 pLowerFrame = pLowerFrame->GetNext();
3586 // Finally adjust the columns if width is set to auto
3587 // Possible optimization: execute this code earlier in this function and
3588 // return???
3589 if ( !(( (aRectFnSet.IsVert() && bHeightChgd) || (! aRectFnSet.IsVert() && bWidthChgd) ) &&
3590 Lower()->IsColumnFrame()) )
3591 return;
3593 // get column attribute
3594 const SwFormatCol* pColAttr = nullptr;
3595 if ( IsPageBodyFrame() )
3597 OSL_ENSURE( GetUpper()->IsPageFrame(), "Upper is not page frame" );
3598 pColAttr = &GetUpper()->GetFormat()->GetCol();
3600 else
3602 OSL_ENSURE( IsFlyFrame() || IsSctFrame(), "Columns not in fly or section" );
3603 pColAttr = &GetFormat()->GetCol();
3606 if ( pColAttr->IsOrtho() && pColAttr->GetNumCols() > 1 )
3607 AdjustColumns( pColAttr, false );
3610 /** "Formats" the Frame; Frame and PrtArea.
3612 * The Fixsize is not set here.
3614 void SwLayoutFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
3616 OSL_ENSURE( pAttrs, "LayoutFrame::Format, pAttrs is 0." );
3618 if ( isFramePrintAreaValid() && isFrameAreaSizeValid() )
3619 return;
3621 bool bHideWhitespace = false;
3622 if (IsPageFrame())
3624 SwViewShell* pShell = getRootFrame()->GetCurrShell();
3625 if (pShell && pShell->GetViewOptions()->IsWhitespaceHidden())
3627 // This is needed so that no space is reserved for the margin on
3628 // the last page of the document. Other pages would have no margin
3629 // set even without this, as their frame height is the content
3630 // height already.
3631 bHideWhitespace = true;
3635 const sal_uInt16 nLeft = o3tl::narrowing<sal_uInt16>(pAttrs->CalcLeft(this));
3636 const sal_uInt16 nUpper = bHideWhitespace ? 0 : pAttrs->CalcTop();
3638 const sal_uInt16 nRight = o3tl::narrowing<sal_uInt16>(pAttrs->CalcRight(this));
3639 const sal_uInt16 nLower = bHideWhitespace ? 0 : pAttrs->CalcBottom();
3641 SwRectFnSet fnRect(IsVertical() && !IsPageFrame(), IsVertLR(), IsVertLRBT());
3642 if ( !isFramePrintAreaValid() )
3644 setFramePrintAreaValid(true);
3645 fnRect.SetXMargins(*this, nLeft, nRight);
3646 fnRect.SetYMargins(*this, nUpper, nLower);
3649 if ( isFrameAreaSizeValid() )
3650 return;
3652 if ( !HasFixSize() )
3654 const SwTwips nBorder = nUpper + nLower;
3655 const SwFormatFrameSize &rSz = GetFormat()->GetFrameSize();
3656 SwTwips nMinHeight = rSz.GetHeightSizeType() == SwFrameSize::Minimum ? rSz.GetHeight() : 0;
3659 setFrameAreaSizeValid(true);
3661 //The size in VarSize is calculated using the content plus the
3662 // borders.
3663 SwTwips nRemaining = 0;
3664 SwFrame *pFrame = Lower();
3665 while ( pFrame )
3666 { nRemaining += fnRect.GetHeight(pFrame->getFrameArea());
3667 if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsUndersized() )
3668 // This TextFrame would like to be a bit bigger
3669 nRemaining += static_cast<SwTextFrame*>(pFrame)->GetParHeight()
3670 - fnRect.GetHeight(pFrame->getFramePrintArea());
3671 else if( pFrame->IsSctFrame() && static_cast<SwSectionFrame*>(pFrame)->IsUndersized() )
3672 nRemaining += static_cast<SwSectionFrame*>(pFrame)->Undersize();
3673 pFrame = pFrame->GetNext();
3675 nRemaining += nBorder;
3676 nRemaining = std::max( nRemaining, nMinHeight );
3677 const SwTwips nDiff = nRemaining - fnRect.GetHeight(getFrameArea());
3678 const tools::Long nOldLeft = fnRect.GetLeft(getFrameArea());
3679 const tools::Long nOldTop = fnRect.GetTop(getFrameArea());
3680 if ( nDiff )
3682 if ( nDiff > 0 )
3683 Grow( nDiff );
3684 else
3685 Shrink( -nDiff );
3686 //Updates the positions using the fast channel.
3687 MakePos();
3689 //Don't exceed the bottom edge of the Upper.
3690 if (GetUpper() && fnRect.GetHeight(getFrameArea()))
3692 const SwTwips nLimit = fnRect.GetPrtBottom(*GetUpper());
3693 if( fnRect.SetLimit(*this, nLimit) &&
3694 nOldLeft == fnRect.GetLeft(getFrameArea()) &&
3695 nOldTop == fnRect.GetTop(getFrameArea()) )
3697 setFrameAreaSizeValid(true);
3698 setFramePrintAreaValid(true);
3701 } while ( !isFrameAreaSizeValid() );
3703 else if (GetType() & FRM_HEADFOOT)
3706 { if ( getFrameArea().Height() != pAttrs->GetSize().Height() )
3708 ChgSize( Size( getFrameArea().Width(), pAttrs->GetSize().Height()));
3711 setFrameAreaSizeValid(true);
3712 MakePos();
3713 } while ( !isFrameAreaSizeValid() );
3715 else
3717 setFrameAreaSizeValid(true);
3720 // While updating the size, PrtArea might be invalidated.
3721 if (!isFramePrintAreaValid())
3723 setFramePrintAreaValid(true);
3724 fnRect.SetXMargins(*this, nLeft, nRight);
3725 fnRect.SetYMargins(*this, nUpper, nLower);
3729 static void InvaPercentFlys( SwFrame *pFrame, SwTwips nDiff )
3731 OSL_ENSURE( pFrame->GetDrawObjs(), "Can't find any Objects" );
3732 for (SwAnchoredObject* pAnchoredObj : *pFrame->GetDrawObjs())
3734 if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
3736 const SwFormatFrameSize &rSz = pFly->GetFormat()->GetFrameSize();
3737 if ( rSz.GetWidthPercent() || rSz.GetHeightPercent() )
3739 bool bNotify = true;
3740 // If we've a fly with more than 90% relative height...
3741 if( rSz.GetHeightPercent() > 90 && pFly->GetAnchorFrame() &&
3742 rSz.GetHeightPercent() != SwFormatFrameSize::SYNCED && nDiff )
3744 const SwFrame *pRel = pFly->IsFlyLayFrame() ? pFly->GetAnchorFrame():
3745 pFly->GetAnchorFrame()->GetUpper();
3746 // ... and we have already more than 90% height and we
3747 // not allow the text to go through...
3748 // then a notification could cause an endless loop, e.g.
3749 // 100% height and no text wrap inside a cell of a table.
3750 if( pFly->getFrameArea().Height()*10 >
3751 ( nDiff + pRel->getFramePrintArea().Height() )*9 &&
3752 pFly->GetFormat()->GetSurround().GetSurround() !=
3753 css::text::WrapTextMode_THROUGH )
3754 bNotify = false;
3756 if( bNotify )
3757 pFly->InvalidateSize();
3763 void SwLayoutFrame::InvaPercentLowers( SwTwips nDiff )
3765 if ( GetDrawObjs() )
3766 ::InvaPercentFlys( this, nDiff );
3768 SwFrame *pFrame = ContainsContent();
3769 if ( !pFrame )
3770 return;
3774 if ( pFrame->IsInTab() && !IsTabFrame() )
3776 SwFrame *pTmp = pFrame->FindTabFrame();
3777 OSL_ENSURE( pTmp, "Where's my TabFrame?" );
3778 if( IsAnLower( pTmp ) )
3779 pFrame = pTmp;
3782 if ( pFrame->IsTabFrame() )
3784 const SwFormatFrameSize &rSz = static_cast<SwLayoutFrame*>(pFrame)->GetFormat()->GetFrameSize();
3785 if ( rSz.GetWidthPercent() || rSz.GetHeightPercent() )
3786 pFrame->InvalidatePrt();
3788 else if ( pFrame->GetDrawObjs() )
3789 ::InvaPercentFlys( pFrame, nDiff );
3790 pFrame = pFrame->FindNextCnt();
3791 } while ( pFrame && IsAnLower( pFrame ) ) ;
3794 tools::Long SwLayoutFrame::CalcRel( const SwFormatFrameSize &rSz ) const
3796 tools::Long nRet = rSz.GetWidth(),
3797 nPercent = rSz.GetWidthPercent();
3799 if ( nPercent )
3801 const SwFrame *pRel = GetUpper();
3802 tools::Long nRel = LONG_MAX;
3803 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
3804 const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
3805 if( pRel->IsPageBodyFrame() && pSh && bBrowseMode && pSh->VisArea().Width() )
3807 nRel = pSh->GetBrowseWidth();
3808 tools::Long nDiff = nRel - pRel->getFramePrintArea().Width();
3809 if ( nDiff > 0 )
3810 nRel -= nDiff;
3812 nRel = std::min( nRel, pRel->getFramePrintArea().Width() );
3813 nRet = nRel * nPercent / 100;
3815 return nRet;
3818 // Local helpers for SwLayoutFrame::FormatWidthCols()
3820 static tools::Long lcl_CalcMinColDiff( SwLayoutFrame *pLayFrame )
3822 tools::Long nDiff = 0, nFirstDiff = 0;
3823 SwLayoutFrame *pCol = static_cast<SwLayoutFrame*>(pLayFrame->Lower());
3824 OSL_ENSURE( pCol, "Where's the columnframe?" );
3825 SwFrame *pFrame = pCol->Lower();
3828 if( pFrame && pFrame->IsBodyFrame() )
3829 pFrame = static_cast<SwBodyFrame*>(pFrame)->Lower();
3830 if ( pFrame && pFrame->IsTextFrame() )
3832 const tools::Long nTmp = static_cast<SwTextFrame*>(pFrame)->FirstLineHeight();
3833 if (nTmp != std::numeric_limits<SwTwips>::max())
3835 if ( pCol == pLayFrame->Lower() )
3836 nFirstDiff = nTmp;
3837 else
3838 nDiff = nDiff ? std::min( nDiff, nTmp ) : nTmp;
3841 //Skip empty columns!
3842 pCol = static_cast<SwLayoutFrame*>(pCol->GetNext());
3843 while ( pCol && nullptr == (pFrame = pCol->Lower()) )
3844 pCol = static_cast<SwLayoutFrame*>(pCol->GetNext());
3846 } while ( pFrame && pCol );
3848 return nDiff ? nDiff : nFirstDiff ? nFirstDiff : 240;
3851 static bool lcl_IsFlyHeightClipped( SwLayoutFrame *pLay )
3853 SwFrame *pFrame = pLay->ContainsContent();
3854 while ( pFrame )
3856 if ( pFrame->IsInTab() )
3857 pFrame = pFrame->FindTabFrame();
3859 if ( pFrame->GetDrawObjs() )
3861 const size_t nCnt = pFrame->GetDrawObjs()->size();
3862 for ( size_t i = 0; i < nCnt; ++i )
3864 SwAnchoredObject* pAnchoredObj = (*pFrame->GetDrawObjs())[i];
3865 if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
3867 if ( pFly->IsHeightClipped() &&
3868 ( !pFly->IsFlyFreeFrame() || pFly->GetPageFrame() ) )
3869 return true;
3873 pFrame = pFrame->FindNextCnt();
3875 return false;
3878 void SwLayoutFrame::FormatWidthCols( const SwBorderAttrs &rAttrs,
3879 const SwTwips nBorder, const SwTwips nMinHeight )
3881 //If there are columns involved, the size is adjusted using the last column.
3882 //1. Format content.
3883 //2. Calculate height of the last column: if it's too big, the Fly has to
3884 // grow. The amount by which the Fly grows is not the amount of the
3885 // overhang because we have to act on the assumption that some text flows
3886 // back which will generate some more space.
3887 // The amount which we grow by equals the overhang
3888 // divided by the amount of columns or the overhang itself if it's smaller
3889 // than the amount of columns.
3890 //3. Go back to 1. until everything is stable.
3892 const SwFormatCol &rCol = rAttrs.GetAttrSet().GetCol();
3893 const sal_uInt16 nNumCols = rCol.GetNumCols();
3895 bool bEnd = false;
3896 bool bBackLock = false;
3897 SwViewShell *pSh = getRootFrame()->GetCurrShell();
3898 SwViewShellImp *pImp = pSh ? pSh->Imp() : nullptr;
3899 vcl::RenderContext* pRenderContext = pSh ? pSh->GetOut() : nullptr;
3901 // Underlying algorithm
3902 // We try to find the optimal height for the column.
3903 // nMinimum starts with the passed minimum height and is then maintained
3904 // as the maximum height on which column content did not fit into a
3905 // column.
3906 // nMaximum starts with LONG_MAX and is then maintained as the minimum
3907 // height on which the content fit into the columns.
3908 // In column based sections nMaximum starts at the maximum value which
3909 // the environment defines, this can certainly be a value on which
3910 // content doesn't fit.
3911 // The columns are formatted. If content still juts out, nMinimum is
3912 // adjusted accordingly, then we grow, at least by nMinDiff but not
3913 // over a certain nMaximum. If no content juts out but there is still
3914 // some space left in a column, shrinking is done accordingly, at
3915 // least by nMinDiff but not below the nMinimum.
3916 // Cancel as soon as no content juts out and the difference from minimum
3917 // to maximum is less than nMinDiff or the maximum which was defined by
3918 // the environment is reached and some content still doesn't fit.
3920 // Criticism of this implementation
3921 // 1. Theoretically situations are possible in which the content fits in
3922 // a lower height but not in a higher height. To ensure that the code
3923 // handles such situations the code contains a few checks concerning
3924 // minimum and maximum which probably are never triggered.
3925 // 2. We use the same nMinDiff for shrinking and growing, but nMinDiff
3926 // is more or less the smallest first line height and doesn't seem ideal
3927 // as minimum value for shrinking.
3929 tools::Long nMinimum = nMinHeight;
3930 tools::Long nMaximum;
3931 bool bNoBalance = false;
3932 SwRectFnSet aRectFnSet(this);
3933 if( IsSctFrame() )
3935 nMaximum = aRectFnSet.GetHeight(getFrameArea()) - nBorder +
3936 aRectFnSet.BottomDist(getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
3937 nMaximum += GetUpper()->Grow( LONG_MAX, true );
3938 if( nMaximum < nMinimum )
3940 if( nMaximum < 0 )
3941 nMinimum = nMaximum = 0;
3942 else
3943 nMinimum = nMaximum;
3945 if( nMaximum > BROWSE_HEIGHT )
3946 nMaximum = BROWSE_HEIGHT;
3948 bNoBalance = static_cast<SwSectionFrame*>(this)->GetSection()->GetFormat()->
3949 GetBalancedColumns().GetValue();
3950 SwFrame* pAny = ContainsAny();
3951 if( bNoBalance ||
3952 ( !aRectFnSet.GetHeight(getFrameArea()) && pAny ) )
3954 tools::Long nTop = aRectFnSet.GetTopMargin(*this);
3955 // #i23129# - correction
3956 // to the calculated maximum height.
3958 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
3959 aRectFnSet.AddBottom( aFrm, nMaximum - aRectFnSet.GetHeight(getFrameArea()) );
3962 if( nTop > nMaximum )
3963 nTop = nMaximum;
3964 aRectFnSet.SetYMargins( *this, nTop, 0 );
3966 if( !pAny && !static_cast<SwSectionFrame*>(this)->IsFootnoteLock() )
3968 SwFootnoteContFrame* pFootnoteCont = static_cast<SwSectionFrame*>(this)->ContainsFootnoteCont();
3969 if( pFootnoteCont )
3971 SwFrame* pFootnoteAny = pFootnoteCont->ContainsAny();
3972 if( pFootnoteAny && pFootnoteAny->isFrameAreaDefinitionValid() )
3974 bBackLock = true;
3975 static_cast<SwSectionFrame*>(this)->SetFootnoteLock( true );
3980 else
3981 nMaximum = LONG_MAX;
3983 // #i3317# - reset temporarily consideration
3984 // of wrapping style influence
3985 SwPageFrame* pPageFrame = FindPageFrame();
3986 SwSortedObjs* pObjs = pPageFrame ? pPageFrame->GetSortedObjs() : nullptr;
3987 if ( pObjs )
3989 for (SwAnchoredObject* pAnchoredObj : *pObjs)
3991 if ( IsAnLower( pAnchoredObj->GetAnchorFrame() ) )
3993 pAnchoredObj->SetTmpConsiderWrapInfluence( false );
3999 //Could take a while therefore check for Waitcrsr here.
4000 if ( pImp )
4001 pImp->CheckWaitCursor();
4003 setFrameAreaSizeValid(true);
4004 //First format the column, before using lots of stack space here.
4005 //Also set width and height of the column (if they are wrong)
4006 //while we are at it.
4007 SwLayoutFrame *pCol = static_cast<SwLayoutFrame*>(Lower());
4009 // #i27399#
4010 // Simply setting the column width based on the values returned by
4011 // CalcColWidth does not work for automatic column width.
4012 AdjustColumns( &rCol, false );
4014 for ( sal_uInt16 i = 0; i < nNumCols; ++i )
4016 pCol->Calc(pRenderContext);
4017 // ColumnFrames have a BodyFrame now, which needs to be calculated
4018 pCol->Lower()->Calc(pRenderContext);
4019 if( pCol->Lower()->GetNext() )
4020 pCol->Lower()->GetNext()->Calc(pRenderContext); // SwFootnoteCont
4021 pCol = static_cast<SwLayoutFrame*>(pCol->GetNext());
4024 ::CalcContent( this );
4026 pCol = static_cast<SwLayoutFrame*>(Lower());
4027 OSL_ENSURE( pCol && pCol->GetNext(), ":-( column making holidays?");
4028 // set bMinDiff if no empty columns exist
4029 bool bMinDiff = true;
4030 // OD 28.03.2003 #108446# - check for all column content and all columns
4031 while ( bMinDiff && pCol )
4033 bMinDiff = nullptr != pCol->ContainsContent();
4034 pCol = static_cast<SwLayoutFrame*>(pCol->GetNext());
4036 pCol = static_cast<SwLayoutFrame*>(Lower());
4038 SwTwips nDiff = 0;
4039 SwTwips nMaxFree = 0;
4040 SwTwips nAllFree = LONG_MAX;
4041 // set bFoundLower if there is at least one non-empty column
4042 bool bFoundLower = false;
4043 while( pCol )
4045 SwLayoutFrame* pLay = static_cast<SwLayoutFrame*>(pCol->Lower());
4046 SwTwips nInnerHeight = aRectFnSet.GetHeight(pLay->getFrameArea()) -
4047 aRectFnSet.GetHeight(pLay->getFramePrintArea());
4048 if( pLay->Lower() )
4050 bFoundLower = true;
4051 nInnerHeight += pLay->InnerHeight();
4053 else if( nInnerHeight < 0 )
4054 nInnerHeight = 0;
4056 if( pLay->GetNext() )
4058 bFoundLower = true;
4059 pLay = static_cast<SwLayoutFrame*>(pLay->GetNext());
4060 OSL_ENSURE( pLay->IsFootnoteContFrame(),"FootnoteContainer expected" );
4061 nInnerHeight += pLay->InnerHeight();
4062 nInnerHeight += aRectFnSet.GetHeight(pLay->getFrameArea()) -
4063 aRectFnSet.GetHeight(pLay->getFramePrintArea());
4065 nInnerHeight -= aRectFnSet.GetHeight(pCol->getFramePrintArea());
4066 if( nInnerHeight > nDiff )
4068 nDiff = nInnerHeight;
4069 nAllFree = 0;
4071 else
4073 if( nMaxFree < -nInnerHeight )
4074 nMaxFree = -nInnerHeight;
4075 if( nAllFree > -nInnerHeight )
4076 nAllFree = -nInnerHeight;
4078 pCol = static_cast<SwLayoutFrame*>(pCol->GetNext());
4081 if ( bFoundLower || ( IsSctFrame() && static_cast<SwSectionFrame*>(this)->HasFollow() ) )
4083 SwTwips nMinDiff = ::lcl_CalcMinColDiff( this );
4084 // Here we decide if growing is needed - this is the case, if
4085 // column content (nDiff) or a Fly juts over.
4086 // In sections with columns we take into account to set the size
4087 // when having a non-empty Follow.
4088 if ( nDiff || ::lcl_IsFlyHeightClipped( this ) ||
4089 ( IsSctFrame() && static_cast<SwSectionFrame*>(this)->CalcMinDiff( nMinDiff ) ) )
4091 tools::Long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
4092 // The minimum must not be smaller than our PrtHeight as
4093 // long as something juts over.
4094 if( nMinimum < nPrtHeight )
4095 nMinimum = nPrtHeight;
4096 // The maximum must not be smaller than PrtHeight if
4097 // something still juts over.
4098 if( nMaximum < nPrtHeight )
4099 nMaximum = nPrtHeight; // Robust, but will this ever happen?
4100 if( !nDiff ) // If only Flys jut over, we grow by nMinDiff
4101 nDiff = nMinDiff;
4102 // If we should grow more than by nMinDiff we split it over
4103 // the columns
4104 if ( std::abs(nDiff - nMinDiff) > nNumCols && nDiff > static_cast<tools::Long>(nNumCols) )
4105 nDiff /= nNumCols;
4107 if ( bMinDiff )
4108 { // If no empty column exists, we want to grow at least
4109 // by nMinDiff. Special case: If we are smaller than the
4110 // minimal FrameHeight and PrtHeight is smaller than
4111 // nMindiff we grow in a way that PrtHeight is exactly
4112 // nMinDiff afterwards.
4113 tools::Long nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
4114 if ( nFrameHeight > nMinHeight || nPrtHeight >= nMinDiff )
4115 nDiff = std::max( nDiff, nMinDiff );
4116 else if( nDiff < nMinDiff )
4117 nDiff = nMinDiff - nPrtHeight + 1;
4119 // nMaximum is a size in which the content fit or the
4120 // requested value from the environment, therefore we don't
4121 // should not exceed this value.
4122 if( nDiff + nPrtHeight > nMaximum )
4123 nDiff = nMaximum - nPrtHeight;
4125 else if (nMaximum > nMinimum) // We fit, do we still have some opportunity to shrink?
4127 tools::Long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
4128 if ( nMaximum < nPrtHeight )
4129 nDiff = nMaximum - nPrtHeight; // We grew over a working
4130 // height and shrink back to it, but will this ever
4131 // happen?
4132 else
4133 { // We have a new maximum, a size where the content fits.
4134 nMaximum = nPrtHeight;
4135 // If the extra space in the column is bigger than
4136 // nMinDiff, we shrink a bit, unless size would drop
4137 // below the minimum.
4138 if ( !bNoBalance &&
4139 // #i23129# - <nMinDiff> can be
4140 // big, because of an object at the beginning of
4141 // a column. Thus, decrease optimization here.
4142 //nMaxFree >= nMinDiff &&
4143 nMaxFree > 0 &&
4144 ( !nAllFree ||
4145 nMinimum < nPrtHeight - nMinDiff ) )
4147 nMaxFree /= nNumCols; // disperse over the columns
4148 nDiff = nMaxFree < nMinDiff ? -nMinDiff : -nMaxFree; // min nMinDiff
4149 if( nPrtHeight + nDiff <= nMinimum ) // below the minimum?
4150 nDiff = ( nMinimum - nMaximum ) / 2; // Take the center
4152 else if( nAllFree )
4154 nDiff = -nAllFree;
4155 if( nPrtHeight + nDiff <= nMinimum ) // Less than minimum?
4156 nDiff = ( nMinimum - nMaximum ) / 2; // Take the center
4160 if( nDiff ) // now we shrink or grow...
4162 Size aOldSz( getFramePrintArea().SSize() );
4163 tools::Long nTop = aRectFnSet.GetTopMargin(*this);
4164 nDiff = aRectFnSet.GetHeight(getFramePrintArea()) + nDiff + nBorder - aRectFnSet.GetHeight(getFrameArea());
4167 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
4168 aRectFnSet.AddBottom( aFrm, nDiff );
4171 // #i68520#
4172 SwFlyFrame *pFlyFrame = IsFlyFrame() ? static_cast<SwFlyFrame*>(this) : nullptr;
4173 if (pFlyFrame)
4175 pFlyFrame->InvalidateObjRectWithSpaces();
4177 aRectFnSet.SetYMargins( *this, nTop, nBorder - nTop );
4178 ChgLowersProp( aOldSz );
4179 NotifyLowerObjs();
4181 // #i3317# - reset temporarily consideration
4182 // of wrapping style influence
4183 SwPageFrame* pTmpPageFrame = FindPageFrame();
4184 SwSortedObjs* pTmpObjs = pTmpPageFrame ? pTmpPageFrame->GetSortedObjs() : nullptr;
4185 if ( pTmpObjs )
4187 for (SwAnchoredObject* pAnchoredObj : *pTmpObjs)
4189 if ( IsAnLower( pAnchoredObj->GetAnchorFrame() ) )
4191 pAnchoredObj->SetTmpConsiderWrapInfluence( false );
4195 //Invalidate suitable to nicely balance the Frames.
4196 //- Every first lower starting from the second column gets a
4197 // InvalidatePos();
4198 pCol = static_cast<SwLayoutFrame*>(Lower()->GetNext());
4199 while ( pCol )
4201 SwFrame *pLow = pCol->Lower();
4202 if ( pLow )
4203 pLow->InvalidatePos_();
4204 pCol = static_cast<SwLayoutFrame*>(pCol->GetNext());
4206 if( IsSctFrame() && static_cast<SwSectionFrame*>(this)->HasFollow() )
4208 // If we created a Follow, we need to give its content
4209 // the opportunity to flow back inside the CalcContent
4210 SwContentFrame* pTmpContent =
4211 static_cast<SwSectionFrame*>(this)->GetFollow()->ContainsContent();
4212 if( pTmpContent )
4213 pTmpContent->InvalidatePos_();
4216 else
4217 bEnd = true;
4219 else
4220 bEnd = true;
4222 } while ( !bEnd || !isFrameAreaSizeValid() );
4224 // OD 01.04.2003 #108446# - Don't collect endnotes for sections. Thus, set
4225 // 2nd parameter to <true>.
4226 ::CalcContent( this, true );
4227 if( IsSctFrame() )
4229 // OD 14.03.2003 #i11760# - adjust 2nd parameter - sal_True --> true
4230 setFrameAreaSizeValid(true);
4231 ::CalcContent( this, true );
4232 if( bBackLock )
4233 static_cast<SwSectionFrame*>(this)->SetFootnoteLock( false );
4237 void SwLayoutFrame::dumpAsXmlAttributes(xmlTextWriterPtr writer) const
4239 SwFrame::dumpAsXmlAttributes(writer);
4241 const SwFrameFormat* pFormat = GetFormat();
4242 if (pFormat)
4244 (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "format" ), "%p", pFormat);
4245 (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "formatName" ), "%s", BAD_CAST(pFormat->GetName().toUtf8().getStr()));
4249 static SwContentFrame* lcl_InvalidateSection( SwFrame *pCnt, SwInvalidateFlags nInv )
4251 SwSectionFrame* pSect = pCnt->FindSctFrame();
4252 // If our ContentFrame is placed inside a table or a footnote, only sections
4253 // which are also placed inside are meant.
4254 // Exception: If a table is directly passed.
4255 if( ( ( pCnt->IsInTab() && !pSect->IsInTab() ) ||
4256 ( pCnt->IsInFootnote() && !pSect->IsInFootnote() ) ) && !pCnt->IsTabFrame() )
4257 return nullptr;
4258 if( nInv & SwInvalidateFlags::Size )
4259 pSect->InvalidateSize_();
4260 if( nInv & SwInvalidateFlags::Pos )
4261 pSect->InvalidatePos_();
4262 if( nInv & SwInvalidateFlags::PrtArea )
4263 pSect->InvalidatePrt_();
4264 SwFlowFrame *pFoll = pSect->GetFollow();
4265 // Temporary separation from follow
4266 pSect->SetFollow( nullptr );
4267 SwContentFrame* pRet = pSect->FindLastContent();
4268 pSect->SetFollow( pFoll );
4269 return pRet;
4272 static SwContentFrame* lcl_InvalidateTable( SwTabFrame *pTable, SwInvalidateFlags nInv )
4274 if( ( nInv & SwInvalidateFlags::Section ) && pTable->IsInSct() )
4275 lcl_InvalidateSection( pTable, nInv );
4276 if( nInv & SwInvalidateFlags::Size )
4277 pTable->InvalidateSize_();
4278 if( nInv & SwInvalidateFlags::Pos )
4279 pTable->InvalidatePos_();
4280 if( nInv & SwInvalidateFlags::PrtArea )
4281 pTable->InvalidatePrt_();
4282 return pTable->FindLastContent();
4285 static void lcl_InvalidateAllContent( SwContentFrame *pCnt, SwInvalidateFlags nInv );
4287 static void lcl_InvalidateContent( SwContentFrame *pCnt, SwInvalidateFlags nInv )
4289 SwContentFrame *pLastTabCnt = nullptr;
4290 SwContentFrame *pLastSctCnt = nullptr;
4291 while ( pCnt )
4293 if( nInv & SwInvalidateFlags::Section )
4295 if( pCnt->IsInSct() )
4297 // See above at tables
4298 if( !pLastSctCnt )
4299 pLastSctCnt = lcl_InvalidateSection( pCnt, nInv );
4300 if( pLastSctCnt == pCnt )
4301 pLastSctCnt = nullptr;
4303 #if OSL_DEBUG_LEVEL > 0
4304 else
4305 OSL_ENSURE( !pLastSctCnt, "Where's the last SctContent?" );
4306 #endif
4308 if( nInv & SwInvalidateFlags::Table )
4310 if( pCnt->IsInTab() )
4312 // To not call FindTabFrame() for each ContentFrame of a table and
4313 // then invalidate the table, we remember the last ContentFrame of
4314 // the table and ignore IsInTab() until we are past it.
4315 // When entering the table, LastSctCnt is set to null, so
4316 // sections inside the table are correctly invalidated.
4317 // If the table itself is in a section the
4318 // invalidation is done three times, which is acceptable.
4319 if( !pLastTabCnt )
4321 pLastTabCnt = lcl_InvalidateTable( pCnt->FindTabFrame(), nInv );
4322 pLastSctCnt = nullptr;
4324 if( pLastTabCnt == pCnt )
4326 pLastTabCnt = nullptr;
4327 pLastSctCnt = nullptr;
4330 #if OSL_DEBUG_LEVEL > 0
4331 else
4332 OSL_ENSURE( !pLastTabCnt, "Where's the last TabContent?" );
4333 #endif
4336 if( nInv & SwInvalidateFlags::Size )
4337 pCnt->Prepare( PrepareHint::Clear, nullptr, false );
4338 if( nInv & SwInvalidateFlags::Pos )
4339 pCnt->InvalidatePos_();
4340 if( nInv & SwInvalidateFlags::PrtArea )
4341 pCnt->InvalidatePrt_();
4342 if ( nInv & SwInvalidateFlags::LineNum )
4343 pCnt->InvalidateLineNum();
4344 if ( pCnt->GetDrawObjs() )
4345 lcl_InvalidateAllContent( pCnt, nInv );
4346 pCnt = pCnt->GetNextContentFrame();
4350 static void lcl_InvalidateAllContent( SwContentFrame *pCnt, SwInvalidateFlags nInv )
4352 SwSortedObjs &rObjs = *pCnt->GetDrawObjs();
4353 for (SwAnchoredObject* pAnchoredObj : rObjs)
4355 if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
4357 if ( pFly->IsFlyInContentFrame() )
4359 ::lcl_InvalidateContent( pFly->ContainsContent(), nInv );
4360 if( nInv & SwInvalidateFlags::Direction )
4361 pFly->CheckDirChange();
4367 void SwRootFrame::InvalidateAllContent( SwInvalidateFlags nInv )
4369 // First process all page bound FlyFrames.
4370 SwPageFrame *pPage = static_cast<SwPageFrame*>(Lower());
4371 while( pPage )
4373 pPage->InvalidateFlyLayout();
4374 pPage->InvalidateFlyContent();
4375 pPage->InvalidateFlyInCnt();
4376 pPage->InvalidateLayout();
4377 pPage->InvalidateContent();
4378 pPage->InvalidatePage( pPage ); // So even the Turbo disappears if applicable
4380 if ( pPage->GetSortedObjs() )
4382 const SwSortedObjs &rObjs = *pPage->GetSortedObjs();
4383 for (SwAnchoredObject* pAnchoredObj : rObjs)
4385 if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
4387 ::lcl_InvalidateContent( pFly->ContainsContent(), nInv );
4388 if ( nInv & SwInvalidateFlags::Direction )
4389 pFly->CheckDirChange();
4393 if( nInv & SwInvalidateFlags::Direction )
4394 pPage->CheckDirChange();
4395 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
4398 //Invalidate the whole document content and the character bound Flys here.
4399 ::lcl_InvalidateContent( ContainsContent(), nInv );
4401 if( nInv & SwInvalidateFlags::PrtArea )
4403 SwViewShell *pSh = getRootFrame()->GetCurrShell();
4404 if( pSh )
4405 pSh->InvalidateWindows( getFrameArea() );
4410 * Invalidate/re-calculate the position of all floating screen objects (Writer fly frames and
4411 * drawing objects), that are anchored to paragraph or to character. (2004-03-16 #i11860#)
4413 void SwRootFrame::InvalidateAllObjPos()
4415 const SwPageFrame* pPageFrame = static_cast<const SwPageFrame*>(Lower());
4416 while( pPageFrame )
4418 pPageFrame->InvalidateFlyLayout();
4420 if ( pPageFrame->GetSortedObjs() )
4422 const SwSortedObjs& rObjs = *(pPageFrame->GetSortedObjs());
4423 for (SwAnchoredObject* pAnchoredObj : rObjs)
4425 const SwFormatAnchor& rAnch = pAnchoredObj->GetFrameFormat()->GetAnchor();
4426 if ((rAnch.GetAnchorId() != RndStdIds::FLY_AT_PARA) &&
4427 (rAnch.GetAnchorId() != RndStdIds::FLY_AT_CHAR))
4429 // only to paragraph and to character anchored objects are considered.
4430 continue;
4432 // #i28701# - special invalidation for anchored
4433 // objects, whose wrapping style influence has to be considered.
4434 if ( pAnchoredObj->ConsiderObjWrapInfluenceOnObjPos() )
4435 pAnchoredObj->InvalidateObjPosForConsiderWrapInfluence();
4436 else
4437 pAnchoredObj->InvalidateObjPos();
4441 pPageFrame = static_cast<const SwPageFrame*>(pPageFrame->GetNext());
4445 static void AddRemoveFlysForNode(
4446 SwTextFrame & rFrame, SwTextNode & rTextNode,
4447 std::set<SwNodeOffset> *const pSkipped,
4448 const sw::FrameFormats<sw::SpzFrameFormat*>& rTable,
4449 SwPageFrame *const pPage,
4450 SwTextNode const*const pNode,
4451 std::vector<sw::Extent>::const_iterator const& rIterFirst,
4452 std::vector<sw::Extent>::const_iterator const& rIterEnd,
4453 SwTextNode const*const pFirstNode, SwTextNode const*const pLastNode)
4455 if (pNode == &rTextNode)
4456 { // remove existing hidden at-char anchored flys
4457 RemoveHiddenObjsOfNode(rTextNode, &rIterFirst, &rIterEnd, pFirstNode, pLastNode);
4459 else if (rTextNode.GetIndex() < pNode->GetIndex())
4461 // pNode's frame has been deleted by CheckParaRedlineMerge()
4462 AppendObjsOfNode(&rTable,
4463 pNode->GetIndex(), &rFrame, pPage, &rTextNode.GetDoc(),
4464 &rIterFirst, &rIterEnd, pFirstNode, pLastNode);
4465 if (pSkipped)
4467 // if a fly has been added by AppendObjsOfNode, it must be skipped; if not, then it doesn't matter if it's skipped or not because it has no frames and because of that it would be skipped anyway
4468 for (auto const pFly : pNode->GetAnchoredFlys())
4470 if (pFly->Which() != RES_DRAWFRMFMT)
4472 pSkipped->insert(pFly->GetContent().GetContentIdx()->GetIndex());
4479 namespace sw {
4481 /// rTextNode is the first one of the "new" merge - if rTextNode isn't the same
4482 /// as MergedPara::pFirstNode, then nodes before rTextNode have their flys
4483 /// already properly attached, so only the other nodes need handling here.
4484 void AddRemoveFlysAnchoredToFrameStartingAtNode(
4485 SwTextFrame & rFrame, SwTextNode & rTextNode,
4486 std::set<SwNodeOffset> *const pSkipped)
4488 auto const pMerged(rFrame.GetMergedPara());
4489 if (!pMerged
4490 // do this only *once*, for the *last* frame
4491 // otherwise AppendObj would create multiple frames for fly-frames!
4492 || rFrame.GetFollow())
4493 return;
4495 assert(pMerged->pFirstNode->GetIndex() <= rTextNode.GetIndex()
4496 && rTextNode.GetIndex() <= pMerged->pLastNode->GetIndex());
4497 // add visible flys in non-first node to merged frame
4498 // (hidden flys remain and are deleted via DelFrames())
4499 sw::SpzFrameFormats& rTable(*rTextNode.GetDoc().GetSpzFrameFormats());
4500 SwPageFrame *const pPage(rFrame.FindPageFrame());
4501 std::vector<sw::Extent>::const_iterator iterFirst(pMerged->extents.begin());
4502 std::vector<sw::Extent>::const_iterator iter(iterFirst);
4503 SwTextNode const* pNode(pMerged->pFirstNode);
4504 for ( ; ; ++iter)
4506 if (iter == pMerged->extents.end()
4507 || iter->pNode != pNode)
4509 AddRemoveFlysForNode(rFrame, rTextNode, pSkipped, rTable, pPage,
4510 pNode, iterFirst, iter,
4511 pMerged->pFirstNode, pMerged->pLastNode);
4512 SwNodeOffset const until = iter == pMerged->extents.end()
4513 ? pMerged->pLastNode->GetIndex() + 1
4514 : iter->pNode->GetIndex();
4515 for (SwNodeOffset i = pNode->GetIndex() + 1; i < until; ++i)
4517 // let's show at-para flys on nodes that contain start/end of
4518 // redline too, even if there's no text there
4519 SwNode const*const pTmp(pNode->GetNodes()[i]);
4520 if (pTmp->GetRedlineMergeFlag() == SwNode::Merge::NonFirst)
4522 AddRemoveFlysForNode(rFrame, rTextNode, pSkipped,
4523 rTable, pPage, pTmp->GetTextNode(), iter, iter,
4524 pMerged->pFirstNode, pMerged->pLastNode);
4527 if (iter == pMerged->extents.end())
4529 break;
4531 pNode = iter->pNode;
4532 iterFirst = iter;
4537 } // namespace sw
4539 static void UnHideRedlines(SwRootFrame & rLayout,
4540 SwNodes & rNodes, SwNode const& rEndOfSectionNode,
4541 std::set<SwNodeOffset> *const pSkipped)
4543 assert(rEndOfSectionNode.IsEndNode());
4544 assert(rNodes[rEndOfSectionNode.StartOfSectionNode()->GetIndex() + 1]->IsCreateFrameWhenHidingRedlines()); // first node is never hidden
4545 for (SwNodeOffset i = rEndOfSectionNode.StartOfSectionNode()->GetIndex() + 1;
4546 i < rEndOfSectionNode.GetIndex(); ++i)
4548 SwNode & rNode(*rNodes[i]);
4549 if (rNode.IsTextNode()) // only text nodes are 1st node of a merge
4551 SwTextNode & rTextNode(*rNode.GetTextNode());
4552 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(rTextNode);
4553 std::vector<SwTextFrame*> frames;
4554 for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
4556 if (pFrame->getRootFrame() == &rLayout)
4558 if (pFrame->IsFollow())
4560 frames.push_back(pFrame);
4561 } // when hiding, the loop must remove the anchored flys
4562 else // *before* resetting SetMergedPara anywhere - else
4563 { // the fly deletion code will access multiple of the
4564 // frames with inconsistent MergedPara and assert
4565 frames.insert(frames.begin(), pFrame);
4569 // this messes with pRegisteredIn so do it outside SwIterator
4570 auto eMode(sw::FrameMode::Existing);
4571 for (SwTextFrame * pFrame : frames)
4573 if (rLayout.HasMergedParas())
4575 assert(!pFrame->GetMergedPara() ||
4576 !rNode.IsCreateFrameWhenHidingRedlines() ||
4577 // FIXME: skip this assert in tables with deleted rows
4578 pFrame->IsInTab());
4579 if (rNode.IsCreateFrameWhenHidingRedlines())
4582 auto pMerged(CheckParaRedlineMerge(*pFrame,
4583 rTextNode, eMode));
4584 pFrame->SetMergedPara(std::move(pMerged));
4586 auto const pMerged(pFrame->GetMergedPara());
4587 if (pMerged)
4589 // invalidate SwInvalidateFlags::Size
4590 pFrame->Prepare(PrepareHint::Clear, nullptr, false);
4591 pFrame->InvalidatePage();
4592 if (auto const pObjs = pFrame->GetDrawObjs())
4593 { // also invalidate position of existing flys
4594 // because they may need to be moved
4595 for (auto const pObject : *pObjs)
4597 pObject->InvalidateObjPos();
4601 sw::AddRemoveFlysAnchoredToFrameStartingAtNode(*pFrame, rTextNode, pSkipped);
4602 // only *first* frame of node gets Existing because it
4603 eMode = sw::FrameMode::New; // is not idempotent!
4606 else
4608 if (auto const pMergedPara = pFrame->GetMergedPara())
4610 // invalidate SwInvalidateFlags::Size
4611 pFrame->Prepare(PrepareHint::Clear, nullptr, false);
4612 pFrame->InvalidatePage();
4613 if (auto const pObjs = pFrame->GetDrawObjs())
4614 { // also invalidate position of existing flys
4615 for (auto const pObject : *pObjs)
4617 pObject->InvalidateObjPos();
4620 // SwFlyAtContentFrame::SwClientNotify() always appends to
4621 // the master frame, so do the same here.
4622 // (RemoveFootnotesForNode must be called at least once)
4623 if (!pFrame->IsFollow())
4625 // the new text frames don't exist yet, so at this point
4626 // we can only delete the footnote frames so they don't
4627 // point to the merged SwTextFrame any more...
4628 assert(&rTextNode == pMergedPara->pFirstNode);
4629 // iterate over nodes, not extents: if a node has
4630 // no extents now but did have extents initially,
4631 // its flys need their frames deleted too!
4632 for (SwNodeOffset j = rTextNode.GetIndex() + 1;
4633 j <= pMergedPara->pLastNode->GetIndex(); ++j)
4635 SwNode *const pNode(rTextNode.GetNodes()[j]);
4636 assert(!pNode->IsEndNode());
4637 if (pNode->IsStartNode())
4639 j = pNode->EndOfSectionIndex();
4641 else if (pNode->IsTextNode())
4643 sw::RemoveFootnotesForNode(rLayout, *pNode->GetTextNode(), nullptr);
4644 // similarly, remove the anchored flys
4645 for (SwFrameFormat * pFormat : pNode->GetAnchoredFlys())
4647 pFormat->DelFrames(/*&rLayout*/);
4651 // rely on AppendAllObjs call at the end to add
4652 // all flys in first node that are hidden
4654 pFrame->SetMergedPara(nullptr);
4657 pFrame->Broadcast(SfxHint()); // notify SwAccessibleParagraph
4659 // all nodes, not just merged ones! it may be in the same list as
4660 if (rTextNode.IsNumbered(nullptr)) // a preceding merged one...
4661 { // notify frames so they reformat numbering portions
4662 rTextNode.NumRuleChgd();
4665 else if (rNode.IsTableNode() && rLayout.IsHideRedlines())
4667 SwTableNode * pTableNd = rNode.GetTableNode();
4668 SwPosition const tmp(rNode);
4669 SwRangeRedline const*const pRedline(
4670 rLayout.GetFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedline(tmp, nullptr));
4671 // pathology: redline that starts on a TableNode; cannot
4672 // be created in UI but by import filters...
4673 if (pRedline
4674 && pRedline->GetType() == RedlineType::Delete
4675 && &pRedline->Start()->GetNode() == &rNode)
4677 for (SwNodeOffset j = rNode.GetIndex(); j <= rNode.EndOfSectionIndex(); ++j)
4679 rNode.GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::Hidden);
4681 pTableNd->DelFrames(&rLayout);
4683 else if ( pTableNd->GetTable().HasDeletedRowOrCell() )
4685 pTableNd->DelFrames(&rLayout);
4686 if ( !pTableNd->GetTable().IsDeleted() )
4688 pTableNd->MakeOwnFrames();
4692 else if (rNode.IsTableNode() && !rLayout.IsHideRedlines() &&
4693 rNode.GetTableNode()->GetTable().HasDeletedRowOrCell() )
4695 SwTableNode * pTableNd = rNode.GetTableNode();
4696 pTableNd->DelFrames(&rLayout);
4697 pTableNd->MakeOwnFrames();
4700 if (!rNode.IsCreateFrameWhenHidingRedlines())
4702 if (rLayout.HasMergedParas())
4704 if (rNode.IsContentNode())
4706 // note: nothing to do here, already done
4707 #ifndef NDEBUG
4708 auto const pFrame(static_cast<SwContentNode&>(rNode).getLayoutFrame(&rLayout));
4709 assert(!pFrame || static_cast<SwTextFrame*>(pFrame)->GetMergedPara()->pFirstNode != &rNode);
4710 #endif
4713 else
4715 assert(!rNode.IsContentNode() || !rNode.GetContentNode()->getLayoutFrame(&rLayout) ||
4716 // FIXME: skip this assert in tables with deleted rows
4717 rNode.GetContentNode()->getLayoutFrame(&rLayout)->IsInTab());
4718 SwNodeOffset j = i + 1;
4719 for ( ; j < rEndOfSectionNode.GetIndex(); ++j)
4721 if (rNodes[j]->IsCreateFrameWhenHidingRedlines())
4723 break;
4726 // call MakeFrames once, because sections/tables
4727 // InsertCnt_ also checks for hidden sections
4729 sw::FlyCreationSuppressor aSuppressor(false);
4730 ::MakeFrames(rLayout.GetFormat()->GetDoc(), *rNodes[i], *rNodes[j]);
4732 i = j - 1; // will be incremented again
4738 static void UnHideRedlinesExtras(SwRootFrame & rLayout,
4739 SwNodes & rNodes, SwNode const& rEndOfExtraSectionNode,
4740 std::set<SwNodeOffset> *const pSkipped)
4742 assert(rEndOfExtraSectionNode.IsEndNode());
4743 for (SwNodeOffset i = rEndOfExtraSectionNode.StartOfSectionNode()->GetIndex()
4744 + 1; i < rEndOfExtraSectionNode.GetIndex(); ++i)
4746 SwNode const& rStartNode(*rNodes[i]);
4747 assert(rStartNode.IsStartNode());
4748 assert(rStartNode.GetRedlineMergeFlag() == SwNode::Merge::None);
4749 SwNode const& rEndNode(*rStartNode.EndOfSectionNode());
4750 bool bSkip(pSkipped && pSkipped->find(i) != pSkipped->end());
4751 i = rEndNode.GetIndex();
4752 for (SwNodeOffset j = rStartNode.GetIndex() + 1; j < i; ++j)
4754 // note: SwStartNode has no way to access the frames, so check
4755 // whether the first content-node inside the section has frames
4756 SwNode const& rNode(*rNodes[j]);
4757 if (rNode.IsSectionNode() &&
4758 static_cast<SwSectionNode const&>(rNode).GetSection().IsHiddenFlag())
4759 { // skip hidden sections - they can be inserted in fly-frames :(
4760 j = rNode.EndOfSectionNode()->GetIndex();
4761 continue;
4763 if (rNode.IsContentNode())
4765 SwContentNode const& rCNode(static_cast<SwContentNode const&>(rNode));
4766 if (!rCNode.getLayoutFrame(&rLayout))
4767 { // ignore footnote/fly/header/footer with no layout frame
4768 bSkip = true; // they will be created from scratch later if needed
4770 break;
4773 if (!bSkip)
4775 UnHideRedlines(rLayout, rNodes, rEndNode, pSkipped);
4780 static void UnHide(SwRootFrame & rLayout)
4782 assert(rLayout.GetCurrShell()->ActionPend()); // tdf#125754 avoid recursive layout
4783 SwDoc & rDoc(*rLayout.GetFormat()->GetDoc());
4784 // don't do early return if there are no redlines:
4785 // Show->Hide must init hidden number trees
4786 // Hide->Show may be called after all redlines have been deleted but there
4787 // may still be MergedParas because those aren't deleted yet...
4788 #if 0
4789 if (!bHideRedlines
4790 && rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())
4792 return;
4794 #endif
4795 // Hide->Show: clear MergedPara, create frames
4796 // Show->Hide: call CheckParaRedlineMerge, delete frames
4797 // Traverse the document via the nodes-array; traversing via the layout
4798 // wouldn't find the nodes that don't have frames in the ->Show case.
4799 // In-order traversal of each nodes array section should init the flags
4800 // in nodes before they are iterated.
4801 // Actual creation of frames should be done with existing functions
4802 // if possible, particularly InsertCnt_() or its wrapper ::MakeFrames().
4803 SwNodes /*const*/& rNodes(rDoc.GetNodes());
4804 // Flys/footnotes: must iterate and find all the ones that already exist
4805 // with frames and have redlines inside them; if any don't have frames at
4806 // all, they will be created (if necessary) from scratch and completely by
4807 // MakeFrames().
4809 // Flys before footnotes: because footnotes may contain flys but not
4810 // vice-versa; alas flys may contain flys, so we skip some of them
4811 // if they have already been created from scratch via their anchor flys.
4812 std::set<SwNodeOffset> skippedFlys;
4813 UnHideRedlinesExtras(rLayout, rNodes, rNodes.GetEndOfAutotext(),
4814 // when un-hiding, delay all fly frame creation to AppendAllObjs below
4815 rLayout.HasMergedParas() ? &skippedFlys : nullptr);
4816 // Footnotes are created automatically (after invalidation etc.) by
4817 // ConnectFootnote(), but need to be deleted manually. Footnotes do not
4818 // occur in flys or headers/footers.
4819 UnHideRedlinesExtras(rLayout, rNodes, rNodes.GetEndOfInserts(), nullptr);
4820 UnHideRedlines(rLayout, rNodes, rNodes.GetEndOfContent(), nullptr);
4822 if (!rLayout.HasMergedParas())
4823 { // create all previously hidden flys at once:
4824 // * Flys on first node of pre-existing merged frames that are hidden
4825 // (in delete redline), to be added to the existing frame
4826 // * Flys on non-first (hidden/merged) nodes of pre-existing merged
4827 // frames, to be added to the new frame of their node
4828 // * Flys anchored in other flys that are hidden
4829 AppendAllObjs(rDoc.GetSpzFrameFormats(), &rLayout);
4832 const bool bIsShowChangesInMargin = rLayout.GetCurrShell()->GetViewOptions()->IsShowChangesInMargin();
4833 for (auto const pRedline : rDoc.getIDocumentRedlineAccess().GetRedlineTable())
4834 { // DELETE are handled by the code above; for other types, need to
4835 // trigger repaint of text frames to add/remove the redline color font
4836 // (handle deletions showed in margin also here)
4837 if (bIsShowChangesInMargin || pRedline->GetType() != RedlineType::Delete)
4839 pRedline->InvalidateRange(SwRangeRedline::Invalidation::Add);
4843 SwFootnoteIdxs & rFootnotes(rDoc.GetFootnoteIdxs());
4844 if (rDoc.GetFootnoteInfo().m_eNum == FTNNUM_CHAPTER)
4846 // sadly determining which node is outline node requires hidden layout
4847 rFootnotes.UpdateAllFootnote();
4849 // invalidate all footnotes to reformat their numbers
4850 for (SwTextFootnote *const pFootnote : rFootnotes)
4852 SwFormatFootnote const& rFootnote(pFootnote->GetFootnote());
4853 if (rFootnote.GetNumber() != rFootnote.GetNumberRLHidden()
4854 && rFootnote.GetNumStr().isEmpty())
4856 pFootnote->InvalidateNumberInLayout();
4859 // update various fields to re-expand them with the new layout
4860 IDocumentFieldsAccess & rIDFA(rDoc.getIDocumentFieldsAccess());
4861 auto const pAuthType(rIDFA.GetFieldType(
4862 SwFieldIds::TableOfAuthorities, OUString(), false));
4863 if (pAuthType) // created on demand...
4864 { // calling DelSequenceArray() should be unnecessary here since the
4865 // sequence doesn't depend on frames
4866 pAuthType->UpdateFields();
4868 rIDFA.GetFieldType(SwFieldIds::RefPageGet, OUString(), false)->UpdateFields();
4869 rIDFA.GetSysFieldType(SwFieldIds::Chapter)->UpdateFields();
4870 rIDFA.UpdateExpFields(nullptr, false);
4871 rIDFA.UpdateRefFields();
4873 // update SwPostItMgr / notes in the margin
4874 // note: as long as all shells share layout, broadcast to all shells!
4875 rDoc.GetDocShell()->Broadcast( SwFormatFieldHint(nullptr, rLayout.HasMergedParas()
4876 ? SwFormatFieldHintWhich::REMOVED
4877 : SwFormatFieldHintWhich::INSERTED) );
4880 // InvalidateAllContent(SwInvalidateFlags::Size); // ??? TODO what to invalidate? this is the big hammer
4883 void SwRootFrame::SetHideRedlines(bool const bHideRedlines)
4885 if (bHideRedlines == mbHideRedlines)
4887 return;
4889 // TODO: remove temporary ShowBoth
4890 sw::FieldmarkMode const eMode(m_FieldmarkMode);
4891 sw::ParagraphBreakMode const ePBMode(m_ParagraphBreakMode);
4892 if (HasMergedParas())
4894 m_FieldmarkMode = sw::FieldmarkMode::ShowBoth;
4895 m_ParagraphBreakMode = sw::ParagraphBreakMode::Shown;
4896 mbHideRedlines = false;
4897 UnHide(*this);
4899 if (bHideRedlines || eMode != m_FieldmarkMode || ePBMode != m_ParagraphBreakMode)
4901 m_FieldmarkMode = eMode;
4902 m_ParagraphBreakMode = ePBMode;
4903 mbHideRedlines = bHideRedlines;
4904 UnHide(*this);
4908 void SwRootFrame::SetFieldmarkMode(sw::FieldmarkMode const eFMMode, sw::ParagraphBreakMode const ePBMode)
4910 if (eFMMode == m_FieldmarkMode && ePBMode == m_ParagraphBreakMode)
4912 return;
4914 // TODO: remove temporary ShowBoth
4915 bool const isHideRedlines(mbHideRedlines);
4916 if (HasMergedParas())
4918 mbHideRedlines = false;
4919 m_FieldmarkMode = sw::FieldmarkMode::ShowBoth;
4920 m_ParagraphBreakMode = sw::ParagraphBreakMode::Shown;
4921 UnHide(*this);
4923 if (isHideRedlines || eFMMode != sw::FieldmarkMode::ShowBoth || ePBMode == sw::ParagraphBreakMode::Hidden)
4925 mbHideRedlines = isHideRedlines;
4926 m_FieldmarkMode = eFMMode;
4927 m_ParagraphBreakMode = ePBMode;
4928 UnHide(*this);
4932 bool SwRootFrame::HasMergedParas() const
4934 return IsHideRedlines()
4935 || GetFieldmarkMode() != sw::FieldmarkMode::ShowBoth
4936 || GetParagraphBreakMode() == sw::ParagraphBreakMode::Hidden;
4939 namespace {
4940 xmlTextWriterPtr lcl_createDefaultWriter()
4942 xmlTextWriterPtr writer = xmlNewTextWriterFilename( "layout.xml", 0 );
4943 xmlTextWriterSetIndent(writer,1);
4944 (void)xmlTextWriterSetIndentString(writer, BAD_CAST(" "));
4945 (void)xmlTextWriterStartDocument( writer, nullptr, nullptr, nullptr );
4946 return writer;
4949 void lcl_freeWriter( xmlTextWriterPtr writer )
4951 (void)xmlTextWriterEndDocument( writer );
4952 xmlFreeTextWriter( writer );
4956 void SwRootFrame::dumpAsXml(xmlTextWriterPtr writer) const
4958 bool bCreateWriter = (nullptr == writer);
4959 if (bCreateWriter)
4960 writer = lcl_createDefaultWriter();
4962 (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("root"));
4963 dumpAsXmlAttributes(writer);
4965 (void)xmlTextWriterStartElement(writer, BAD_CAST("sfxViewShells"));
4966 SwView* pView = static_cast<SwView*>(SfxViewShell::GetFirst(true, checkSfxViewShell<SwView>));
4967 while (pView)
4969 if (GetCurrShell()->GetSfxViewShell() && pView->GetObjectShell() == GetCurrShell()->GetSfxViewShell()->GetObjectShell())
4970 pView->dumpAsXml(writer);
4971 pView = static_cast<SwView*>(SfxViewShell::GetNext(*pView, true, checkSfxViewShell<SwView>));
4973 (void)xmlTextWriterEndElement(writer);
4975 (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
4976 dumpInfosAsXml(writer);
4977 (void)xmlTextWriterEndElement(writer);
4978 dumpChildrenAsXml(writer);
4979 (void)xmlTextWriterEndElement(writer);
4981 if (bCreateWriter)
4982 lcl_freeWriter(writer);
4985 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */