diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /sw/source/core/layout/sectfrm.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/core/layout/sectfrm.cxx')
-rw-r--r-- | sw/source/core/layout/sectfrm.cxx | 2916 |
1 files changed, 2916 insertions, 0 deletions
diff --git a/sw/source/core/layout/sectfrm.cxx b/sw/source/core/layout/sectfrm.cxx new file mode 100644 index 000000000..2ad9d79e5 --- /dev/null +++ b/sw/source/core/layout/sectfrm.cxx @@ -0,0 +1,2916 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> +#include <sal/log.hxx> + +#include <o3tl/safeint.hxx> +#include <svl/itemiter.hxx> +#include <txtftn.hxx> +#include <fmtftn.hxx> +#include <fmtclbl.hxx> +#include <sectfrm.hxx> +#include <cellfrm.hxx> +#include <section.hxx> +#include <IDocumentSettingAccess.hxx> +#include <rootfrm.hxx> +#include <pagefrm.hxx> +#include <txtfrm.hxx> +#include <fmtclds.hxx> +#include <colfrm.hxx> +#include <tabfrm.hxx> +#include <ftnfrm.hxx> +#include <layouter.hxx> +#include <dbg_lay.hxx> +#include <viewopt.hxx> +#include <viewimp.hxx> +#include <editeng/brushitem.hxx> +#include <fmtftntx.hxx> +#include <flyfrm.hxx> +#include <sortedobjs.hxx> +#include <hints.hxx> +#include <frmatr.hxx> +#include <frmtool.hxx> + +namespace +{ +/** + * Performs the correct type of position invalidation depending on if we're in + * CalcContent(). + */ +void InvalidateFramePos(SwFrame* pFrame, bool bInCalcContent) +{ + if (bInCalcContent) + pFrame->InvalidatePos_(); + else + pFrame->InvalidatePos(); +} +} + +SwSectionFrame::SwSectionFrame( SwSection &rSect, SwFrame* pSib ) + : SwLayoutFrame( rSect.GetFormat(), pSib ) + , SwFlowFrame( static_cast<SwFrame&>(*this) ) + , m_pSection( &rSect ) + , m_bFootnoteAtEnd(false) + , m_bEndnAtEnd(false) + , m_bContentLock(false) + , m_bOwnFootnoteNum(false) + , m_bFootnoteLock(false) +{ + mnFrameType = SwFrameType::Section; + + CalcFootnoteAtEndFlag(); + CalcEndAtEndFlag(); +} + +SwSectionFrame::SwSectionFrame( SwSectionFrame &rSect, bool bMaster ) : + SwLayoutFrame( rSect.GetFormat(), rSect.getRootFrame() ), + SwFlowFrame( static_cast<SwFrame&>(*this) ), + m_pSection( rSect.GetSection() ), + m_bFootnoteAtEnd( rSect.IsFootnoteAtEnd() ), + m_bEndnAtEnd( rSect.IsEndnAtEnd() ), + m_bContentLock( false ), + m_bOwnFootnoteNum( false ), + m_bFootnoteLock( false ) +{ + mnFrameType = SwFrameType::Section; + + PROTOCOL( this, PROT::Section, bMaster ? DbgAction::CreateMaster : DbgAction::CreateFollow, &rSect ) + + if( bMaster ) + { + SwSectionFrame* pMaster = rSect.IsFollow() ? rSect.FindMaster() : nullptr; + if (pMaster) + pMaster->SetFollow( this ); + SetFollow( &rSect ); + } + else + { + SetFollow( rSect.GetFollow() ); + rSect.SetFollow( this ); + if( !GetFollow() ) + rSect.SimpleFormat(); + if( !rSect.IsColLocked() ) + rSect.InvalidateSize(); + } +} + +// NOTE: call <SwSectionFrame::Init()> directly after creation of a new section +// frame and its insert in the layout. +void SwSectionFrame::Init() +{ + assert(GetUpper() && "SwSectionFrame::Init before insertion?!"); + SwRectFnSet aRectFnSet(this); + long nWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()); + + { + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this); + aRectFnSet.SetWidth( aFrm, nWidth ); + aRectFnSet.SetHeight( aFrm, 0 ); + } + + // #109700# LRSpace for sections + const SvxLRSpaceItem& rLRSpace = GetFormat()->GetLRSpace(); + + { + SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this); + aRectFnSet.SetLeft( aPrt, rLRSpace.GetLeft() ); + aRectFnSet.SetWidth( aPrt, nWidth - rLRSpace.GetLeft() - rLRSpace.GetRight() ); + aRectFnSet.SetHeight( aPrt, 0 ); + } + + const SwFormatCol &rCol = GetFormat()->GetCol(); + if( ( rCol.GetNumCols() > 1 || IsAnyNoteAtEnd() ) && !IsInFootnote() ) + { + const SwFormatCol *pOld = Lower() ? &rCol : new SwFormatCol; + ChgColumns( *pOld, rCol, IsAnyNoteAtEnd() ); + if( pOld != &rCol ) + delete pOld; + } +} + +void SwSectionFrame::DestroyImpl() +{ + if( GetFormat() && !GetFormat()->GetDoc()->IsInDtor() ) + { + SwRootFrame *pRootFrame = getRootFrame(); + if( pRootFrame ) + pRootFrame->RemoveFromList( this ); + if( IsFollow() ) + { + SwSectionFrame *pMaster = FindMaster(); + if( pMaster ) + { + PROTOCOL( this, PROT::Section, DbgAction::DelFollow, pMaster ) + pMaster->SetFollow( GetFollow() ); + // A Master always grabs the space until the lower edge of his + // Upper. If he doesn't have a Follow anymore, he can + // release it, which is why the Size of the Master is + // invalidated. + if( !GetFollow() ) + pMaster->InvalidateSize(); + } + } +#if defined DBG_UTIL + else if( HasFollow() ) + { + PROTOCOL( this, PROT::Section, DbgAction::DelMaster, GetFollow() ) + } +#endif + } + + SwLayoutFrame::DestroyImpl(); +} + +SwSectionFrame::~SwSectionFrame() +{ +} + +void SwSectionFrame::DelEmpty( bool bRemove ) +{ + if( IsColLocked() ) + { + OSL_ENSURE( !bRemove, "Don't delete locked SectionFrames" ); + return; + } + SwFrame* pUp = GetUpper(); + if( pUp ) + { + // #i27138# + // notify accessibility paragraphs objects about changed + // CONTENT_FLOWS_FROM/_TO relation. + // Relation CONTENT_FLOWS_FROM for current next paragraph will change + // and relation CONTENT_FLOWS_TO for current previous paragraph will change. + { + SwViewShell* pViewShell( getRootFrame()->GetCurrShell() ); + if ( pViewShell && pViewShell->GetLayout() && + pViewShell->GetLayout()->IsAnyShellAccessible() ) + { + pViewShell->InvalidateAccessibleParaFlowRelation( + dynamic_cast<SwTextFrame*>(FindNextCnt( true )), + dynamic_cast<SwTextFrame*>(FindPrevCnt()) ); + } + } + Cut_( bRemove ); + } + SwSectionFrame *pMaster = IsFollow() ? FindMaster() : nullptr; + if (pMaster) + { + pMaster->SetFollow( GetFollow() ); + // A Master always grabs the space until the lower edge of his + // Upper. If he doesn't have a Follow anymore, he can + // release it, which is why the Size of the Master is + // invalidated. + if( !GetFollow() && !pMaster->IsColLocked() ) + pMaster->InvalidateSize(); + } + SetFollow(nullptr); + if( pUp ) + { + { + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this); + aFrm.Height( 0 ); + } + + // If we are destroyed immediately anyway, we don't need + // to put us into the list + if( bRemove ) + { // If we already were half dead before this DelEmpty, + // we are likely in the list and have to remove us from + // it + if( !m_pSection && getRootFrame() ) + getRootFrame()->RemoveFromList( this ); + } + else if( getRootFrame() ) + { + getRootFrame()->InsertEmptySct( this ); + } + + m_pSection = nullptr; // like this a reanimation is virtually impossible though + } +} + +void SwSectionFrame::Cut() +{ + Cut_( true ); +} + +void SwSectionFrame::Cut_( bool bRemove ) +{ + OSL_ENSURE( GetUpper(), "Cut without Upper()." ); + + PROTOCOL( this, PROT::Cut, DbgAction::NONE, GetUpper() ) + + SwPageFrame *pPage = FindPageFrame(); + InvalidatePage( pPage ); + SwFrame *pFrame = GetNext(); + SwFrame* pPrepFrame = nullptr; + while( pFrame && pFrame->IsSctFrame() && !static_cast<SwSectionFrame*>(pFrame)->GetSection() ) + pFrame = pFrame->GetNext(); + if( pFrame ) + { // The former successor might have calculated a gap to the predecessor + // which is now obsolete since he becomes the first + pFrame->InvalidatePrt_(); + pFrame->InvalidatePos_(); + if( pFrame->IsSctFrame() ) + pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny(); + if ( pFrame && pFrame->IsContentFrame() ) + { + pFrame->InvalidatePage( pPage ); + if( IsInFootnote() && !GetIndPrev() ) + pPrepFrame = pFrame; + } + } + else + { + InvalidateNextPos(); + // Someone has to take over the retouching: predecessor or Upper + if ( nullptr != (pFrame = GetPrev()) ) + { pFrame->SetRetouche(); + pFrame->Prepare( PrepareHint::WidowsOrphans ); + if ( pFrame->IsContentFrame() ) + pFrame->InvalidatePage( pPage ); + } + // If I am (was) the only FlowFrame in my Upper, then he has to take over + // the retouching. + // Furthermore a blank page could have emerged + else + { SwRootFrame *pRoot = static_cast<SwRootFrame*>(pPage->GetUpper()); + pRoot->SetSuperfluous(); + GetUpper()->SetCompletePaint(); + } + } + // First remove, then shrink Upper + SwLayoutFrame *pUp = GetUpper(); + if( bRemove ) + { + RemoveFromLayout(); + if( pUp && !pUp->Lower() && pUp->IsFootnoteFrame() && !pUp->IsColLocked() && + pUp->GetUpper() ) + { + pUp->Cut(); + SwFrame::DestroyFrame(pUp); + pUp = nullptr; + } + } + if( pPrepFrame ) + pPrepFrame->Prepare( PrepareHint::FootnoteInvalidation ); + if ( pUp ) + { + SwRectFnSet aRectFnSet(this); + SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea()); + if( nFrameHeight > 0 ) + { + if( !bRemove ) + { + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this); + aRectFnSet.SetHeight( aFrm, 0 ); + + SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this); + aRectFnSet.SetHeight( aPrt, 0 ); + } + + pUp->Shrink( nFrameHeight ); + } + } +} + +void SwSectionFrame::Paste( SwFrame* pParent, SwFrame* pSibling ) +{ + OSL_ENSURE( pParent, "No parent for Paste()." ); + OSL_ENSURE( pParent->IsLayoutFrame(), "Parent is ContentFrame." ); + OSL_ENSURE( pParent != this, "I'm my own parent." ); + OSL_ENSURE( pSibling != this, "I'm my own neighbour." ); + OSL_ENSURE( !GetPrev() && !GetUpper(), + "I am still registered somewhere." ); + + PROTOCOL( this, PROT::Paste, DbgAction::NONE, GetUpper() ) + + // Add to the tree + SwSectionFrame* pSect = pParent->FindSctFrame(); + // Assure that parent is not inside a table frame, which is inside the found section frame. + if ( pSect ) + { + SwTabFrame* pTableFrame = pParent->FindTabFrame(); + if ( pTableFrame && + pSect->IsAnLower( pTableFrame ) ) + { + pSect = nullptr; + } + } + + SwRectFnSet aRectFnSet(pParent); + if( pSect && HasToBreak( pSect ) ) + { + if( pParent->IsColBodyFrame() ) // dealing with a single-column area + { + // If we are coincidentally at the end of a column, pSibling + // has to point to the first frame of the next column in order + // for the content of the next column to be moved correctly to the + // newly created pSect by the InsertGroup + SwColumnFrame *pCol = static_cast<SwColumnFrame*>(pParent->GetUpper()); + while( !pSibling && nullptr != ( pCol = static_cast<SwColumnFrame*>(pCol->GetNext()) ) ) + pSibling = static_cast<SwLayoutFrame*>(pCol->Lower())->Lower(); + if( pSibling ) + { + // Even worse: every following column content has to + // be attached to the pSibling-chain in order to be + // taken along + SwFrame *pTmp = pSibling; + while ( nullptr != ( pCol = static_cast<SwColumnFrame*>(pCol->GetNext()) ) ) + { + while ( pTmp->GetNext() ) + pTmp = pTmp->GetNext(); + SwFrame* pSave = ::SaveContent( pCol ); + if (pSave) + ::RestoreContent( pSave, pSibling->GetUpper(), pTmp ); + } + } + } + pParent = pSect; + pSect = new SwSectionFrame( *static_cast<SwSectionFrame*>(pParent)->GetSection(), pParent ); + // if pParent is decomposed into two parts, its Follow has to be attached + // to the new second part + pSect->SetFollow( static_cast<SwSectionFrame*>(pParent)->GetFollow() ); + static_cast<SwSectionFrame*>(pParent)->SetFollow( nullptr ); + if( pSect->GetFollow() ) + pParent->InvalidateSize_(); + + const bool bInserted = InsertGroupBefore( pParent, pSibling, pSect ); + if (bInserted) + { + pSect->Init(); + aRectFnSet.MakePos( *pSect, pSect->GetUpper(), pSect->GetPrev(), true); + } + if( !static_cast<SwLayoutFrame*>(pParent)->Lower() ) + { + SwSectionFrame::MoveContentAndDelete( static_cast<SwSectionFrame*>(pParent), false ); + pParent = this; + } + } + else + InsertGroupBefore( pParent, pSibling, nullptr ); + + InvalidateAll_(); + SwPageFrame *pPage = FindPageFrame(); + InvalidatePage( pPage ); + + if ( pSibling ) + { + pSibling->InvalidatePos_(); + pSibling->InvalidatePrt_(); + if ( pSibling->IsContentFrame() ) + pSibling->InvalidatePage( pPage ); + } + + SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea()); + if( nFrameHeight ) + pParent->Grow( nFrameHeight ); + + if ( GetPrev() && !IsFollow() ) + { + GetPrev()->InvalidateSize(); + if ( GetPrev()->IsContentFrame() ) + GetPrev()->InvalidatePage( pPage ); + } +} + +/** +|* Here it's decided whether the this-SectionFrame should break up +|* the passed (Section)frm (or not). +|* Initially, all superior sections are broken up. Later on that could +|* be made configurable. +|*/ +bool SwSectionFrame::HasToBreak( const SwFrame* pFrame ) const +{ + if( !pFrame->IsSctFrame() ) + return false; + + const SwSectionFormat *pTmp = static_cast<const SwSectionFormat*>(GetFormat()); + + const SwFrameFormat *pOtherFormat = static_cast<const SwSectionFrame*>(pFrame)->GetFormat(); + do + { + pTmp = pTmp->GetParent(); + if( !pTmp ) + return false; + if( pTmp == pOtherFormat ) + return true; + } while( true ); // ( pTmp->GetSect().GetValue() ); +} + +/** +|* Merges two SectionFrames, in case it's about the same section. +|* This can be necessary when a (sub)section is deleted that had +|* divided another part into two. +|*/ +void SwSectionFrame::MergeNext( SwSectionFrame* pNxt ) +{ + if (pNxt->IsDeleteForbidden()) + return; + + if (!pNxt->IsJoinLocked() && GetSection() == pNxt->GetSection()) + { + PROTOCOL( this, PROT::Section, DbgAction::Merge, pNxt ) + + SwFrame* pTmp = ::SaveContent( pNxt ); + if( pTmp ) + { + SwFrame* pLast = Lower(); + SwLayoutFrame* pLay = this; + if( pLast ) + { + while( pLast->GetNext() ) + pLast = pLast->GetNext(); + if( pLast->IsColumnFrame() ) + { // Columns now with BodyFrame + pLay = static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(pLast)->Lower()); + pLast = pLay->Lower(); + if( pLast ) + while( pLast->GetNext() ) + pLast = pLast->GetNext(); + } + } + ::RestoreContent( pTmp, pLay, pLast ); + } + SetFollow( pNxt->GetFollow() ); + pNxt->SetFollow( nullptr ); + pNxt->Cut(); + SwFrame::DestroyFrame(pNxt); + InvalidateSize(); + } +} + +/** +|* Divides a SectionFrame into two parts. The second one starts with the +|* passed frame. +|* This is required when inserting an inner section, because the MoveFwd +|* cannot have the desired effect within a frame or a table cell. +|*/ +bool SwSectionFrame::SplitSect( SwFrame* pFrame, bool bApres ) +{ + assert(pFrame && "SplitSect: Why?"); + SwFrame* pOther = bApres ? pFrame->FindNext() : pFrame->FindPrev(); + if( !pOther ) + return false; + SwSectionFrame* pSect = pOther->FindSctFrame(); + if( pSect != this ) + return false; + // Put the content aside + SwFrame* pSav = ::SaveContent( this, bApres ? pOther : pFrame ); + OSL_ENSURE( pSav, "SplitSect: What's on?" ); + if( pSav ) // be robust + { // Create a new SctFrame, not as a Follower/master + SwSectionFrame* pNew = new SwSectionFrame( *pSect->GetSection(), pSect ); + pNew->InsertBehind( pSect->GetUpper(), pSect ); + pNew->Init(); + SwRectFnSet aRectFnSet(this); + aRectFnSet.MakePos( *pNew, nullptr, pSect, true ); + // OD 25.03.2003 #108339# - restore content: + // determine layout frame for restoring content after the initialization + // of the section frame. In the section initialization the columns are + // created. + { + SwLayoutFrame* pLay = pNew; + // Search for last layout frame, e.g. for columned sections. + while( pLay->Lower() && pLay->Lower()->IsLayoutFrame() ) + pLay = static_cast<SwLayoutFrame*>(pLay->Lower()); + ::RestoreContent( pSav, pLay, nullptr ); + } + InvalidateSize_(); + if( HasFollow() ) + { + pNew->SetFollow( GetFollow() ); + SetFollow( nullptr ); + } + return true; + } + return false; +} + +/** +|* MoveContent is called for destroying a SectionFrames, due to +|* the cancellation or hiding of a section, to handle the content. +|* If the SectionFrame hasn't broken up another one, then the content +|* is moved to the Upper. Otherwise the content is moved to another +|* SectionFrame, which has to be potentially merged. +|*/ +// If a multi-column section is cancelled, the ContentFrames have to be +// invalidated +static void lcl_InvalidateInfFlags( SwFrame* pFrame, bool bInva ) +{ + while ( pFrame ) + { + pFrame->InvalidateInfFlags(); + if( bInva ) + { + pFrame->InvalidatePos_(); + pFrame->InvalidateSize_(); + pFrame->InvalidatePrt_(); + } + if( pFrame->IsLayoutFrame() ) + lcl_InvalidateInfFlags( static_cast<SwLayoutFrame*>(pFrame)->GetLower(), false ); + pFrame = pFrame->GetNext(); + } +} + +// Works like SwContentFrame::ImplGetNextContentFrame, but starts with a LayoutFrame +static SwContentFrame* lcl_GetNextContentFrame( const SwLayoutFrame* pLay, bool bFwd ) +{ + if ( bFwd ) + { + if ( pLay->GetNext() && pLay->GetNext()->IsContentFrame() ) + return const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(pLay->GetNext())); + } + else + { + if ( pLay->GetPrev() && pLay->GetPrev()->IsContentFrame() ) + return const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(pLay->GetPrev())); + } + + const SwFrame* pFrame = pLay; + SwContentFrame *pContentFrame = nullptr; + bool bGoingUp = true; + do { + const SwFrame *p = nullptr; + bool bGoingFwdOrBwd = false; + + bool bGoingDown = !bGoingUp && pFrame->IsLayoutFrame(); + if (bGoingDown) + { + p = static_cast<const SwLayoutFrame*>(pFrame)->Lower(); + bGoingDown = nullptr != p; + } + if ( !bGoingDown ) + { + p = pFrame->IsFlyFrame() ? + ( bFwd ? static_cast<const SwFlyFrame*>(pFrame)->GetNextLink() : static_cast<const SwFlyFrame*>(pFrame)->GetPrevLink() ) : + ( bFwd ? pFrame->GetNext() :pFrame->GetPrev() ); + bGoingFwdOrBwd = nullptr != p; + if ( !bGoingFwdOrBwd ) + { + p = pFrame->GetUpper(); + bGoingUp = nullptr != p; + if ( !bGoingUp ) + return nullptr; + } + } + + bGoingUp = !( bGoingFwdOrBwd || bGoingDown ); + assert(p); + if (!bFwd && bGoingDown) + while ( p->GetNext() ) + p = p->GetNext(); + + pFrame = p; + } while ( nullptr == (pContentFrame = (pFrame->IsContentFrame() ? const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(pFrame)) : nullptr) )); + + return pContentFrame; +} + +namespace +{ + SwLayoutFrame* FirstLeaf(SwSectionFrame* pLayFrame) + { + if (pLayFrame->Lower() && pLayFrame->Lower()->IsColumnFrame()) + return pLayFrame->GetNextLayoutLeaf(); + return pLayFrame; + } + + /// Checks if pFrame has a parent that can contain a split section frame. + bool CanContainSplitSection(const SwFrame* pFrame) + { + if (!pFrame->IsInTab()) + return true; + + // The frame is in a table, see if the table is in a section. + bool bRet = !pFrame->FindTabFrame()->IsInSct(); + + if (bRet) + { + // Don't try to split if the frame itself is a section frame with + // multiple columns. + if (pFrame->IsSctFrame()) + { + const SwFrame* pLower = pFrame->GetLower(); + if (pLower && pLower->IsColumnFrame()) + bRet = false; + } + } + + return bRet; + } +} + +void SwSectionFrame::MoveContentAndDelete( SwSectionFrame* pDel, bool bSave ) +{ + bool bSize = pDel->Lower() && pDel->Lower()->IsColumnFrame(); + SwFrame* pPrv = pDel->GetPrev(); + SwLayoutFrame* pUp = pDel->GetUpper(); + // OD 27.03.2003 #i12711# - initialize local pointer variables. + SwSectionFrame* pPrvSct = nullptr; + SwSectionFrame* pNxtSct = nullptr; + SwSectionFormat* pParent = static_cast<SwSectionFormat*>(pDel->GetFormat())->GetParent(); + if( pDel->IsInTab() && pParent ) + { + SwTabFrame *pTab = pDel->FindTabFrame(); + // If we are within a table, we can only have broken up sections that + // are inside as well, but not a section that contains the whole table. + if( pTab->IsInSct() && pParent == pTab->FindSctFrame()->GetFormat() ) + pParent = nullptr; + } + // If our Format has a parent, we have probably broken up another + // SectionFrame, which has to be checked. To do so we first acquire the + // succeeding and the preceding ContentFrame, let's see if they + // lay in the SectionFrames. + // OD 27.03.2003 #i12711# - check, if previous and next section belonging + // together and can be joined, *not* only if deleted section contains content. + if ( pParent ) + { + SwFrame* pPrvContent = lcl_GetNextContentFrame( pDel, false ); + pPrvSct = pPrvContent ? pPrvContent->FindSctFrame() : nullptr; + SwFrame* pNxtContent = lcl_GetNextContentFrame( pDel, true ); + pNxtSct = pNxtContent ? pNxtContent->FindSctFrame() : nullptr; + } + else + { + pParent = nullptr; + pPrvSct = pNxtSct = nullptr; + } + + // Now the content is put aside and the frame is destroyed + SwFrame *pSave = bSave ? ::SaveContent( pDel ) : nullptr; + bool bOldFootnote = true; + if( pSave && pUp->IsFootnoteFrame() ) + { + bOldFootnote = static_cast<SwFootnoteFrame*>(pUp)->IsColLocked(); + static_cast<SwFootnoteFrame*>(pUp)->ColLock(); + } + pDel->DelEmpty( true ); + SwFrame::DestroyFrame(pDel); + if( pParent ) + { // Search for the appropriate insert position + if( pNxtSct && pNxtSct->GetFormat() == pParent ) + { // Here we can insert ourselves at the beginning + pUp = FirstLeaf( pNxtSct ); + pPrv = nullptr; + if( pPrvSct && ( pPrvSct->GetFormat() != pParent ) ) + pPrvSct = nullptr; // In order that nothing is merged + } + else if( pPrvSct && pPrvSct->GetFormat() == pParent ) + { // Wonderful, here we can insert ourselves at the end + pUp = pPrvSct; + if( pUp->Lower() && pUp->Lower()->IsColumnFrame() ) + { + pUp = static_cast<SwLayoutFrame*>(pUp->GetLastLower()); + // The body of the last column + pUp = static_cast<SwLayoutFrame*>(pUp->Lower()); + } + // In order to perform the insertion after the last one + pPrv = pUp->GetLastLower(); + pPrvSct = nullptr; // Such that nothing is merged + } + else + { + if( pSave ) + { // Following situations: before and after the section-to-be + // deleted there is the section boundary of the enclosing + // section, or another (sibling) section connects subsequently, + // that derives from the same Parent. + // In that case, there's not (yet) a part of our parent available + // that can store the content, so we create it here. + pPrvSct = new SwSectionFrame( *pParent->GetSection(), pUp ); + pPrvSct->InsertBehind( pUp, pPrv ); + pPrvSct->Init(); + SwRectFnSet aRectFnSet(pUp); + aRectFnSet.MakePos( *pPrvSct, pUp, pPrv, true ); + pUp = FirstLeaf( pPrvSct ); + pPrv = nullptr; + } + pPrvSct = nullptr; // Such that nothing will be merged + } + } + // The content is going to be inserted... + if( pSave ) + { + lcl_InvalidateInfFlags( pSave, bSize ); + ::RestoreContent( pSave, pUp, pPrv ); + pUp->FindPageFrame()->InvalidateContent(); + if( !bOldFootnote ) + static_cast<SwFootnoteFrame*>(pUp)->ColUnlock(); + } + // Now two parts of the superior section could possibly be merged + if( pPrvSct && !pPrvSct->IsJoinLocked() ) + { + OSL_ENSURE( pNxtSct, "MoveContent: No Merge" ); + pPrvSct->MergeNext( pNxtSct ); + } +} + +void SwSectionFrame::MakeAll(vcl::RenderContext* pRenderContext) +{ + if ( IsJoinLocked() || IsColLocked() || StackHack::IsLocked() || StackHack::Count() > 50 ) + return; + if( !m_pSection ) // Via DelEmpty + { +#ifdef DBG_UTIL + OSL_ENSURE( getRootFrame()->IsInDelList( this ), "SectionFrame without Section" ); +#endif + if( !isFrameAreaPositionValid() ) + { + if( GetUpper() ) + { + SwRectFnSet aRectFnSet(GetUpper()); + aRectFnSet.MakePos( *this, GetUpper(), GetPrev(), false ); + } + + if (getFrameArea().Height() == 0) + { + // SwLayoutFrame::MakeAll() is not called for to-be-deleted + // section frames (which would invalidate the position of the + // next frame via the SwLayNotify dtor), so call it manually. + if (SwFrame* pNext = GetNext()) + pNext->InvalidatePos(); + } + } + + setFrameAreaPositionValid(true); + setFrameAreaSizeValid(true); + setFramePrintAreaValid(true); + return; + } + LockJoin(); // I don't let myself to be destroyed on the way + + while( GetNext() && GetNext() == GetFollow() ) + { + const SwFrame* pFoll = GetFollow(); + MergeNext( static_cast<SwSectionFrame*>(GetNext()) ); + if( pFoll == GetFollow() ) + break; + } + + // OD 2004-03-15 #116561# - In online layout join the follows, if section + // can grow. + const SwViewShell *pSh = getRootFrame()->GetCurrShell(); + + // Split sections inside table cells: need to merge all follows of the + // section here, as later we won't attempt doing so. + bool bCanContainSplitSection = false; + if (IsInTab() && GetUpper()) + bCanContainSplitSection = CanContainSplitSection(GetUpper()); + + if( pSh && (pSh->GetViewOptions()->getBrowseMode() || bCanContainSplitSection) && + ( Grow( LONG_MAX, true ) > 0 ) ) + { + while( GetFollow() ) + { + const SwFrame* pFoll = GetFollow(); + MergeNext( GetFollow() ); + if( pFoll == GetFollow() ) + break; + } + } + + // A section with Follow uses all the space until the lower edge of the + // Upper. If it moves, its size can grow or decrease... + if( !isFrameAreaPositionValid() && ToMaximize( false ) ) + { + setFrameAreaSizeValid(false); + } + + SwLayoutFrame::MakeAll(getRootFrame()->GetCurrShell()->GetOut()); + + if (IsInTab()) + { + // In case the section is in a table, then calculate the lower right + // now. Just setting the valid size flag of the lower to false may not + // be enough, as lcl_RecalcRow() can call + // SwFrame::ValidateThisAndAllLowers(), and then we don't attempt + // calculating the proper position of the lower. + SwFrame* pLower = Lower(); + if (pLower && !pLower->isFrameAreaPositionValid()) + pLower->Calc(pRenderContext); + } + + UnlockJoin(); + if( m_pSection && IsSuperfluous() ) + DelEmpty( false ); +} + +bool SwSectionFrame::ShouldBwdMoved( SwLayoutFrame *, bool & ) +{ + OSL_FAIL( "Oops, where is my tinfoil hat?" ); + return false; +} + +const SwSectionFormat* SwSectionFrame::GetEndSectFormat_() const +{ + const SwSectionFormat *pFormat = m_pSection->GetFormat(); + while( !pFormat->GetEndAtTextEnd().IsAtEnd() ) + { + if( dynamic_cast< const SwSectionFormat *>( pFormat->GetRegisteredIn()) != nullptr ) + pFormat = static_cast<const SwSectionFormat*>(pFormat->GetRegisteredIn()); + else + return nullptr; + } + return pFormat; +} + +static void lcl_FindContentFrame( SwContentFrame* &rpContentFrame, SwFootnoteFrame* &rpFootnoteFrame, + SwFrame* pFrame, bool &rbChkFootnote ) +{ + if( pFrame ) + { + while( pFrame->GetNext() ) + pFrame = pFrame->GetNext(); + while( !rpContentFrame && pFrame ) + { + if( pFrame->IsContentFrame() ) + rpContentFrame = static_cast<SwContentFrame*>(pFrame); + else if( pFrame->IsLayoutFrame() ) + { + if( pFrame->IsFootnoteFrame() ) + { + if( rbChkFootnote ) + { + rpFootnoteFrame = static_cast<SwFootnoteFrame*>(pFrame); + rbChkFootnote = rpFootnoteFrame->GetAttr()->GetFootnote().IsEndNote(); + } + } + else + lcl_FindContentFrame( rpContentFrame, rpFootnoteFrame, + static_cast<SwLayoutFrame*>(pFrame)->Lower(), rbChkFootnote ); + } + pFrame = pFrame->GetPrev(); + } + } +} + +SwContentFrame *SwSectionFrame::FindLastContent( SwFindMode nMode ) +{ + SwContentFrame *pRet = nullptr; + SwFootnoteFrame *pFootnoteFrame = nullptr; + SwSectionFrame *pSect = this; + if( nMode != SwFindMode::None ) + { + const SwSectionFormat *pFormat = IsEndnAtEnd() ? GetEndSectFormat() : + m_pSection->GetFormat(); + do { + while( pSect->HasFollow() ) + pSect = pSect->GetFollow(); + SwFrame* pTmp = pSect->FindNext(); + while( pTmp && pTmp->IsSctFrame() && + !static_cast<SwSectionFrame*>(pTmp)->GetSection() ) + pTmp = pTmp->FindNext(); + if( pTmp && pTmp->IsSctFrame() && + static_cast<SwSectionFrame*>(pTmp)->IsDescendantFrom( pFormat ) ) + pSect = static_cast<SwSectionFrame*>(pTmp); + else + break; + } while( true ); + } + bool bFootnoteFound = nMode == SwFindMode::EndNote; + do + { + lcl_FindContentFrame( pRet, pFootnoteFrame, pSect->Lower(), bFootnoteFound ); + if( pRet || !pSect->IsFollow() || nMode == SwFindMode::None || + ( SwFindMode::MyLast == nMode && this == pSect ) ) + break; + pSect = pSect->FindMaster(); + } while( pSect ); + if( ( nMode == SwFindMode::EndNote ) && pFootnoteFrame ) + pRet = pFootnoteFrame->ContainsContent(); + return pRet; +} + +bool SwSectionFrame::CalcMinDiff( SwTwips& rMinDiff ) const +{ + if( ToMaximize( true ) ) + { + SwRectFnSet aRectFnSet(this); + rMinDiff = aRectFnSet.GetPrtBottom(*GetUpper()); + rMinDiff = aRectFnSet.BottomDist( getFrameArea(), rMinDiff ); + return true; + } + return false; +} + +/** + * CollectEndnotes looks for endnotes in the sectionfrm and his follows, + * the endnotes will cut off the layout and put into the array. + * If the first endnote is not a master-SwFootnoteFrame, the whole sectionfrm + * contains only endnotes and it is not necessary to collect them. + */ +static SwFootnoteFrame* lcl_FindEndnote( SwSectionFrame* &rpSect, bool &rbEmpty, + SwLayouter *pLayouter ) +{ + // if rEmpty is set, the rpSect is already searched + SwSectionFrame* pSect = rbEmpty ? rpSect->GetFollow() : rpSect; + while( pSect ) + { + OSL_ENSURE( (pSect->Lower() && pSect->Lower()->IsColumnFrame()) || pSect->GetUpper()->IsFootnoteFrame(), + "InsertEndnotes: Where's my column?" ); + + // i73332: Columned section in endnote + SwColumnFrame* pCol = nullptr; + if(pSect->Lower() && pSect->Lower()->IsColumnFrame()) + pCol = static_cast<SwColumnFrame*>(pSect->Lower()); + + while( pCol ) // check all columns + { + SwFootnoteContFrame* pFootnoteCont = pCol->FindFootnoteCont(); + if( pFootnoteCont ) + { + SwFootnoteFrame* pRet = static_cast<SwFootnoteFrame*>(pFootnoteCont->Lower()); + while( pRet ) // look for endnotes + { + /* CollectEndNode can destroy pRet so we need to get the + next early + */ + SwFootnoteFrame* pRetNext = static_cast<SwFootnoteFrame*>(pRet->GetNext()); + if( pRet->GetAttr()->GetFootnote().IsEndNote() ) + { + if( pRet->GetMaster() ) + { + if( pLayouter ) + pLayouter->CollectEndnote( pRet ); + else + return nullptr; + } + else + return pRet; // Found + } + pRet = pRetNext; + } + } + pCol = static_cast<SwColumnFrame*>(pCol->GetNext()); + } + rpSect = pSect; + pSect = pLayouter ? pSect->GetFollow() : nullptr; + rbEmpty = true; + } + return nullptr; +} + +static void lcl_ColumnRefresh( SwSectionFrame* pSect, bool bFollow ) +{ + vcl::RenderContext* pRenderContext = pSect->getRootFrame()->GetCurrShell()->GetOut(); + while( pSect ) + { + bool bOldLock = pSect->IsColLocked(); + pSect->ColLock(); + if( pSect->Lower() && pSect->Lower()->IsColumnFrame() ) + { + SwColumnFrame *pCol = static_cast<SwColumnFrame*>(pSect->Lower()); + do + { pCol->InvalidateSize_(); + pCol->InvalidatePos_(); + static_cast<SwLayoutFrame*>(pCol)->Lower()->InvalidateSize_(); + pCol->Calc(pRenderContext); // calculation of column and + static_cast<SwLayoutFrame*>(pCol)->Lower()->Calc(pRenderContext); // body + pCol = static_cast<SwColumnFrame*>(pCol->GetNext()); + } while ( pCol ); + } + if( !bOldLock ) + pSect->ColUnlock(); + if( bFollow ) + pSect = pSect->GetFollow(); + else + pSect = nullptr; + } +} + +void SwSectionFrame::CollectEndnotes( SwLayouter* pLayouter ) +{ + OSL_ENSURE( IsColLocked(), "CollectEndnotes: You love the risk?" ); + // i73332: Section in footnode does not have columns! + OSL_ENSURE( (Lower() && Lower()->IsColumnFrame()) || GetUpper()->IsFootnoteFrame(), "Where's my column?" ); + + SwSectionFrame* pSect = this; + SwFootnoteFrame* pFootnote; + bool bEmpty = false; + // pSect is the last sectionfrm without endnotes or the this-pointer + // the first sectionfrm with endnotes may be destroyed, when the endnotes + // is cutted + while( nullptr != (pFootnote = lcl_FindEndnote( pSect, bEmpty, pLayouter )) ) + pLayouter->CollectEndnote( pFootnote ); + if( pLayouter->HasEndnotes() ) + lcl_ColumnRefresh( this, true ); +} + +/** Fits the size to the surroundings. +|* +|* Those that have a Follow or foot notes, have to extend until +|* the lower edge of a upper (bMaximize) +|* They must not extend above the Upper, as the case may be one can +|* try to grow its upper (bGrow) +|* If the size had to be changed, the content is calculated. +|* +|* @note: perform calculation of content, only if height has changed (OD 18.09.2002 #100522#) +|*/ +void SwSectionFrame::CheckClipping( bool bGrow, bool bMaximize ) +{ + SwRectFnSet aRectFnSet(this); + long nDiff; + SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper()); + if( bGrow && ( !IsInFly() || !GetUpper()->IsColBodyFrame() || + !FindFlyFrame()->IsLocked() ) ) + { + nDiff = -aRectFnSet.BottomDist( getFrameArea(), nDeadLine ); + if( !bMaximize ) + nDiff += Undersize(); + if( nDiff > 0 ) + { + long nAdd = GetUpper()->Grow( nDiff ); + if( aRectFnSet.IsVert() ) + nDeadLine -= nAdd; + else + nDeadLine += nAdd; + } + } + nDiff = -aRectFnSet.BottomDist( getFrameArea(), nDeadLine ); + SetUndersized( !bMaximize && nDiff >= 0 ); + const bool bCalc = ( IsUndersized() || bMaximize ) && + ( nDiff || + aRectFnSet.GetTop(getFramePrintArea()) > aRectFnSet.GetHeight(getFrameArea()) ); + // OD 03.11.2003 #i19737# - introduce local variable <bExtraCalc> to indicate + // that a calculation has to be done beside the value of <bCalc>. + bool bExtraCalc = false; + if( !bCalc && !bGrow && IsAnyNoteAtEnd() && !IsInFootnote() ) + { + SwSectionFrame *pSect = this; + bool bEmpty = false; + SwLayoutFrame* pFootnote = IsEndnAtEnd() ? + lcl_FindEndnote( pSect, bEmpty, nullptr ) : nullptr; + if( pFootnote ) + { + pFootnote = pFootnote->FindFootnoteBossFrame(); + SwFrame* pTmp = FindLastContent( SwFindMode::LastCnt ); + // OD 08.11.2002 #104840# - use <SwLayoutFrame::IsBefore(..)> + if ( pTmp && pFootnote->IsBefore( pTmp->FindFootnoteBossFrame() ) ) + bExtraCalc = true; + } + else if( GetFollow() && !GetFollow()->ContainsAny() ) + bExtraCalc = true; + } + if ( bCalc || bExtraCalc ) + { + nDiff = aRectFnSet.YDiff( nDeadLine, aRectFnSet.GetTop(getFrameArea()) ); + if( nDiff < 0 ) + nDeadLine = aRectFnSet.GetTop(getFrameArea()); + const Size aOldSz( getFramePrintArea().SSize() ); + long nTop = aRectFnSet.GetTopMargin(*this); + + { + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this); + aRectFnSet.SetBottom( aFrm, nDeadLine ); + } + + nDiff = aRectFnSet.GetHeight(getFrameArea()); + if( nTop > nDiff ) + nTop = nDiff; + aRectFnSet.SetYMargins( *this, nTop, 0 ); + + // OD 18.09.2002 #100522# + // Determine, if height has changed. + // Note: In vertical layout the height equals the width value. + bool bHeightChanged = aRectFnSet.IsVert() ? + (aOldSz.Width() != getFramePrintArea().Width()) : + (aOldSz.Height() != getFramePrintArea().Height()); + // Last but not least we have changed the height again, thus the inner + // layout (columns) is calculated and the content as well. + // OD 18.09.2002 #100522# + // calculate content, only if height has changed. + // OD 03.11.2003 #i19737# - restriction of content calculation too strong. + // If an endnote has an incorrect position or a follow section contains + // no content except footnotes/endnotes, the content has also been calculated. + if ( ( bHeightChanged || bExtraCalc ) && Lower() ) + { + if( Lower()->IsColumnFrame() ) + { + lcl_ColumnRefresh( this, false ); + ::CalcContent( this ); + } + else + { + ChgLowersProp( aOldSz ); + if( !bMaximize && !IsContentLocked() ) + ::CalcContent( this ); + } + } + } +} + +void SwSectionFrame::SimpleFormat() +{ + if ( IsJoinLocked() || IsColLocked() ) + return; + LockJoin(); + SwRectFnSet aRectFnSet(this); + if( GetPrev() || GetUpper() ) + { + // assure notifications on position changes. + const SwLayNotify aNotify( this ); + aRectFnSet.MakePos( *this, GetUpper(), GetPrev(), false ); + setFrameAreaPositionValid(true); + } + SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper()); + // OD 22.10.2002 #97265# - call always method <lcl_ColumnRefresh(..)>, in + // order to get calculated lowers, not only if there space left in its upper. + if( aRectFnSet.BottomDist( getFrameArea(), nDeadLine ) >= 0 ) + { + { + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this); + aRectFnSet.SetBottom( aFrm, nDeadLine ); + } + + long nHeight = aRectFnSet.GetHeight(getFrameArea()); + long nTop = CalcUpperSpace(); + if( nTop > nHeight ) + nTop = nHeight; + aRectFnSet.SetYMargins( *this, nTop, 0 ); + } + lcl_ColumnRefresh( this, false ); + UnlockJoin(); +} + +namespace { + +// #i40147# - helper class to perform extra section format +// to position anchored objects and to keep the position of whose objects locked. +class ExtraFormatToPositionObjs +{ + private: + SwSectionFrame* mpSectFrame; + bool mbExtraFormatPerformed; + + public: + explicit ExtraFormatToPositionObjs( SwSectionFrame& _rSectFrame) + : mpSectFrame( &_rSectFrame ), + mbExtraFormatPerformed( false ) + {} + + ~ExtraFormatToPositionObjs() + { + if ( mbExtraFormatPerformed ) + { + // release keep locked position of lower floating screen objects + SwPageFrame* pPageFrame = mpSectFrame->FindPageFrame(); + SwSortedObjs* pObjs = pPageFrame ? pPageFrame->GetSortedObjs() : nullptr; + if ( pObjs ) + { + for (SwAnchoredObject* pAnchoredObj : *pObjs) + { + if ( mpSectFrame->IsAnLower( pAnchoredObj->GetAnchorFrame() ) ) + { + pAnchoredObj->SetKeepPosLocked( false ); + } + } + } + } + } + + // #i81555# + void InitObjs( SwFrame& rFrame ) + { + SwSortedObjs* pObjs = rFrame.GetDrawObjs(); + if ( pObjs ) + { + for (SwAnchoredObject* pAnchoredObj : *pObjs) + { + pAnchoredObj->UnlockPosition(); + pAnchoredObj->SetClearedEnvironment( false ); + } + } + SwLayoutFrame* pLayoutFrame = dynamic_cast<SwLayoutFrame*>(&rFrame); + if ( pLayoutFrame != nullptr ) + { + SwFrame* pLowerFrame = pLayoutFrame->GetLower(); + while ( pLowerFrame != nullptr ) + { + InitObjs( *pLowerFrame ); + + pLowerFrame = pLowerFrame->GetNext(); + } + } + } + + void FormatSectionToPositionObjs() + { + vcl::RenderContext* pRenderContext = mpSectFrame->getRootFrame()->GetCurrShell()->GetOut(); + // perform extra format for multi-columned section. + if ( !(mpSectFrame->Lower() && mpSectFrame->Lower()->IsColumnFrame() && + mpSectFrame->Lower()->GetNext()) ) + return; + + // grow section till bottom of printing area of upper frame + SwRectFnSet aRectFnSet(mpSectFrame); + SwTwips nTopMargin = aRectFnSet.GetTopMargin(*mpSectFrame); + Size aOldSectPrtSize( mpSectFrame->getFramePrintArea().SSize() ); + SwTwips nDiff = aRectFnSet.BottomDist( mpSectFrame->getFrameArea(), aRectFnSet.GetPrtBottom(*mpSectFrame->GetUpper()) ); + + { + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*mpSectFrame); + aRectFnSet.AddBottom( aFrm, nDiff ); + } + + aRectFnSet.SetYMargins( *mpSectFrame, nTopMargin, 0 ); + // #i59789# + // suppress formatting, if printing area of section is too narrow + if ( aRectFnSet.GetHeight(mpSectFrame->getFramePrintArea()) <= 0 ) + { + return; + } + mpSectFrame->ChgLowersProp( aOldSectPrtSize ); + + // format column frames and its body and footnote container + SwColumnFrame* pColFrame = static_cast<SwColumnFrame*>(mpSectFrame->Lower()); + while ( pColFrame ) + { + pColFrame->Calc(pRenderContext); + pColFrame->Lower()->Calc(pRenderContext); + if ( pColFrame->Lower()->GetNext() ) + { + pColFrame->Lower()->GetNext()->Calc(pRenderContext); + } + + pColFrame = static_cast<SwColumnFrame*>(pColFrame->GetNext()); + } + + // unlock position of lower floating screen objects for the extra format + // #i81555# + // Section frame can already have changed the page and its content + // can still be on the former page. + // Thus, initialize objects via lower-relationship + InitObjs( *mpSectFrame ); + + // format content - first with collecting its foot-/endnotes before content + // format, second without collecting its foot-/endnotes. + ::CalcContent( mpSectFrame ); + ::CalcContent( mpSectFrame, true ); + + // keep locked position of lower floating screen objects + SwPageFrame* pPageFrame = mpSectFrame->FindPageFrame(); + SwSortedObjs* pObjs = pPageFrame ? pPageFrame->GetSortedObjs() : nullptr; + if ( pObjs ) + { + for (SwAnchoredObject* pAnchoredObj : *pObjs) + { + if ( mpSectFrame->IsAnLower( pAnchoredObj->GetAnchorFrame() ) ) + { + pAnchoredObj->SetKeepPosLocked( true ); + } + } + } + + mbExtraFormatPerformed = true; + + } +}; + +} + +/// "formats" the frame; Frame and PrtArea +void SwSectionFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs *pAttr ) +{ + if( !m_pSection ) // via DelEmpty + { +#ifdef DBG_UTIL + OSL_ENSURE( getRootFrame()->IsInDelList( this ), "SectionFrame without Section" ); +#endif + setFrameAreaPositionValid(true); + setFrameAreaSizeValid(true); + setFramePrintAreaValid(true); + return; + } + + SwRectFnSet aRectFnSet(this); + + if ( !isFramePrintAreaValid() ) + { + PROTOCOL( this, PROT::PrintArea, DbgAction::NONE, nullptr ) + setFramePrintAreaValid(true); + SwTwips nUpper = CalcUpperSpace(); + + // #109700# LRSpace for sections + const SvxLRSpaceItem& rLRSpace = GetFormat()->GetLRSpace(); + aRectFnSet.SetXMargins( *this, rLRSpace.GetLeft(), rLRSpace.GetRight() ); + + if( nUpper != aRectFnSet.GetTopMargin(*this) ) + { + setFrameAreaSizeValid(false); + SwFrame* pOwn = ContainsAny(); + if( pOwn ) + pOwn->InvalidatePos_(); + } + aRectFnSet.SetYMargins( *this, nUpper, 0 ); + } + + if ( isFrameAreaSizeValid() ) + return; + + PROTOCOL_ENTER( this, PROT::Size, DbgAction::NONE, nullptr ) + const long nOldHeight = aRectFnSet.GetHeight(getFrameArea()); + bool bOldLock = IsColLocked(); + ColLock(); + + setFrameAreaSizeValid(true); + + // The size is only determined by the content, if the SectFrame does not have a + // Follow. Otherwise it fills (occupies) the Upper down to the lower edge. + // It is not responsible for the text flow, but the content is. + bool bMaximize = ToMaximize( false ); + + // OD 2004-05-17 #i28701# - If the wrapping style has to be considered + // on object positioning, an extra formatting has to be performed + // to determine the correct positions the floating screen objects. + // #i40147# + // use new helper class <ExtraFormatToPositionObjs>. + // This class additionally keep the locked position of the objects + // and releases this position lock keeping on destruction. + ExtraFormatToPositionObjs aExtraFormatToPosObjs( *this ); + if ( !bMaximize && + GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) && + !GetFormat()->GetBalancedColumns().GetValue() ) + { + aExtraFormatToPosObjs.FormatSectionToPositionObjs(); + } + + // Column widths have to be adjusted before calling CheckClipping. + // CheckClipping can cause the formatting of the lower frames + // which still have a width of 0. + const bool bHasColumns = Lower() && Lower()->IsColumnFrame(); + if ( bHasColumns && Lower()->GetNext() ) + AdjustColumns( nullptr, false ); + + if( GetUpper() ) + { + const long nWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()); + + { + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this); + aRectFnSet.SetWidth( aFrm, nWidth ); + } + + // #109700# LRSpace for sections + { + const SvxLRSpaceItem& rLRSpace = GetFormat()->GetLRSpace(); + SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this); + aRectFnSet.SetWidth( aPrt, nWidth - rLRSpace.GetLeft() - rLRSpace.GetRight() ); + } + + // OD 15.10.2002 #103517# - allow grow in online layout + // Thus, set <..IsBrowseMode()> as parameter <bGrow> on calling + // method <CheckClipping(..)>. + const SwViewShell *pSh = getRootFrame()->GetCurrShell(); + CheckClipping( pSh && pSh->GetViewOptions()->getBrowseMode(), bMaximize ); + bMaximize = ToMaximize( false ); + setFrameAreaSizeValid(true); + } + + // Check the width of the columns and adjust if necessary + if ( bHasColumns && ! Lower()->GetNext() && bMaximize ) + static_cast<SwColumnFrame*>(Lower())->Lower()->Calc(pRenderContext); + + if ( !bMaximize ) + { + SwTwips nRemaining = aRectFnSet.GetTopMargin(*this); + SwFrame *pFrame = m_pLower; + if( pFrame ) + { + if( pFrame->IsColumnFrame() && pFrame->GetNext() ) + { + // #i61435# + // suppress formatting, if upper frame has height <= 0 + if ( aRectFnSet.GetHeight(GetUpper()->getFrameArea()) > 0 ) + { + FormatWidthCols( *pAttr, nRemaining, MINLAY ); + } + // #126020# - adjust check for empty section + // #130797# - correct fix #126020# + while( HasFollow() && !GetFollow()->ContainsContent() && + !GetFollow()->ContainsAny( true ) ) + { + SwFrame* pOld = GetFollow(); + GetFollow()->DelEmpty( false ); + if( pOld == GetFollow() ) + break; + } + bMaximize = ToMaximize( false ); + nRemaining += aRectFnSet.GetHeight(pFrame->getFrameArea()); + } + else + { + if( pFrame->IsColumnFrame() ) + { + pFrame->Calc(pRenderContext); + pFrame = static_cast<SwColumnFrame*>(pFrame)->Lower(); + pFrame->Calc(pRenderContext); + pFrame = static_cast<SwLayoutFrame*>(pFrame)->Lower(); + CalcFootnoteContent(); + } + // If we are in a columned frame which calls a CalcContent + // in the FormatWidthCols, the content might need calculating + if( pFrame && !pFrame->isFrameAreaDefinitionValid() && IsInFly() && + FindFlyFrame()->IsColLocked() ) + ::CalcContent( this ); + nRemaining += InnerHeight(); + bMaximize = HasFollow(); + } + } + + SwTwips nDiff = aRectFnSet.GetHeight(getFrameArea()) - nRemaining; + if( nDiff < 0) + { + SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper()); + { + long nBottom = aRectFnSet.GetBottom(getFrameArea()); + nBottom = aRectFnSet.YInc( nBottom, -nDiff ); + long nTmpDiff = aRectFnSet.YDiff( nBottom, nDeadLine ); + if( nTmpDiff > 0 ) + { + nTmpDiff = GetUpper()->Grow( nTmpDiff, true ); + nDeadLine = aRectFnSet.YInc( nDeadLine, nTmpDiff ); + nTmpDiff = aRectFnSet.YDiff( nBottom, nDeadLine ); + if( nTmpDiff > 0 ) + nDiff += nTmpDiff; + if( nDiff > 0 ) + nDiff = 0; + } + } + } + if( nDiff ) + { + long nTmp = nRemaining - aRectFnSet.GetHeight(getFrameArea()); + long nTop = aRectFnSet.GetTopMargin(*this); + + { + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this); + aRectFnSet.AddBottom( aFrm, nTmp ); + } + + aRectFnSet.SetYMargins( *this, nTop, 0 ); + InvalidateNextPos(); + + if (m_pLower && (!m_pLower->IsColumnFrame() || !m_pLower->GetNext())) + { + // If a single-column section just created the space that + // was requested by the "undersized" paragraphs, then they + // have to be invalidated and calculated, so they fully cover it + pFrame = m_pLower; + if( pFrame->IsColumnFrame() ) + { + pFrame->InvalidateSize_(); + pFrame->InvalidatePos_(); + pFrame->Calc(pRenderContext); + pFrame = static_cast<SwColumnFrame*>(pFrame)->Lower(); + pFrame->Calc(pRenderContext); + pFrame = static_cast<SwLayoutFrame*>(pFrame)->Lower(); + CalcFootnoteContent(); + } + bool bUnderSz = false; + while( pFrame ) + { + if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsUndersized() ) + { + pFrame->Prepare( PrepareHint::AdjustSizeWithoutFormatting ); + bUnderSz = true; + } + pFrame = pFrame->GetNext(); + } + if( bUnderSz && !IsContentLocked() ) + ::CalcContent( this ); + } + } + } + + // Do not exceed the lower edge of the Upper. + // Do not extend below the lower edge with Sections with Follows + if ( GetUpper() ) + CheckClipping( true, bMaximize ); + if( !bOldLock ) + ColUnlock(); + long nDiff = nOldHeight - aRectFnSet.GetHeight(getFrameArea()); + + if( nDiff > 0 ) + { + if( !GetNext() ) + SetRetouche(); // Take over the retouching ourselves + if( GetUpper() && !GetUpper()->IsFooterFrame() ) + GetUpper()->Shrink( nDiff ); + } + + if( IsUndersized() ) + { + setFramePrintAreaValid(true); + } + +} + +/// Returns the next layout sheet where the frame can be moved in. +/// New pages are created only if specified by the parameter. +SwLayoutFrame *SwFrame::GetNextSctLeaf( MakePageType eMakePage ) +{ + // Attention: Nested sections are currently not supported + + PROTOCOL_ENTER( this, PROT::Leaf, DbgAction::NextSect, GetUpper()->FindSctFrame() ) + + // Shortcuts for "columned" sections, if we're not in the last column + // Can we slide to the next column of the section? + if( IsColBodyFrame() && GetUpper()->GetNext() ) + return static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(GetUpper()->GetNext())->Lower()); + if( GetUpper()->IsColBodyFrame() && GetUpper()->GetUpper()->GetNext() ) + return static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(GetUpper()->GetUpper()->GetNext())->Lower()); + // Inside a table-in-section, or sections of headers/footers, there can be only + // one column shift be made, one of the above shortcuts should have applied! + if( !CanContainSplitSection(GetUpper()) || FindFooterOrHeader() ) + return nullptr; + + SwSectionFrame *pSect = FindSctFrame(); + bool bWrongPage = false; + assert(pSect && "GetNextSctLeaf: Missing SectionFrame"); + + // Shortcut for sections with Follows. That's ok, + // if no columns or pages (except dummy pages) lie in between. + // In case of linked frames and in footnotes the shortcut would get + // even more costly + if( pSect->HasFollow() && pSect->IsInDocBody() && !pSect->IsInTab() ) + { + if( pSect->GetFollow() == pSect->GetNext() ) + { + SwPageFrame *pPg = pSect->GetFollow()->FindPageFrame(); + if( WrongPageDesc( pPg ) ) + bWrongPage = true; + else + return FirstLeaf( pSect->GetFollow() ); + } + else + { + SwFrame* pTmp; + if( !pSect->GetUpper()->IsColBodyFrame() || + nullptr == ( pTmp = pSect->GetUpper()->GetUpper()->GetNext() ) ) + pTmp = pSect->FindPageFrame()->GetNext(); + if( pTmp ) // is now the next column or page + { + SwFrame* pTmpX = pTmp; + if( pTmp->IsPageFrame() && static_cast<SwPageFrame*>(pTmp)->IsEmptyPage() ) + pTmp = pTmp->GetNext(); // skip dummy pages + SwFrame *pUp = pSect->GetFollow()->GetUpper(); + // pUp becomes the next column if the Follow lies in a column + // that is not a "not first" one, otherwise the page + if( !pUp->IsColBodyFrame() || + !( pUp = pUp->GetUpper() )->GetPrev() ) + pUp = pUp->FindPageFrame(); + // Now pUp and pTmp have to be the same page/column, otherwise + // pages or columns lie between Master and Follow + if( pUp == pTmp || pUp->GetNext() == pTmpX ) + { + SwPageFrame* pNxtPg = pUp->IsPageFrame() ? + static_cast<SwPageFrame*>(pUp) : pUp->FindPageFrame(); + if( WrongPageDesc( pNxtPg ) ) + bWrongPage = true; + else + return FirstLeaf( pSect->GetFollow() ); + } + } + } + } + +#ifndef NDEBUG + std::vector<SwFrame *> parents; + for (SwFrame * pTmp = GetUpper(); pTmp && !pTmp->IsPageFrame(); pTmp = pTmp->GetUpper()) + { + parents.push_back(pTmp); + } +#endif + + // Always end up in the same section: Body again inside Body etc. + const bool bBody = IsInDocBody(); + const bool bFootnotePage = FindPageFrame()->IsFootnotePage(); + + // The "pLayLeaf is in a table" case is rejected by default, so that it + // can't happen that we try to move a table to one of its own cells. + bool bLayLeafTableAllowed = false; + SwLayoutFrame *pLayLeaf; + + SwLayoutFrame* pCellLeaf = nullptr; + if (GetUpper()->IsInTab()) + { + if (IsTabFrame()) + { + return nullptr; // table in section in table: split disabled for now + } + // We are *in* a table (not an outermost SwTabFrame), see if there + // is a follow cell frame created already. + pCellLeaf = GetNextCellLeaf(); + if (!pCellLeaf) + { + SAL_WARN("sw.layout", "section is in table, but the table is not split"); + return nullptr; + } + } + + // A shortcut for TabFrames such that not all cells need to be visited + if( bWrongPage ) + pLayLeaf = nullptr; + else if( IsTabFrame() ) + { + SwFrame *const pTmpCnt = static_cast<SwTabFrame*>(this)->FindLastContentOrTable(); + pLayLeaf = pTmpCnt ? pTmpCnt->GetUpper() : nullptr; + } + else if (pCellLeaf && CanContainSplitSection(this)) + { + // This frame is in a table-not-in-section, its follow should be + // inserted under the follow of the frame's cell. + pLayLeaf = pCellLeaf; + if (pLayLeaf->FindTabFrame() == FindTabFrame()) + SAL_WARN("sw.layout", "my table frame and my follow's table frame is the same"); + // In this case pLayLeaf pointing to an in-table frame is OK. + bLayLeafTableAllowed = true; + } + else + { + pLayLeaf = GetNextLayoutLeaf(); + if( IsColumnFrame() ) + { + while( pLayLeaf && static_cast<SwColumnFrame*>(this)->IsAnLower( pLayLeaf ) ) + pLayLeaf = pLayLeaf->GetNextLayoutLeaf(); + } + } + + SwLayoutFrame *pOldLayLeaf = nullptr; // Such that in case of newly + // created pages, the search is + // not started over at the beginning + + while( true ) + { + if( pLayLeaf ) + { + // A layout leaf was found, let's see whether it can store me or + // another SectionFrame can be inserted here, or we have to continue + // searching + SwPageFrame* pNxtPg = pLayLeaf->FindPageFrame(); + if ( !bFootnotePage && pNxtPg->IsFootnotePage() ) + { // If I reached the end note pages it's over + pLayLeaf = nullptr; + continue; + } + // Once inBody always inBody, don't step into tables-in-sections and not into other sections + if ( (bBody && !pLayLeaf->IsInDocBody()) || + (IsInFootnote() != pLayLeaf->IsInFootnote() ) || + (pLayLeaf->IsInTab() && !bLayLeafTableAllowed) || + ( pLayLeaf->IsInSct() && ( !pSect->HasFollow() + || pSect->GetFollow() != pLayLeaf->FindSctFrame() ) ) ) + { + // Rejected - try again. + pOldLayLeaf = pLayLeaf; + pLayLeaf = pLayLeaf->GetNextLayoutLeaf(); + continue; + } + // Page desc is never wrong in case of sections-in-tables: in that + // case pLayLeaf points to our section's cell's follow, which is + // fine to be on the same page. New page creation is handled when + // creating / moving the cell frame. + if( WrongPageDesc( pNxtPg ) && !bLayLeafTableAllowed ) + { + if( bWrongPage ) + break; // there's a column between me and my right page + pLayLeaf = nullptr; + bWrongPage = true; + pOldLayLeaf = nullptr; + continue; + } + } + // There is no further LayoutFrame that fits, so a new page + // has to be created, although new pages are worthless within a frame + else if( !pSect->IsInFly() && + ( eMakePage == MAKEPAGE_APPEND || eMakePage == MAKEPAGE_INSERT ) ) + { + InsertPage(pOldLayLeaf ? pOldLayLeaf->FindPageFrame() : FindPageFrame(), + false ); + // and again the whole thing + if (pCellLeaf && CanContainSplitSection(this)) + // GetNextLayoutLeaf() would refer to the next cell in the same + // row, avoid that. pCellLeaf points to the correct cell in the + // follow table, and in the next round it'll be used, as we now + // have a next page. + pLayLeaf = pCellLeaf; + else + pLayLeaf = pOldLayLeaf ? pOldLayLeaf : GetNextLayoutLeaf(); + continue; + } + break; + } + + if( pLayLeaf ) + { + // We have found the suitable layout sheet. If there (in the sheet) is + // already a Follow of our section, we take its first layout sheet, + // otherwise it is time to create a section follow + SwSectionFrame* pNew = nullptr; + + // This can be omitted if existing Follows were cut short + SwFrame* pFirst = pLayLeaf->Lower(); + // Here SectionFrames that are to be deleted must be ignored + while( pFirst && pFirst->IsSctFrame() && !static_cast<SwSectionFrame*>(pFirst)->GetSection() ) + pFirst = pFirst->GetNext(); + if( pFirst && pFirst->IsSctFrame() && pSect->GetFollow() == pFirst ) + pNew = pSect->GetFollow(); + else if( MAKEPAGE_NOSECTION == eMakePage ) + return pLayLeaf; + else if (pSect->GetSection()) + { + pNew = new SwSectionFrame( *pSect, false ); + pNew->InsertBefore( pLayLeaf, pLayLeaf->Lower() ); + pNew->Init(); + SwRectFnSet aRectFnSet(pNew); + aRectFnSet.MakePos( *pNew, pLayLeaf, nullptr, true ); + +#ifndef NDEBUG + { // sanity check the parents of the new frame vs. the old frame + SwFrame * pTmp = pNew; + auto iter(parents.begin()); + if (parents.size() >= 2 && + parents[0]->IsBodyFrame() && parents[1]->IsColumnFrame()) + { // this only inserts section frame - remove column + assert(parents[2]->IsSctFrame() || IsSctFrame()); + if (parents[2]->IsSctFrame()) + std::advance(iter, +2); + else + pTmp = pTmp->GetUpper(); + } + else if (IsBodyFrame() && parents.size() >= 1 + && parents[0]->IsColumnFrame()) + { // same as above, special case: "this" is the body frame + assert(parents[1]->IsSctFrame()); + std::advance(iter, +1); + } + else if (IsSctFrame()) // special case: "this" is the section + { + pTmp = pTmp->GetUpper(); + } + + for ( ; iter != parents.end(); ++iter) + { + if (pTmp->IsPageFrame()) + { + if ((*iter)->IsColumnFrame() && + (iter + 1) != parents.end() && (*(iter + 1))->IsBodyFrame()) + { // page style has columns - evidently these are + break; // added later? + } + assert(!pTmp->IsPageFrame()); + } + assert(pTmp->GetType() == (*iter)->GetType()); + // for cell frames and table frames: + // 1) there may be multiple follow frames of the old one + // 2) the new frame may be identical to the old one + // (not sure if this is allowed, but it happens now + // for the outer table of a nested table) + if (pTmp->IsCellFrame()) + { + SwCellFrame const*const pNewF(static_cast<SwCellFrame*>(pTmp)); + SwCellFrame const*const pOldF(static_cast<SwCellFrame*>(*iter)); + bool bFollowFound(false); + for (SwCellFrame const* pOldIter = pOldF; + pOldIter; pOldIter = pOldIter->GetFollowCell()) + { + if (pOldIter == pNewF) + { + bFollowFound = true; + break; + } + } + assert(bFollowFound); + } + else if (pTmp->IsFlowFrame()) + { + SwFlowFrame const*const pNewF(SwFlowFrame::CastFlowFrame(pTmp)); + SwFlowFrame const*const pOldF(SwFlowFrame::CastFlowFrame(*iter)); + bool bFollowFound(false); + for (SwFlowFrame const* pOldIter = pOldF; + pOldIter; pOldIter = pOldIter->GetFollow()) + { + if (pOldIter == pNewF) + { + bFollowFound = true; + break; + } + } + assert(bFollowFound); + } + pTmp = pTmp->GetUpper(); + } + assert(pTmp == nullptr /* SwFlyAtContentFrame case */ + || pTmp->IsPageFrame() // usual case + // the new page has columns, but the old page did not + || (pTmp->IsColumnFrame() && pTmp->GetUpper()->IsBodyFrame() + && pTmp->GetUpper()->GetUpper()->IsPageFrame())); + } +#endif + + // If our section frame has a successor then that has to be + // moved behind the new Follow of the section frames + SwFrame* pTmp = pSect->GetNext(); + if( pTmp && pTmp != pSect->GetFollow() ) + { + SwFlowFrame* pNxt; + SwContentFrame* pNxtContent = nullptr; + if( pTmp->IsContentFrame() ) + { + pNxt = static_cast<SwContentFrame*>(pTmp); + pNxtContent = static_cast<SwContentFrame*>(pTmp); + } + else + { + pNxtContent = static_cast<SwLayoutFrame*>(pTmp)->ContainsContent(); + if( pTmp->IsSctFrame() ) + pNxt = static_cast<SwSectionFrame*>(pTmp); + else + { + assert(pTmp->IsTabFrame()); + pNxt = static_cast<SwTabFrame*>(pTmp); + } + while( !pNxtContent && nullptr != ( pTmp = pTmp->GetNext() ) ) + { + if( pTmp->IsContentFrame() ) + pNxtContent = static_cast<SwContentFrame*>(pTmp); + else + pNxtContent = static_cast<SwLayoutFrame*>(pTmp)->ContainsContent(); + } + } + if( pNxtContent ) + { + SwFootnoteBossFrame* pOldBoss = pSect->FindFootnoteBossFrame( true ); + if( pOldBoss == pNxtContent->FindFootnoteBossFrame( true ) ) + { + SwSaveFootnoteHeight aHeight( pOldBoss, + pOldBoss->getFrameArea().Top() + pOldBoss->getFrameArea().Height() ); + pSect->GetUpper()->MoveLowerFootnotes( pNxtContent, pOldBoss, + pLayLeaf->FindFootnoteBossFrame( true ), false ); + } + } + pNxt->MoveSubTree( pLayLeaf, pNew->GetNext() ); + } + if( pNew->GetFollow() ) + pNew->SimpleFormat(); + } + // The wanted layout sheet is now the first of the determined SctFrames: + pLayLeaf = pNew ? FirstLeaf(pNew) : nullptr; + } + return pLayLeaf; +} + +/// Returns the preceding layout sheet where the frame can be moved into +SwLayoutFrame *SwFrame::GetPrevSctLeaf() +{ + PROTOCOL_ENTER( this, PROT::Leaf, DbgAction::PrevSect, GetUpper()->FindSctFrame() ) + + SwLayoutFrame* pCol; + // ColumnFrame always contain a BodyFrame now + if( IsColBodyFrame() ) + pCol = GetUpper(); + else if( GetUpper()->IsColBodyFrame() ) + pCol = GetUpper()->GetUpper(); + else + pCol = nullptr; + bool bJump = false; + if( pCol ) + { + if( pCol->GetPrev() ) + { + do + { + pCol = static_cast<SwLayoutFrame*>(pCol->GetPrev()); + // Is there any content? + if( static_cast<SwLayoutFrame*>(pCol->Lower())->Lower() ) + { + if( bJump ) // Did we skip a blank page? + SwFlowFrame::SetMoveBwdJump( true ); + return static_cast<SwLayoutFrame*>(pCol->Lower()); // The columnm body + } + bJump = true; + } while( pCol->GetPrev() ); + + // We get here when all columns are empty, pCol is now the + // first column, we need the body though + pCol = static_cast<SwLayoutFrame*>(pCol->Lower()); + } + else + pCol = nullptr; + } + + if( bJump ) // Did we skip a blank page? + SwFlowFrame::SetMoveBwdJump( true ); + + SwSectionFrame *pSect = FindSctFrame(); + if (!pCol && pSect && IsInTab() && CanContainSplitSection(this)) + { + // We don't have a previous section yet, and we're in a + // section-in-table. + if (SwFlowFrame* pPrecede = pSect->GetPrecede()) + { + // Our section has a precede, work with that. + if (pPrecede->GetFrame().IsLayoutFrame()) + pCol = static_cast<SwLayoutFrame*>(&pPrecede->GetFrame()); + } + } + + // Within sections in tables or section in headers/footers there can + // be only one column change be made, one of the above shortcuts should + // have applied, also when the section has a pPrev. + // Now we even consider an empty column... + OSL_ENSURE( pSect, "GetNextSctLeaf: Missing SectionFrame" ); + if (!pSect || (IsInTab() && !IsTabFrame()) || FindFooterOrHeader()) + return pCol; + + // === IMPORTANT === + // Precondition, which needs to be hold, is that the <this> frame can be + // inside a table, but then the found section frame <pSect> is also inside + // this table. + + // #i95698# + // A table cell containing directly a section does not break - see lcl_FindSectionsInRow(..) + // Thus, a table inside a section, which is inside another table can only + // flow backward in the columns of its section. + // Note: The table cell, which contains the section, can not have a master table cell. + if ( IsTabFrame() && pSect->IsInTab() ) + { + return pCol; + } + + { + if (SwFrame *pPrv = pSect->GetIndPrev()) + { + // Mooching, half dead SectionFrames shouldn't confuse us + while( pPrv && pPrv->IsSctFrame() && !static_cast<SwSectionFrame*>(pPrv)->GetSection() ) + pPrv = pPrv->GetPrev(); + if( pPrv ) + return pCol; + } + } + + const bool bBody = IsInDocBody(); + const bool bFly = IsInFly(); + + SwLayoutFrame *pLayLeaf = GetPrevLayoutLeaf(); + SwLayoutFrame *pPrevLeaf = nullptr; + + while ( pLayLeaf ) + { + // Never step into tables or sections + if ( pLayLeaf->IsInTab() || pLayLeaf->IsInSct() ) + { + pLayLeaf = pLayLeaf->GetPrevLayoutLeaf(); + } + else if ( bBody && pLayLeaf->IsInDocBody() ) + { + // If there is a pLayLeaf has a lower pLayLeaf is the frame we are looking for. + // Exception: pLayLeaf->Lower() is a zombie section frame + const SwFrame* pTmp = pLayLeaf->Lower(); + // OD 11.04.2003 #108824# - consider, that the zombie section frame + // can have frame below it in the found layout leaf. + // Thus, skipping zombie section frame, if possible. + while ( pTmp && pTmp->IsSctFrame() && + !( static_cast<const SwSectionFrame*>(pTmp)->GetSection() ) && + pTmp->GetNext() + ) + { + pTmp = pTmp->GetNext(); + } + if ( pTmp && + ( !pTmp->IsSctFrame() || + ( static_cast<const SwSectionFrame*>(pTmp)->GetSection() ) + ) + ) + { + break; + } + pPrevLeaf = pLayLeaf; + pLayLeaf = pLayLeaf->GetPrevLayoutLeaf(); + if ( pLayLeaf ) + SwFlowFrame::SetMoveBwdJump( true ); + } + else if ( bFly ) + break; // Contents in Flys every layout sheet should be right. Why? + else + pLayLeaf = pLayLeaf->GetPrevLayoutLeaf(); + } + if( !pLayLeaf ) + { + if( !pPrevLeaf ) + return pCol; + pLayLeaf = pPrevLeaf; + } + + SwSectionFrame* pNew = nullptr; + // At first go to the end of the layout sheet + SwFrame *pTmp = pLayLeaf->Lower(); + if( pTmp ) + { + while( pTmp->GetNext() ) + pTmp = pTmp->GetNext(); + if( pTmp->IsSctFrame() ) + { + // Half dead ones only interfere here + while( !static_cast<SwSectionFrame*>(pTmp)->GetSection() && pTmp->GetPrev() && + pTmp->GetPrev()->IsSctFrame() ) + pTmp = pTmp->GetPrev(); + if( static_cast<SwSectionFrame*>(pTmp)->GetFollow() == pSect ) + pNew = static_cast<SwSectionFrame*>(pTmp); + } + } + if( !pNew ) + { + pNew = new SwSectionFrame( *pSect, true ); + pNew->InsertBefore( pLayLeaf, nullptr ); + pNew->Init(); + SwRectFnSet aRectFnSet(pNew); + aRectFnSet.MakePos( *pNew, pLayLeaf, pNew->GetPrev(), true ); + + pLayLeaf = FirstLeaf( pNew ); + if( !pNew->Lower() ) // Format single column sections + { + pNew->MakePos(); + pLayLeaf->Format(getRootFrame()->GetCurrShell()->GetOut()); // In order that the PrtArea is correct for the MoveBwd + } + else + pNew->SimpleFormat(); + } + else + { + pLayLeaf = FirstLeaf( pNew ); + if( pLayLeaf->IsColBodyFrame() ) + { + // In existent section columns we're looking for the last not empty + // column. + SwLayoutFrame *pTmpLay = pLayLeaf; + while( pLayLeaf->GetUpper()->GetNext() ) + { + pLayLeaf = static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(pLayLeaf->GetUpper()->GetNext())->Lower()); + if( pLayLeaf->Lower() ) + pTmpLay = pLayLeaf; + } + // If we skipped an empty column, we've to set the jump-flag + if( pLayLeaf != pTmpLay ) + { + pLayLeaf = pTmpLay; + SwFlowFrame::SetMoveBwdJump( true ); + } + } + } + return pLayLeaf; +} + +static SwTwips lcl_DeadLine( const SwFrame* pFrame ) +{ + const SwLayoutFrame* pUp = pFrame->GetUpper(); + while( pUp && pUp->IsInSct() ) + { + if( pUp->IsSctFrame() ) + pUp = pUp->GetUpper(); + // Columns now with BodyFrame + else if( pUp->IsColBodyFrame() && pUp->GetUpper()->GetUpper()->IsSctFrame() ) + pUp = pUp->GetUpper()->GetUpper(); + else + break; + } + SwRectFnSet aRectFnSet(pFrame); + return pUp ? aRectFnSet.GetPrtBottom(*pUp) : + aRectFnSet.GetBottom(pFrame->getFrameArea()); +} + +/// checks whether the SectionFrame is still able to grow, as case may be the environment has to be asked +bool SwSectionFrame::Growable() const +{ + SwRectFnSet aRectFnSet(this); + if( aRectFnSet.YDiff( lcl_DeadLine( this ), + aRectFnSet.GetBottom(getFrameArea()) ) > 0 ) + return true; + + return ( GetUpper() && const_cast<SwFrame*>(static_cast<SwFrame const *>(GetUpper()))->Grow( LONG_MAX, true ) ); +} + +SwTwips SwSectionFrame::Grow_( SwTwips nDist, bool bTst ) +{ + if ( !IsColLocked() && !HasFixSize() ) + { + SwRectFnSet aRectFnSet(this); + long nFrameHeight = aRectFnSet.GetHeight(getFrameArea()); + if( nFrameHeight > 0 && nDist > (LONG_MAX - nFrameHeight) ) + nDist = LONG_MAX - nFrameHeight; + + if ( nDist <= 0 ) + return 0; + + bool bInCalcContent = GetUpper() && IsInFly() && FindFlyFrame()->IsLocked(); + // OD 2004-03-15 #116561# - allow grow in online layout + bool bGrow = !Lower() || !Lower()->IsColumnFrame() || !Lower()->GetNext(); + if (!bGrow) + { + SwSection* pSection = GetSection(); + bGrow = pSection && pSection->GetFormat()->GetBalancedColumns().GetValue(); + } + if( !bGrow ) + { + const SwViewShell *pSh = getRootFrame()->GetCurrShell(); + bGrow = pSh && pSh->GetViewOptions()->getBrowseMode(); + } + if( bGrow ) + { + SwTwips nGrow; + if( IsInFootnote() ) + nGrow = 0; + else + { + nGrow = lcl_DeadLine( this ); + nGrow = aRectFnSet.YDiff( nGrow, aRectFnSet.GetBottom(getFrameArea()) ); + } + SwTwips nSpace = nGrow; + if( !bInCalcContent && nGrow < nDist && GetUpper() ) + nGrow = o3tl::saturating_add( + nGrow, GetUpper()->Grow( LONG_MAX, true )); + + if( nGrow > nDist ) + nGrow = nDist; + if( nGrow <= 0 ) + { + nGrow = 0; + if (!bTst) + { + if( bInCalcContent ) + InvalidateSize_(); + else + InvalidateSize(); + } + } + else if( !bTst ) + { + if( bInCalcContent ) + InvalidateSize_(); + else if( nSpace < nGrow && nDist != nSpace + GetUpper()-> + Grow( nGrow - nSpace ) ) + InvalidateSize(); + else + { + const SvxGraphicPosition ePos = + GetAttrSet()->GetBackground().GetGraphicPos(); + if ( GPOS_RT < ePos && GPOS_TILED != ePos ) + { + SetCompletePaint(); + InvalidatePage(); + } + if( GetUpper() && GetUpper()->IsHeaderFrame() ) + GetUpper()->InvalidateSize(); + } + + { + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this); + aRectFnSet.AddBottom( aFrm, nGrow ); + } + + { + const long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea()) + nGrow; + SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this); + aRectFnSet.SetHeight( aPrt, nPrtHeight ); + } + + if( Lower() && Lower()->IsColumnFrame() && Lower()->GetNext() ) + { + SwFrame* pTmp = Lower(); + do + { + pTmp->InvalidateSize_(); + pTmp = pTmp->GetNext(); + } while ( pTmp ); + InvalidateSize_(); + } + if( GetNext() ) + { + // Own height changed, need to invalidate the position of + // next frames. + SwFrame *pFrame = GetNext(); + while( pFrame && pFrame->IsSctFrame() && !static_cast<SwSectionFrame*>(pFrame)->GetSection() ) + { + // Invalidate all in-between frames, otherwise position + // calculation (which only looks back to one relative + // frame) will have an incorrect result. + InvalidateFramePos(pFrame, bInCalcContent); + pFrame = pFrame->GetNext(); + } + if( pFrame ) + { + InvalidateFramePos(pFrame, bInCalcContent); + } + } + // #i28701# - Due to the new object positioning + // the frame on the next page/column can flow backward (e.g. it + // was moved forward due to the positioning of its objects ). + // Thus, invalivate this next frame, if document compatibility + // option 'Consider wrapping style influence on object positioning' is ON. + else if ( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) ) + { + InvalidateNextPos(); + } + } + return nGrow; + } + if ( !bTst ) + { + if( bInCalcContent ) + InvalidateSize_(); + else + InvalidateSize(); + } + } + return 0; +} + +SwTwips SwSectionFrame::Shrink_( SwTwips nDist, bool bTst ) +{ + if ( Lower() && !IsColLocked() && !HasFixSize() ) + { + if( ToMaximize( false ) ) + { + if( !bTst ) + InvalidateSize(); + } + else + { + SwRectFnSet aRectFnSet(this); + long nFrameHeight = aRectFnSet.GetHeight(getFrameArea()); + if ( nDist > nFrameHeight ) + nDist = nFrameHeight; + + if ( Lower()->IsColumnFrame() && Lower()->GetNext() && // FootnoteAtEnd + !GetSection()->GetFormat()->GetBalancedColumns().GetValue() ) + { // With column bases the format takes over the control of the + // growth (because of the balance) + if ( !bTst ) + InvalidateSize(); + return nDist; + } + else if( !bTst ) + { + const SvxGraphicPosition ePos = + GetAttrSet()->GetBackground().GetGraphicPos(); + if ( GPOS_RT < ePos && GPOS_TILED != ePos ) + { + SetCompletePaint(); + InvalidatePage(); + } + + { + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this); + aRectFnSet.AddBottom( aFrm, -nDist ); + } + + { + const long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea()) - nDist; + SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this); + aRectFnSet.SetHeight( aPrt, nPrtHeight ); + } + + // We do not allow a section frame to shrink the its upper + // footer frame. This is because in the calculation of a + // footer frame, the content of the section frame is _not_ + // calculated. If there is a fly frame overlapping with the + // footer frame, the section frame is not affected by this + // during the calculation of the footer frame size. + // The footer frame does not grow in its FormatSize function + // but during the calculation of the content of the section + // frame. The section frame grows until some of its text is + // located on top of the fly frame. The next call of CalcContent + // tries to shrink the section and here it would also shrink + // the footer. This may not happen, because shrinking the footer + // would cause the top of the section frame to overlap with the + // fly frame again, this would result in a perfect loop. + if( GetUpper() && !GetUpper()->IsFooterFrame() ) + GetUpper()->Shrink( nDist, bTst ); + + if( Lower() && Lower()->IsColumnFrame() && Lower()->GetNext() ) + { + SwFrame* pTmp = Lower(); + do + { + pTmp->InvalidateSize_(); + pTmp = pTmp->GetNext(); + } while ( pTmp ); + } + if( GetNext() ) + { + SwFrame* pFrame = GetNext(); + while( pFrame && pFrame->IsSctFrame() && !static_cast<SwSectionFrame*>(pFrame)->GetSection() ) + pFrame = pFrame->GetNext(); + if( pFrame ) + pFrame->InvalidatePos(); + else + SetRetouche(); + } + else + SetRetouche(); + return nDist; + } + } + } + return 0; +} + +/* +|* When are Frames within a SectionFrames moveable? +|* If they are not in the last column of a SectionFrames yet, +|* if there is no Follow, +|* if the SectionFrame cannot grow anymore, then it gets more complicated, +|* in that case it depends on whether the SectionFrame can find a next +|* layout sheet. In (column based/chained) Flys this is checked via +|* GetNextLayout, in tables and headers/footers there is none, however in the +|* DocBody and in foot notes there is always one. +|* +|* This routine is used in the TextFormatter to decided whether it's allowed to +|* create a (paragraph-)Follow or whether the paragraph has to stick together +|*/ +bool SwSectionFrame::MoveAllowed( const SwFrame* pFrame) const +{ + // Is there a Follow or is the Frame not in the last column? + if( HasFollow() || ( pFrame->GetUpper()->IsColBodyFrame() && + pFrame->GetUpper()->GetUpper()->GetNext() ) ) + return true; + if( pFrame->IsInFootnote() ) + { + if( IsInFootnote() ) + { + if( GetUpper()->IsInSct() ) + { + if( Growable() ) + return false; + return GetUpper()->FindSctFrame()->MoveAllowed( this ); + } + else + return true; + } + // The content of footnote inside a columned sectionfrm is moveable + // except in the last column + const SwLayoutFrame *pLay = pFrame->FindFootnoteFrame()->GetUpper()->GetUpper(); + if( pLay->IsColumnFrame() && pLay->GetNext() ) + { + // The first paragraph in the first footnote in the first column + // in the sectionfrm at the top of the page is not moveable, + // if the columnbody is empty. + bool bRet = false; + if( pLay->GetIndPrev() || pFrame->GetIndPrev() || + pFrame->FindFootnoteFrame()->GetPrev() ) + bRet = true; + else + { + const SwLayoutFrame* pBody = static_cast<const SwColumnFrame*>(pLay)->FindBodyCont(); + if( pBody && pBody->Lower() ) + bRet = true; + } + if( bRet && ( IsFootnoteAtEnd() || !Growable() ) ) + return true; + } + } + // Or can the section still grow? + if( !IsColLocked() && Growable() ) + return false; + // Now it has to be examined whether there is a layout sheet wherein + // a section Follow can be created + if( !CanContainSplitSection(this) || ( !IsInDocBody() && FindFooterOrHeader() ) ) + return false; // It doesn't work in table-in-sections/nested tables/headers/footers + if( IsInFly() ) // In column based or chained frames + return nullptr != const_cast<SwFrame*>(static_cast<SwFrame const *>(GetUpper()))->GetNextLeaf( MAKEPAGE_NONE ); + return true; +} + +/** Called for a frame inside a section with no direct previous frame (or only + previous empty section frames) the previous frame of the outer section is + returned, if the frame is the first flowing content of this section. + + Note: For a frame inside a table frame, which is inside a section frame, + NULL is returned. +*/ +SwFrame* SwFrame::GetIndPrev_() const +{ + SwFrame *pRet = nullptr; + // #i79774# + // Do not assert, if the frame has a direct previous frame, because it + // could be an empty section frame. The caller has to assure, that the + // frame has no direct previous frame or only empty section frames as + // previous frames. + OSL_ENSURE( /*!pPrev &&*/ IsInSct(), "Why?" ); + const SwFrame* pSct = GetUpper(); + if( !pSct ) + return nullptr; + if( pSct->IsSctFrame() ) + pRet = pSct->GetIndPrev(); + else if( pSct->IsColBodyFrame() && (pSct = pSct->GetUpper()->GetUpper())->IsSctFrame() ) + { + // Do not return the previous frame of the outer section, if in one + // of the previous columns is content. + const SwFrame* pCol = GetUpper()->GetUpper()->GetPrev(); + while( pCol ) + { + OSL_ENSURE( pCol->IsColumnFrame(), "GetIndPrev(): ColumnFrame expected" ); + OSL_ENSURE( pCol->GetLower() && pCol->GetLower()->IsBodyFrame(), + "GetIndPrev(): Where's the body?"); + if( static_cast<const SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pCol)->Lower())->Lower() ) + return nullptr; + pCol = pCol->GetPrev(); + } + pRet = pSct->GetIndPrev(); + } + + // skip empty section frames + while( pRet && pRet->IsSctFrame() && !static_cast<SwSectionFrame*>(pRet)->GetSection() ) + pRet = pRet->GetIndPrev(); + return pRet; +} + +SwFrame* SwFrame::GetIndNext_() +{ + OSL_ENSURE( !mpNext && IsInSct(), "Why?" ); + SwFrame* pSct = GetUpper(); + if( !pSct ) + return nullptr; + if( pSct->IsSctFrame() ) + return pSct->GetIndNext(); + if( pSct->IsColBodyFrame() && (pSct = pSct->GetUpper()->GetUpper())->IsSctFrame() ) + { // We can only return the successor of the SectionFrames if there is no + // content in the successive columns + SwFrame* pCol = GetUpper()->GetUpper()->GetNext(); + while( pCol ) + { + OSL_ENSURE( pCol->IsColumnFrame(), "GetIndNext(): ColumnFrame expected" ); + OSL_ENSURE( pCol->GetLower() && pCol->GetLower()->IsBodyFrame(), + "GetIndNext(): Where's the body?"); + if( static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(pCol)->Lower())->Lower() ) + return nullptr; + pCol = pCol->GetNext(); + } + return pSct->GetIndNext(); + } + return nullptr; +} + +bool SwSectionFrame::IsDescendantFrom( const SwSectionFormat* pFormat ) const +{ + if( !m_pSection || !pFormat ) + return false; + const SwSectionFormat *pMyFormat = m_pSection->GetFormat(); + while( pFormat != pMyFormat ) + { + if( dynamic_cast< const SwSectionFormat *>( pMyFormat->GetRegisteredIn()) != nullptr ) + pMyFormat = static_cast<const SwSectionFormat*>(pMyFormat->GetRegisteredIn()); + else + return false; + } + return true; +} + +void SwSectionFrame::CalcFootnoteAtEndFlag() +{ + SwSectionFormat *pFormat = GetSection()->GetFormat(); + sal_uInt16 nVal = pFormat->GetFootnoteAtTextEnd( false ).GetValue(); + m_bFootnoteAtEnd = FTNEND_ATPGORDOCEND != nVal; + m_bOwnFootnoteNum = FTNEND_ATTXTEND_OWNNUMSEQ == nVal || + FTNEND_ATTXTEND_OWNNUMANDFMT == nVal; + while( !m_bFootnoteAtEnd && !m_bOwnFootnoteNum ) + { + if( dynamic_cast< const SwSectionFormat *>( pFormat->GetRegisteredIn()) != nullptr ) + pFormat = static_cast<SwSectionFormat*>(pFormat->GetRegisteredIn()); + else + break; + nVal = pFormat->GetFootnoteAtTextEnd( false ).GetValue(); + if( FTNEND_ATPGORDOCEND != nVal ) + { + m_bFootnoteAtEnd = true; + m_bOwnFootnoteNum = m_bOwnFootnoteNum ||FTNEND_ATTXTEND_OWNNUMSEQ == nVal || + FTNEND_ATTXTEND_OWNNUMANDFMT == nVal; + } + } +} + +bool SwSectionFrame::IsEndnoteAtMyEnd() const +{ + return m_pSection->GetFormat()->GetEndAtTextEnd( false ).IsAtEnd(); +} + +void SwSectionFrame::CalcEndAtEndFlag() +{ + SwSectionFormat *pFormat = GetSection()->GetFormat(); + m_bEndnAtEnd = pFormat->GetEndAtTextEnd( false ).IsAtEnd(); + while( !m_bEndnAtEnd ) + { + if( dynamic_cast< const SwSectionFormat *>( pFormat->GetRegisteredIn()) != nullptr ) + pFormat = static_cast<SwSectionFormat*>(pFormat->GetRegisteredIn()); + else + break; + m_bEndnAtEnd = pFormat->GetEndAtTextEnd( false ).IsAtEnd(); + } +} + +void SwSectionFrame::Modify( const SfxPoolItem* pOld, const SfxPoolItem * pNew ) +{ + sal_uInt8 nInvFlags = 0; + + if( pNew && RES_ATTRSET_CHG == pNew->Which() ) + { + SfxItemIter aNIter( *static_cast<const SwAttrSetChg*>(pNew)->GetChgSet() ); + SfxItemIter aOIter( *static_cast<const SwAttrSetChg*>(pOld)->GetChgSet() ); + const SfxPoolItem* pNItem = aNIter.GetCurItem(); + const SfxPoolItem* pOItem = aOIter.GetCurItem(); + SwAttrSetChg aOldSet( *static_cast<const SwAttrSetChg*>(pOld) ); + SwAttrSetChg aNewSet( *static_cast<const SwAttrSetChg*>(pNew) ); + do + { + UpdateAttr_(pOItem, pNItem, nInvFlags, &aOldSet, &aNewSet); + pNItem = aNIter.NextItem(); + pOItem = aOIter.NextItem(); + } while (pNItem); + if ( aOldSet.Count() || aNewSet.Count() ) + SwLayoutFrame::Modify( &aOldSet, &aNewSet ); + } + else + UpdateAttr_( pOld, pNew, nInvFlags ); + + if ( nInvFlags != 0 ) + { + if ( nInvFlags & 0x01 ) + InvalidateSize(); + if ( nInvFlags & 0x10 ) + SetCompletePaint(); + } +} + +void SwSectionFrame::SwClientNotify( const SwModify& rMod, const SfxHint& rHint ) +{ + SwFrame::SwClientNotify(rMod, rHint); + // #i117863# + if(&rMod != GetDep()) + return; + const auto pHint = dynamic_cast<const SwSectionFrameMoveAndDeleteHint*>(&rHint); + if(!pHint) + return; + SwSectionFrame::MoveContentAndDelete( this, pHint->IsSaveContent() ); +} + +void SwSectionFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew, + sal_uInt8 &rInvFlags, + SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet ) +{ + bool bClear = true; + const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0; + switch( nWhich ) + { // Suppress multi columns in foot notes + case RES_FMT_CHG: + { + const SwFormatCol& rNewCol = GetFormat()->GetCol(); + if( !IsInFootnote() ) + { + // Nasty case. When allocating a template we can not count + // on the old column attribute. We're left with creating a + // temporary attribute here. + SwFormatCol aCol; + if ( Lower() && Lower()->IsColumnFrame() ) + { + sal_uInt16 nCol = 0; + SwFrame *pTmp = Lower(); + do + { ++nCol; + pTmp = pTmp->GetNext(); + } while ( pTmp ); + aCol.Init( nCol, 0, 1000 ); + } + bool bChgFootnote = IsFootnoteAtEnd(); + bool const bChgEndn = IsEndnAtEnd(); + bool const bChgMyEndn = IsEndnoteAtMyEnd(); + CalcFootnoteAtEndFlag(); + CalcEndAtEndFlag(); + bChgFootnote = ( bChgFootnote != IsFootnoteAtEnd() ) || + ( bChgEndn != IsEndnAtEnd() ) || + ( bChgMyEndn != IsEndnoteAtMyEnd() ); + ChgColumns( aCol, rNewCol, bChgFootnote ); + rInvFlags |= 0x10; + } + rInvFlags |= 0x01; + bClear = false; + } + break; + + case RES_COL: + if( !IsInFootnote() ) + { + assert(pOld && pNew); + if (pOld && pNew) + { + ChgColumns( *static_cast<const SwFormatCol*>(pOld), *static_cast<const SwFormatCol*>(pNew) ); + rInvFlags |= 0x11; + } + } + break; + + case RES_FTN_AT_TXTEND: + if( !IsInFootnote() ) + { + bool const bOld = IsFootnoteAtEnd(); + CalcFootnoteAtEndFlag(); + if (bOld != IsFootnoteAtEnd()) + { + const SwFormatCol& rNewCol = GetFormat()->GetCol(); + ChgColumns( rNewCol, rNewCol, true ); + rInvFlags |= 0x01; + } + } + break; + + case RES_END_AT_TXTEND: + if( !IsInFootnote() ) + { + bool const bOld = IsEndnAtEnd(); + bool const bMyOld = IsEndnoteAtMyEnd(); + CalcEndAtEndFlag(); + if (bOld != IsEndnAtEnd() || bMyOld != IsEndnoteAtMyEnd()) + { + const SwFormatCol& rNewCol = GetFormat()->GetCol(); + ChgColumns( rNewCol, rNewCol, true ); + rInvFlags |= 0x01; + } + } + break; + case RES_COLUMNBALANCE: + rInvFlags |= 0x01; + break; + + case RES_FRAMEDIR : + SetDerivedR2L( false ); + CheckDirChange(); + break; + + case RES_PROTECT: + { + SwViewShell *pSh = getRootFrame()->GetCurrShell(); + if( pSh && pSh->GetLayout()->IsAnyShellAccessible() ) + pSh->Imp()->InvalidateAccessibleEditableState( true, this ); + } + break; + + default: + bClear = false; + } + if ( bClear ) + { + if ( pOldSet || pNewSet ) + { + if ( pOldSet ) + pOldSet->ClearItem( nWhich ); + if ( pNewSet ) + pNewSet->ClearItem( nWhich ); + } + else + SwLayoutFrame::Modify( pOld, pNew ); + } +} + +/// A follow or a ftncontainer at the end of the page causes a maximal Size of the sectionframe. +bool SwSectionFrame::ToMaximize( bool bCheckFollow ) const +{ + if( HasFollow() ) + { + if( !bCheckFollow ) // Don't check superfluous follows + return true; + const SwSectionFrame* pFoll = GetFollow(); + while( pFoll && pFoll->IsSuperfluous() ) + pFoll = pFoll->GetFollow(); + if( pFoll ) + return true; + } + if( IsFootnoteAtEnd() ) + return false; + const SwFootnoteContFrame* pCont = ContainsFootnoteCont(); + if( !IsEndnAtEnd() ) + return nullptr != pCont; + bool bRet = false; + while( pCont && !bRet ) + { + if( pCont->FindFootNote() ) + bRet = true; + else + pCont = ContainsFootnoteCont( pCont ); + } + return bRet; +} + +/// Check every Column for FootnoteContFrames. +SwFootnoteContFrame* SwSectionFrame::ContainsFootnoteCont( const SwFootnoteContFrame* pCont ) const +{ + SwFootnoteContFrame* pRet = nullptr; + const SwLayoutFrame* pLay; + if( pCont ) + { + pLay = pCont->FindFootnoteBossFrame(); + OSL_ENSURE( IsAnLower( pLay ), "ConatainsFootnoteCont: Wrong FootnoteContainer" ); + pLay = static_cast<const SwLayoutFrame*>(pLay->GetNext()); + } + else if( Lower() && Lower()->IsColumnFrame() ) + pLay = static_cast<const SwLayoutFrame*>(Lower()); + else + pLay = nullptr; + while ( !pRet && pLay ) + { + if( pLay->Lower() && pLay->Lower()->GetNext() ) + { + OSL_ENSURE( pLay->Lower()->GetNext()->IsFootnoteContFrame(), + "ToMaximize: Unexpected Frame" ); + pRet = const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pLay->Lower()->GetNext())); + } + OSL_ENSURE( !pLay->GetNext() || pLay->GetNext()->IsLayoutFrame(), + "ToMaximize: ColFrame expected" ); + pLay = static_cast<const SwLayoutFrame*>(pLay->GetNext()); + } + return pRet; +} + +void SwSectionFrame::InvalidateFootnotePos() +{ + SwFootnoteContFrame* pCont = ContainsFootnoteCont(); + if( pCont ) + { + SwFrame *pTmp = pCont->ContainsContent(); + if( pTmp ) + pTmp->InvalidatePos_(); + } +} + +SwTwips SwSectionFrame::CalcUndersize() const +{ + SwRectFnSet aRectFnSet(this); + return InnerHeight() - aRectFnSet.GetHeight(getFramePrintArea()); +} + +SwTwips SwSectionFrame::Undersize() +{ + const auto nRet = CalcUndersize(); + m_bUndersized = (nRet > 0); + return nRet <= 0 ? 0 : nRet; +} + +void SwSectionFrame::CalcFootnoteContent() +{ + vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut(); + SwFootnoteContFrame* pCont = ContainsFootnoteCont(); + if( pCont ) + { + SwFrame* pFrame = pCont->ContainsAny(); + if( pFrame ) + pCont->Calc(pRenderContext); + while( pFrame && IsAnLower( pFrame ) ) + { + SwFootnoteFrame* pFootnote = pFrame->FindFootnoteFrame(); + if( pFootnote ) + pFootnote->Calc(pRenderContext); + pFrame->Calc(pRenderContext); + if( pFrame->IsSctFrame() ) + { + SwFrame *pTmp = static_cast<SwSectionFrame*>(pFrame)->ContainsAny(); + if( pTmp ) + { + pFrame = pTmp; + continue; + } + } + pFrame = pFrame->FindNext(); + } + } +} + +/* + * If a SectionFrame gets empty, e.g. because its content changes the page/column, + * it is not destroyed immediately (there could be a pointer left to it on the + * stack), instead it puts itself in a list at the RootFrame, which is processed + * later on (in Layaction::Action among others). Its size is set to Null and + * the pointer to its page as well. Such SectionFrames that are to be deleted + * must be ignored by the layout/during formatting. + * + * With InsertEmptySct the RootFrame stores a SectionFrame in the list, + * with RemoveFromList it can be removed from the list (Dtor), + * with DeleteEmptySct the list is processed and the SectionFrames are destroyed. + */ +void SwRootFrame::InsertEmptySct( SwSectionFrame* pDel ) +{ + if( !mpDestroy ) + mpDestroy.reset( new SwDestroyList ); + mpDestroy->insert( pDel ); +} + +void SwRootFrame::DeleteEmptySct_() +{ + assert(mpDestroy); + while( !mpDestroy->empty() ) + { + SwSectionFrame* pSect = *mpDestroy->begin(); + mpDestroy->erase( mpDestroy->begin() ); + OSL_ENSURE( !pSect->IsColLocked() && !pSect->IsJoinLocked(), + "DeleteEmptySct: Locked SectionFrame" ); + if( !pSect->getFrameArea().HasArea() && !pSect->ContainsContent() ) + { + SwLayoutFrame* pUp = pSect->GetUpper(); + pSect->RemoveFromLayout(); + SwFrame::DestroyFrame(pSect); + if( pUp && !pUp->Lower() ) + { + if( pUp->IsPageBodyFrame() ) + pUp->getRootFrame()->SetSuperfluous(); + else if( pUp->IsFootnoteFrame() && !pUp->IsColLocked() && + pUp->GetUpper() ) + { + pUp->Cut(); + SwFrame::DestroyFrame(pUp); + } + } + } + else { + OSL_ENSURE( pSect->GetSection(), "DeleteEmptySct: Half-dead SectionFrame?!" ); + } + } +} + +void SwRootFrame::RemoveFromList_( SwSectionFrame* pSct ) +{ + assert(mpDestroy && "Where's my list?"); + mpDestroy->erase( pSct ); +} + +#ifdef DBG_UTIL +bool SwRootFrame::IsInDelList( SwSectionFrame* pSct ) const +{ + return mpDestroy && mpDestroy->find( pSct ) != mpDestroy->end(); +} +#endif + +bool SwSectionFrame::IsBalancedSection() const +{ + bool bRet = false; + if ( GetSection() && Lower() && Lower()->IsColumnFrame() && Lower()->GetNext() ) + { + bRet = !GetSection()->GetFormat()->GetBalancedColumns().GetValue(); + } + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |