3203 lines
116 KiB
C++
3203 lines
116 KiB
C++
/* -*- 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 <config_wasm_strip.h>
|
|
|
|
#include <sal/config.h>
|
|
#include <sal/log.hxx>
|
|
|
|
#include <o3tl/safeint.hxx>
|
|
#include <osl/diagnose.h>
|
|
#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 "layhelp.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)
|
|
{
|
|
StartListening(rSect.GetFormat()->GetNotifier());
|
|
|
|
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 )
|
|
{
|
|
m_bEndNoteSection = rSect.m_bEndNoteSection;
|
|
StartListening(rSect.GetFormat()->GetNotifier());
|
|
|
|
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);
|
|
tools::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.ResolveLeft({}));
|
|
aRectFnSet.SetWidth(aPrt, nWidth - rLRSpace.ResolveLeft({}) - rLRSpace.ResolveRight({}));
|
|
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 (!SfxPoolItem::areSame( 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()
|
|
{
|
|
}
|
|
|
|
//virtual
|
|
bool SwSectionFrame::IsHiddenNow() const
|
|
{
|
|
const auto* pSection = GetSection();
|
|
return !pSection || pSection->CalcHiddenFlag();
|
|
}
|
|
|
|
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.
|
|
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
|
|
{
|
|
SwViewShell* pViewShell( getRootFrame()->GetCurrShell() );
|
|
if ( pViewShell && pViewShell->GetLayout() &&
|
|
pViewShell->GetLayout()->IsAnyShellAccessible() )
|
|
{
|
|
auto pNext = FindNextCnt( true );
|
|
auto pPrev = FindPrevCnt();
|
|
pViewShell->InvalidateAccessibleParaFlowRelation(
|
|
pNext ? pNext->DynCastTextFrame() : nullptr,
|
|
pPrev ? pPrev->DynCastTextFrame() : nullptr );
|
|
}
|
|
}
|
|
#endif
|
|
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 )
|
|
return;
|
|
|
|
{
|
|
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
|
|
pFrame = GetPrev();
|
|
if ( nullptr != pFrame )
|
|
{
|
|
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 )
|
|
return;
|
|
|
|
SwRectFnSet aRectFnSet(this);
|
|
SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
|
|
if( nFrameHeight <= 0 )
|
|
return;
|
|
|
|
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())
|
|
return;
|
|
|
|
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 content of the second one
|
|
|* starts after pFrameStartAfter; the created second section frame itself
|
|
|* is put after pFramePutAfter.
|
|
|* If pFrameStartAfter is nullptr, the split happens at the start.
|
|
|* This is required when inserting an inner section, because the MoveFwd
|
|
|* cannot have the desired effect within a frame or a table cell.
|
|
|* Splitting at the start/end makes sense, because the empty frame would
|
|
|* be removed after the InsertCnt_ finished.
|
|
|*/
|
|
SwSectionFrame* SwSectionFrame::SplitSect( SwFrame* pFrameStartAfter, SwFrame* pFramePutAfter )
|
|
{
|
|
assert(!pFrameStartAfter || IsAnLower(pFrameStartAfter));
|
|
SwFrame* pSav;
|
|
if (pFrameStartAfter)
|
|
{
|
|
pSav = pFrameStartAfter->FindNext();
|
|
// If pFrameStartAfter is a complex object like table, and it has no next,
|
|
// its FindNext may return its own last subframe. In this case, assume that
|
|
// we are at the end.
|
|
if (pSav && pFrameStartAfter->IsLayoutFrame())
|
|
if (static_cast<SwLayoutFrame*>(pFrameStartAfter)->IsAnLower(pSav))
|
|
pSav = nullptr;
|
|
}
|
|
else
|
|
{
|
|
pSav = ContainsAny();
|
|
}
|
|
if (pSav && !IsAnLower(pSav))
|
|
pSav = nullptr; // we are at the very end
|
|
|
|
// Put the content aside
|
|
if (pSav)
|
|
pSav = ::SaveContent( this, pSav );
|
|
|
|
// Create a new SctFrame, not as a Follower/master
|
|
if (!pFramePutAfter)
|
|
pFramePutAfter = this;
|
|
SwSectionFrame* pNew = new SwSectionFrame( *GetSection(), this );
|
|
pNew->InsertBehind( pFramePutAfter->GetUpper(), pFramePutAfter );
|
|
pNew->Init();
|
|
SwRectFnSet aRectFnSet(this);
|
|
aRectFnSet.MakePos( *pNew, nullptr, pFramePutAfter, 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.
|
|
if (pSav)
|
|
{
|
|
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 pNew;
|
|
}
|
|
|
|
/**
|
|
|* 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;
|
|
}
|
|
|
|
if (!GetPrev() && !IsFollow() && IsInDocBody() && IsHiddenNow())
|
|
{
|
|
// This may be the first frame on a page, and it may had moved to that page because its
|
|
// content required that (a page break in the first paragraph, or a tall first line, or
|
|
// "do not break paragraph" setting, or the like). Try to move back, to allow following
|
|
// frames to move back, if possible. Sections cannot move back; workaround by a call to
|
|
// GetPrevSctLeaf(), which may return a candidate upper frame on a previous page, or it
|
|
// may create a new master for this at the end of the previous page. Cut and paste this
|
|
// appropriately; then drop the temporary, if needed.
|
|
if (SwLayoutFrame* moveBackPos = GetPrevSctLeaf())
|
|
{
|
|
if (moveBackPos->IsColBodyFrame())
|
|
{
|
|
moveBackPos = moveBackPos->GetUpper()->GetUpper();
|
|
}
|
|
SwLayoutFrame* newUpper = moveBackPos;
|
|
SwFrame* newSibling = nullptr;
|
|
const bool temporaryMasterCreated = IsFollow();
|
|
if (temporaryMasterCreated)
|
|
{
|
|
assert(moveBackPos == &GetPrecede()->GetFrame());
|
|
newUpper = moveBackPos->GetUpper();
|
|
newSibling = moveBackPos->GetNext(); // actually, will be also nullptr
|
|
}
|
|
if (newUpper != GetUpper())
|
|
{
|
|
// Can't use MoveSubTree, because the move needs to fire events to re-layout
|
|
Cut();
|
|
Paste(newUpper, newSibling);
|
|
}
|
|
if (temporaryMasterCreated)
|
|
{
|
|
moveBackPos->Cut();
|
|
DestroyFrame(moveBackPos);
|
|
}
|
|
assert(!IsFollow());
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (GetFollow() && IsHiddenNow())
|
|
{
|
|
// Merge all the follows of this hidden section
|
|
while (auto* follow = GetFollow())
|
|
{
|
|
MergeNext(follow);
|
|
if (GetFollow() == follow) // failed to merge
|
|
break; // avoid endless loop
|
|
}
|
|
}
|
|
|
|
// 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( auto pNewFormat = dynamic_cast< const SwSectionFormat *>( pFormat->GetRegisteredIn()) )
|
|
pFormat = pNewFormat;
|
|
else
|
|
return nullptr;
|
|
}
|
|
return pFormat;
|
|
}
|
|
|
|
static void lcl_FindContentFrame( SwContentFrame* &rpContentFrame, SwFootnoteFrame* &rpFootnoteFrame,
|
|
SwFrame* pFrame, bool &rbChkFootnote )
|
|
{
|
|
if( !pFrame )
|
|
return;
|
|
|
|
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);
|
|
tools::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 )
|
|
{
|
|
tools::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) )
|
|
return;
|
|
|
|
nDiff = aRectFnSet.YDiff( nDeadLine, aRectFnSet.GetTop(getFrameArea()) );
|
|
if( nDiff < 0 )
|
|
nDeadLine = aRectFnSet.GetTop(getFrameArea());
|
|
const Size aOldSz( getFramePrintArea().SSize() );
|
|
tools::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()) )
|
|
return;
|
|
|
|
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 );
|
|
}
|
|
|
|
tools::Long nHeight = aRectFnSet.GetHeight(getFrameArea());
|
|
tools::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 )
|
|
return;
|
|
|
|
// 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 );
|
|
}
|
|
}
|
|
if ( rFrame.IsLayoutFrame() )
|
|
{
|
|
SwFrame* pLowerFrame = rFrame.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 (GetSection()->CalcHiddenFlag())
|
|
{
|
|
{
|
|
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
|
|
aRectFnSet.SetHeight(aFrm, 0);
|
|
}
|
|
{
|
|
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
|
|
aRectFnSet.SetHeight(aPrt, 0);
|
|
}
|
|
setFrameAreaSizeValid(true);
|
|
setFramePrintAreaValid(true);
|
|
}
|
|
|
|
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.ResolveLeft({}), rLRSpace.ResolveRight({}));
|
|
|
|
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 tools::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 tools::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.ResolveLeft({}) - rLRSpace.ResolveRight({}));
|
|
}
|
|
|
|
// 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());
|
|
{
|
|
tools::Long nBottom = aRectFnSet.GetBottom(getFrameArea());
|
|
nBottom = aRectFnSet.YInc( nBottom, -nDiff );
|
|
tools::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 )
|
|
{
|
|
tools::Long nTmp = nRemaining - aRectFnSet.GetHeight(getFrameArea());
|
|
tools::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();
|
|
tools::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.
|
|
// It doesn't make sense to move to a page that starts with break?
|
|
if (pNxtPg != FindPageFrame() // tdf#156725 not between columns!
|
|
&& !FindFlyFrame() // tdf#156419 linked fly frames don't care!
|
|
&& (WrongPageDesc(pNxtPg) || HasPageBreakBefore(*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, SwResizeLimitReason& reason, bool bTst)
|
|
{
|
|
SwSection* pSection = GetSection();
|
|
if (pSection && pSection->CalcHiddenFlag())
|
|
{
|
|
reason = SwResizeLimitReason::Unspecified;
|
|
return 0;
|
|
}
|
|
|
|
if (IsColLocked() || HasFixSize())
|
|
{
|
|
reason = HasFixSize() ? SwResizeLimitReason::FixedSizeFrame : SwResizeLimitReason::Unspecified;
|
|
return 0;
|
|
}
|
|
|
|
const auto nOrigDist = nDist;
|
|
reason = SwResizeLimitReason::Unspecified;
|
|
SwRectFnSet aRectFnSet(this);
|
|
tools::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)
|
|
{
|
|
bGrow = pSection && pSection->GetFormat()->GetBalancedColumns().GetValue();
|
|
if (!bGrow && nOrigDist)
|
|
reason = SwResizeLimitReason::BalancedColumns;
|
|
}
|
|
if( !bGrow )
|
|
{
|
|
const SwViewShell *pSh = getRootFrame()->GetCurrShell();
|
|
bGrow = pSh && pSh->GetViewOptions()->getBrowseMode();
|
|
}
|
|
if (!bGrow)
|
|
{
|
|
if (!bTst)
|
|
{
|
|
if (bInCalcContent)
|
|
InvalidateSize_();
|
|
else
|
|
InvalidateSize();
|
|
}
|
|
return 0;
|
|
}
|
|
reason = SwResizeLimitReason::Unspecified; // reset what maybe was set in balanced columns check
|
|
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, reason, false, false))
|
|
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 tools::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;
|
|
}
|
|
|
|
SwTwips SwSectionFrame::Shrink_( SwTwips nDist, bool bTst )
|
|
{
|
|
if ( Lower() && !IsColLocked() && !HasFixSize() )
|
|
{
|
|
if( ToMaximize( false ) )
|
|
{
|
|
if( !bTst )
|
|
InvalidateSize();
|
|
}
|
|
else
|
|
{
|
|
SwRectFnSet aRectFnSet(this);
|
|
tools::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 tools::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 )
|
|
{
|
|
assert(pCol->IsColumnFrame());
|
|
assert(pCol->GetLower() && pCol->GetLower()->IsBodyFrame());
|
|
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 )
|
|
{
|
|
assert(pCol->IsColumnFrame());
|
|
assert(pCol->GetLower() && pCol->GetLower()->IsBodyFrame());
|
|
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( auto pNewFormat = dynamic_cast< const SwSectionFormat *>( pMyFormat->GetRegisteredIn()) )
|
|
pMyFormat = pNewFormat;
|
|
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( auto pNewFormat = dynamic_cast<SwSectionFormat *>( pFormat->GetRegisteredIn()) )
|
|
pFormat = pNewFormat;
|
|
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( auto pNewFormat = dynamic_cast<SwSectionFormat *>( pFormat->GetRegisteredIn()) )
|
|
pFormat = pNewFormat;
|
|
else
|
|
break;
|
|
m_bEndnAtEnd = pFormat->GetEndAtTextEnd( false ).IsAtEnd();
|
|
}
|
|
}
|
|
|
|
static void InvalidateFramesInSection(SwFrame * pFrame)
|
|
{
|
|
while (pFrame)
|
|
{
|
|
pFrame->InvalidateAll();
|
|
pFrame->InvalidateObjs(false);
|
|
if (pFrame->IsLayoutFrame())
|
|
{
|
|
InvalidateFramesInSection(pFrame->GetLower());
|
|
}
|
|
else if (pFrame->IsTextFrame())
|
|
{
|
|
pFrame->Prepare(PrepareHint::Clear, nullptr, false);
|
|
}
|
|
pFrame = pFrame->GetNext();
|
|
}
|
|
}
|
|
|
|
void SwSectionFrame::Notify(SfxHint const& rHint)
|
|
{
|
|
SwSectionFormat *const pFormat(GetSection()->GetFormat());
|
|
assert(pFormat);
|
|
SwClientNotify(*pFormat, rHint);
|
|
}
|
|
|
|
void SwSectionFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
|
|
{
|
|
if (rHint.GetId() == SfxHintId::SwLegacyModify || rHint.GetId() == SfxHintId::SwFormatChange)
|
|
{
|
|
SwSectionFrameInvFlags eInvFlags = SwSectionFrameInvFlags::NONE;
|
|
if (rHint.GetId() == SfxHintId::SwLegacyModify)
|
|
{
|
|
auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
|
|
if(pLegacy->m_pNew && RES_ATTRSET_CHG == pLegacy->m_pNew->Which())
|
|
{
|
|
auto& rOldSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pOld);
|
|
auto& rNewSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew);
|
|
SfxItemIter aOIter(*rOldSetChg.GetChgSet());
|
|
SfxItemIter aNIter(*rNewSetChg.GetChgSet());
|
|
const SfxPoolItem* pOItem = aOIter.GetCurItem();
|
|
const SfxPoolItem* pNItem = aNIter.GetCurItem();
|
|
SwAttrSetChg aOldSet(rOldSetChg);
|
|
SwAttrSetChg aNewSet(rNewSetChg);
|
|
do
|
|
{
|
|
UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
|
|
pNItem = aNIter.NextItem();
|
|
pOItem = aOIter.NextItem();
|
|
} while (pNItem);
|
|
if(aOldSet.Count() || aNewSet.Count())
|
|
SwLayoutFrame::SwClientNotify(rMod, sw::LegacyModifyHint(&aOldSet, &aNewSet));
|
|
}
|
|
else
|
|
UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
|
|
}
|
|
else // rHint.GetId() == SfxHintId::SwFormatChange)
|
|
{
|
|
UpdateAttrForFormatChange(eInvFlags);
|
|
}
|
|
|
|
if (eInvFlags != SwSectionFrameInvFlags::NONE)
|
|
{
|
|
if(eInvFlags & SwSectionFrameInvFlags::InvalidateSize)
|
|
InvalidateSize();
|
|
if(eInvFlags & SwSectionFrameInvFlags::SetCompletePaint)
|
|
SetCompletePaint();
|
|
}
|
|
}
|
|
else if(rHint.GetId() == SfxHintId::SwSectionFrameMoveAndDelete)
|
|
{
|
|
const auto pHint = static_cast<const SwSectionFrameMoveAndDeleteHint*>(&rHint);
|
|
// #i117863#
|
|
if(&rMod != GetDep())
|
|
return;
|
|
SwSectionFrame::MoveContentAndDelete(this, pHint->IsSaveContent());
|
|
}
|
|
else if (rHint.GetId() == SfxHintId::SwSectionHidden)
|
|
{
|
|
InvalidateAll();
|
|
InvalidateObjs(false);
|
|
{
|
|
// Set it to a huge positive value, to make sure a recalculation fires
|
|
constexpr SwTwips HUGE_POSITIVE = o3tl::toTwips(100, o3tl::Length::m);
|
|
SwFrameAreaDefinition::FrameAreaWriteAccess area(*this);
|
|
SwRectFnSet(this).SetHeight(area, HUGE_POSITIVE);
|
|
}
|
|
|
|
InvalidateFramesInSection(Lower());
|
|
if (Lower())
|
|
Lower()->HideAndShowObjects(); // recursive
|
|
// Check if any page-breaks have been unhidden, create the new pages.
|
|
// Call IsHiddenNow() because a parent section could still hide.
|
|
if (!IsFollow() && IsInDocBody() && !IsInTab() && !IsHiddenNow())
|
|
{
|
|
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
|
|
SwViewShell *const pViewShell(getRootFrame()->GetCurrShell());
|
|
// no notification if SwViewShell is in construction
|
|
if (pViewShell && !pViewShell->IsInConstructor()
|
|
&& pViewShell->GetLayout()
|
|
&& pViewShell->GetLayout()->IsAnyShellAccessible())
|
|
{
|
|
auto pNext = FindNextCnt(true);
|
|
auto pPrev = FindPrevCnt();
|
|
pViewShell->InvalidateAccessibleParaFlowRelation(
|
|
pNext ? pNext->DynCastTextFrame() : nullptr,
|
|
pPrev ? pPrev->DynCastTextFrame() : nullptr );
|
|
}
|
|
#endif
|
|
SwSectionFrame * pFollow{this};
|
|
SwPageFrame * pPage{FindPageFrame()};
|
|
SwLayoutFrame * pLay{nullptr};
|
|
bool isBreakAfter{false};
|
|
SwFrame * pFirstOnPage{pPage->FindFirstBodyContent()};
|
|
while (pFirstOnPage->GetUpper()->IsInTab())
|
|
{
|
|
pFirstOnPage = pFirstOnPage->GetUpper();
|
|
}
|
|
assert(pFirstOnPage->IsContentFrame() || pFirstOnPage->IsTabFrame());
|
|
SwColumnFrame* pColumn{ Lower() && Lower()->IsColumnFrame()
|
|
? static_cast<SwColumnFrame*>(Lower())
|
|
: nullptr };
|
|
auto IterateLower = [&pColumn](SwFrame *const pLowerFrame) -> SwFrame*
|
|
{
|
|
if (pLowerFrame->GetNext())
|
|
{
|
|
return pLowerFrame->GetNext();
|
|
}
|
|
if (pColumn)
|
|
{
|
|
pColumn = static_cast<SwColumnFrame*>(pColumn->GetNext());
|
|
if (pColumn)
|
|
{
|
|
return static_cast<SwLayoutFrame*>(pColumn->Lower())->Lower();
|
|
}
|
|
}
|
|
return nullptr;
|
|
};
|
|
for (SwFrame* pLowerFrame = pColumn
|
|
? static_cast<SwLayoutFrame*>(pColumn->Lower())->Lower()
|
|
: Lower();
|
|
pLowerFrame;
|
|
pLowerFrame = IterateLower(pLowerFrame))
|
|
{
|
|
if (pLowerFrame == pFirstOnPage)
|
|
{
|
|
continue;
|
|
}
|
|
assert(pLowerFrame->IsContentFrame() || pLowerFrame->IsTabFrame());
|
|
if (SwLayHelper::CheckInsertPage(pPage, pLay, pLowerFrame, isBreakAfter, false))
|
|
{
|
|
if (pLowerFrame == Lower())
|
|
{ // move the whole section
|
|
assert(pFollow == this);
|
|
MoveSubTree(pLay, nullptr);
|
|
}
|
|
else
|
|
{
|
|
if (pFollow->GetNext())
|
|
{
|
|
assert(pFollow->GetNext()->IsFlowFrame());
|
|
SwFlowFrame::CastFlowFrame(pFollow->GetNext())->MoveSubTree(pLay, nullptr);
|
|
}
|
|
pFollow = new SwSectionFrame(*pFollow, false);
|
|
SimpleFormat();
|
|
pFollow->InsertBehind(pLay, nullptr);
|
|
pFollow->Init();
|
|
SwLayoutFrame *const pTarget{pColumn
|
|
? static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(pFollow->Lower())->Lower())
|
|
: pFollow};
|
|
SwFlowFrame::CastFlowFrame(pLowerFrame)->MoveSubTree(pTarget, nullptr);
|
|
}
|
|
}
|
|
}
|
|
CheckPageDescs(FindPageFrame());
|
|
}
|
|
}
|
|
else
|
|
SwFrame::SwClientNotify(rMod, rHint);
|
|
}
|
|
|
|
void SwSectionFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
|
|
SwSectionFrameInvFlags &rInvFlags,
|
|
SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
|
|
{
|
|
bool bClear = true;
|
|
const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
|
|
switch( nWhich )
|
|
{
|
|
case RES_COL:
|
|
if( !IsInFootnote() )
|
|
{
|
|
assert(pOld && pNew);
|
|
if (pOld && pNew)
|
|
{
|
|
ChgColumns( *static_cast<const SwFormatCol*>(pOld), *static_cast<const SwFormatCol*>(pNew) );
|
|
rInvFlags |= SwSectionFrameInvFlags::InvalidateSize | SwSectionFrameInvFlags::SetCompletePaint;
|
|
}
|
|
}
|
|
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 |= SwSectionFrameInvFlags::InvalidateSize;
|
|
}
|
|
}
|
|
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 |= SwSectionFrameInvFlags::InvalidateSize;
|
|
}
|
|
}
|
|
break;
|
|
case RES_COLUMNBALANCE:
|
|
rInvFlags |= SwSectionFrameInvFlags::InvalidateSize;
|
|
break;
|
|
|
|
case RES_FRAMEDIR :
|
|
SetDerivedR2L( false );
|
|
CheckDirChange();
|
|
break;
|
|
|
|
case RES_PROTECT:
|
|
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
|
|
{
|
|
SwViewShell *pSh = getRootFrame()->GetCurrShell();
|
|
if( pSh && pSh->GetLayout()->IsAnyShellAccessible() )
|
|
pSh->Imp()->InvalidateAccessibleEditableState( true, this );
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
bClear = false;
|
|
}
|
|
if ( !bClear )
|
|
return;
|
|
|
|
if ( pOldSet || pNewSet )
|
|
{
|
|
if ( pOldSet )
|
|
pOldSet->ClearItem( nWhich );
|
|
if ( pNewSet )
|
|
pNewSet->ClearItem( nWhich );
|
|
}
|
|
else
|
|
{
|
|
SwModify aMod;
|
|
SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
|
|
}
|
|
}
|
|
|
|
void SwSectionFrame::UpdateAttrForFormatChange( SwSectionFrameInvFlags &rInvFlags )
|
|
{
|
|
// Suppress multi columns in foot notes
|
|
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;
|
|
SwFrame* pLower = Lower();
|
|
if ( pLower && pLower->IsColumnFrame() )
|
|
{
|
|
sal_uInt16 nCol = 0;
|
|
SwFrame *pTmp = pLower;
|
|
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 |= SwSectionFrameInvFlags::SetCompletePaint;
|
|
}
|
|
rInvFlags |= SwSectionFrameInvFlags::InvalidateSize;
|
|
}
|
|
|
|
/// 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 ), "ContainsFootnoteCont: 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 )
|
|
return;
|
|
|
|
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" );
|
|
SAL_WARN_IF(pSect->IsDeleteForbidden(), "sw.layout", "not allowed delete SwFrame");
|
|
if( !pSect->getFrameArea().HasArea() && !pSect->ContainsContent() && !pSect->IsDeleteForbidden() )
|
|
{
|
|
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;
|
|
}
|
|
|
|
void SwSectionFrame::dumpAsXml(xmlTextWriterPtr writer) const
|
|
{
|
|
(void)xmlTextWriterStartElement(writer, BAD_CAST("section"));
|
|
dumpAsXmlAttributes( writer );
|
|
if ( HasFollow() )
|
|
(void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "follow" ), "%" SAL_PRIuUINT32, GetFollow()->GetFrameId() );
|
|
|
|
if (m_pPrecede != nullptr)
|
|
(void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "precede" ), "%" SAL_PRIuUINT32, m_pPrecede->GetFrame().GetFrameId() );
|
|
|
|
(void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
|
|
dumpInfosAsXml(writer);
|
|
(void)xmlTextWriterEndElement(writer);
|
|
dumpChildrenAsXml(writer);
|
|
|
|
(void)xmlTextWriterEndElement(writer);
|
|
}
|
|
|
|
void SwSectionFrame::dumpAsXmlAttributes(xmlTextWriterPtr writer) const
|
|
{
|
|
SwLayoutFrame::dumpAsXmlAttributes(writer);
|
|
|
|
SwSectionNode const*const pNode(GetSection() ? GetSection()->GetFormat()->GetSectionNode() : nullptr);
|
|
(void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("sectionNodeIndex"), "%" SAL_PRIdINT32, pNode ? sal_Int32(pNode->GetIndex()) : -1);
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|