1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <pagefrm.hxx>
21 #include <fmtcntnt.hxx>
22 #include <fmthdft.hxx>
23 #include <fmtfsize.hxx>
24 #include <viewopt.hxx>
26 #include <rootfrm.hxx>
28 #include <sectfrm.hxx>
30 #include <frmtool.hxx>
31 #include <hfspacingitem.hxx>
32 #include <sortedobjs.hxx>
33 #include <objectformatter.hxx>
34 #include <ndindex.hxx>
35 #include <osl/diagnose.h>
36 #include <sal/log.hxx>
38 static SwTwips
lcl_GetFrameMinHeight(const SwLayoutFrame
& rFrame
)
40 const SwFormatFrameSize
&rSz
= rFrame
.GetFormat()->GetFrameSize();
43 switch (rSz
.GetHeightSizeType())
45 case SwFrameSize::Minimum
:
46 nMinHeight
= rSz
.GetHeight();
57 static SwTwips
lcl_CalcContentHeight(SwLayoutFrame
& frm
)
59 SwTwips nRemaining
= 0;
60 SwFrame
* pFrame
= frm
.Lower();
66 nTmp
= pFrame
->getFrameArea().Height();
68 if( pFrame
->IsTextFrame() && static_cast<SwTextFrame
*>(pFrame
)->IsUndersized() )
70 nTmp
= static_cast<SwTextFrame
*>(pFrame
)->GetParHeight()
71 - pFrame
->getFramePrintArea().Height();
72 // This TextFrame would like to be a bit bigger
75 else if( pFrame
->IsSctFrame() && static_cast<SwSectionFrame
*>(pFrame
)->IsUndersized() )
77 nTmp
= static_cast<SwSectionFrame
*>(pFrame
)->Undersize();
80 pFrame
= pFrame
->GetNext();
86 static void lcl_LayoutFrameEnsureMinHeight(SwLayoutFrame
& rFrame
)
88 SwTwips nMinHeight
= lcl_GetFrameMinHeight(rFrame
);
90 if (rFrame
.getFrameArea().Height() < nMinHeight
)
92 rFrame
.Grow(nMinHeight
- rFrame
.getFrameArea().Height());
96 SwHeadFootFrame::SwHeadFootFrame( SwFrameFormat
* pFormat
, SwFrame
* pSib
, SwFrameType nTypeIn
)
97 : SwLayoutFrame( pFormat
, pSib
)
99 mnFrameType
= nTypeIn
;
100 SetDerivedVert( false );
102 const SwFormatContent
&rCnt
= pFormat
->GetContent();
104 OSL_ENSURE( rCnt
.GetContentIdx(), "No content for Header." );
106 // Have the objects created right now for header and footer
107 bool bOld
= bObjsDirect
;
109 SwNodeOffset nIndex
= rCnt
.GetContentIdx()->GetIndex();
110 ::InsertCnt_( this, pFormat
->GetDoc(), ++nIndex
);
114 void SwHeadFootFrame::FormatPrt(SwTwips
& nUL
, const SwBorderAttrs
* pAttrs
)
118 /* The minimal height of the print area is the minimal height of the
119 frame without the height needed for borders and shadow. */
120 SwTwips nMinHeight
= lcl_GetFrameMinHeight(*this);
122 nMinHeight
-= pAttrs
->CalcTop();
123 nMinHeight
-= pAttrs
->CalcBottom();
125 /* If the minimal height of the print area is negative, try to
126 compensate by overlapping */
127 SwTwips nOverlap
= 0;
130 nOverlap
= -nMinHeight
;
134 /* Calculate desired height of content. The minimal height has to be
138 if ( ! HasFixSize() )
139 nHeight
= lcl_CalcContentHeight(*this);
141 nHeight
= nMinHeight
;
143 if (nHeight
< nMinHeight
)
144 nHeight
= nMinHeight
;
146 /* calculate initial spacing/line space */
147 SwTwips nSpace
, nLine
;
151 nSpace
= pAttrs
->CalcBottom();
152 nLine
= pAttrs
->CalcBottomLine();
156 nSpace
= pAttrs
->CalcTop();
157 nLine
= pAttrs
->CalcTopLine();
160 /* calculate overlap and correct spacing */
161 nOverlap
+= nHeight
- nMinHeight
;
162 if (nOverlap
< nSpace
- nLine
)
167 /* calculate real vertical space between frame and print area */
169 nUL
= pAttrs
->CalcTop() + nSpace
;
171 nUL
= pAttrs
->CalcBottom() + nSpace
;
174 SwTwips nLR
= pAttrs
->CalcLeft( this ) + pAttrs
->CalcRight( this );
175 SwFrameAreaDefinition::FramePrintAreaWriteAccess
aPrt(*this);
177 aPrt
.Left(pAttrs
->CalcLeft(this));
181 aPrt
.Top(pAttrs
->CalcTop());
188 aPrt
.Width(getFrameArea().Width() - nLR
);
192 if (nUL
< getFrameArea().Height())
194 nNewHeight
= getFrameArea().Height() - nUL
;
201 aPrt
.Height(nNewHeight
);
206 SwFrameAreaDefinition::FramePrintAreaWriteAccess
aPrt(*this);
207 aPrt
.Left( pAttrs
->CalcLeft( this ) );
208 aPrt
.Top ( pAttrs
->CalcTop() );
210 // Set sizes - the sizes are given by the surrounding Frame, just
211 // subtract the borders.
212 SwTwips nLR
= pAttrs
->CalcLeft( this ) + pAttrs
->CalcRight( this );
213 aPrt
.Width ( getFrameArea().Width() - nLR
);
214 aPrt
.Height( getFrameArea().Height()- nUL
);
217 setFramePrintAreaValid(true);
220 void SwHeadFootFrame::FormatSize(SwTwips nUL
, const SwBorderAttrs
* pAttrs
)
226 setFramePrintAreaValid(true);
227 setFrameAreaSizeValid(true);
229 const SwTwips nBorder
= nUL
;
230 SwTwips nMinHeight
= lcl_GetFrameMinHeight(*this);
231 nMinHeight
-= pAttrs
->CalcTop();
232 nMinHeight
-= pAttrs
->CalcBottom();
239 SwTwips nMaxHeight
= LONG_MAX
;
240 SwTwips nRemaining
, nOldHeight
;
242 // use the position of the footer printing area to control invalidation
243 // of the first footer content.
244 Point aOldFooterPrtPos
;
248 nOldHeight
= getFramePrintArea().Height();
249 SwFrame
* pFrame
= Lower();
252 aOldFooterPrtPos
!= ( getFrameArea().Pos() + getFramePrintArea().Pos() ) )
254 pFrame
->InvalidatePos_();
255 aOldFooterPrtPos
= getFrameArea().Pos() + getFramePrintArea().Pos();
257 int nLoopControl
= 0;
260 pFrame
->Calc(getRootFrame()->GetCurrShell()->GetOut());
261 // #i43771# - format also object anchored
263 // #i46941# - frame has to be valid.
264 // Note: frame could be invalid after calling its format,
266 OSL_ENSURE( StackHack::IsLocked() || !pFrame
->IsTextFrame() ||
267 pFrame
->isFrameAreaDefinitionValid() ||
268 static_cast<SwTextFrame
*>(pFrame
)->IsJoinLocked(),
269 "<SwHeadFootFrame::FormatSize(..)> - text frame invalid and not locked." );
271 if ( pFrame
->IsTextFrame() && pFrame
->isFrameAreaDefinitionValid() )
273 if ( !SwObjectFormatter::FormatObjsAtFrame( *pFrame
,
274 *(pFrame
->FindPageFrame()) ) )
276 if (nLoopControl
++ < 20)
278 // restart format with first content
283 SAL_WARN("sw", "SwHeadFootFrame::FormatSize: loop detection triggered");
286 pFrame
= pFrame
->GetNext();
293 nRemaining
+= pFrame
->getFrameArea().Height();
295 if( pFrame
->IsTextFrame() &&
296 static_cast<SwTextFrame
*>(pFrame
)->IsUndersized() )
297 // This TextFrame would like to be a bit bigger
298 nRemaining
+= static_cast<SwTextFrame
*>(pFrame
)->GetParHeight()
299 - pFrame
->getFramePrintArea().Height();
300 else if( pFrame
->IsSctFrame() &&
301 static_cast<SwSectionFrame
*>(pFrame
)->IsUndersized() )
302 nRemaining
+= static_cast<SwSectionFrame
*>(pFrame
)->Undersize();
303 pFrame
= pFrame
->GetNext();
305 if ( nRemaining
< nMinHeight
)
306 nRemaining
= nMinHeight
;
308 SwTwips nDiff
= nRemaining
- nOldHeight
;
314 nMaxHeight
= nOldHeight
;
316 if( nRemaining
<= nMinHeight
)
317 nRemaining
= ( nMaxHeight
+ nMinHeight
+ 1 ) / 2;
321 if (nOldHeight
> nMinHeight
)
322 nMinHeight
= nOldHeight
;
324 if( nRemaining
>= nMaxHeight
)
325 nRemaining
= ( nMaxHeight
+ nMinHeight
+ 1 ) / 2;
328 nDiff
= nRemaining
- nOldHeight
;
341 if( pFrame
->IsTextFrame())
343 SwTextFrame
* pTmpFrame
= static_cast<SwTextFrame
*>(pFrame
);
344 if (pTmpFrame
->IsUndersized() )
346 pTmpFrame
->InvalidateSize();
347 pTmpFrame
->Prepare(PrepareHint::AdjustSizeWithoutFormatting
);
350 /* #i3568# Undersized sections need to be
352 else if (pFrame
->IsSctFrame())
354 SwSectionFrame
* pTmpFrame
=
355 static_cast<SwSectionFrame
*>(pFrame
);
356 if (pTmpFrame
->IsUndersized() )
358 pTmpFrame
->InvalidateSize();
359 pTmpFrame
->Prepare(PrepareHint::AdjustSizeWithoutFormatting
);
362 pFrame
= pFrame
->GetNext();
368 // Quickly update the position
375 // Don't overwrite the lower edge of the upper
376 if ( GetUpper() && getFrameArea().Height() )
378 const SwTwips nDeadLine
= GetUpper()->getFrameArea().Top() + GetUpper()->getFramePrintArea().Bottom();
379 const SwTwips nBot
= getFrameArea().Bottom();
381 if ( nBot
> nDeadLine
)
383 SwFrameAreaDefinition::FrameAreaWriteAccess
aFrm(*this);
384 aFrm
.Bottom( nDeadLine
);
386 SwFrameAreaDefinition::FramePrintAreaWriteAccess
aPrt(*this);
387 aPrt
.Height( getFrameArea().Height() - nBorder
);
391 setFramePrintAreaValid(true);
392 setFrameAreaSizeValid(true);
393 } while( nRemaining
<=nMaxHeight
&& nOldHeight
!=getFramePrintArea().Height() );
397 setFramePrintAreaValid(true);
398 setFrameAreaSizeValid(true);
400 else //if (GetType() & FRM_HEADFOOT)
404 if ( getFrameArea().Height() != pAttrs
->GetSize().Height() )
406 ChgSize( Size( getFrameArea().Width(), pAttrs
->GetSize().Height()));
409 setFrameAreaSizeValid(true);
411 } while ( !isFrameAreaSizeValid() );
415 void SwHeadFootFrame::Format(vcl::RenderContext
* pRenderContext
, const SwBorderAttrs
* pAttrs
)
417 OSL_ENSURE( pAttrs
, "SwFooterFrame::Format, pAttrs is 0." );
419 if ( isFramePrintAreaValid() && isFrameAreaSizeValid() )
422 if ( ! GetEatSpacing() && IsHeaderFrame())
424 SwLayoutFrame::Format(pRenderContext
, pAttrs
);
428 lcl_LayoutFrameEnsureMinHeight(*this);
430 SwTwips nUL
= pAttrs
->CalcTop() + pAttrs
->CalcBottom();
432 if ( !isFramePrintAreaValid() )
433 FormatPrt(nUL
, pAttrs
);
435 if ( !isFrameAreaSizeValid() )
436 FormatSize(nUL
, pAttrs
);
440 SwTwips
SwHeadFootFrame::GrowFrame( SwTwips nDist
, bool bTst
, bool bInfo
)
448 else if (!GetEatSpacing())
450 nResult
= SwLayoutFrame::GrowFrame(nDist
, bTst
, bInfo
);
456 std::optional
<SwBorderAttrAccess
> oAccess(std::in_place
, SwFrame::GetCache(), this);
457 OSL_ENSURE(oAccess
, "no border attributes");
459 SwBorderAttrs
* pAttrs
= oAccess
->Get();
461 /* First assume the whole amount to grow can be provided by eating
463 SwTwips nEat
= nDist
;
466 /* calculate maximum eatable spacing */
468 nMaxEat
= getFrameArea().Height() - getFramePrintArea().Top() - getFramePrintArea().Height() - pAttrs
->CalcBottomLine();
470 nMaxEat
= getFramePrintArea().Top() - pAttrs
->CalcTopLine();
475 /* If the frame is too small, eat less spacing thus letting the frame
477 SwTwips nMinHeight
= lcl_GetFrameMinHeight(*this);
478 SwTwips nFrameTooSmall
= nMinHeight
- getFrameArea().Height();
480 if (nFrameTooSmall
> 0)
481 nEat
-= nFrameTooSmall
;
483 /* No negative eating, not eating more than allowed. */
486 else if (nEat
> nMaxEat
)
489 // Notify fly frame, if header frame
490 // grows. Consider, that 'normal' grow of layout frame already notifies
492 bool bNotifyFlys
= false;
497 if (! IsHeaderFrame())
499 SwFrameAreaDefinition::FramePrintAreaWriteAccess
aPrt(*this);
500 aPrt
.Top(aPrt
.Top() - nEat
);
501 aPrt
.Height(aPrt
.Height() - nEat
);
508 // trigger fly frame notify.
509 if ( IsHeaderFrame() )
515 if (nDist
- nEat
> 0)
517 const SwTwips nFrameGrow
=
518 SwLayoutFrame::GrowFrame( nDist
- nEat
, bTst
, bInfo
);
520 nResult
+= nFrameGrow
;
521 if ( nFrameGrow
> 0 )
527 // notify fly frames, if necessary and triggered.
528 if ( ( nResult
> 0 ) && bNotifyFlys
)
534 if ( nResult
&& !bTst
)
540 SwTwips
SwHeadFootFrame::ShrinkFrame( SwTwips nDist
, bool bTst
, bool bInfo
)
548 else if (! GetEatSpacing())
550 nResult
= SwLayoutFrame::ShrinkFrame(nDist
, bTst
, bInfo
);
556 SwTwips nMinHeight
= lcl_GetFrameMinHeight(*this);
557 SwTwips nOldHeight
= getFrameArea().Height();
558 SwTwips nRest
= 0; // Amount to shrink by spitting out spacing
560 if ( nOldHeight
>= nMinHeight
)
562 /* If the frame's height is bigger than its minimum height, shrink
563 the frame towards its minimum height. If this is not sufficient
564 to provide the shrinking requested provide the rest by spitting
567 SwTwips nBiggerThanMin
= nOldHeight
- nMinHeight
;
569 if (nBiggerThanMin
< nDist
)
571 nRest
= nDist
- nBiggerThanMin
;
573 /* info: declaration of nRest -> else nRest = 0 */
576 /* The frame cannot shrink. Provide shrinking by spitting out
580 // Notify fly frame, if header/footer frame shrinks.
581 // Consider, that 'normal' shrink of layout frame already notifies the fly frames.
582 bool bNotifyFlys
= false;
585 std::optional
<SwBorderAttrAccess
> oAccess(std::in_place
, SwFrame::GetCache(), this);
586 OSL_ENSURE(oAccess
, "no border attributes");
588 SwBorderAttrs
* pAttrs
= oAccess
->Get();
590 /* minimal height of print area */
591 SwTwips nMinPrtHeight
= nMinHeight
593 - pAttrs
->CalcBottom();
595 if (nMinPrtHeight
< 0)
598 /* assume all shrinking can be provided */
599 SwTwips nShrink
= nRest
;
601 /* calculate maximum shrinking */
602 SwTwips nMaxShrink
= getFramePrintArea().Height() - nMinPrtHeight
;
604 /* shrink no more than maximum shrinking */
605 if (nShrink
> nMaxShrink
)
607 //nRest -= nShrink - nMaxShrink;
608 nShrink
= nMaxShrink
;
613 if (! IsHeaderFrame() )
615 SwFrameAreaDefinition::FramePrintAreaWriteAccess
aPrt(*this);
616 aPrt
.Top(aPrt
.Top() + nShrink
);
617 aPrt
.Height(aPrt
.Height() - nShrink
);
623 // Trigger fly frame notify.
624 if ( IsHeaderFrame() )
630 /* The shrinking not providable by spitting out spacing has to be done
632 if (nDist
- nRest
> 0)
634 SwTwips nShrinkAmount
= SwLayoutFrame::ShrinkFrame( nDist
- nRest
, bTst
, bInfo
);
635 nResult
+= nShrinkAmount
;
636 if ( nShrinkAmount
> 0 )
642 // Notify fly frames, if necessary.
643 if ( ( nResult
> 0 ) && bNotifyFlys
)
652 bool SwHeadFootFrame::GetEatSpacing() const
654 const SwFrameFormat
* pFormat
= GetFormat();
655 OSL_ENSURE(pFormat
, "SwHeadFootFrame: no format?");
657 return pFormat
->GetHeaderAndFooterEatSpacing().GetValue();
660 static void DelFlys( const SwLayoutFrame
& rFrame
, SwPageFrame
&rPage
)
663 while ( rPage
.GetSortedObjs() &&
664 rPage
.GetSortedObjs()->size() &&
665 i
< rPage
.GetSortedObjs()->size() )
667 SwAnchoredObject
* pObj
= (*rPage
.GetSortedObjs())[i
];
668 if (SwFlyFrame
* pFlyFrame
= pObj
->DynCastFlyFrame())
670 if (rFrame
.IsAnLower(pFlyFrame
))
672 SwFrame::DestroyFrame(pFlyFrame
);
673 // Do not increment index, in this case
681 /// Creates or removes headers
682 void SwPageFrame::PrepareHeader()
684 SwLayoutFrame
*pLay
= static_cast<SwLayoutFrame
*>(Lower());
688 const SwFormatHeader
&rH
= static_cast<SwFrameFormat
*>(GetDep())->GetHeader();
690 const SwViewShell
*pSh
= getRootFrame()->GetCurrShell();
691 const bool bOn
= !(pSh
&& (pSh
->GetViewOptions()->getBrowseMode() ||
692 pSh
->GetViewOptions()->IsWhitespaceHidden()));
694 if ( bOn
&& rH
.IsActive() )
695 { //Implant header, but remove first, if already present
696 OSL_ENSURE( rH
.GetHeaderFormat(), "FrameFormat for Header not found." );
698 if ( pLay
->GetFormat() == rH
.GetHeaderFormat() )
699 return; // Header is already the correct one.
701 if ( pLay
->IsHeaderFrame() )
702 { SwLayoutFrame
*pDel
= pLay
;
703 pLay
= static_cast<SwLayoutFrame
*>(pLay
->GetNext());
704 ::DelFlys(*pDel
, *this);
706 SwFrame::DestroyFrame(pDel
);
708 OSL_ENSURE( pLay
, "Where to with the Header?" );
709 SwHeaderFrame
*pH
= new SwHeaderFrame( const_cast<SwFrameFormat
*>(rH
.GetHeaderFormat()), this );
710 pH
->Paste( this, pLay
);
712 ::RegistFlys( this, pH
);
714 else if (pLay
->IsHeaderFrame())
715 { // Remove header if present.
716 ::DelFlys(*pLay
, *this);
718 SwFrame::DestroyFrame(pLay
);
722 /// Creates or removes footer
723 void SwPageFrame::PrepareFooter()
725 SwLayoutFrame
*pLay
= static_cast<SwLayoutFrame
*>(Lower());
729 const SwFormatFooter
&rF
= static_cast<SwFrameFormat
*>(GetDep())->GetFooter();
730 while ( pLay
->GetNext() )
731 pLay
= static_cast<SwLayoutFrame
*>(pLay
->GetNext());
733 const SwViewShell
*pSh
= getRootFrame()->GetCurrShell();
734 const bool bOn
= !(pSh
&& (pSh
->GetViewOptions()->getBrowseMode() ||
735 pSh
->GetViewOptions()->IsWhitespaceHidden()));
737 if ( bOn
&& rF
.IsActive() )
738 { //Implant footer, but remove first, if already present
739 OSL_ENSURE( rF
.GetFooterFormat(), "FrameFormat for Footer not found." );
741 if ( pLay
->GetFormat() == rF
.GetFooterFormat() )
742 return; // Footer is already the correct one.
744 if ( pLay
->IsFooterFrame() )
746 ::DelFlys(*pLay
, *this);
748 SwFrame::DestroyFrame(pLay
);
750 SwFooterFrame
*pF
= new SwFooterFrame( const_cast<SwFrameFormat
*>(rF
.GetFooterFormat()), this );
753 ::RegistFlys( this, pF
);
755 else if ( pLay
->IsFooterFrame() )
757 // Remove footer if already present
758 ::DelFlys(*pLay
, *this);
760 if ( pLay
->GetPrev() && nullptr != (pShell
= getRootFrame()->GetCurrShell()) &&
761 pShell
->VisArea().HasArea() )
762 pShell
->InvalidateWindows( pShell
->VisArea() );
764 SwFrame::DestroyFrame(pLay
);
768 void SwHeaderFrame::dumpAsXml(xmlTextWriterPtr writer
) const
770 (void)xmlTextWriterStartElement(writer
, reinterpret_cast<const xmlChar
*>("header"));
771 dumpAsXmlAttributes(writer
);
773 (void)xmlTextWriterStartElement(writer
, BAD_CAST("infos"));
774 dumpInfosAsXml(writer
);
775 (void)xmlTextWriterEndElement(writer
);
776 dumpChildrenAsXml(writer
);
778 (void)xmlTextWriterEndElement(writer
);
781 void SwFooterFrame::dumpAsXml(xmlTextWriterPtr writer
) const
783 (void)xmlTextWriterStartElement(writer
, reinterpret_cast<const xmlChar
*>("footer"));
784 dumpAsXmlAttributes(writer
);
786 (void)xmlTextWriterStartElement(writer
, BAD_CAST("infos"));
787 dumpInfosAsXml(writer
);
788 (void)xmlTextWriterEndElement(writer
);
789 dumpChildrenAsXml(writer
);
791 (void)xmlTextWriterEndElement(writer
);
794 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */