Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / layout / sectfrm.cxx
blobde4e54e27d5d18fb915364fb4d83548c788be283
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 <sal/config.h>
23 #include <sal/log.hxx>
25 #include <o3tl/safeint.hxx>
26 #include <osl/diagnose.h>
27 #include <svl/itemiter.hxx>
28 #include <txtftn.hxx>
29 #include <fmtftn.hxx>
30 #include <fmtclbl.hxx>
31 #include <sectfrm.hxx>
32 #include <cellfrm.hxx>
33 #include <section.hxx>
34 #include <IDocumentSettingAccess.hxx>
35 #include <rootfrm.hxx>
36 #include <pagefrm.hxx>
37 #include <txtfrm.hxx>
38 #include <fmtclds.hxx>
39 #include <colfrm.hxx>
40 #include <tabfrm.hxx>
41 #include <ftnfrm.hxx>
42 #include <layouter.hxx>
43 #include <dbg_lay.hxx>
44 #include <viewopt.hxx>
45 #include <viewimp.hxx>
46 #include <editeng/brushitem.hxx>
47 #include <fmtftntx.hxx>
48 #include <flyfrm.hxx>
49 #include <sortedobjs.hxx>
50 #include <hints.hxx>
51 #include <frmatr.hxx>
52 #include <frmtool.hxx>
54 namespace
56 /**
57 * Performs the correct type of position invalidation depending on if we're in
58 * CalcContent().
60 void InvalidateFramePos(SwFrame* pFrame, bool bInCalcContent)
62 if (bInCalcContent)
63 pFrame->InvalidatePos_();
64 else
65 pFrame->InvalidatePos();
69 SwSectionFrame::SwSectionFrame( SwSection &rSect, SwFrame* pSib )
70 : SwLayoutFrame( rSect.GetFormat(), pSib )
71 , SwFlowFrame( static_cast<SwFrame&>(*this) )
72 , m_pSection( &rSect )
73 , m_bFootnoteAtEnd(false)
74 , m_bEndnAtEnd(false)
75 , m_bContentLock(false)
76 , m_bOwnFootnoteNum(false)
77 , m_bFootnoteLock(false)
79 StartListening(rSect.GetFormat()->GetNotifier());
81 mnFrameType = SwFrameType::Section;
83 CalcFootnoteAtEndFlag();
84 CalcEndAtEndFlag();
87 SwSectionFrame::SwSectionFrame( SwSectionFrame &rSect, bool bMaster ) :
88 SwLayoutFrame( rSect.GetFormat(), rSect.getRootFrame() ),
89 SwFlowFrame( static_cast<SwFrame&>(*this) ),
90 m_pSection( rSect.GetSection() ),
91 m_bFootnoteAtEnd( rSect.IsFootnoteAtEnd() ),
92 m_bEndnAtEnd( rSect.IsEndnAtEnd() ),
93 m_bContentLock( false ),
94 m_bOwnFootnoteNum( false ),
95 m_bFootnoteLock( false )
97 StartListening(rSect.GetFormat()->GetNotifier());
99 mnFrameType = SwFrameType::Section;
101 PROTOCOL( this, PROT::Section, bMaster ? DbgAction::CreateMaster : DbgAction::CreateFollow, &rSect )
103 if( bMaster )
105 SwSectionFrame* pMaster = rSect.IsFollow() ? rSect.FindMaster() : nullptr;
106 if (pMaster)
107 pMaster->SetFollow( this );
108 SetFollow( &rSect );
110 else
112 SetFollow( rSect.GetFollow() );
113 rSect.SetFollow( this );
114 if( !GetFollow() )
115 rSect.SimpleFormat();
116 if( !rSect.IsColLocked() )
117 rSect.InvalidateSize();
121 // NOTE: call <SwSectionFrame::Init()> directly after creation of a new section
122 // frame and its insert in the layout.
123 void SwSectionFrame::Init()
125 assert(GetUpper() && "SwSectionFrame::Init before insertion?!");
126 SwRectFnSet aRectFnSet(this);
127 tools::Long nWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
130 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
131 aRectFnSet.SetWidth( aFrm, nWidth );
132 aRectFnSet.SetHeight( aFrm, 0 );
135 // #109700# LRSpace for sections
136 const SvxLRSpaceItem& rLRSpace = GetFormat()->GetLRSpace();
139 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
140 aRectFnSet.SetLeft( aPrt, rLRSpace.GetLeft() );
141 aRectFnSet.SetWidth( aPrt, nWidth - rLRSpace.GetLeft() - rLRSpace.GetRight() );
142 aRectFnSet.SetHeight( aPrt, 0 );
145 const SwFormatCol &rCol = GetFormat()->GetCol();
146 if( ( rCol.GetNumCols() > 1 || IsAnyNoteAtEnd() ) && !IsInFootnote() )
148 const SwFormatCol *pOld = Lower() ? &rCol : new SwFormatCol;
149 ChgColumns( *pOld, rCol, IsAnyNoteAtEnd() );
150 if( pOld != &rCol )
151 delete pOld;
155 void SwSectionFrame::DestroyImpl()
157 if( GetFormat() && !GetFormat()->GetDoc()->IsInDtor() )
159 SwRootFrame *pRootFrame = getRootFrame();
160 if( pRootFrame )
161 pRootFrame->RemoveFromList( this );
162 if( IsFollow() )
164 SwSectionFrame *pMaster = FindMaster();
165 if( pMaster )
167 PROTOCOL( this, PROT::Section, DbgAction::DelFollow, pMaster )
168 pMaster->SetFollow( GetFollow() );
169 // A Master always grabs the space until the lower edge of his
170 // Upper. If he doesn't have a Follow anymore, he can
171 // release it, which is why the Size of the Master is
172 // invalidated.
173 if( !GetFollow() )
174 pMaster->InvalidateSize();
177 #if defined DBG_UTIL
178 else if( HasFollow() )
180 PROTOCOL( this, PROT::Section, DbgAction::DelMaster, GetFollow() )
182 #endif
185 SwLayoutFrame::DestroyImpl();
188 SwSectionFrame::~SwSectionFrame()
192 void SwSectionFrame::DelEmpty( bool bRemove )
194 if( IsColLocked() )
196 OSL_ENSURE( !bRemove, "Don't delete locked SectionFrames" );
197 return;
199 SwFrame* pUp = GetUpper();
200 if( pUp )
202 // #i27138#
203 // notify accessibility paragraphs objects about changed
204 // CONTENT_FLOWS_FROM/_TO relation.
205 // Relation CONTENT_FLOWS_FROM for current next paragraph will change
206 // and relation CONTENT_FLOWS_TO for current previous paragraph will change.
207 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
209 SwViewShell* pViewShell( getRootFrame()->GetCurrShell() );
210 if ( pViewShell && pViewShell->GetLayout() &&
211 pViewShell->GetLayout()->IsAnyShellAccessible() )
213 auto pNext = FindNextCnt( true );
214 auto pPrev = FindPrevCnt();
215 pViewShell->InvalidateAccessibleParaFlowRelation(
216 pNext ? pNext->DynCastTextFrame() : nullptr,
217 pPrev ? pPrev->DynCastTextFrame() : nullptr );
220 #endif
221 Cut_( bRemove );
223 SwSectionFrame *pMaster = IsFollow() ? FindMaster() : nullptr;
224 if (pMaster)
226 pMaster->SetFollow( GetFollow() );
227 // A Master always grabs the space until the lower edge of his
228 // Upper. If he doesn't have a Follow anymore, he can
229 // release it, which is why the Size of the Master is
230 // invalidated.
231 if( !GetFollow() && !pMaster->IsColLocked() )
232 pMaster->InvalidateSize();
234 SetFollow(nullptr);
235 if( !pUp )
236 return;
239 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
240 aFrm.Height( 0 );
243 // If we are destroyed immediately anyway, we don't need
244 // to put us into the list
245 if( bRemove )
246 { // If we already were half dead before this DelEmpty,
247 // we are likely in the list and have to remove us from
248 // it
249 if( !m_pSection && getRootFrame() )
250 getRootFrame()->RemoveFromList( this );
252 else if( getRootFrame() )
254 getRootFrame()->InsertEmptySct( this );
257 m_pSection = nullptr; // like this a reanimation is virtually impossible though
260 void SwSectionFrame::Cut()
262 Cut_( true );
265 void SwSectionFrame::Cut_( bool bRemove )
267 OSL_ENSURE( GetUpper(), "Cut without Upper()." );
269 PROTOCOL( this, PROT::Cut, DbgAction::NONE, GetUpper() )
271 SwPageFrame *pPage = FindPageFrame();
272 InvalidatePage( pPage );
273 SwFrame *pFrame = GetNext();
274 SwFrame* pPrepFrame = nullptr;
275 while( pFrame && pFrame->IsSctFrame() && !static_cast<SwSectionFrame*>(pFrame)->GetSection() )
276 pFrame = pFrame->GetNext();
277 if( pFrame )
278 { // The former successor might have calculated a gap to the predecessor
279 // which is now obsolete since he becomes the first
280 pFrame->InvalidatePrt_();
281 pFrame->InvalidatePos_();
282 if( pFrame->IsSctFrame() )
283 pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
284 if ( pFrame && pFrame->IsContentFrame() )
286 pFrame->InvalidatePage( pPage );
287 if( IsInFootnote() && !GetIndPrev() )
288 pPrepFrame = pFrame;
291 else
293 InvalidateNextPos();
294 // Someone has to take over the retouching: predecessor or Upper
295 pFrame = GetPrev();
296 if ( nullptr != pFrame )
298 pFrame->SetRetouche();
299 pFrame->Prepare( PrepareHint::WidowsOrphans );
300 if ( pFrame->IsContentFrame() )
301 pFrame->InvalidatePage( pPage );
303 // If I am (was) the only FlowFrame in my Upper, then he has to take over
304 // the retouching.
305 // Furthermore a blank page could have emerged
306 else
307 { SwRootFrame *pRoot = static_cast<SwRootFrame*>(pPage->GetUpper());
308 pRoot->SetSuperfluous();
309 GetUpper()->SetCompletePaint();
312 // First remove, then shrink Upper
313 SwLayoutFrame *pUp = GetUpper();
314 if( bRemove )
316 RemoveFromLayout();
317 if( pUp && !pUp->Lower() && pUp->IsFootnoteFrame() && !pUp->IsColLocked() &&
318 pUp->GetUpper() )
320 pUp->Cut();
321 SwFrame::DestroyFrame(pUp);
322 pUp = nullptr;
325 if( pPrepFrame )
326 pPrepFrame->Prepare( PrepareHint::FootnoteInvalidation );
327 if ( !pUp )
328 return;
330 SwRectFnSet aRectFnSet(this);
331 SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
332 if( nFrameHeight <= 0 )
333 return;
335 if( !bRemove )
337 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
338 aRectFnSet.SetHeight( aFrm, 0 );
340 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
341 aRectFnSet.SetHeight( aPrt, 0 );
344 pUp->Shrink( nFrameHeight );
347 void SwSectionFrame::Paste( SwFrame* pParent, SwFrame* pSibling )
349 OSL_ENSURE( pParent, "No parent for Paste()." );
350 OSL_ENSURE( pParent->IsLayoutFrame(), "Parent is ContentFrame." );
351 OSL_ENSURE( pParent != this, "I'm my own parent." );
352 OSL_ENSURE( pSibling != this, "I'm my own neighbour." );
353 OSL_ENSURE( !GetPrev() && !GetUpper(),
354 "I am still registered somewhere." );
356 PROTOCOL( this, PROT::Paste, DbgAction::NONE, GetUpper() )
358 // Add to the tree
359 SwSectionFrame* pSect = pParent->FindSctFrame();
360 // Assure that parent is not inside a table frame, which is inside the found section frame.
361 if ( pSect )
363 SwTabFrame* pTableFrame = pParent->FindTabFrame();
364 if ( pTableFrame &&
365 pSect->IsAnLower( pTableFrame ) )
367 pSect = nullptr;
371 SwRectFnSet aRectFnSet(pParent);
372 if( pSect && HasToBreak( pSect ) )
374 if( pParent->IsColBodyFrame() ) // dealing with a single-column area
376 // If we are coincidentally at the end of a column, pSibling
377 // has to point to the first frame of the next column in order
378 // for the content of the next column to be moved correctly to the
379 // newly created pSect by the InsertGroup
380 SwColumnFrame *pCol = static_cast<SwColumnFrame*>(pParent->GetUpper());
381 while( !pSibling && nullptr != ( pCol = static_cast<SwColumnFrame*>(pCol->GetNext()) ) )
382 pSibling = static_cast<SwLayoutFrame*>(pCol->Lower())->Lower();
383 if( pSibling )
385 // Even worse: every following column content has to
386 // be attached to the pSibling-chain in order to be
387 // taken along
388 SwFrame *pTmp = pSibling;
389 while ( nullptr != ( pCol = static_cast<SwColumnFrame*>(pCol->GetNext()) ) )
391 while ( pTmp->GetNext() )
392 pTmp = pTmp->GetNext();
393 SwFrame* pSave = ::SaveContent( pCol );
394 if (pSave)
395 ::RestoreContent( pSave, pSibling->GetUpper(), pTmp );
399 pParent = pSect;
400 pSect = new SwSectionFrame( *static_cast<SwSectionFrame*>(pParent)->GetSection(), pParent );
401 // if pParent is decomposed into two parts, its Follow has to be attached
402 // to the new second part
403 pSect->SetFollow( static_cast<SwSectionFrame*>(pParent)->GetFollow() );
404 static_cast<SwSectionFrame*>(pParent)->SetFollow( nullptr );
405 if( pSect->GetFollow() )
406 pParent->InvalidateSize_();
408 const bool bInserted = InsertGroupBefore( pParent, pSibling, pSect );
409 if (bInserted)
411 pSect->Init();
412 aRectFnSet.MakePos( *pSect, pSect->GetUpper(), pSect->GetPrev(), true);
414 if( !static_cast<SwLayoutFrame*>(pParent)->Lower() )
416 SwSectionFrame::MoveContentAndDelete( static_cast<SwSectionFrame*>(pParent), false );
417 pParent = this;
420 else
421 InsertGroupBefore( pParent, pSibling, nullptr );
423 InvalidateAll_();
424 SwPageFrame *pPage = FindPageFrame();
425 InvalidatePage( pPage );
427 if ( pSibling )
429 pSibling->InvalidatePos_();
430 pSibling->InvalidatePrt_();
431 if ( pSibling->IsContentFrame() )
432 pSibling->InvalidatePage( pPage );
435 SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
436 if( nFrameHeight )
437 pParent->Grow( nFrameHeight );
439 if ( GetPrev() && !IsFollow() )
441 GetPrev()->InvalidateSize();
442 if ( GetPrev()->IsContentFrame() )
443 GetPrev()->InvalidatePage( pPage );
448 |* Here it's decided whether the this-SectionFrame should break up
449 |* the passed (Section)frm (or not).
450 |* Initially, all superior sections are broken up. Later on that could
451 |* be made configurable.
453 bool SwSectionFrame::HasToBreak( const SwFrame* pFrame ) const
455 if( !pFrame->IsSctFrame() )
456 return false;
458 const SwSectionFormat *pTmp = static_cast<const SwSectionFormat*>(GetFormat());
460 const SwFrameFormat *pOtherFormat = static_cast<const SwSectionFrame*>(pFrame)->GetFormat();
463 pTmp = pTmp->GetParent();
464 if( !pTmp )
465 return false;
466 if( pTmp == pOtherFormat )
467 return true;
468 } while( true ); // ( pTmp->GetSect().GetValue() );
472 |* Merges two SectionFrames, in case it's about the same section.
473 |* This can be necessary when a (sub)section is deleted that had
474 |* divided another part into two.
476 void SwSectionFrame::MergeNext( SwSectionFrame* pNxt )
478 if (pNxt->IsDeleteForbidden())
479 return;
481 if (pNxt->IsJoinLocked() || GetSection() != pNxt->GetSection())
482 return;
484 PROTOCOL( this, PROT::Section, DbgAction::Merge, pNxt )
486 SwFrame* pTmp = ::SaveContent( pNxt );
487 if( pTmp )
489 SwFrame* pLast = Lower();
490 SwLayoutFrame* pLay = this;
491 if( pLast )
493 while( pLast->GetNext() )
494 pLast = pLast->GetNext();
495 if( pLast->IsColumnFrame() )
496 { // Columns now with BodyFrame
497 pLay = static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(pLast)->Lower());
498 pLast = pLay->Lower();
499 if( pLast )
500 while( pLast->GetNext() )
501 pLast = pLast->GetNext();
504 ::RestoreContent( pTmp, pLay, pLast );
506 SetFollow( pNxt->GetFollow() );
507 pNxt->SetFollow( nullptr );
508 pNxt->Cut();
509 SwFrame::DestroyFrame(pNxt);
510 InvalidateSize();
514 |* Divides a SectionFrame into two parts. The content of the second one
515 |* starts after pFrameStartAfter; the created second section frame itself
516 |* is put after pFramePutAfter.
517 |* If pFrameStartAfter is nullptr, the split happens at the start.
518 |* This is required when inserting an inner section, because the MoveFwd
519 |* cannot have the desired effect within a frame or a table cell.
520 |* Splitting at the start/end makes sense, because the empty frame would
521 |* be removed after the InsertCnt_ finished.
523 SwSectionFrame* SwSectionFrame::SplitSect( SwFrame* pFrameStartAfter, SwFrame* pFramePutAfter )
525 assert(!pFrameStartAfter || IsAnLower(pFrameStartAfter));
526 SwFrame* pSav;
527 if (pFrameStartAfter)
529 pSav = pFrameStartAfter->FindNext();
530 // If pFrameStartAfter is a complex object like table, and it has no next,
531 // its FindNext may return its own last subframe. In this case, assume that
532 // we are at the end.
533 if (pSav && pFrameStartAfter->IsLayoutFrame())
534 if (static_cast<SwLayoutFrame*>(pFrameStartAfter)->IsAnLower(pSav))
535 pSav = nullptr;
537 else
539 pSav = ContainsAny();
541 if (pSav && !IsAnLower(pSav))
542 pSav = nullptr; // we are at the very end
544 // Put the content aside
545 if (pSav)
546 pSav = ::SaveContent( this, pSav );
548 // Create a new SctFrame, not as a Follower/master
549 if (!pFramePutAfter)
550 pFramePutAfter = this;
551 SwSectionFrame* pNew = new SwSectionFrame( *GetSection(), this );
552 pNew->InsertBehind( pFramePutAfter->GetUpper(), pFramePutAfter );
553 pNew->Init();
554 SwRectFnSet aRectFnSet(this);
555 aRectFnSet.MakePos( *pNew, nullptr, pFramePutAfter, true );
556 // OD 25.03.2003 #108339# - restore content:
557 // determine layout frame for restoring content after the initialization
558 // of the section frame. In the section initialization the columns are
559 // created.
560 if (pSav)
562 SwLayoutFrame* pLay = pNew;
563 // Search for last layout frame, e.g. for columned sections.
564 while( pLay->Lower() && pLay->Lower()->IsLayoutFrame() )
565 pLay = static_cast<SwLayoutFrame*>(pLay->Lower());
566 ::RestoreContent( pSav, pLay, nullptr );
568 InvalidateSize_();
569 if( HasFollow() )
571 pNew->SetFollow( GetFollow() );
572 SetFollow( nullptr );
574 return pNew;
578 |* MoveContent is called for destroying a SectionFrames, due to
579 |* the cancellation or hiding of a section, to handle the content.
580 |* If the SectionFrame hasn't broken up another one, then the content
581 |* is moved to the Upper. Otherwise the content is moved to another
582 |* SectionFrame, which has to be potentially merged.
584 // If a multi-column section is cancelled, the ContentFrames have to be
585 // invalidated
586 static void lcl_InvalidateInfFlags( SwFrame* pFrame, bool bInva )
588 while ( pFrame )
590 pFrame->InvalidateInfFlags();
591 if( bInva )
593 pFrame->InvalidatePos_();
594 pFrame->InvalidateSize_();
595 pFrame->InvalidatePrt_();
597 if( pFrame->IsLayoutFrame() )
598 lcl_InvalidateInfFlags( static_cast<SwLayoutFrame*>(pFrame)->GetLower(), false );
599 pFrame = pFrame->GetNext();
603 // Works like SwContentFrame::ImplGetNextContentFrame, but starts with a LayoutFrame
604 static SwContentFrame* lcl_GetNextContentFrame( const SwLayoutFrame* pLay, bool bFwd )
606 if ( bFwd )
608 if ( pLay->GetNext() && pLay->GetNext()->IsContentFrame() )
609 return const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(pLay->GetNext()));
611 else
613 if ( pLay->GetPrev() && pLay->GetPrev()->IsContentFrame() )
614 return const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(pLay->GetPrev()));
617 const SwFrame* pFrame = pLay;
618 SwContentFrame *pContentFrame = nullptr;
619 bool bGoingUp = true;
620 do {
621 const SwFrame *p = nullptr;
622 bool bGoingFwdOrBwd = false;
624 bool bGoingDown = !bGoingUp && pFrame->IsLayoutFrame();
625 if (bGoingDown)
627 p = static_cast<const SwLayoutFrame*>(pFrame)->Lower();
628 bGoingDown = nullptr != p;
630 if ( !bGoingDown )
632 p = pFrame->IsFlyFrame() ?
633 ( bFwd ? static_cast<const SwFlyFrame*>(pFrame)->GetNextLink() : static_cast<const SwFlyFrame*>(pFrame)->GetPrevLink() ) :
634 ( bFwd ? pFrame->GetNext() :pFrame->GetPrev() );
635 bGoingFwdOrBwd = nullptr != p;
636 if ( !bGoingFwdOrBwd )
638 p = pFrame->GetUpper();
639 bGoingUp = nullptr != p;
640 if ( !bGoingUp )
641 return nullptr;
645 bGoingUp = !( bGoingFwdOrBwd || bGoingDown );
646 assert(p);
647 if (!bFwd && bGoingDown)
648 while ( p->GetNext() )
649 p = p->GetNext();
651 pFrame = p;
652 } while ( nullptr == (pContentFrame = (pFrame->IsContentFrame() ? const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(pFrame)) : nullptr) ));
654 return pContentFrame;
657 namespace
659 SwLayoutFrame* FirstLeaf(SwSectionFrame* pLayFrame)
661 if (pLayFrame->Lower() && pLayFrame->Lower()->IsColumnFrame())
662 return pLayFrame->GetNextLayoutLeaf();
663 return pLayFrame;
666 /// Checks if pFrame has a parent that can contain a split section frame.
667 bool CanContainSplitSection(const SwFrame* pFrame)
669 if (!pFrame->IsInTab())
670 return true;
672 // The frame is in a table, see if the table is in a section.
673 bool bRet = !pFrame->FindTabFrame()->IsInSct();
675 if (bRet)
677 // Don't try to split if the frame itself is a section frame with
678 // multiple columns.
679 if (pFrame->IsSctFrame())
681 const SwFrame* pLower = pFrame->GetLower();
682 if (pLower && pLower->IsColumnFrame())
683 bRet = false;
687 return bRet;
691 void SwSectionFrame::MoveContentAndDelete( SwSectionFrame* pDel, bool bSave )
693 bool bSize = pDel->Lower() && pDel->Lower()->IsColumnFrame();
694 SwFrame* pPrv = pDel->GetPrev();
695 SwLayoutFrame* pUp = pDel->GetUpper();
696 // OD 27.03.2003 #i12711# - initialize local pointer variables.
697 SwSectionFrame* pPrvSct = nullptr;
698 SwSectionFrame* pNxtSct = nullptr;
699 SwSectionFormat* pParent = static_cast<SwSectionFormat*>(pDel->GetFormat())->GetParent();
700 if( pDel->IsInTab() && pParent )
702 SwTabFrame *pTab = pDel->FindTabFrame();
703 // If we are within a table, we can only have broken up sections that
704 // are inside as well, but not a section that contains the whole table.
705 if( pTab->IsInSct() && pParent == pTab->FindSctFrame()->GetFormat() )
706 pParent = nullptr;
708 // If our Format has a parent, we have probably broken up another
709 // SectionFrame, which has to be checked. To do so we first acquire the
710 // succeeding and the preceding ContentFrame, let's see if they
711 // lay in the SectionFrames.
712 // OD 27.03.2003 #i12711# - check, if previous and next section belonging
713 // together and can be joined, *not* only if deleted section contains content.
714 if ( pParent )
716 SwFrame* pPrvContent = lcl_GetNextContentFrame( pDel, false );
717 pPrvSct = pPrvContent ? pPrvContent->FindSctFrame() : nullptr;
718 SwFrame* pNxtContent = lcl_GetNextContentFrame( pDel, true );
719 pNxtSct = pNxtContent ? pNxtContent->FindSctFrame() : nullptr;
721 else
723 pParent = nullptr;
724 pPrvSct = pNxtSct = nullptr;
727 // Now the content is put aside and the frame is destroyed
728 SwFrame *pSave = bSave ? ::SaveContent( pDel ) : nullptr;
729 bool bOldFootnote = true;
730 if( pSave && pUp->IsFootnoteFrame() )
732 bOldFootnote = static_cast<SwFootnoteFrame*>(pUp)->IsColLocked();
733 static_cast<SwFootnoteFrame*>(pUp)->ColLock();
735 pDel->DelEmpty( true );
736 SwFrame::DestroyFrame(pDel);
737 if( pParent )
738 { // Search for the appropriate insert position
739 if( pNxtSct && pNxtSct->GetFormat() == pParent )
740 { // Here we can insert ourselves at the beginning
741 pUp = FirstLeaf( pNxtSct );
742 pPrv = nullptr;
743 if( pPrvSct && ( pPrvSct->GetFormat() != pParent ) )
744 pPrvSct = nullptr; // In order that nothing is merged
746 else if( pPrvSct && pPrvSct->GetFormat() == pParent )
747 { // Wonderful, here we can insert ourselves at the end
748 pUp = pPrvSct;
749 if( pUp->Lower() && pUp->Lower()->IsColumnFrame() )
751 pUp = static_cast<SwLayoutFrame*>(pUp->GetLastLower());
752 // The body of the last column
753 pUp = static_cast<SwLayoutFrame*>(pUp->Lower());
755 // In order to perform the insertion after the last one
756 pPrv = pUp->GetLastLower();
757 pPrvSct = nullptr; // Such that nothing is merged
759 else
761 if( pSave )
762 { // Following situations: before and after the section-to-be
763 // deleted there is the section boundary of the enclosing
764 // section, or another (sibling) section connects subsequently,
765 // that derives from the same Parent.
766 // In that case, there's not (yet) a part of our parent available
767 // that can store the content, so we create it here.
768 pPrvSct = new SwSectionFrame( *pParent->GetSection(), pUp );
769 pPrvSct->InsertBehind( pUp, pPrv );
770 pPrvSct->Init();
771 SwRectFnSet aRectFnSet(pUp);
772 aRectFnSet.MakePos( *pPrvSct, pUp, pPrv, true );
773 pUp = FirstLeaf( pPrvSct );
774 pPrv = nullptr;
776 pPrvSct = nullptr; // Such that nothing will be merged
779 // The content is going to be inserted...
780 if( pSave )
782 lcl_InvalidateInfFlags( pSave, bSize );
783 ::RestoreContent( pSave, pUp, pPrv );
784 pUp->FindPageFrame()->InvalidateContent();
785 if( !bOldFootnote )
786 static_cast<SwFootnoteFrame*>(pUp)->ColUnlock();
788 // Now two parts of the superior section could possibly be merged
789 if( pPrvSct && !pPrvSct->IsJoinLocked() )
791 OSL_ENSURE( pNxtSct, "MoveContent: No Merge" );
792 pPrvSct->MergeNext( pNxtSct );
796 void SwSectionFrame::MakeAll(vcl::RenderContext* pRenderContext)
798 if ( IsJoinLocked() || IsColLocked() || StackHack::IsLocked() || StackHack::Count() > 50 )
799 return;
800 if( !m_pSection ) // Via DelEmpty
802 #ifdef DBG_UTIL
803 OSL_ENSURE( getRootFrame()->IsInDelList( this ), "SectionFrame without Section" );
804 #endif
805 if( !isFrameAreaPositionValid() )
807 if( GetUpper() )
809 SwRectFnSet aRectFnSet(GetUpper());
810 aRectFnSet.MakePos( *this, GetUpper(), GetPrev(), false );
813 if (getFrameArea().Height() == 0)
815 // SwLayoutFrame::MakeAll() is not called for to-be-deleted
816 // section frames (which would invalidate the position of the
817 // next frame via the SwLayNotify dtor), so call it manually.
818 if (SwFrame* pNext = GetNext())
819 pNext->InvalidatePos();
823 setFrameAreaPositionValid(true);
824 setFrameAreaSizeValid(true);
825 setFramePrintAreaValid(true);
826 return;
828 LockJoin(); // I don't let myself to be destroyed on the way
830 while( GetNext() && GetNext() == GetFollow() )
832 const SwFrame* pFoll = GetFollow();
833 MergeNext( static_cast<SwSectionFrame*>(GetNext()) );
834 if( pFoll == GetFollow() )
835 break;
838 // OD 2004-03-15 #116561# - In online layout join the follows, if section
839 // can grow.
840 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
842 // Split sections inside table cells: need to merge all follows of the
843 // section here, as later we won't attempt doing so.
844 bool bCanContainSplitSection = false;
845 if (IsInTab() && GetUpper())
846 bCanContainSplitSection = CanContainSplitSection(GetUpper());
848 if( pSh && (pSh->GetViewOptions()->getBrowseMode() || bCanContainSplitSection) &&
849 ( Grow( LONG_MAX, true ) > 0 ) )
851 while( GetFollow() )
853 const SwFrame* pFoll = GetFollow();
854 MergeNext( GetFollow() );
855 if( pFoll == GetFollow() )
856 break;
860 // A section with Follow uses all the space until the lower edge of the
861 // Upper. If it moves, its size can grow or decrease...
862 if( !isFrameAreaPositionValid() && ToMaximize( false ) )
864 setFrameAreaSizeValid(false);
867 SwLayoutFrame::MakeAll(getRootFrame()->GetCurrShell()->GetOut());
869 if (IsInTab())
871 // In case the section is in a table, then calculate the lower right
872 // now. Just setting the valid size flag of the lower to false may not
873 // be enough, as lcl_RecalcRow() can call
874 // SwFrame::ValidateThisAndAllLowers(), and then we don't attempt
875 // calculating the proper position of the lower.
876 SwFrame* pLower = Lower();
877 if (pLower && !pLower->isFrameAreaPositionValid())
878 pLower->Calc(pRenderContext);
881 UnlockJoin();
882 if( m_pSection && IsSuperfluous() )
883 DelEmpty( false );
886 bool SwSectionFrame::ShouldBwdMoved( SwLayoutFrame *, bool & )
888 OSL_FAIL( "Oops, where is my tinfoil hat?" );
889 return false;
892 const SwSectionFormat* SwSectionFrame::GetEndSectFormat_() const
894 const SwSectionFormat *pFormat = m_pSection->GetFormat();
895 while( !pFormat->GetEndAtTextEnd().IsAtEnd() )
897 if( auto pNewFormat = dynamic_cast< const SwSectionFormat *>( pFormat->GetRegisteredIn()) )
898 pFormat = pNewFormat;
899 else
900 return nullptr;
902 return pFormat;
905 static void lcl_FindContentFrame( SwContentFrame* &rpContentFrame, SwFootnoteFrame* &rpFootnoteFrame,
906 SwFrame* pFrame, bool &rbChkFootnote )
908 if( !pFrame )
909 return;
911 while( pFrame->GetNext() )
912 pFrame = pFrame->GetNext();
913 while( !rpContentFrame && pFrame )
915 if( pFrame->IsContentFrame() )
916 rpContentFrame = static_cast<SwContentFrame*>(pFrame);
917 else if( pFrame->IsLayoutFrame() )
919 if( pFrame->IsFootnoteFrame() )
921 if( rbChkFootnote )
923 rpFootnoteFrame = static_cast<SwFootnoteFrame*>(pFrame);
924 rbChkFootnote = rpFootnoteFrame->GetAttr()->GetFootnote().IsEndNote();
927 else
928 lcl_FindContentFrame( rpContentFrame, rpFootnoteFrame,
929 static_cast<SwLayoutFrame*>(pFrame)->Lower(), rbChkFootnote );
931 pFrame = pFrame->GetPrev();
935 SwContentFrame *SwSectionFrame::FindLastContent( SwFindMode nMode )
937 SwContentFrame *pRet = nullptr;
938 SwFootnoteFrame *pFootnoteFrame = nullptr;
939 SwSectionFrame *pSect = this;
940 if( nMode != SwFindMode::None )
942 const SwSectionFormat *pFormat = IsEndnAtEnd() ? GetEndSectFormat() :
943 m_pSection->GetFormat();
944 do {
945 while( pSect->HasFollow() )
946 pSect = pSect->GetFollow();
947 SwFrame* pTmp = pSect->FindNext();
948 while( pTmp && pTmp->IsSctFrame() &&
949 !static_cast<SwSectionFrame*>(pTmp)->GetSection() )
950 pTmp = pTmp->FindNext();
951 if( pTmp && pTmp->IsSctFrame() &&
952 static_cast<SwSectionFrame*>(pTmp)->IsDescendantFrom( pFormat ) )
953 pSect = static_cast<SwSectionFrame*>(pTmp);
954 else
955 break;
956 } while( true );
958 bool bFootnoteFound = nMode == SwFindMode::EndNote;
961 lcl_FindContentFrame( pRet, pFootnoteFrame, pSect->Lower(), bFootnoteFound );
962 if( pRet || !pSect->IsFollow() || nMode == SwFindMode::None ||
963 ( SwFindMode::MyLast == nMode && this == pSect ) )
964 break;
965 pSect = pSect->FindMaster();
966 } while( pSect );
967 if( ( nMode == SwFindMode::EndNote ) && pFootnoteFrame )
968 pRet = pFootnoteFrame->ContainsContent();
969 return pRet;
972 bool SwSectionFrame::CalcMinDiff( SwTwips& rMinDiff ) const
974 if( ToMaximize( true ) )
976 SwRectFnSet aRectFnSet(this);
977 rMinDiff = aRectFnSet.GetPrtBottom(*GetUpper());
978 rMinDiff = aRectFnSet.BottomDist( getFrameArea(), rMinDiff );
979 return true;
981 return false;
985 * CollectEndnotes looks for endnotes in the sectionfrm and his follows,
986 * the endnotes will cut off the layout and put into the array.
987 * If the first endnote is not a master-SwFootnoteFrame, the whole sectionfrm
988 * contains only endnotes and it is not necessary to collect them.
990 static SwFootnoteFrame* lcl_FindEndnote( SwSectionFrame* &rpSect, bool &rbEmpty,
991 SwLayouter *pLayouter )
993 // if rEmpty is set, the rpSect is already searched
994 SwSectionFrame* pSect = rbEmpty ? rpSect->GetFollow() : rpSect;
995 while( pSect )
997 OSL_ENSURE( (pSect->Lower() && pSect->Lower()->IsColumnFrame()) || pSect->GetUpper()->IsFootnoteFrame(),
998 "InsertEndnotes: Where's my column?" );
1000 // i73332: Columned section in endnote
1001 SwColumnFrame* pCol = nullptr;
1002 if(pSect->Lower() && pSect->Lower()->IsColumnFrame())
1003 pCol = static_cast<SwColumnFrame*>(pSect->Lower());
1005 while( pCol ) // check all columns
1007 SwFootnoteContFrame* pFootnoteCont = pCol->FindFootnoteCont();
1008 if( pFootnoteCont )
1010 SwFootnoteFrame* pRet = static_cast<SwFootnoteFrame*>(pFootnoteCont->Lower());
1011 while( pRet ) // look for endnotes
1013 /* CollectEndNode can destroy pRet so we need to get the
1014 next early
1016 SwFootnoteFrame* pRetNext = static_cast<SwFootnoteFrame*>(pRet->GetNext());
1017 if( pRet->GetAttr()->GetFootnote().IsEndNote() )
1019 if( pRet->GetMaster() )
1021 if( pLayouter )
1022 pLayouter->CollectEndnote( pRet );
1023 else
1024 return nullptr;
1026 else
1027 return pRet; // Found
1029 pRet = pRetNext;
1032 pCol = static_cast<SwColumnFrame*>(pCol->GetNext());
1034 rpSect = pSect;
1035 pSect = pLayouter ? pSect->GetFollow() : nullptr;
1036 rbEmpty = true;
1038 return nullptr;
1041 static void lcl_ColumnRefresh( SwSectionFrame* pSect, bool bFollow )
1043 vcl::RenderContext* pRenderContext = pSect->getRootFrame()->GetCurrShell()->GetOut();
1044 while( pSect )
1046 bool bOldLock = pSect->IsColLocked();
1047 pSect->ColLock();
1048 if( pSect->Lower() && pSect->Lower()->IsColumnFrame() )
1050 SwColumnFrame *pCol = static_cast<SwColumnFrame*>(pSect->Lower());
1052 { pCol->InvalidateSize_();
1053 pCol->InvalidatePos_();
1054 static_cast<SwLayoutFrame*>(pCol)->Lower()->InvalidateSize_();
1055 pCol->Calc(pRenderContext); // calculation of column and
1056 static_cast<SwLayoutFrame*>(pCol)->Lower()->Calc(pRenderContext); // body
1057 pCol = static_cast<SwColumnFrame*>(pCol->GetNext());
1058 } while ( pCol );
1060 if( !bOldLock )
1061 pSect->ColUnlock();
1062 if( bFollow )
1063 pSect = pSect->GetFollow();
1064 else
1065 pSect = nullptr;
1069 void SwSectionFrame::CollectEndnotes( SwLayouter* pLayouter )
1071 OSL_ENSURE( IsColLocked(), "CollectEndnotes: You love the risk?" );
1072 // i73332: Section in footnode does not have columns!
1073 OSL_ENSURE( (Lower() && Lower()->IsColumnFrame()) || GetUpper()->IsFootnoteFrame(), "Where's my column?" );
1075 SwSectionFrame* pSect = this;
1076 SwFootnoteFrame* pFootnote;
1077 bool bEmpty = false;
1078 // pSect is the last sectionfrm without endnotes or the this-pointer
1079 // the first sectionfrm with endnotes may be destroyed, when the endnotes
1080 // is cutted
1081 while( nullptr != (pFootnote = lcl_FindEndnote( pSect, bEmpty, pLayouter )) )
1082 pLayouter->CollectEndnote( pFootnote );
1083 if( pLayouter->HasEndnotes() )
1084 lcl_ColumnRefresh( this, true );
1087 /** Fits the size to the surroundings.
1089 |* Those that have a Follow or foot notes, have to extend until
1090 |* the lower edge of a upper (bMaximize)
1091 |* They must not extend above the Upper, as the case may be one can
1092 |* try to grow its upper (bGrow)
1093 |* If the size had to be changed, the content is calculated.
1095 |* @note: perform calculation of content, only if height has changed (OD 18.09.2002 #100522#)
1097 void SwSectionFrame::CheckClipping( bool bGrow, bool bMaximize )
1099 SwRectFnSet aRectFnSet(this);
1100 tools::Long nDiff;
1101 SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
1102 if( bGrow && ( !IsInFly() || !GetUpper()->IsColBodyFrame() ||
1103 !FindFlyFrame()->IsLocked() ) )
1105 nDiff = -aRectFnSet.BottomDist( getFrameArea(), nDeadLine );
1106 if( !bMaximize )
1107 nDiff += Undersize();
1108 if( nDiff > 0 )
1110 tools::Long nAdd = GetUpper()->Grow( nDiff );
1111 if( aRectFnSet.IsVert() )
1112 nDeadLine -= nAdd;
1113 else
1114 nDeadLine += nAdd;
1117 nDiff = -aRectFnSet.BottomDist( getFrameArea(), nDeadLine );
1118 SetUndersized( !bMaximize && nDiff >= 0 );
1119 const bool bCalc = ( IsUndersized() || bMaximize ) &&
1120 ( nDiff ||
1121 aRectFnSet.GetTop(getFramePrintArea()) > aRectFnSet.GetHeight(getFrameArea()) );
1122 // OD 03.11.2003 #i19737# - introduce local variable <bExtraCalc> to indicate
1123 // that a calculation has to be done beside the value of <bCalc>.
1124 bool bExtraCalc = false;
1125 if( !bCalc && !bGrow && IsAnyNoteAtEnd() && !IsInFootnote() )
1127 SwSectionFrame *pSect = this;
1128 bool bEmpty = false;
1129 SwLayoutFrame* pFootnote = IsEndnAtEnd() ?
1130 lcl_FindEndnote( pSect, bEmpty, nullptr ) : nullptr;
1131 if( pFootnote )
1133 pFootnote = pFootnote->FindFootnoteBossFrame();
1134 SwFrame* pTmp = FindLastContent( SwFindMode::LastCnt );
1135 // OD 08.11.2002 #104840# - use <SwLayoutFrame::IsBefore(..)>
1136 if ( pTmp && pFootnote->IsBefore( pTmp->FindFootnoteBossFrame() ) )
1137 bExtraCalc = true;
1139 else if( GetFollow() && !GetFollow()->ContainsAny() )
1140 bExtraCalc = true;
1142 if ( !(bCalc || bExtraCalc) )
1143 return;
1145 nDiff = aRectFnSet.YDiff( nDeadLine, aRectFnSet.GetTop(getFrameArea()) );
1146 if( nDiff < 0 )
1147 nDeadLine = aRectFnSet.GetTop(getFrameArea());
1148 const Size aOldSz( getFramePrintArea().SSize() );
1149 tools::Long nTop = aRectFnSet.GetTopMargin(*this);
1152 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1153 aRectFnSet.SetBottom( aFrm, nDeadLine );
1156 nDiff = aRectFnSet.GetHeight(getFrameArea());
1157 if( nTop > nDiff )
1158 nTop = nDiff;
1159 aRectFnSet.SetYMargins( *this, nTop, 0 );
1161 // OD 18.09.2002 #100522#
1162 // Determine, if height has changed.
1163 // Note: In vertical layout the height equals the width value.
1164 bool bHeightChanged = aRectFnSet.IsVert() ?
1165 (aOldSz.Width() != getFramePrintArea().Width()) :
1166 (aOldSz.Height() != getFramePrintArea().Height());
1167 // Last but not least we have changed the height again, thus the inner
1168 // layout (columns) is calculated and the content as well.
1169 // OD 18.09.2002 #100522#
1170 // calculate content, only if height has changed.
1171 // OD 03.11.2003 #i19737# - restriction of content calculation too strong.
1172 // If an endnote has an incorrect position or a follow section contains
1173 // no content except footnotes/endnotes, the content has also been calculated.
1174 if ( !(( bHeightChanged || bExtraCalc ) && Lower()) )
1175 return;
1177 if( Lower()->IsColumnFrame() )
1179 lcl_ColumnRefresh( this, false );
1180 ::CalcContent( this );
1182 else
1184 ChgLowersProp( aOldSz );
1185 if( !bMaximize && !IsContentLocked() )
1186 ::CalcContent( this );
1190 void SwSectionFrame::SimpleFormat()
1192 if ( IsJoinLocked() || IsColLocked() )
1193 return;
1194 LockJoin();
1195 SwRectFnSet aRectFnSet(this);
1196 if( GetPrev() || GetUpper() )
1198 // assure notifications on position changes.
1199 const SwLayNotify aNotify( this );
1200 aRectFnSet.MakePos( *this, GetUpper(), GetPrev(), false );
1201 setFrameAreaPositionValid(true);
1203 SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
1204 // OD 22.10.2002 #97265# - call always method <lcl_ColumnRefresh(..)>, in
1205 // order to get calculated lowers, not only if there space left in its upper.
1206 if( aRectFnSet.BottomDist( getFrameArea(), nDeadLine ) >= 0 )
1209 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1210 aRectFnSet.SetBottom( aFrm, nDeadLine );
1213 tools::Long nHeight = aRectFnSet.GetHeight(getFrameArea());
1214 tools::Long nTop = CalcUpperSpace();
1215 if( nTop > nHeight )
1216 nTop = nHeight;
1217 aRectFnSet.SetYMargins( *this, nTop, 0 );
1219 lcl_ColumnRefresh( this, false );
1220 UnlockJoin();
1223 namespace {
1225 // #i40147# - helper class to perform extra section format
1226 // to position anchored objects and to keep the position of whose objects locked.
1227 class ExtraFormatToPositionObjs
1229 private:
1230 SwSectionFrame* mpSectFrame;
1231 bool mbExtraFormatPerformed;
1233 public:
1234 explicit ExtraFormatToPositionObjs( SwSectionFrame& _rSectFrame)
1235 : mpSectFrame( &_rSectFrame ),
1236 mbExtraFormatPerformed( false )
1239 ~ExtraFormatToPositionObjs()
1241 if ( !mbExtraFormatPerformed )
1242 return;
1244 // release keep locked position of lower floating screen objects
1245 SwPageFrame* pPageFrame = mpSectFrame->FindPageFrame();
1246 SwSortedObjs* pObjs = pPageFrame ? pPageFrame->GetSortedObjs() : nullptr;
1247 if ( pObjs )
1249 for (SwAnchoredObject* pAnchoredObj : *pObjs)
1251 if ( mpSectFrame->IsAnLower( pAnchoredObj->GetAnchorFrame() ) )
1253 pAnchoredObj->SetKeepPosLocked( false );
1259 // #i81555#
1260 void InitObjs( SwFrame& rFrame )
1262 SwSortedObjs* pObjs = rFrame.GetDrawObjs();
1263 if ( pObjs )
1265 for (SwAnchoredObject* pAnchoredObj : *pObjs)
1267 pAnchoredObj->UnlockPosition();
1268 pAnchoredObj->SetClearedEnvironment( false );
1271 if ( rFrame.IsLayoutFrame() )
1273 SwFrame* pLowerFrame = rFrame.GetLower();
1274 while ( pLowerFrame != nullptr )
1276 InitObjs( *pLowerFrame );
1278 pLowerFrame = pLowerFrame->GetNext();
1283 void FormatSectionToPositionObjs()
1285 vcl::RenderContext* pRenderContext = mpSectFrame->getRootFrame()->GetCurrShell()->GetOut();
1286 // perform extra format for multi-columned section.
1287 if ( !(mpSectFrame->Lower() && mpSectFrame->Lower()->IsColumnFrame() &&
1288 mpSectFrame->Lower()->GetNext()) )
1289 return;
1291 // grow section till bottom of printing area of upper frame
1292 SwRectFnSet aRectFnSet(mpSectFrame);
1293 SwTwips nTopMargin = aRectFnSet.GetTopMargin(*mpSectFrame);
1294 Size aOldSectPrtSize( mpSectFrame->getFramePrintArea().SSize() );
1295 SwTwips nDiff = aRectFnSet.BottomDist( mpSectFrame->getFrameArea(), aRectFnSet.GetPrtBottom(*mpSectFrame->GetUpper()) );
1298 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*mpSectFrame);
1299 aRectFnSet.AddBottom( aFrm, nDiff );
1302 aRectFnSet.SetYMargins( *mpSectFrame, nTopMargin, 0 );
1303 // #i59789#
1304 // suppress formatting, if printing area of section is too narrow
1305 if ( aRectFnSet.GetHeight(mpSectFrame->getFramePrintArea()) <= 0 )
1307 return;
1309 mpSectFrame->ChgLowersProp( aOldSectPrtSize );
1311 // format column frames and its body and footnote container
1312 SwColumnFrame* pColFrame = static_cast<SwColumnFrame*>(mpSectFrame->Lower());
1313 while ( pColFrame )
1315 pColFrame->Calc(pRenderContext);
1316 pColFrame->Lower()->Calc(pRenderContext);
1317 if ( pColFrame->Lower()->GetNext() )
1319 pColFrame->Lower()->GetNext()->Calc(pRenderContext);
1322 pColFrame = static_cast<SwColumnFrame*>(pColFrame->GetNext());
1325 // unlock position of lower floating screen objects for the extra format
1326 // #i81555#
1327 // Section frame can already have changed the page and its content
1328 // can still be on the former page.
1329 // Thus, initialize objects via lower-relationship
1330 InitObjs( *mpSectFrame );
1332 // format content - first with collecting its foot-/endnotes before content
1333 // format, second without collecting its foot-/endnotes.
1334 ::CalcContent( mpSectFrame );
1335 ::CalcContent( mpSectFrame, true );
1337 // keep locked position of lower floating screen objects
1338 SwPageFrame* pPageFrame = mpSectFrame->FindPageFrame();
1339 SwSortedObjs* pObjs = pPageFrame ? pPageFrame->GetSortedObjs() : nullptr;
1340 if ( pObjs )
1342 for (SwAnchoredObject* pAnchoredObj : *pObjs)
1344 if ( mpSectFrame->IsAnLower( pAnchoredObj->GetAnchorFrame() ) )
1346 pAnchoredObj->SetKeepPosLocked( true );
1351 mbExtraFormatPerformed = true;
1358 /// "formats" the frame; Frame and PrtArea
1359 void SwSectionFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs *pAttr )
1361 if( !m_pSection ) // via DelEmpty
1363 #ifdef DBG_UTIL
1364 OSL_ENSURE( getRootFrame()->IsInDelList( this ), "SectionFrame without Section" );
1365 #endif
1366 setFrameAreaPositionValid(true);
1367 setFrameAreaSizeValid(true);
1368 setFramePrintAreaValid(true);
1369 return;
1372 SwRectFnSet aRectFnSet(this);
1374 if ( !isFramePrintAreaValid() )
1376 PROTOCOL( this, PROT::PrintArea, DbgAction::NONE, nullptr )
1377 setFramePrintAreaValid(true);
1378 SwTwips nUpper = CalcUpperSpace();
1380 // #109700# LRSpace for sections
1381 const SvxLRSpaceItem& rLRSpace = GetFormat()->GetLRSpace();
1382 aRectFnSet.SetXMargins( *this, rLRSpace.GetLeft(), rLRSpace.GetRight() );
1384 if( nUpper != aRectFnSet.GetTopMargin(*this) )
1386 setFrameAreaSizeValid(false);
1387 SwFrame* pOwn = ContainsAny();
1388 if( pOwn )
1389 pOwn->InvalidatePos_();
1391 aRectFnSet.SetYMargins( *this, nUpper, 0 );
1394 if ( isFrameAreaSizeValid() )
1395 return;
1397 PROTOCOL_ENTER( this, PROT::Size, DbgAction::NONE, nullptr )
1398 const tools::Long nOldHeight = aRectFnSet.GetHeight(getFrameArea());
1399 bool bOldLock = IsColLocked();
1400 ColLock();
1402 setFrameAreaSizeValid(true);
1404 // The size is only determined by the content, if the SectFrame does not have a
1405 // Follow. Otherwise it fills (occupies) the Upper down to the lower edge.
1406 // It is not responsible for the text flow, but the content is.
1407 bool bMaximize = ToMaximize( false );
1409 // OD 2004-05-17 #i28701# - If the wrapping style has to be considered
1410 // on object positioning, an extra formatting has to be performed
1411 // to determine the correct positions the floating screen objects.
1412 // #i40147#
1413 // use new helper class <ExtraFormatToPositionObjs>.
1414 // This class additionally keep the locked position of the objects
1415 // and releases this position lock keeping on destruction.
1416 ExtraFormatToPositionObjs aExtraFormatToPosObjs( *this );
1417 if ( !bMaximize &&
1418 GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) &&
1419 !GetFormat()->GetBalancedColumns().GetValue() )
1421 aExtraFormatToPosObjs.FormatSectionToPositionObjs();
1424 // Column widths have to be adjusted before calling CheckClipping.
1425 // CheckClipping can cause the formatting of the lower frames
1426 // which still have a width of 0.
1427 const bool bHasColumns = Lower() && Lower()->IsColumnFrame();
1428 if ( bHasColumns && Lower()->GetNext() )
1429 AdjustColumns( nullptr, false );
1431 if( GetUpper() )
1433 const tools::Long nWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
1436 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1437 aRectFnSet.SetWidth( aFrm, nWidth );
1440 // #109700# LRSpace for sections
1442 const SvxLRSpaceItem& rLRSpace = GetFormat()->GetLRSpace();
1443 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
1444 aRectFnSet.SetWidth( aPrt, nWidth - rLRSpace.GetLeft() - rLRSpace.GetRight() );
1447 // OD 15.10.2002 #103517# - allow grow in online layout
1448 // Thus, set <..IsBrowseMode()> as parameter <bGrow> on calling
1449 // method <CheckClipping(..)>.
1450 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
1451 CheckClipping( pSh && pSh->GetViewOptions()->getBrowseMode(), bMaximize );
1452 bMaximize = ToMaximize( false );
1453 setFrameAreaSizeValid(true);
1456 // Check the width of the columns and adjust if necessary
1457 if ( bHasColumns && ! Lower()->GetNext() && bMaximize )
1458 static_cast<SwColumnFrame*>(Lower())->Lower()->Calc(pRenderContext);
1460 if ( !bMaximize )
1462 SwTwips nRemaining = aRectFnSet.GetTopMargin(*this);
1463 SwFrame *pFrame = m_pLower;
1464 if( pFrame )
1466 if( pFrame->IsColumnFrame() && pFrame->GetNext() )
1468 // #i61435#
1469 // suppress formatting, if upper frame has height <= 0
1470 if ( aRectFnSet.GetHeight(GetUpper()->getFrameArea()) > 0 )
1472 FormatWidthCols( *pAttr, nRemaining, MINLAY );
1474 // #126020# - adjust check for empty section
1475 // #130797# - correct fix #126020#
1476 while( HasFollow() && !GetFollow()->ContainsContent() &&
1477 !GetFollow()->ContainsAny( true ) )
1479 SwFrame* pOld = GetFollow();
1480 GetFollow()->DelEmpty( false );
1481 if( pOld == GetFollow() )
1482 break;
1484 bMaximize = ToMaximize( false );
1485 nRemaining += aRectFnSet.GetHeight(pFrame->getFrameArea());
1487 else
1489 if( pFrame->IsColumnFrame() )
1491 pFrame->Calc(pRenderContext);
1492 pFrame = static_cast<SwColumnFrame*>(pFrame)->Lower();
1493 pFrame->Calc(pRenderContext);
1494 pFrame = static_cast<SwLayoutFrame*>(pFrame)->Lower();
1495 CalcFootnoteContent();
1497 // If we are in a columned frame which calls a CalcContent
1498 // in the FormatWidthCols, the content might need calculating
1499 if( pFrame && !pFrame->isFrameAreaDefinitionValid() && IsInFly() &&
1500 FindFlyFrame()->IsColLocked() )
1501 ::CalcContent( this );
1502 nRemaining += InnerHeight();
1503 bMaximize = HasFollow();
1507 SwTwips nDiff = aRectFnSet.GetHeight(getFrameArea()) - nRemaining;
1508 if( nDiff < 0)
1510 SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
1512 tools::Long nBottom = aRectFnSet.GetBottom(getFrameArea());
1513 nBottom = aRectFnSet.YInc( nBottom, -nDiff );
1514 tools::Long nTmpDiff = aRectFnSet.YDiff( nBottom, nDeadLine );
1515 if( nTmpDiff > 0 )
1517 nTmpDiff = GetUpper()->Grow( nTmpDiff, true );
1518 nDeadLine = aRectFnSet.YInc( nDeadLine, nTmpDiff );
1519 nTmpDiff = aRectFnSet.YDiff( nBottom, nDeadLine );
1520 if( nTmpDiff > 0 )
1521 nDiff += nTmpDiff;
1522 if( nDiff > 0 )
1523 nDiff = 0;
1527 if( nDiff )
1529 tools::Long nTmp = nRemaining - aRectFnSet.GetHeight(getFrameArea());
1530 tools::Long nTop = aRectFnSet.GetTopMargin(*this);
1533 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1534 aRectFnSet.AddBottom( aFrm, nTmp );
1537 aRectFnSet.SetYMargins( *this, nTop, 0 );
1538 InvalidateNextPos();
1540 if (m_pLower && (!m_pLower->IsColumnFrame() || !m_pLower->GetNext()))
1542 // If a single-column section just created the space that
1543 // was requested by the "undersized" paragraphs, then they
1544 // have to be invalidated and calculated, so they fully cover it
1545 pFrame = m_pLower;
1546 if( pFrame->IsColumnFrame() )
1548 pFrame->InvalidateSize_();
1549 pFrame->InvalidatePos_();
1550 pFrame->Calc(pRenderContext);
1551 pFrame = static_cast<SwColumnFrame*>(pFrame)->Lower();
1552 pFrame->Calc(pRenderContext);
1553 pFrame = static_cast<SwLayoutFrame*>(pFrame)->Lower();
1554 CalcFootnoteContent();
1556 bool bUnderSz = false;
1557 while( pFrame )
1559 if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsUndersized() )
1561 pFrame->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
1562 bUnderSz = true;
1564 pFrame = pFrame->GetNext();
1566 if( bUnderSz && !IsContentLocked() )
1567 ::CalcContent( this );
1572 // Do not exceed the lower edge of the Upper.
1573 // Do not extend below the lower edge with Sections with Follows
1574 if ( GetUpper() )
1575 CheckClipping( true, bMaximize );
1576 if( !bOldLock )
1577 ColUnlock();
1578 tools::Long nDiff = nOldHeight - aRectFnSet.GetHeight(getFrameArea());
1580 if( nDiff > 0 )
1582 if( !GetNext() )
1583 SetRetouche(); // Take over the retouching ourselves
1584 if( GetUpper() && !GetUpper()->IsFooterFrame() )
1585 GetUpper()->Shrink( nDiff );
1588 if( IsUndersized() )
1590 setFramePrintAreaValid(true);
1595 /// Returns the next layout sheet where the frame can be moved in.
1596 /// New pages are created only if specified by the parameter.
1597 SwLayoutFrame *SwFrame::GetNextSctLeaf( MakePageType eMakePage )
1599 // Attention: Nested sections are currently not supported
1601 PROTOCOL_ENTER( this, PROT::Leaf, DbgAction::NextSect, GetUpper()->FindSctFrame() )
1603 // Shortcuts for "columned" sections, if we're not in the last column
1604 // Can we slide to the next column of the section?
1605 if( IsColBodyFrame() && GetUpper()->GetNext() )
1606 return static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(GetUpper()->GetNext())->Lower());
1607 if( GetUpper()->IsColBodyFrame() && GetUpper()->GetUpper()->GetNext() )
1608 return static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(GetUpper()->GetUpper()->GetNext())->Lower());
1609 // Inside a table-in-section, or sections of headers/footers, there can be only
1610 // one column shift be made, one of the above shortcuts should have applied!
1611 if( !CanContainSplitSection(GetUpper()) || FindFooterOrHeader() )
1612 return nullptr;
1614 SwSectionFrame *pSect = FindSctFrame();
1615 bool bWrongPage = false;
1616 assert(pSect && "GetNextSctLeaf: Missing SectionFrame");
1618 // Shortcut for sections with Follows. That's ok,
1619 // if no columns or pages (except dummy pages) lie in between.
1620 // In case of linked frames and in footnotes the shortcut would get
1621 // even more costly
1622 if( pSect->HasFollow() && pSect->IsInDocBody() && !pSect->IsInTab() )
1624 if( pSect->GetFollow() == pSect->GetNext() )
1626 SwPageFrame *pPg = pSect->GetFollow()->FindPageFrame();
1627 if( WrongPageDesc( pPg ) )
1628 bWrongPage = true;
1629 else
1630 return FirstLeaf( pSect->GetFollow() );
1632 else
1634 SwFrame* pTmp;
1635 if( !pSect->GetUpper()->IsColBodyFrame() ||
1636 nullptr == ( pTmp = pSect->GetUpper()->GetUpper()->GetNext() ) )
1637 pTmp = pSect->FindPageFrame()->GetNext();
1638 if( pTmp ) // is now the next column or page
1640 SwFrame* pTmpX = pTmp;
1641 if( pTmp->IsPageFrame() && static_cast<SwPageFrame*>(pTmp)->IsEmptyPage() )
1642 pTmp = pTmp->GetNext(); // skip dummy pages
1643 SwFrame *pUp = pSect->GetFollow()->GetUpper();
1644 // pUp becomes the next column if the Follow lies in a column
1645 // that is not a "not first" one, otherwise the page
1646 if( !pUp->IsColBodyFrame() ||
1647 !( pUp = pUp->GetUpper() )->GetPrev() )
1648 pUp = pUp->FindPageFrame();
1649 // Now pUp and pTmp have to be the same page/column, otherwise
1650 // pages or columns lie between Master and Follow
1651 if( pUp == pTmp || pUp->GetNext() == pTmpX )
1653 SwPageFrame* pNxtPg = pUp->IsPageFrame() ?
1654 static_cast<SwPageFrame*>(pUp) : pUp->FindPageFrame();
1655 if( WrongPageDesc( pNxtPg ) )
1656 bWrongPage = true;
1657 else
1658 return FirstLeaf( pSect->GetFollow() );
1664 #ifndef NDEBUG
1665 std::vector<SwFrame *> parents;
1666 for (SwFrame * pTmp = GetUpper(); pTmp && !pTmp->IsPageFrame(); pTmp = pTmp->GetUpper())
1668 parents.push_back(pTmp);
1670 #endif
1672 // Always end up in the same section: Body again inside Body etc.
1673 const bool bBody = IsInDocBody();
1674 const bool bFootnotePage = FindPageFrame()->IsFootnotePage();
1676 // The "pLayLeaf is in a table" case is rejected by default, so that it
1677 // can't happen that we try to move a table to one of its own cells.
1678 bool bLayLeafTableAllowed = false;
1679 SwLayoutFrame *pLayLeaf;
1681 SwLayoutFrame* pCellLeaf = nullptr;
1682 if (GetUpper()->IsInTab())
1684 if (IsTabFrame())
1686 return nullptr; // table in section in table: split disabled for now
1688 // We are *in* a table (not an outermost SwTabFrame), see if there
1689 // is a follow cell frame created already.
1690 pCellLeaf = GetNextCellLeaf();
1691 if (!pCellLeaf)
1693 SAL_WARN("sw.layout", "section is in table, but the table is not split");
1694 return nullptr;
1698 // A shortcut for TabFrames such that not all cells need to be visited
1699 if( bWrongPage )
1700 pLayLeaf = nullptr;
1701 else if( IsTabFrame() )
1703 SwFrame *const pTmpCnt = static_cast<SwTabFrame*>(this)->FindLastContentOrTable();
1704 pLayLeaf = pTmpCnt ? pTmpCnt->GetUpper() : nullptr;
1706 else if (pCellLeaf && CanContainSplitSection(this))
1708 // This frame is in a table-not-in-section, its follow should be
1709 // inserted under the follow of the frame's cell.
1710 pLayLeaf = pCellLeaf;
1711 if (pLayLeaf->FindTabFrame() == FindTabFrame())
1712 SAL_WARN("sw.layout", "my table frame and my follow's table frame is the same");
1713 // In this case pLayLeaf pointing to an in-table frame is OK.
1714 bLayLeafTableAllowed = true;
1716 else
1718 pLayLeaf = GetNextLayoutLeaf();
1719 if( IsColumnFrame() )
1721 while( pLayLeaf && static_cast<SwColumnFrame*>(this)->IsAnLower( pLayLeaf ) )
1722 pLayLeaf = pLayLeaf->GetNextLayoutLeaf();
1726 SwLayoutFrame *pOldLayLeaf = nullptr; // Such that in case of newly
1727 // created pages, the search is
1728 // not started over at the beginning
1730 while( true )
1732 if( pLayLeaf )
1734 // A layout leaf was found, let's see whether it can store me or
1735 // another SectionFrame can be inserted here, or we have to continue
1736 // searching
1737 SwPageFrame* pNxtPg = pLayLeaf->FindPageFrame();
1738 if ( !bFootnotePage && pNxtPg->IsFootnotePage() )
1739 { // If I reached the end note pages it's over
1740 pLayLeaf = nullptr;
1741 continue;
1743 // Once inBody always inBody, don't step into tables-in-sections and not into other sections
1744 if ( (bBody && !pLayLeaf->IsInDocBody()) ||
1745 (IsInFootnote() != pLayLeaf->IsInFootnote() ) ||
1746 (pLayLeaf->IsInTab() && !bLayLeafTableAllowed) ||
1747 ( pLayLeaf->IsInSct() && ( !pSect->HasFollow()
1748 || pSect->GetFollow() != pLayLeaf->FindSctFrame() ) ) )
1750 // Rejected - try again.
1751 pOldLayLeaf = pLayLeaf;
1752 pLayLeaf = pLayLeaf->GetNextLayoutLeaf();
1753 continue;
1755 // Page desc is never wrong in case of sections-in-tables: in that
1756 // case pLayLeaf points to our section's cell's follow, which is
1757 // fine to be on the same page. New page creation is handled when
1758 // creating / moving the cell frame.
1759 // It doesn't make sense to move to a page that starts with break?
1760 if (pNxtPg != FindPageFrame() // tdf#156725 not between columns!
1761 && !FindFlyFrame() // tdf#156419 linked fly frames don't care!
1762 && (WrongPageDesc(pNxtPg) || HasPageBreakBefore(*pNxtPg))
1763 && !bLayLeafTableAllowed)
1765 if( bWrongPage )
1766 break; // there's a column between me and my right page
1767 pLayLeaf = nullptr;
1768 bWrongPage = true;
1769 pOldLayLeaf = nullptr;
1770 continue;
1773 // There is no further LayoutFrame that fits, so a new page
1774 // has to be created, although new pages are worthless within a frame
1775 else if( !pSect->IsInFly() &&
1776 ( eMakePage == MAKEPAGE_APPEND || eMakePage == MAKEPAGE_INSERT ) )
1778 InsertPage(pOldLayLeaf ? pOldLayLeaf->FindPageFrame() : FindPageFrame(),
1779 false );
1780 // and again the whole thing
1781 if (pCellLeaf && CanContainSplitSection(this))
1782 // GetNextLayoutLeaf() would refer to the next cell in the same
1783 // row, avoid that. pCellLeaf points to the correct cell in the
1784 // follow table, and in the next round it'll be used, as we now
1785 // have a next page.
1786 pLayLeaf = pCellLeaf;
1787 else
1788 pLayLeaf = pOldLayLeaf ? pOldLayLeaf : GetNextLayoutLeaf();
1789 continue;
1791 break;
1794 if( pLayLeaf )
1796 // We have found the suitable layout sheet. If there (in the sheet) is
1797 // already a Follow of our section, we take its first layout sheet,
1798 // otherwise it is time to create a section follow
1799 SwSectionFrame* pNew = nullptr;
1801 // This can be omitted if existing Follows were cut short
1802 SwFrame* pFirst = pLayLeaf->Lower();
1803 // Here SectionFrames that are to be deleted must be ignored
1804 while( pFirst && pFirst->IsSctFrame() && !static_cast<SwSectionFrame*>(pFirst)->GetSection() )
1805 pFirst = pFirst->GetNext();
1806 if( pFirst && pFirst->IsSctFrame() && pSect->GetFollow() == pFirst )
1807 pNew = pSect->GetFollow();
1808 else if( MAKEPAGE_NOSECTION == eMakePage )
1809 return pLayLeaf;
1810 else if (pSect->GetSection())
1812 pNew = new SwSectionFrame( *pSect, false );
1813 pNew->InsertBefore( pLayLeaf, pLayLeaf->Lower() );
1814 pNew->Init();
1815 SwRectFnSet aRectFnSet(pNew);
1816 aRectFnSet.MakePos( *pNew, pLayLeaf, nullptr, true );
1818 #ifndef NDEBUG
1819 { // sanity check the parents of the new frame vs. the old frame
1820 SwFrame * pTmp = pNew;
1821 auto iter(parents.begin());
1822 if (parents.size() >= 2 &&
1823 parents[0]->IsBodyFrame() && parents[1]->IsColumnFrame())
1824 { // this only inserts section frame - remove column
1825 assert(parents[2]->IsSctFrame() || IsSctFrame());
1826 if (parents[2]->IsSctFrame())
1827 std::advance(iter, +2);
1828 else
1829 pTmp = pTmp->GetUpper();
1831 else if (IsBodyFrame() && parents.size() >= 1
1832 && parents[0]->IsColumnFrame())
1833 { // same as above, special case: "this" is the body frame
1834 assert(parents[1]->IsSctFrame());
1835 std::advance(iter, +1);
1837 else if (IsSctFrame()) // special case: "this" is the section
1839 pTmp = pTmp->GetUpper();
1842 for ( ; iter != parents.end(); ++iter)
1844 if (pTmp->IsPageFrame())
1846 if ((*iter)->IsColumnFrame() &&
1847 (iter + 1) != parents.end() && (*(iter + 1))->IsBodyFrame())
1848 { // page style has columns - evidently these are
1849 break; // added later?
1851 assert(!pTmp->IsPageFrame());
1853 assert(pTmp->GetType() == (*iter)->GetType());
1854 // for cell frames and table frames:
1855 // 1) there may be multiple follow frames of the old one
1856 // 2) the new frame may be identical to the old one
1857 // (not sure if this is allowed, but it happens now
1858 // for the outer table of a nested table)
1859 if (pTmp->IsCellFrame())
1861 SwCellFrame const*const pNewF(static_cast<SwCellFrame*>(pTmp));
1862 SwCellFrame const*const pOldF(static_cast<SwCellFrame*>(*iter));
1863 bool bFollowFound(false);
1864 for (SwCellFrame const* pOldIter = pOldF;
1865 pOldIter; pOldIter = pOldIter->GetFollowCell())
1867 if (pOldIter == pNewF)
1869 bFollowFound = true;
1870 break;
1873 assert(bFollowFound);
1875 else if (pTmp->IsFlowFrame())
1877 SwFlowFrame const*const pNewF(SwFlowFrame::CastFlowFrame(pTmp));
1878 SwFlowFrame const*const pOldF(SwFlowFrame::CastFlowFrame(*iter));
1879 bool bFollowFound(false);
1880 for (SwFlowFrame const* pOldIter = pOldF;
1881 pOldIter; pOldIter = pOldIter->GetFollow())
1883 if (pOldIter == pNewF)
1885 bFollowFound = true;
1886 break;
1889 assert(bFollowFound);
1891 pTmp = pTmp->GetUpper();
1893 assert(pTmp == nullptr /* SwFlyAtContentFrame case */
1894 || pTmp->IsPageFrame() // usual case
1895 // the new page has columns, but the old page did not
1896 || (pTmp->IsColumnFrame() && pTmp->GetUpper()->IsBodyFrame()
1897 && pTmp->GetUpper()->GetUpper()->IsPageFrame()));
1899 #endif
1901 // If our section frame has a successor then that has to be
1902 // moved behind the new Follow of the section frames
1903 SwFrame* pTmp = pSect->GetNext();
1904 if( pTmp && pTmp != pSect->GetFollow() )
1906 SwFlowFrame* pNxt;
1907 SwContentFrame* pNxtContent = nullptr;
1908 if( pTmp->IsContentFrame() )
1910 pNxt = static_cast<SwContentFrame*>(pTmp);
1911 pNxtContent = static_cast<SwContentFrame*>(pTmp);
1913 else
1915 pNxtContent = static_cast<SwLayoutFrame*>(pTmp)->ContainsContent();
1916 if( pTmp->IsSctFrame() )
1917 pNxt = static_cast<SwSectionFrame*>(pTmp);
1918 else
1920 assert(pTmp->IsTabFrame());
1921 pNxt = static_cast<SwTabFrame*>(pTmp);
1923 while( !pNxtContent && nullptr != ( pTmp = pTmp->GetNext() ) )
1925 if( pTmp->IsContentFrame() )
1926 pNxtContent = static_cast<SwContentFrame*>(pTmp);
1927 else
1928 pNxtContent = static_cast<SwLayoutFrame*>(pTmp)->ContainsContent();
1931 if( pNxtContent )
1933 SwFootnoteBossFrame* pOldBoss = pSect->FindFootnoteBossFrame( true );
1934 if( pOldBoss == pNxtContent->FindFootnoteBossFrame( true ) )
1936 SwSaveFootnoteHeight aHeight( pOldBoss,
1937 pOldBoss->getFrameArea().Top() + pOldBoss->getFrameArea().Height() );
1938 pSect->GetUpper()->MoveLowerFootnotes( pNxtContent, pOldBoss,
1939 pLayLeaf->FindFootnoteBossFrame( true ), false );
1942 pNxt->MoveSubTree( pLayLeaf, pNew->GetNext() );
1944 if( pNew->GetFollow() )
1945 pNew->SimpleFormat();
1947 // The wanted layout sheet is now the first of the determined SctFrames:
1948 pLayLeaf = pNew ? FirstLeaf(pNew) : nullptr;
1950 return pLayLeaf;
1953 /// Returns the preceding layout sheet where the frame can be moved into
1954 SwLayoutFrame *SwFrame::GetPrevSctLeaf()
1956 PROTOCOL_ENTER( this, PROT::Leaf, DbgAction::PrevSect, GetUpper()->FindSctFrame() )
1958 SwLayoutFrame* pCol;
1959 // ColumnFrame always contain a BodyFrame now
1960 if( IsColBodyFrame() )
1961 pCol = GetUpper();
1962 else if( GetUpper()->IsColBodyFrame() )
1963 pCol = GetUpper()->GetUpper();
1964 else
1965 pCol = nullptr;
1966 bool bJump = false;
1967 if( pCol )
1969 if( pCol->GetPrev() )
1973 pCol = static_cast<SwLayoutFrame*>(pCol->GetPrev());
1974 // Is there any content?
1975 if( static_cast<SwLayoutFrame*>(pCol->Lower())->Lower() )
1977 if( bJump ) // Did we skip a blank page?
1978 SwFlowFrame::SetMoveBwdJump( true );
1979 return static_cast<SwLayoutFrame*>(pCol->Lower()); // The columnm body
1981 bJump = true;
1982 } while( pCol->GetPrev() );
1984 // We get here when all columns are empty, pCol is now the
1985 // first column, we need the body though
1986 pCol = static_cast<SwLayoutFrame*>(pCol->Lower());
1988 else
1989 pCol = nullptr;
1992 if( bJump ) // Did we skip a blank page?
1993 SwFlowFrame::SetMoveBwdJump( true );
1995 SwSectionFrame *pSect = FindSctFrame();
1996 if (!pCol && pSect && IsInTab() && CanContainSplitSection(this))
1998 // We don't have a previous section yet, and we're in a
1999 // section-in-table.
2000 if (SwFlowFrame* pPrecede = pSect->GetPrecede())
2002 // Our section has a precede, work with that.
2003 if (pPrecede->GetFrame().IsLayoutFrame())
2004 pCol = static_cast<SwLayoutFrame*>(&pPrecede->GetFrame());
2008 // Within sections in tables or section in headers/footers there can
2009 // be only one column change be made, one of the above shortcuts should
2010 // have applied, also when the section has a pPrev.
2011 // Now we even consider an empty column...
2012 OSL_ENSURE( pSect, "GetNextSctLeaf: Missing SectionFrame" );
2013 if (!pSect || (IsInTab() && !IsTabFrame()) || FindFooterOrHeader())
2014 return pCol;
2016 // === IMPORTANT ===
2017 // Precondition, which needs to be hold, is that the <this> frame can be
2018 // inside a table, but then the found section frame <pSect> is also inside
2019 // this table.
2021 // #i95698#
2022 // A table cell containing directly a section does not break - see lcl_FindSectionsInRow(..)
2023 // Thus, a table inside a section, which is inside another table can only
2024 // flow backward in the columns of its section.
2025 // Note: The table cell, which contains the section, can not have a master table cell.
2026 if ( IsTabFrame() && pSect->IsInTab() )
2028 return pCol;
2032 if (SwFrame *pPrv = pSect->GetIndPrev())
2034 // Mooching, half dead SectionFrames shouldn't confuse us
2035 while( pPrv && pPrv->IsSctFrame() && !static_cast<SwSectionFrame*>(pPrv)->GetSection() )
2036 pPrv = pPrv->GetPrev();
2037 if( pPrv )
2038 return pCol;
2042 const bool bBody = IsInDocBody();
2043 const bool bFly = IsInFly();
2045 SwLayoutFrame *pLayLeaf = GetPrevLayoutLeaf();
2046 SwLayoutFrame *pPrevLeaf = nullptr;
2048 while ( pLayLeaf )
2050 // Never step into tables or sections
2051 if ( pLayLeaf->IsInTab() || pLayLeaf->IsInSct() )
2053 pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
2055 else if ( bBody && pLayLeaf->IsInDocBody() )
2057 // If there is a pLayLeaf has a lower pLayLeaf is the frame we are looking for.
2058 // Exception: pLayLeaf->Lower() is a zombie section frame
2059 const SwFrame* pTmp = pLayLeaf->Lower();
2060 // OD 11.04.2003 #108824# - consider, that the zombie section frame
2061 // can have frame below it in the found layout leaf.
2062 // Thus, skipping zombie section frame, if possible.
2063 while ( pTmp && pTmp->IsSctFrame() &&
2064 !( static_cast<const SwSectionFrame*>(pTmp)->GetSection() ) &&
2065 pTmp->GetNext()
2068 pTmp = pTmp->GetNext();
2070 if ( pTmp &&
2071 ( !pTmp->IsSctFrame() ||
2072 ( static_cast<const SwSectionFrame*>(pTmp)->GetSection() )
2076 break;
2078 pPrevLeaf = pLayLeaf;
2079 pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
2080 if ( pLayLeaf )
2081 SwFlowFrame::SetMoveBwdJump( true );
2083 else if ( bFly )
2084 break; // Contents in Flys every layout sheet should be right. Why?
2085 else
2086 pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
2088 if( !pLayLeaf )
2090 if( !pPrevLeaf )
2091 return pCol;
2092 pLayLeaf = pPrevLeaf;
2095 SwSectionFrame* pNew = nullptr;
2096 // At first go to the end of the layout sheet
2097 SwFrame *pTmp = pLayLeaf->Lower();
2098 if( pTmp )
2100 while( pTmp->GetNext() )
2101 pTmp = pTmp->GetNext();
2102 if( pTmp->IsSctFrame() )
2104 // Half dead ones only interfere here
2105 while( !static_cast<SwSectionFrame*>(pTmp)->GetSection() && pTmp->GetPrev() &&
2106 pTmp->GetPrev()->IsSctFrame() )
2107 pTmp = pTmp->GetPrev();
2108 if( static_cast<SwSectionFrame*>(pTmp)->GetFollow() == pSect )
2109 pNew = static_cast<SwSectionFrame*>(pTmp);
2112 if( !pNew )
2114 pNew = new SwSectionFrame( *pSect, true );
2115 pNew->InsertBefore( pLayLeaf, nullptr );
2116 pNew->Init();
2117 SwRectFnSet aRectFnSet(pNew);
2118 aRectFnSet.MakePos( *pNew, pLayLeaf, pNew->GetPrev(), true );
2120 pLayLeaf = FirstLeaf( pNew );
2121 if( !pNew->Lower() ) // Format single column sections
2123 pNew->MakePos();
2124 pLayLeaf->Format(getRootFrame()->GetCurrShell()->GetOut()); // In order that the PrtArea is correct for the MoveBwd
2126 else
2127 pNew->SimpleFormat();
2129 else
2131 pLayLeaf = FirstLeaf( pNew );
2132 if( pLayLeaf->IsColBodyFrame() )
2134 // In existent section columns we're looking for the last not empty
2135 // column.
2136 SwLayoutFrame *pTmpLay = pLayLeaf;
2137 while( pLayLeaf->GetUpper()->GetNext() )
2139 pLayLeaf = static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(pLayLeaf->GetUpper()->GetNext())->Lower());
2140 if( pLayLeaf->Lower() )
2141 pTmpLay = pLayLeaf;
2143 // If we skipped an empty column, we've to set the jump-flag
2144 if( pLayLeaf != pTmpLay )
2146 pLayLeaf = pTmpLay;
2147 SwFlowFrame::SetMoveBwdJump( true );
2151 return pLayLeaf;
2154 static SwTwips lcl_DeadLine( const SwFrame* pFrame )
2156 const SwLayoutFrame* pUp = pFrame->GetUpper();
2157 while( pUp && pUp->IsInSct() )
2159 if( pUp->IsSctFrame() )
2160 pUp = pUp->GetUpper();
2161 // Columns now with BodyFrame
2162 else if( pUp->IsColBodyFrame() && pUp->GetUpper()->GetUpper()->IsSctFrame() )
2163 pUp = pUp->GetUpper()->GetUpper();
2164 else
2165 break;
2167 SwRectFnSet aRectFnSet(pFrame);
2168 return pUp ? aRectFnSet.GetPrtBottom(*pUp) :
2169 aRectFnSet.GetBottom(pFrame->getFrameArea());
2172 /// checks whether the SectionFrame is still able to grow, as case may be the environment has to be asked
2173 bool SwSectionFrame::Growable() const
2175 SwRectFnSet aRectFnSet(this);
2176 if( aRectFnSet.YDiff( lcl_DeadLine( this ),
2177 aRectFnSet.GetBottom(getFrameArea()) ) > 0 )
2178 return true;
2180 return ( GetUpper() && const_cast<SwFrame*>(static_cast<SwFrame const *>(GetUpper()))->Grow( LONG_MAX, true ) );
2183 SwTwips SwSectionFrame::Grow_( SwTwips nDist, bool bTst )
2185 if ( !IsColLocked() && !HasFixSize() )
2187 SwRectFnSet aRectFnSet(this);
2188 tools::Long nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
2189 if( nFrameHeight > 0 && nDist > (LONG_MAX - nFrameHeight) )
2190 nDist = LONG_MAX - nFrameHeight;
2192 if ( nDist <= 0 )
2193 return 0;
2195 bool bInCalcContent = GetUpper() && IsInFly() && FindFlyFrame()->IsLocked();
2196 // OD 2004-03-15 #116561# - allow grow in online layout
2197 bool bGrow = !Lower() || !Lower()->IsColumnFrame() || !Lower()->GetNext();
2198 if (!bGrow)
2200 SwSection* pSection = GetSection();
2201 bGrow = pSection && pSection->GetFormat()->GetBalancedColumns().GetValue();
2203 if( !bGrow )
2205 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
2206 bGrow = pSh && pSh->GetViewOptions()->getBrowseMode();
2208 if( bGrow )
2210 SwTwips nGrow;
2211 if( IsInFootnote() )
2212 nGrow = 0;
2213 else
2215 nGrow = lcl_DeadLine( this );
2216 nGrow = aRectFnSet.YDiff( nGrow, aRectFnSet.GetBottom(getFrameArea()) );
2218 SwTwips nSpace = nGrow;
2219 if( !bInCalcContent && nGrow < nDist && GetUpper() )
2220 nGrow = o3tl::saturating_add(
2221 nGrow, GetUpper()->Grow( LONG_MAX, true ));
2223 if( nGrow > nDist )
2224 nGrow = nDist;
2225 if( nGrow <= 0 )
2227 nGrow = 0;
2228 if (!bTst)
2230 if( bInCalcContent )
2231 InvalidateSize_();
2232 else
2233 InvalidateSize();
2236 else if( !bTst )
2238 if( bInCalcContent )
2239 InvalidateSize_();
2240 else if( nSpace < nGrow && nDist != nSpace + GetUpper()->
2241 Grow( nGrow - nSpace ) )
2242 InvalidateSize();
2243 else
2245 const SvxGraphicPosition ePos =
2246 GetAttrSet()->GetBackground().GetGraphicPos();
2247 if ( GPOS_RT < ePos && GPOS_TILED != ePos )
2249 SetCompletePaint();
2250 InvalidatePage();
2252 if( GetUpper() && GetUpper()->IsHeaderFrame() )
2253 GetUpper()->InvalidateSize();
2257 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
2258 aRectFnSet.AddBottom( aFrm, nGrow );
2262 const tools::Long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea()) + nGrow;
2263 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
2264 aRectFnSet.SetHeight( aPrt, nPrtHeight );
2267 if( Lower() && Lower()->IsColumnFrame() && Lower()->GetNext() )
2269 SwFrame* pTmp = Lower();
2272 pTmp->InvalidateSize_();
2273 pTmp = pTmp->GetNext();
2274 } while ( pTmp );
2275 InvalidateSize_();
2277 if( GetNext() )
2279 // Own height changed, need to invalidate the position of
2280 // next frames.
2281 SwFrame *pFrame = GetNext();
2282 while( pFrame && pFrame->IsSctFrame() && !static_cast<SwSectionFrame*>(pFrame)->GetSection() )
2284 // Invalidate all in-between frames, otherwise position
2285 // calculation (which only looks back to one relative
2286 // frame) will have an incorrect result.
2287 InvalidateFramePos(pFrame, bInCalcContent);
2288 pFrame = pFrame->GetNext();
2290 if( pFrame )
2292 InvalidateFramePos(pFrame, bInCalcContent);
2295 // #i28701# - Due to the new object positioning
2296 // the frame on the next page/column can flow backward (e.g. it
2297 // was moved forward due to the positioning of its objects ).
2298 // Thus, invalivate this next frame, if document compatibility
2299 // option 'Consider wrapping style influence on object positioning' is ON.
2300 else if ( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
2302 InvalidateNextPos();
2305 return nGrow;
2307 if ( !bTst )
2309 if( bInCalcContent )
2310 InvalidateSize_();
2311 else
2312 InvalidateSize();
2315 return 0;
2318 SwTwips SwSectionFrame::Shrink_( SwTwips nDist, bool bTst )
2320 if ( Lower() && !IsColLocked() && !HasFixSize() )
2322 if( ToMaximize( false ) )
2324 if( !bTst )
2325 InvalidateSize();
2327 else
2329 SwRectFnSet aRectFnSet(this);
2330 tools::Long nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
2331 if ( nDist > nFrameHeight )
2332 nDist = nFrameHeight;
2334 if ( Lower()->IsColumnFrame() && Lower()->GetNext() && // FootnoteAtEnd
2335 !GetSection()->GetFormat()->GetBalancedColumns().GetValue() )
2336 { // With column bases the format takes over the control of the
2337 // growth (because of the balance)
2338 if ( !bTst )
2339 InvalidateSize();
2340 return nDist;
2342 else if( !bTst )
2344 const SvxGraphicPosition ePos =
2345 GetAttrSet()->GetBackground().GetGraphicPos();
2346 if ( GPOS_RT < ePos && GPOS_TILED != ePos )
2348 SetCompletePaint();
2349 InvalidatePage();
2353 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
2354 aRectFnSet.AddBottom( aFrm, -nDist );
2358 const tools::Long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea()) - nDist;
2359 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
2360 aRectFnSet.SetHeight( aPrt, nPrtHeight );
2363 // We do not allow a section frame to shrink the its upper
2364 // footer frame. This is because in the calculation of a
2365 // footer frame, the content of the section frame is _not_
2366 // calculated. If there is a fly frame overlapping with the
2367 // footer frame, the section frame is not affected by this
2368 // during the calculation of the footer frame size.
2369 // The footer frame does not grow in its FormatSize function
2370 // but during the calculation of the content of the section
2371 // frame. The section frame grows until some of its text is
2372 // located on top of the fly frame. The next call of CalcContent
2373 // tries to shrink the section and here it would also shrink
2374 // the footer. This may not happen, because shrinking the footer
2375 // would cause the top of the section frame to overlap with the
2376 // fly frame again, this would result in a perfect loop.
2377 if( GetUpper() && !GetUpper()->IsFooterFrame() )
2378 GetUpper()->Shrink( nDist, bTst );
2380 if( Lower() && Lower()->IsColumnFrame() && Lower()->GetNext() )
2382 SwFrame* pTmp = Lower();
2385 pTmp->InvalidateSize_();
2386 pTmp = pTmp->GetNext();
2387 } while ( pTmp );
2389 if( GetNext() )
2391 SwFrame* pFrame = GetNext();
2392 while( pFrame && pFrame->IsSctFrame() && !static_cast<SwSectionFrame*>(pFrame)->GetSection() )
2393 pFrame = pFrame->GetNext();
2394 if( pFrame )
2395 pFrame->InvalidatePos();
2396 else
2397 SetRetouche();
2399 else
2400 SetRetouche();
2401 return nDist;
2405 return 0;
2409 |* When are Frames within a SectionFrames moveable?
2410 |* If they are not in the last column of a SectionFrames yet,
2411 |* if there is no Follow,
2412 |* if the SectionFrame cannot grow anymore, then it gets more complicated,
2413 |* in that case it depends on whether the SectionFrame can find a next
2414 |* layout sheet. In (column based/chained) Flys this is checked via
2415 |* GetNextLayout, in tables and headers/footers there is none, however in the
2416 |* DocBody and in foot notes there is always one.
2418 |* This routine is used in the TextFormatter to decided whether it's allowed to
2419 |* create a (paragraph-)Follow or whether the paragraph has to stick together
2421 bool SwSectionFrame::MoveAllowed( const SwFrame* pFrame) const
2423 // Is there a Follow or is the Frame not in the last column?
2424 if( HasFollow() || ( pFrame->GetUpper()->IsColBodyFrame() &&
2425 pFrame->GetUpper()->GetUpper()->GetNext() ) )
2426 return true;
2427 if( pFrame->IsInFootnote() )
2429 if( IsInFootnote() )
2431 if( GetUpper()->IsInSct() )
2433 if( Growable() )
2434 return false;
2435 return GetUpper()->FindSctFrame()->MoveAllowed( this );
2437 else
2438 return true;
2440 // The content of footnote inside a columned sectionfrm is moveable
2441 // except in the last column
2442 const SwLayoutFrame *pLay = pFrame->FindFootnoteFrame()->GetUpper()->GetUpper();
2443 if( pLay->IsColumnFrame() && pLay->GetNext() )
2445 // The first paragraph in the first footnote in the first column
2446 // in the sectionfrm at the top of the page is not moveable,
2447 // if the columnbody is empty.
2448 bool bRet = false;
2449 if( pLay->GetIndPrev() || pFrame->GetIndPrev() ||
2450 pFrame->FindFootnoteFrame()->GetPrev() )
2451 bRet = true;
2452 else
2454 const SwLayoutFrame* pBody = static_cast<const SwColumnFrame*>(pLay)->FindBodyCont();
2455 if( pBody && pBody->Lower() )
2456 bRet = true;
2458 if( bRet && ( IsFootnoteAtEnd() || !Growable() ) )
2459 return true;
2462 // Or can the section still grow?
2463 if( !IsColLocked() && Growable() )
2464 return false;
2465 // Now it has to be examined whether there is a layout sheet wherein
2466 // a section Follow can be created
2467 if( !CanContainSplitSection(this) || ( !IsInDocBody() && FindFooterOrHeader() ) )
2468 return false; // It doesn't work in table-in-sections/nested tables/headers/footers
2469 if( IsInFly() ) // In column based or chained frames
2470 return nullptr != const_cast<SwFrame*>(static_cast<SwFrame const *>(GetUpper()))->GetNextLeaf( MAKEPAGE_NONE );
2471 return true;
2474 /** Called for a frame inside a section with no direct previous frame (or only
2475 previous empty section frames) the previous frame of the outer section is
2476 returned, if the frame is the first flowing content of this section.
2478 Note: For a frame inside a table frame, which is inside a section frame,
2479 NULL is returned.
2481 SwFrame* SwFrame::GetIndPrev_() const
2483 SwFrame *pRet = nullptr;
2484 // #i79774#
2485 // Do not assert, if the frame has a direct previous frame, because it
2486 // could be an empty section frame. The caller has to assure, that the
2487 // frame has no direct previous frame or only empty section frames as
2488 // previous frames.
2489 OSL_ENSURE( /*!pPrev &&*/ IsInSct(), "Why?" );
2490 const SwFrame* pSct = GetUpper();
2491 if( !pSct )
2492 return nullptr;
2493 if( pSct->IsSctFrame() )
2494 pRet = pSct->GetIndPrev();
2495 else if( pSct->IsColBodyFrame() && (pSct = pSct->GetUpper()->GetUpper())->IsSctFrame() )
2497 // Do not return the previous frame of the outer section, if in one
2498 // of the previous columns is content.
2499 const SwFrame* pCol = GetUpper()->GetUpper()->GetPrev();
2500 while( pCol )
2502 assert(pCol->IsColumnFrame());
2503 assert(pCol->GetLower() && pCol->GetLower()->IsBodyFrame());
2504 if( static_cast<const SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pCol)->Lower())->Lower() )
2505 return nullptr;
2506 pCol = pCol->GetPrev();
2508 pRet = pSct->GetIndPrev();
2511 // skip empty section frames
2512 while( pRet && pRet->IsSctFrame() && !static_cast<SwSectionFrame*>(pRet)->GetSection() )
2513 pRet = pRet->GetIndPrev();
2514 return pRet;
2517 SwFrame* SwFrame::GetIndNext_()
2519 OSL_ENSURE( !mpNext && IsInSct(), "Why?" );
2520 SwFrame* pSct = GetUpper();
2521 if( !pSct )
2522 return nullptr;
2523 if( pSct->IsSctFrame() )
2524 return pSct->GetIndNext();
2525 if( pSct->IsColBodyFrame() && (pSct = pSct->GetUpper()->GetUpper())->IsSctFrame() )
2526 { // We can only return the successor of the SectionFrames if there is no
2527 // content in the successive columns
2528 SwFrame* pCol = GetUpper()->GetUpper()->GetNext();
2529 while( pCol )
2531 assert(pCol->IsColumnFrame());
2532 assert(pCol->GetLower() && pCol->GetLower()->IsBodyFrame());
2533 if( static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(pCol)->Lower())->Lower() )
2534 return nullptr;
2535 pCol = pCol->GetNext();
2537 return pSct->GetIndNext();
2539 return nullptr;
2542 bool SwSectionFrame::IsDescendantFrom( const SwSectionFormat* pFormat ) const
2544 if( !m_pSection || !pFormat )
2545 return false;
2546 const SwSectionFormat *pMyFormat = m_pSection->GetFormat();
2547 while( pFormat != pMyFormat )
2549 if( auto pNewFormat = dynamic_cast< const SwSectionFormat *>( pMyFormat->GetRegisteredIn()) )
2550 pMyFormat = pNewFormat;
2551 else
2552 return false;
2554 return true;
2557 void SwSectionFrame::CalcFootnoteAtEndFlag()
2559 SwSectionFormat *pFormat = GetSection()->GetFormat();
2560 sal_uInt16 nVal = pFormat->GetFootnoteAtTextEnd( false ).GetValue();
2561 m_bFootnoteAtEnd = FTNEND_ATPGORDOCEND != nVal;
2562 m_bOwnFootnoteNum = FTNEND_ATTXTEND_OWNNUMSEQ == nVal ||
2563 FTNEND_ATTXTEND_OWNNUMANDFMT == nVal;
2564 while( !m_bFootnoteAtEnd && !m_bOwnFootnoteNum )
2566 if( auto pNewFormat = dynamic_cast<SwSectionFormat *>( pFormat->GetRegisteredIn()) )
2567 pFormat = pNewFormat;
2568 else
2569 break;
2570 nVal = pFormat->GetFootnoteAtTextEnd( false ).GetValue();
2571 if( FTNEND_ATPGORDOCEND != nVal )
2573 m_bFootnoteAtEnd = true;
2574 m_bOwnFootnoteNum = m_bOwnFootnoteNum ||FTNEND_ATTXTEND_OWNNUMSEQ == nVal ||
2575 FTNEND_ATTXTEND_OWNNUMANDFMT == nVal;
2580 bool SwSectionFrame::IsEndnoteAtMyEnd() const
2582 return m_pSection->GetFormat()->GetEndAtTextEnd( false ).IsAtEnd();
2585 void SwSectionFrame::CalcEndAtEndFlag()
2587 SwSectionFormat *pFormat = GetSection()->GetFormat();
2588 m_bEndnAtEnd = pFormat->GetEndAtTextEnd( false ).IsAtEnd();
2589 while( !m_bEndnAtEnd )
2591 if( auto pNewFormat = dynamic_cast<SwSectionFormat *>( pFormat->GetRegisteredIn()) )
2592 pFormat = pNewFormat;
2593 else
2594 break;
2595 m_bEndnAtEnd = pFormat->GetEndAtTextEnd( false ).IsAtEnd();
2599 void SwSectionFrame::Notify(SfxHint const& rHint)
2601 SwSectionFormat *const pFormat(GetSection()->GetFormat());
2602 assert(pFormat);
2603 SwClientNotify(*pFormat, rHint);
2606 void SwSectionFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
2608 if (rHint.GetId() == SfxHintId::SwLegacyModify)
2610 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
2611 SwSectionFrameInvFlags eInvFlags = SwSectionFrameInvFlags::NONE;
2612 if(pLegacy->m_pNew && RES_ATTRSET_CHG == pLegacy->m_pNew->Which())
2614 auto& rOldSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pOld);
2615 auto& rNewSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew);
2616 SfxItemIter aOIter(*rOldSetChg.GetChgSet());
2617 SfxItemIter aNIter(*rNewSetChg.GetChgSet());
2618 const SfxPoolItem* pOItem = aOIter.GetCurItem();
2619 const SfxPoolItem* pNItem = aNIter.GetCurItem();
2620 SwAttrSetChg aOldSet(rOldSetChg);
2621 SwAttrSetChg aNewSet(rNewSetChg);
2624 UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
2625 pNItem = aNIter.NextItem();
2626 pOItem = aOIter.NextItem();
2627 } while (pNItem);
2628 if(aOldSet.Count() || aNewSet.Count())
2629 SwLayoutFrame::SwClientNotify(rMod, sw::LegacyModifyHint(&aOldSet, &aNewSet));
2631 else
2632 UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
2634 if (eInvFlags != SwSectionFrameInvFlags::NONE)
2636 if(eInvFlags & SwSectionFrameInvFlags::InvalidateSize)
2637 InvalidateSize();
2638 if(eInvFlags & SwSectionFrameInvFlags::SetCompletePaint)
2639 SetCompletePaint();
2642 else if(const auto pHint = dynamic_cast<const SwSectionFrameMoveAndDeleteHint*>(&rHint))
2644 // #i117863#
2645 if(&rMod != GetDep())
2646 return;
2647 SwSectionFrame::MoveContentAndDelete(this, pHint->IsSaveContent());
2649 else
2650 SwFrame::SwClientNotify(rMod, rHint);
2653 void SwSectionFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
2654 SwSectionFrameInvFlags &rInvFlags,
2655 SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
2657 bool bClear = true;
2658 const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
2659 switch( nWhich )
2660 { // Suppress multi columns in foot notes
2661 case RES_FMT_CHG:
2663 const SwFormatCol& rNewCol = GetFormat()->GetCol();
2664 if( !IsInFootnote() )
2666 // Nasty case. When allocating a template we can not count
2667 // on the old column attribute. We're left with creating a
2668 // temporary attribute here.
2669 SwFormatCol aCol;
2670 if ( Lower() && Lower()->IsColumnFrame() )
2672 sal_uInt16 nCol = 0;
2673 SwFrame *pTmp = Lower();
2675 { ++nCol;
2676 pTmp = pTmp->GetNext();
2677 } while ( pTmp );
2678 aCol.Init( nCol, 0, 1000 );
2680 bool bChgFootnote = IsFootnoteAtEnd();
2681 bool const bChgEndn = IsEndnAtEnd();
2682 bool const bChgMyEndn = IsEndnoteAtMyEnd();
2683 CalcFootnoteAtEndFlag();
2684 CalcEndAtEndFlag();
2685 bChgFootnote = ( bChgFootnote != IsFootnoteAtEnd() ) ||
2686 ( bChgEndn != IsEndnAtEnd() ) ||
2687 ( bChgMyEndn != IsEndnoteAtMyEnd() );
2688 ChgColumns( aCol, rNewCol, bChgFootnote );
2689 rInvFlags |= SwSectionFrameInvFlags::SetCompletePaint;
2691 rInvFlags |= SwSectionFrameInvFlags::InvalidateSize;
2692 bClear = false;
2694 break;
2696 case RES_COL:
2697 if( !IsInFootnote() )
2699 assert(pOld && pNew);
2700 if (pOld && pNew)
2702 ChgColumns( *static_cast<const SwFormatCol*>(pOld), *static_cast<const SwFormatCol*>(pNew) );
2703 rInvFlags |= SwSectionFrameInvFlags::InvalidateSize | SwSectionFrameInvFlags::SetCompletePaint;
2706 break;
2708 case RES_FTN_AT_TXTEND:
2709 if( !IsInFootnote() )
2711 bool const bOld = IsFootnoteAtEnd();
2712 CalcFootnoteAtEndFlag();
2713 if (bOld != IsFootnoteAtEnd())
2715 const SwFormatCol& rNewCol = GetFormat()->GetCol();
2716 ChgColumns( rNewCol, rNewCol, true );
2717 rInvFlags |= SwSectionFrameInvFlags::InvalidateSize;
2720 break;
2722 case RES_END_AT_TXTEND:
2723 if( !IsInFootnote() )
2725 bool const bOld = IsEndnAtEnd();
2726 bool const bMyOld = IsEndnoteAtMyEnd();
2727 CalcEndAtEndFlag();
2728 if (bOld != IsEndnAtEnd() || bMyOld != IsEndnoteAtMyEnd())
2730 const SwFormatCol& rNewCol = GetFormat()->GetCol();
2731 ChgColumns( rNewCol, rNewCol, true );
2732 rInvFlags |= SwSectionFrameInvFlags::InvalidateSize;
2735 break;
2736 case RES_COLUMNBALANCE:
2737 rInvFlags |= SwSectionFrameInvFlags::InvalidateSize;
2738 break;
2740 case RES_FRAMEDIR :
2741 SetDerivedR2L( false );
2742 CheckDirChange();
2743 break;
2745 case RES_PROTECT:
2746 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
2748 SwViewShell *pSh = getRootFrame()->GetCurrShell();
2749 if( pSh && pSh->GetLayout()->IsAnyShellAccessible() )
2750 pSh->Imp()->InvalidateAccessibleEditableState( true, this );
2752 #endif
2753 break;
2755 default:
2756 bClear = false;
2758 if ( !bClear )
2759 return;
2761 if ( pOldSet || pNewSet )
2763 if ( pOldSet )
2764 pOldSet->ClearItem( nWhich );
2765 if ( pNewSet )
2766 pNewSet->ClearItem( nWhich );
2768 else
2770 SwModify aMod;
2771 SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
2775 /// A follow or a ftncontainer at the end of the page causes a maximal Size of the sectionframe.
2776 bool SwSectionFrame::ToMaximize( bool bCheckFollow ) const
2778 if( HasFollow() )
2780 if( !bCheckFollow ) // Don't check superfluous follows
2781 return true;
2782 const SwSectionFrame* pFoll = GetFollow();
2783 while( pFoll && pFoll->IsSuperfluous() )
2784 pFoll = pFoll->GetFollow();
2785 if( pFoll )
2786 return true;
2788 if( IsFootnoteAtEnd() )
2789 return false;
2790 const SwFootnoteContFrame* pCont = ContainsFootnoteCont();
2791 if( !IsEndnAtEnd() )
2792 return nullptr != pCont;
2793 bool bRet = false;
2794 while( pCont && !bRet )
2796 if( pCont->FindFootNote() )
2797 bRet = true;
2798 else
2799 pCont = ContainsFootnoteCont( pCont );
2801 return bRet;
2804 /// Check every Column for FootnoteContFrames.
2805 SwFootnoteContFrame* SwSectionFrame::ContainsFootnoteCont( const SwFootnoteContFrame* pCont ) const
2807 SwFootnoteContFrame* pRet = nullptr;
2808 const SwLayoutFrame* pLay;
2809 if( pCont )
2811 pLay = pCont->FindFootnoteBossFrame();
2812 OSL_ENSURE( IsAnLower( pLay ), "ContainsFootnoteCont: Wrong FootnoteContainer" );
2813 pLay = static_cast<const SwLayoutFrame*>(pLay->GetNext());
2815 else if( Lower() && Lower()->IsColumnFrame() )
2816 pLay = static_cast<const SwLayoutFrame*>(Lower());
2817 else
2818 pLay = nullptr;
2819 while ( !pRet && pLay )
2821 if( pLay->Lower() && pLay->Lower()->GetNext() )
2823 OSL_ENSURE( pLay->Lower()->GetNext()->IsFootnoteContFrame(),
2824 "ToMaximize: Unexpected Frame" );
2825 pRet = const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pLay->Lower()->GetNext()));
2827 OSL_ENSURE( !pLay->GetNext() || pLay->GetNext()->IsLayoutFrame(),
2828 "ToMaximize: ColFrame expected" );
2829 pLay = static_cast<const SwLayoutFrame*>(pLay->GetNext());
2831 return pRet;
2834 void SwSectionFrame::InvalidateFootnotePos()
2836 SwFootnoteContFrame* pCont = ContainsFootnoteCont();
2837 if( pCont )
2839 SwFrame *pTmp = pCont->ContainsContent();
2840 if( pTmp )
2841 pTmp->InvalidatePos_();
2845 SwTwips SwSectionFrame::CalcUndersize() const
2847 SwRectFnSet aRectFnSet(this);
2848 return InnerHeight() - aRectFnSet.GetHeight(getFramePrintArea());
2851 SwTwips SwSectionFrame::Undersize()
2853 const auto nRet = CalcUndersize();
2854 m_bUndersized = (nRet > 0);
2855 return nRet <= 0 ? 0 : nRet;
2858 void SwSectionFrame::CalcFootnoteContent()
2860 vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
2861 SwFootnoteContFrame* pCont = ContainsFootnoteCont();
2862 if( !pCont )
2863 return;
2865 SwFrame* pFrame = pCont->ContainsAny();
2866 if( pFrame )
2867 pCont->Calc(pRenderContext);
2868 while( pFrame && IsAnLower( pFrame ) )
2870 SwFootnoteFrame* pFootnote = pFrame->FindFootnoteFrame();
2871 if( pFootnote )
2872 pFootnote->Calc(pRenderContext);
2873 pFrame->Calc(pRenderContext);
2874 if( pFrame->IsSctFrame() )
2876 SwFrame *pTmp = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
2877 if( pTmp )
2879 pFrame = pTmp;
2880 continue;
2883 pFrame = pFrame->FindNext();
2888 * If a SectionFrame gets empty, e.g. because its content changes the page/column,
2889 * it is not destroyed immediately (there could be a pointer left to it on the
2890 * stack), instead it puts itself in a list at the RootFrame, which is processed
2891 * later on (in Layaction::Action among others). Its size is set to Null and
2892 * the pointer to its page as well. Such SectionFrames that are to be deleted
2893 * must be ignored by the layout/during formatting.
2895 * With InsertEmptySct the RootFrame stores a SectionFrame in the list,
2896 * with RemoveFromList it can be removed from the list (Dtor),
2897 * with DeleteEmptySct the list is processed and the SectionFrames are destroyed.
2899 void SwRootFrame::InsertEmptySct( SwSectionFrame* pDel )
2901 if( !mpDestroy )
2902 mpDestroy.reset( new SwDestroyList );
2903 mpDestroy->insert( pDel );
2906 void SwRootFrame::DeleteEmptySct_()
2908 assert(mpDestroy);
2909 while( !mpDestroy->empty() )
2911 SwSectionFrame* pSect = *mpDestroy->begin();
2912 mpDestroy->erase( mpDestroy->begin() );
2913 OSL_ENSURE( !pSect->IsColLocked() && !pSect->IsJoinLocked(),
2914 "DeleteEmptySct: Locked SectionFrame" );
2915 SAL_WARN_IF(pSect->IsDeleteForbidden(), "sw.layout", "not allowed delete SwFrame");
2916 if( !pSect->getFrameArea().HasArea() && !pSect->ContainsContent() && !pSect->IsDeleteForbidden() )
2918 SwLayoutFrame* pUp = pSect->GetUpper();
2919 pSect->RemoveFromLayout();
2920 SwFrame::DestroyFrame(pSect);
2921 if( pUp && !pUp->Lower() )
2923 if( pUp->IsPageBodyFrame() )
2924 pUp->getRootFrame()->SetSuperfluous();
2925 else if( pUp->IsFootnoteFrame() && !pUp->IsColLocked() &&
2926 pUp->GetUpper() )
2928 pUp->Cut();
2929 SwFrame::DestroyFrame(pUp);
2933 else {
2934 OSL_ENSURE( pSect->GetSection(), "DeleteEmptySct: Half-dead SectionFrame?!" );
2939 void SwRootFrame::RemoveFromList_( SwSectionFrame* pSct )
2941 assert(mpDestroy && "Where's my list?");
2942 mpDestroy->erase( pSct );
2945 #ifdef DBG_UTIL
2946 bool SwRootFrame::IsInDelList( SwSectionFrame* pSct ) const
2948 return mpDestroy && mpDestroy->find( pSct ) != mpDestroy->end();
2950 #endif
2952 bool SwSectionFrame::IsBalancedSection() const
2954 bool bRet = false;
2955 if ( GetSection() && Lower() && Lower()->IsColumnFrame() && Lower()->GetNext() )
2957 bRet = !GetSection()->GetFormat()->GetBalancedColumns().GetValue();
2959 return bRet;
2962 void SwSectionFrame::dumpAsXml(xmlTextWriterPtr writer) const
2964 (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("section"));
2965 dumpAsXmlAttributes(writer);
2967 (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
2968 dumpInfosAsXml(writer);
2969 (void)xmlTextWriterEndElement(writer);
2970 dumpChildrenAsXml(writer);
2972 (void)xmlTextWriterEndElement(writer);
2975 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */