From 940b4d1848e8c70ab7642901a68594e8016caffc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Apr 2024 18:51:28 +0200 Subject: Adding upstream version 1:7.0.4. Signed-off-by: Daniel Baumann --- sw/source/core/layout/frmtool.cxx | 3897 +++++++++++++++++++++++++++++++++++++ 1 file changed, 3897 insertions(+) create mode 100644 sw/source/core/layout/frmtool.cxx (limited to 'sw/source/core/layout/frmtool.cxx') diff --git a/sw/source/core/layout/frmtool.cxx b/sw/source/core/layout/frmtool.cxx new file mode 100644 index 000000000..295000c0a --- /dev/null +++ b/sw/source/core/layout/frmtool.cxx @@ -0,0 +1,3897 @@ +/* -*- 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "layhelp.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; + +bool bObjsDirect = true; +bool bDontCreateObjects = false; +bool bSetCompletePaintOnInvalidate = false; + +sal_uInt8 StackHack::nCnt = 0; +bool StackHack::bLocked = false; + +SwFrameNotify::SwFrameNotify( SwFrame *pF ) : + mpFrame( pF ), + maFrame( pF->getFrameArea() ), + maPrt( pF->getFramePrintArea() ), + mbInvaKeep( false ), + mbValidSize( pF->isFrameAreaSizeValid() ) +{ + if ( pF->IsTextFrame() ) + { + mnFlyAnchorOfst = static_cast(pF)->GetBaseOffsetForFly( true ); + mnFlyAnchorOfstNoWrap = static_cast(pF)->GetBaseOffsetForFly( false ); + } + else + { + mnFlyAnchorOfst = 0; + mnFlyAnchorOfstNoWrap = 0; + } + + mbHadFollow = pF->IsContentFrame() && static_cast(pF)->GetFollow(); +} + +SwFrameNotify::~SwFrameNotify() COVERITY_NOEXCEPT_FALSE +{ + SwRectFnSet aRectFnSet(mpFrame); + const bool bAbsP = aRectFnSet.PosDiff(maFrame, mpFrame->getFrameArea()); + const bool bChgWidth = + aRectFnSet.GetWidth(maFrame) != aRectFnSet.GetWidth(mpFrame->getFrameArea()); + const bool bChgHeight = + aRectFnSet.GetHeight(maFrame)!=aRectFnSet.GetHeight(mpFrame->getFrameArea()); + const bool bChgFlyBasePos = mpFrame->IsTextFrame() && + ( ( mnFlyAnchorOfst != static_cast(mpFrame)->GetBaseOffsetForFly( true ) ) || + ( mnFlyAnchorOfstNoWrap != static_cast(mpFrame)->GetBaseOffsetForFly( false ) ) ); + + if ( mpFrame->IsFlowFrame() && !mpFrame->IsInFootnote() ) + { + SwFlowFrame *pFlow = SwFlowFrame::CastFlowFrame( mpFrame ); + + if ( !pFlow->IsFollow() ) + { + if ( !mpFrame->GetIndPrev() ) + { + if ( mbInvaKeep ) + { + SwFrame *pPre = mpFrame->FindPrev(); + if ( pPre && pPre->IsFlowFrame() ) + { + // 1. pPre wants to keep with me: + bool bInvalidPrePos = SwFlowFrame::CastFlowFrame(pPre)->IsKeep(pPre->GetAttrSet()->GetKeep(), pPre->GetBreakItem()) + && pPre->GetIndPrev(); + + // 2. pPre is a table and the last row wants to keep with me: + if ( !bInvalidPrePos && pPre->IsTabFrame() ) + { + SwTabFrame* pPreTab = static_cast(pPre); + if ( pPreTab->GetFormat()->GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::TABLE_ROW_KEEP) ) + { + SwRowFrame* pLastRow = static_cast(pPreTab->GetLastLower()); + if ( pLastRow && pLastRow->ShouldRowKeepWithNext() ) + bInvalidPrePos = true; + } + } + + if ( bInvalidPrePos ) + pPre->InvalidatePos(); + } + } + } + else if ( !pFlow->HasFollow() ) + { + long nOldHeight = aRectFnSet.GetHeight(maFrame); + long nNewHeight = aRectFnSet.GetHeight(mpFrame->getFrameArea()); + if( (nOldHeight > nNewHeight) || (!nOldHeight && nNewHeight) ) + pFlow->CheckKeep(); + } + } + } + + if ( bAbsP ) + { + mpFrame->SetCompletePaint(); + + SwFrame* pNxt = mpFrame->GetIndNext(); + // #121888# - skip empty section frames + while ( pNxt && + pNxt->IsSctFrame() && !static_cast(pNxt)->GetSection() ) + { + pNxt = pNxt->GetIndNext(); + } + + if ( pNxt ) + pNxt->InvalidatePos(); + else + { + // #104100# - correct condition for setting retouche + // flag for vertical layout. + if( mpFrame->IsRetoucheFrame() && + aRectFnSet.TopDist( maFrame, aRectFnSet.GetTop(mpFrame->getFrameArea()) ) > 0 ) + { + mpFrame->SetRetouche(); + } + + // A fresh follow frame does not have to be invalidated, because + // it is already formatted: + if ( mbHadFollow || !mpFrame->IsContentFrame() || !static_cast(mpFrame)->GetFollow() ) + { + if ( !mpFrame->IsTabFrame() || !static_cast(mpFrame)->GetFollow() ) + mpFrame->InvalidateNextPos(); + } + } + } + + //For each resize of the background graphics is a repaint necessary. + const bool bPrtWidth = + aRectFnSet.GetWidth(maPrt) != aRectFnSet.GetWidth(mpFrame->getFramePrintArea()); + const bool bPrtHeight = + aRectFnSet.GetHeight(maPrt)!=aRectFnSet.GetHeight(mpFrame->getFramePrintArea()); + if ( bPrtWidth || bPrtHeight ) + { + bool bUseNewFillProperties(false); + if (mpFrame->supportsFullDrawingLayerFillAttributeSet()) + { + drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes(mpFrame->getSdrAllFillAttributesHelper()); + if(aFillAttributes && aFillAttributes->isUsed()) + { + bUseNewFillProperties = true; + // use SetCompletePaint if needed + if(aFillAttributes->needCompleteRepaint()) + { + mpFrame->SetCompletePaint(); + } + } + } + if (!bUseNewFillProperties) + { + const SvxGraphicPosition ePos = mpFrame->GetAttrSet()->GetBackground().GetGraphicPos(); + if(GPOS_NONE != ePos && GPOS_TILED != ePos) + mpFrame->SetCompletePaint(); + } + } + else + { + // #97597# - consider case that *only* margins between + // frame and printing area has changed. Then, frame has to be repainted, + // in order to force paint of the margin areas. + if ( !bAbsP && (bChgWidth || bChgHeight) ) + { + mpFrame->SetCompletePaint(); + } + } + + const bool bPrtP = aRectFnSet.PosDiff( maPrt, mpFrame->getFramePrintArea() ); + if ( bAbsP || bPrtP || bChgWidth || bChgHeight || + bPrtWidth || bPrtHeight || bChgFlyBasePos ) + { + if( mpFrame->IsAccessibleFrame() ) + { + SwRootFrame *pRootFrame = mpFrame->getRootFrame(); + if( pRootFrame && pRootFrame->IsAnyShellAccessible() && + pRootFrame->GetCurrShell() ) + { + pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( mpFrame, maFrame ); + } + } + + // Notification of anchored objects + if ( mpFrame->GetDrawObjs() ) + { + const SwSortedObjs &rObjs = *mpFrame->GetDrawObjs(); + SwPageFrame* pPageFrame = nullptr; + for (SwAnchoredObject* pObj : rObjs) + { + // OD 2004-03-31 #i26791# - no general distinction between + // Writer fly frames and drawing objects + bool bNotify = false; + bool bNotifySize = false; + SwContact* pContact = ::GetUserCall( pObj->GetDrawObj() ); + const bool bAnchoredAsChar = pContact->ObjAnchoredAsChar(); + if ( !bAnchoredAsChar ) + { + // Notify object, which aren't anchored as-character: + + // always notify objects, if frame position has changed + // or if the object is to-page|to-fly anchored. + if ( bAbsP || + pContact->ObjAnchoredAtPage() || + pContact->ObjAnchoredAtFly() ) + { + bNotify = true; + + // assure that to-fly anchored Writer fly frames are + // registered at the correct page frame, if frame + // position has changed. + if ( bAbsP && pContact->ObjAnchoredAtFly() && + dynamic_cast( pObj) != nullptr ) + { + // determine to-fly anchored Writer fly frame + SwFlyFrame* pFlyFrame = static_cast(pObj); + // determine page frame of to-fly anchored + // Writer fly frame + SwPageFrame* pFlyPageFrame = pFlyFrame->FindPageFrame(); + // determine page frame, if needed. + if ( !pPageFrame ) + { + pPageFrame = mpFrame->FindPageFrame(); + } + if ( pPageFrame != pFlyPageFrame ) + { + OSL_ENSURE( pFlyPageFrame, "~SwFrameNotify: Fly from Nowhere" ); + if( pFlyPageFrame ) + pFlyPageFrame->MoveFly( pFlyFrame, pPageFrame ); + else + pPageFrame->AppendFlyToPage( pFlyFrame ); + } + } + } + // otherwise the objects are notified in dependence to + // its positioning and alignment + else + { + const SwFormatVertOrient& rVert = + pContact->GetFormat()->GetVertOrient(); + if ( ( rVert.GetVertOrient() == text::VertOrientation::CENTER || + rVert.GetVertOrient() == text::VertOrientation::BOTTOM || + rVert.GetRelationOrient() == text::RelOrientation::PRINT_AREA ) && + ( bChgHeight || bPrtHeight ) ) + { + bNotify = true; + } + if ( !bNotify ) + { + const SwFormatHoriOrient& rHori = + pContact->GetFormat()->GetHoriOrient(); + if ( ( rHori.GetHoriOrient() != text::HoriOrientation::NONE || + rHori.GetRelationOrient()== text::RelOrientation::PRINT_AREA || + rHori.GetRelationOrient()== text::RelOrientation::FRAME ) && + ( bChgWidth || bPrtWidth || bChgFlyBasePos ) ) + { + bNotify = true; + } + } + } + } + else if ( bPrtWidth ) + { + // Notify as-character anchored objects, if printing area + // width has changed. + bNotify = true; + bNotifySize = true; + } + + // perform notification via the corresponding invalidations + if ( bNotify ) + { + if ( dynamic_cast( pObj) != nullptr ) + { + SwFlyFrame* pFlyFrame = static_cast(pObj); + if ( bNotifySize ) + pFlyFrame->InvalidateSize_(); + // #115759# - no invalidation of + // position for as-character anchored objects. + if ( !bAnchoredAsChar ) + { + pFlyFrame->InvalidatePos_(); + } + pFlyFrame->Invalidate_(); + } + else if ( dynamic_cast( pObj) != nullptr ) + { + // #115759# - no invalidation of + // position for as-character anchored objects. + if ( !bAnchoredAsChar ) + { + pObj->InvalidateObjPos(); + } + } + else + { + OSL_FAIL( " - unknown anchored object type." ); + } + } + } + } + } + else if( mpFrame->IsTextFrame() && mbValidSize != mpFrame->isFrameAreaSizeValid() ) + { + SwRootFrame *pRootFrame = mpFrame->getRootFrame(); + if( pRootFrame && pRootFrame->IsAnyShellAccessible() && + pRootFrame->GetCurrShell() ) + { + pRootFrame->GetCurrShell()->Imp()->InvalidateAccessibleFrameContent( mpFrame ); + } + } + + // #i9046# Automatic frame width + SwFlyFrame* pFly = nullptr; + // #i35879# Do not trust the inf flags. pFrame does not + // necessarily have to have an upper! + if ( !mpFrame->IsFlyFrame() && nullptr != ( pFly = mpFrame->ImplFindFlyFrame() ) ) + { + // #i61999# + // no invalidation of columned Writer fly frames, because automatic + // width doesn't make sense for such Writer fly frames. + if ( pFly->Lower() && !pFly->Lower()->IsColumnFrame() ) + { + const SwFormatFrameSize &rFrameSz = pFly->GetFormat()->GetFrameSize(); + + // This could be optimized. Basically the fly frame only has to + // be invalidated, if the first line of pFrame (if pFrame is a content + // frame, for other frame types it's the print area) has changed its + // size and pFrame was responsible for the current width of pFly. On + // the other hand, this is only rarely used and re-calculation of + // the fly frame does not cause too much trouble. So we keep it this + // way: + if ( SwFrameSize::Fixed != rFrameSz.GetWidthSizeType() ) + { + // #i50668#, #i50998# - invalidation of position + // of as-character anchored fly frames not needed and can cause + // layout loops + if ( dynamic_cast( pFly) == nullptr ) + { + pFly->InvalidatePos(); + } + pFly->InvalidateSize(); + } + } + } +} + +SwLayNotify::SwLayNotify( SwLayoutFrame *pLayFrame ) : + SwFrameNotify( pLayFrame ), + m_bLowersComplete( false ) +{ +} + +// OD 2004-05-11 #i28701# - local method to invalidate the position of all +// frames inclusive its floating screen objects, which are lowers of the given +// layout frame +static void lcl_InvalidatePosOfLowers( SwLayoutFrame& _rLayoutFrame ) +{ + if( _rLayoutFrame.IsFlyFrame() && _rLayoutFrame.GetDrawObjs() ) + { + _rLayoutFrame.InvalidateObjs( false ); + } + + SwFrame* pLowerFrame = _rLayoutFrame.Lower(); + while ( pLowerFrame ) + { + pLowerFrame->InvalidatePos(); + if ( pLowerFrame->IsTextFrame() ) + { + static_cast(pLowerFrame)->Prepare( PrepareHint::FramePositionChanged ); + } + else if ( pLowerFrame->IsTabFrame() ) + { + pLowerFrame->InvalidatePrt(); + } + + pLowerFrame->InvalidateObjs( false ); + + pLowerFrame = pLowerFrame->GetNext(); + } +} + +SwLayNotify::~SwLayNotify() +{ + SwLayoutFrame *pLay = static_cast(mpFrame); + SwRectFnSet aRectFnSet(pLay); + bool bNotify = false; + if ( pLay->getFramePrintArea().SSize() != maPrt.SSize() ) + { + if ( !IsLowersComplete() ) + { + bool bInvaPercent; + + if ( pLay->IsRowFrame() ) + { + bInvaPercent = true; + long nNew = aRectFnSet.GetHeight(pLay->getFramePrintArea()); + if( nNew != aRectFnSet.GetHeight(maPrt) ) + static_cast(pLay)->AdjustCells( nNew, true); + if( aRectFnSet.GetWidth(pLay->getFramePrintArea()) + != aRectFnSet.GetWidth(maPrt) ) + static_cast(pLay)->AdjustCells( 0, false ); + } + else + { + //Proportional adoption of the internal. + //1. If the formatted is no Fly + //2. If he contains no columns + //3. If the Fly has a fixed height and the columns + // are next to be. + // Hoehe danebenliegen. + //4. Never at SectionFrames. + bool bLow; + if( pLay->IsFlyFrame() ) + { + if ( pLay->Lower() ) + { + bLow = !pLay->Lower()->IsColumnFrame() || + aRectFnSet.GetHeight(pLay->Lower()->getFrameArea()) + != aRectFnSet.GetHeight(pLay->getFramePrintArea()); + } + else + bLow = false; + } + else if( pLay->IsSctFrame() ) + { + if ( pLay->Lower() ) + { + if( pLay->Lower()->IsColumnFrame() && pLay->Lower()->GetNext() ) + bLow = pLay->Lower()->getFrameArea().Height() != pLay->getFramePrintArea().Height(); + else + bLow = pLay->getFramePrintArea().Width() != maPrt.Width(); + } + else + bLow = false; + } + else if( pLay->IsFooterFrame() && !pLay->HasFixSize() ) + bLow = pLay->getFramePrintArea().Width() != maPrt.Width(); + else + bLow = true; + bInvaPercent = bLow; + if ( bLow ) + { + pLay->ChgLowersProp( maPrt.SSize() ); + } + // If the PrtArea has been extended, it might be possible that the chain of parts + // can take another frame. As a result, the "possible right one" needs to be + // invalidated. This only pays off if this or its Uppers are moveable sections. + // A PrtArea has been extended if width or height are larger than before. + if ( (pLay->getFramePrintArea().Height() > maPrt.Height() || + pLay->getFramePrintArea().Width() > maPrt.Width()) && + (pLay->IsMoveable() || pLay->IsFlyFrame()) ) + { + SwFrame *pTmpFrame = pLay->Lower(); + if ( pTmpFrame && pTmpFrame->IsFlowFrame() ) + { + while ( pTmpFrame->GetNext() ) + pTmpFrame = pTmpFrame->GetNext(); + pTmpFrame->InvalidateNextPos(); + } + } + } + bNotify = true; + //EXPENSIVE!! But how we do it more elegant? + if( bInvaPercent ) + pLay->InvaPercentLowers( pLay->getFramePrintArea().Height() - maPrt.Height() ); + } + if ( pLay->IsTabFrame() ) + //So that _only_ the shadow is drawn while resizing. + static_cast(pLay)->SetComplete(); + else + { + const SwViewShell *pSh = pLay->getRootFrame()->GetCurrShell(); + if( !( pSh && pSh->GetViewOptions()->getBrowseMode() ) || + !(pLay->GetType() & (SwFrameType::Body | SwFrameType::Page)) ) + //Thereby the subordinates are retouched clean. + //Example problem: Take the Flys with the handles and downsize. + //Not for body and page, otherwise it flickers when loading HTML. + pLay->SetCompletePaint(); + } + } + //Notify Lower if the position has changed. + const bool bPrtPos = aRectFnSet.PosDiff( maPrt, pLay->getFramePrintArea() ); + const bool bPos = bPrtPos || aRectFnSet.PosDiff( maFrame, pLay->getFrameArea() ); + const bool bSize = pLay->getFrameArea().SSize() != maFrame.SSize(); + + if ( bPos && pLay->Lower() && !IsLowersComplete() ) + { + pLay->Lower()->InvalidatePos(); + SwFootnoteFrame* pFtnFrame = pLay->Lower()->IsFootnoteFrame() ? + static_cast(pLay->Lower()) : nullptr; + SwFrame* pFtnLower = pFtnFrame ? pFtnFrame->Lower() : nullptr; + if (pFtnLower) + pFtnLower->InvalidatePos(); + } + + if ( bPrtPos ) + pLay->SetCompletePaint(); + + //Inform the Follower if the SSize has changed. + if ( bSize ) + { + if( pLay->GetNext() ) + { + if ( pLay->GetNext()->IsLayoutFrame() ) + pLay->GetNext()->InvalidatePos_(); + else + pLay->GetNext()->InvalidatePos(); + } + else if( pLay->IsSctFrame() ) + pLay->InvalidateNextPos(); + } + if ( !IsLowersComplete() && + !(pLay->GetType()&(SwFrameType::Fly|SwFrameType::Section) && + pLay->Lower() && pLay->Lower()->IsColumnFrame()) && + (bPos || bNotify) && + !(pLay->GetType() & (SwFrameType::Row|SwFrameType::Tab|SwFrameType::FtnCont|SwFrameType::Page|SwFrameType::Root))) + { + // #i44016# - force unlock of position of lower objects. + // #i43913# - no unlock of position of objects, + // if is a cell frame, and its table frame resp. its parent table + // frame is locked. + // #i47458# - force unlock of position of lower objects, + // only if position of layout frame has changed. + bool bUnlockPosOfObjs( bPos ); + if ( bUnlockPosOfObjs && pLay->IsCellFrame() ) + { + SwTabFrame* pTabFrame( pLay->FindTabFrame() ); + if ( pTabFrame && + ( pTabFrame->IsJoinLocked() || + ( pTabFrame->IsFollow() && + pTabFrame->FindMaster()->IsJoinLocked() ) ) ) + { + bUnlockPosOfObjs = false; + } + } + // #i49383# - check for footnote frame, if unlock + // of position of lower objects is allowed. + else if ( bUnlockPosOfObjs && pLay->IsFootnoteFrame() ) + { + bUnlockPosOfObjs = static_cast(pLay)->IsUnlockPosOfLowerObjs(); + } + // #i51303# - no unlock of object positions for sections + else if ( bUnlockPosOfObjs && pLay->IsSctFrame() ) + { + bUnlockPosOfObjs = false; + } + pLay->NotifyLowerObjs( bUnlockPosOfObjs ); + } + if ( bPos && pLay->IsFootnoteFrame() && pLay->Lower() ) + { + // OD 2004-05-11 #i28701# + ::lcl_InvalidatePosOfLowers( *pLay ); + } + if( ( bPos || bSize ) && pLay->IsFlyFrame() && static_cast(pLay)->GetAnchorFrame() + && static_cast(pLay)->GetAnchorFrame()->IsFlyFrame() ) + static_cast(pLay)->AnchorFrame()->InvalidateSize(); +} + +SwFlyNotify::SwFlyNotify( SwFlyFrame *pFlyFrame ) : + SwLayNotify( pFlyFrame ), + // #115759# - keep correct page frame - the page frame + // the Writer fly frame is currently registered at. + pOldPage( pFlyFrame->GetPageFrame() ), + aFrameAndSpace( pFlyFrame->GetObjRectWithSpaces() ) +{ +} + +SwFlyNotify::~SwFlyNotify() +{ + SwFlyFrame *pFly = static_cast(mpFrame); + if ( pFly->IsNotifyBack() ) + { + SwViewShell *pSh = pFly->getRootFrame()->GetCurrShell(); + SwViewShellImp *pImp = pSh ? pSh->Imp() : nullptr; + if ( !pImp || !pImp->IsAction() || !pImp->GetLayAction().IsAgain() ) + { + //If in the LayAction the IsAgain is set it can be + //that the old page is destroyed in the meantime! + ::Notify( pFly, pOldPage, aFrameAndSpace, &maPrt ); + // #i35640# - additional notify anchor text frame, + // if Writer fly frame has changed its page + if ( pFly->GetAnchorFrame()->IsTextFrame() && + pFly->GetPageFrame() != pOldPage ) + { + pFly->AnchorFrame()->Prepare( PrepareHint::FlyFrameLeave ); + } + } + pFly->ResetNotifyBack(); + } + + //Have the size or the position changed, + //so should the view know this. + SwRectFnSet aRectFnSet(pFly); + const bool bPosChgd = aRectFnSet.PosDiff( maFrame, pFly->getFrameArea() ); + const bool bFrameChgd = pFly->getFrameArea().SSize() != maFrame.SSize(); + const bool bPrtChgd = maPrt != pFly->getFramePrintArea(); + if ( bPosChgd || bFrameChgd || bPrtChgd ) + { + pFly->NotifyDrawObj(); + } + if ( bPosChgd && maFrame.Pos().X() != FAR_AWAY ) + { + // OD 2004-05-10 #i28701# - no direct move of lower Writer fly frames. + // reason: New positioning and alignment (e.g. to-paragraph anchored, + // but aligned at page) are introduced. + // takes care of invalidation of lower + // floating screen objects by calling method . + + if ( pFly->IsFlyAtContentFrame() ) + { + SwFrame *pNxt = pFly->AnchorFrame()->FindNext(); + if ( pNxt ) + { + pNxt->InvalidatePos(); + } + } + + // #i26945# - notify anchor. + // Needed for negative positioned Writer fly frames + if ( pFly->GetAnchorFrame()->IsTextFrame() ) + { + pFly->AnchorFrame()->Prepare( PrepareHint::FlyFrameLeave ); + } + } + + // OD 2004-05-13 #i28701# + // #i45180# - no adjustment of layout process flags and + // further notifications/invalidations, if format is called by grow/shrink + if ( pFly->ConsiderObjWrapInfluenceOnObjPos() && + ( dynamic_cast( pFly) == nullptr || + !static_cast(pFly)->IsNoMoveOnCheckClip() ) ) + { + // #i54138# - suppress restart of the layout process + // on changed frame height. + // Note: It doesn't seem to be necessary and can cause layout loops. + if ( bPosChgd ) + { + // indicate a restart of the layout process + pFly->SetRestartLayoutProcess( true ); + } + else + { + // lock position + pFly->LockPosition(); + } + + if ( !pFly->ConsiderForTextWrap() ) + { + // indicate that object has to be considered for text wrap + pFly->SetConsiderForTextWrap( true ); + // invalidate 'background' in order to allow its 'background' + // to wrap around it. + pFly->NotifyBackground( pFly->GetPageFrame(), + pFly->GetObjRectWithSpaces(), + PrepareHint::FlyFrameArrive ); + // invalidate position of anchor frame in order to force + // a re-format of the anchor frame, which also causes a + // re-format of the invalid previous frames of the anchor frame. + pFly->AnchorFrame()->InvalidatePos(); + } + } +} + +SwContentNotify::SwContentNotify( SwContentFrame *pContentFrame ) : + SwFrameNotify( pContentFrame ), + // OD 08.01.2004 #i11859# + mbChkHeightOfLastLine( false ), + mnHeightOfLastLine( 0 ), + // OD 2004-02-26 #i25029# + mbInvalidatePrevPrtArea( false ), + mbBordersJoinedWithPrev( false ) +{ + // OD 08.01.2004 #i11859# + if ( pContentFrame->IsTextFrame() ) + { + SwTextFrame* pTextFrame = static_cast(pContentFrame); + if (!pTextFrame->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::OLD_LINE_SPACING)) + { + const SvxLineSpacingItem &rSpace = pTextFrame->GetAttrSet()->GetLineSpacing(); + if ( rSpace.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop ) + { + mbChkHeightOfLastLine = true; + mnHeightOfLastLine = pTextFrame->GetHeightOfLastLine(); + } + } + } +} + +SwContentNotify::~SwContentNotify() +{ + SwContentFrame *pCnt = static_cast(mpFrame); + if ( bSetCompletePaintOnInvalidate ) + pCnt->SetCompletePaint(); + + SwRectFnSet aRectFnSet(pCnt); + if ( pCnt->IsInTab() && ( aRectFnSet.PosDiff( pCnt->getFrameArea(), maFrame ) || + pCnt->getFrameArea().SSize() != maFrame.SSize())) + { + SwLayoutFrame* pCell = pCnt->GetUpper(); + while( !pCell->IsCellFrame() && pCell->GetUpper() ) + pCell = pCell->GetUpper(); + OSL_ENSURE( pCell->IsCellFrame(), "Where's my cell?" ); + if ( text::VertOrientation::NONE != pCell->GetFormat()->GetVertOrient().GetVertOrient() ) + pCell->InvalidatePrt(); //for the vertical align. + } + + // OD 2004-02-26 #i25029# + if ( mbInvalidatePrevPrtArea && mbBordersJoinedWithPrev && + pCnt->IsTextFrame() && + !pCnt->IsFollow() && !pCnt->GetIndPrev() ) + { + // determine previous frame + SwFrame* pPrevFrame = pCnt->FindPrev(); + // skip empty section frames and hidden text frames + { + while ( pPrevFrame && + ( ( pPrevFrame->IsSctFrame() && + !static_cast(pPrevFrame)->GetSection() ) || + ( pPrevFrame->IsTextFrame() && + static_cast(pPrevFrame)->IsHiddenNow() ) ) ) + { + pPrevFrame = pPrevFrame->FindPrev(); + } + } + + // Invalidate printing area of found previous frame + if ( pPrevFrame ) + { + if ( pPrevFrame->IsSctFrame() ) + { + if ( pCnt->IsInSct() ) + { + // Note: found previous frame is a section frame and + // is also inside a section. + // Thus due to , + // had joined its borders/shadow with the + // last content of the found section. + // Invalidate printing area of last content in found section. + SwFrame* pLstContentOfSctFrame = + static_cast(pPrevFrame)->FindLastContent(); + if ( pLstContentOfSctFrame ) + { + pLstContentOfSctFrame->InvalidatePrt(); + } + } + } + else + { + pPrevFrame->InvalidatePrt(); + } + } + } + + const bool bFirst = aRectFnSet.GetWidth(maFrame) == 0; + + if ( pCnt->IsNoTextFrame() ) + { + //Active PlugIn's or OLE-Objects should know something of the change + //thereby they move their window appropriate. + SwViewShell *pSh = pCnt->getRootFrame()->GetCurrShell(); + if ( pSh ) + { + SwOLENode *const pNd(static_cast(pCnt)->GetNode()->GetOLENode()); + if (nullptr != pNd && + (pNd->GetOLEObj().IsOleRef() || + pNd->IsOLESizeInvalid()) ) + { + const bool bNoTextFramePrtAreaChanged = + ( maPrt.SSize().Width() != 0 && + maPrt.SSize().Height() != 0 ) && + maPrt.SSize() != pCnt->getFramePrintArea().SSize(); + OSL_ENSURE( pCnt->IsInFly(), "OLE not in FlyFrame" ); + SwFlyFrame *pFly = pCnt->FindFlyFrame(); + svt::EmbeddedObjectRef& xObj = pNd->GetOLEObj().GetObject(); + SwFEShell *pFESh = nullptr; + for(SwViewShell& rCurrentShell : pSh->GetRingContainer()) + { if ( dynamic_cast( &rCurrentShell) != nullptr ) + { + pFESh = static_cast(&rCurrentShell); + // #108369#: Here used to be the condition if (!bFirst). + // I think this should mean "do not call CalcAndSetScale" + // if the frame is formatted for the first time. + // Unfortunately this is not valid anymore since the + // SwNoTextFrame already gets a width during CalcLowerPreps. + // Nevertheless, the indention of !bFirst seemed to be + // to assure that the OLE objects have already been notified + // if necessary before calling CalcAndSetScale. + // So I replaced !bFirst by !IsOLESizeInvalid. There is + // one additional problem specific to the word import: + // The layout is calculated _before_ calling PrtOLENotify, + // and the OLE objects are not invalidated during import. + // Therefore I added the condition !IsUpdateExpField, + // have a look at the occurrence of CalcLayout in + // uiview/view.cxx. + if ( !pNd->IsOLESizeInvalid() && + !pSh->GetDoc()->getIDocumentState().IsUpdateExpField() ) + pFESh->CalcAndSetScale( xObj, + &pFly->getFramePrintArea(), &pFly->getFrameArea(), + bNoTextFramePrtAreaChanged ); + } + } + + if ( pFESh && pNd->IsOLESizeInvalid() ) + { + pNd->SetOLESizeInvalid( false ); + pFESh->CalcAndSetScale( xObj ); // create client + } + } + //dito animated graphics + if ( getFrameArea().HasArea() && static_cast(pCnt)->HasAnimation() ) + { + static_cast(pCnt)->StopAnimation(); + pSh->InvalidateWindows( getFrameArea() ); + } + } + } + + if ( bFirst ) + { + pCnt->SetRetouche(); //fix(13870) + + SwDoc *const pDoc = pCnt->IsTextFrame() + ? &static_cast(pCnt)->GetDoc() + : static_cast(pCnt)->GetNode()->GetDoc(); + if ( !pDoc->GetSpzFrameFormats()->empty() && + pDoc->DoesContainAtPageObjWithContentAnchor() && !pDoc->getIDocumentState().IsNewDoc() ) + { + // If certain import filters for foreign file format import + // AT_PAGE anchored objects, the corresponding page number is + // typically not known. In this case the content position is + // stored at which the anchored object is found in the + // imported document. + // When this content is formatted it is the time at which + // the page is known. Thus, this data can be corrected now. + + const SwPageFrame *pPage = nullptr; + SwFrameFormats *pTable = pDoc->GetSpzFrameFormats(); + + for ( size_t i = 0; i < pTable->size(); ++i ) + { + SwFrameFormat *pFormat = (*pTable)[i]; + const SwFormatAnchor &rAnch = pFormat->GetAnchor(); + if ( RndStdIds::FLY_AT_PAGE != rAnch.GetAnchorId() || + rAnch.GetContentAnchor() == nullptr ) + { + continue; + } + + if (FrameContainsNode(*pCnt, rAnch.GetContentAnchor()->nNode.GetIndex())) + { + OSL_FAIL( " - to page anchored object with content position." ); + if ( !pPage ) + { + pPage = pCnt->FindPageFrame(); + } + SwFormatAnchor aAnch( rAnch ); + aAnch.SetAnchor( nullptr ); + aAnch.SetPageNum( pPage->GetPhyPageNum() ); + pFormat->SetFormatAttr( aAnch ); + if ( RES_DRAWFRMFMT != pFormat->Which() ) + { + pFormat->MakeFrames(); + } + } + } + } + } + + // OD 12.01.2004 #i11859# - invalidate printing area of following frame, + // if height of last line has changed. + if ( pCnt->IsTextFrame() && mbChkHeightOfLastLine ) + { + if ( mnHeightOfLastLine != static_cast(pCnt)->GetHeightOfLastLine() ) + { + pCnt->InvalidateNextPrtArea(); + } + } + + // #i44049# + if ( pCnt->IsTextFrame() && aRectFnSet.PosDiff( maFrame, pCnt->getFrameArea() ) ) + { + pCnt->InvalidateObjs(); + } + + // #i43255# - move code to invalidate at-character + // anchored objects due to a change of its anchor character from + // method . + if ( pCnt->IsTextFrame() ) + { + SwTextFrame* pMasterFrame = pCnt->IsFollow() + ? static_cast(pCnt)->FindMaster() + : static_cast(pCnt); + if ( pMasterFrame && !pMasterFrame->IsFlyLock() && + pMasterFrame->GetDrawObjs() ) + { + SwSortedObjs* pObjs = pMasterFrame->GetDrawObjs(); + for (SwAnchoredObject* pAnchoredObj : *pObjs) + { + if ( pAnchoredObj->GetFrameFormat().GetAnchor().GetAnchorId() + == RndStdIds::FLY_AT_CHAR ) + { + pAnchoredObj->CheckCharRectAndTopOfLine( !pMasterFrame->IsEmpty() ); + } + } + } + } +} + +// note this *cannot* be static because it's a friend +void AppendObj(SwFrame *const pFrame, SwPageFrame *const pPage, SwFrameFormat *const pFormat, const SwFormatAnchor & rAnch) +{ + const bool bFlyAtFly = rAnch.GetAnchorId() == RndStdIds::FLY_AT_FLY; // LAYER_IMPL + //Is a frame or a SdrObject described? + const bool bSdrObj = RES_DRAWFRMFMT == pFormat->Which(); + // OD 23.06.2003 #108784# - append also drawing objects anchored + // as character. + const bool bDrawObjInContent = bSdrObj && + (rAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR); + + if( bFlyAtFly || + (rAnch.GetAnchorId() == RndStdIds::FLY_AT_PARA) || + (rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR) || + bDrawObjInContent ) + { + SdrObject* pSdrObj = nullptr; + if ( bSdrObj && nullptr == (pSdrObj = pFormat->FindSdrObject()) ) + { + OSL_ENSURE( !bSdrObj, "DrawObject not found." ); + pFormat->GetDoc()->DelFrameFormat( pFormat ); + return; + } + if ( pSdrObj ) + { + if ( !pSdrObj->getSdrPageFromSdrObject() ) + { + pFormat->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0)-> + InsertObject(pSdrObj, pSdrObj->GetOrdNumDirect()); + } + + SwDrawContact* pNew = + static_cast(GetUserCall( pSdrObj )); + if ( !pNew->GetAnchorFrame() ) + { + pFrame->AppendDrawObj( *(pNew->GetAnchoredObj( nullptr )) ); + } + // OD 19.06.2003 #108784# - add 'virtual' drawing object, + // if necessary. But control objects have to be excluded. + else if ( !::CheckControlLayer( pSdrObj ) && + pNew->GetAnchorFrame() != pFrame && + !pNew->GetDrawObjectByAnchorFrame( *pFrame ) ) + { + SwDrawVirtObj* pDrawVirtObj = pNew->AddVirtObj(); + pFrame->AppendDrawObj( *(pNew->GetAnchoredObj( pDrawVirtObj )) ); + + pDrawVirtObj->ActionChanged(); + } + } + else + { + SwFlyFrame *pFly; + if( bFlyAtFly ) + pFly = new SwFlyLayFrame( static_cast(pFormat), pFrame, pFrame ); + else + pFly = new SwFlyAtContentFrame( static_cast(pFormat), pFrame, pFrame ); + pFly->Lock(); + pFrame->AppendFly( pFly ); + pFly->Unlock(); + if ( pPage ) + ::RegistFlys( pPage, pFly ); + } + } +} + +static bool IsShown(sal_uLong const nIndex, + const SwFormatAnchor & rAnch, + std::vector::const_iterator const*const pIter, + std::vector::const_iterator const*const pEnd, + SwTextNode const*const pFirstNode, SwTextNode const*const pLastNode) +{ + assert(!pIter || *pIter == *pEnd || (*pIter)->pNode->GetIndex() == nIndex); + SwPosition const& rAnchor(*rAnch.GetContentAnchor()); + if (rAnchor.nNode.GetIndex() != nIndex) + { + return false; + } + if (rAnch.GetAnchorId() == RndStdIds::FLY_AT_PARA) + { + return pIter == nullptr // not merged + || pIter != pEnd // at least one char visible in node + || !IsSelectFrameAnchoredAtPara(rAnchor, + SwPosition(const_cast(*pFirstNode), 0), + SwPosition(const_cast(*pLastNode), pLastNode->Len())); + } + if (pIter) + { + // note: frames are not sorted by anchor position. + assert(pEnd); + assert(pFirstNode); + assert(pLastNode); + assert(rAnch.GetAnchorId() != RndStdIds::FLY_AT_FLY); + for (auto iter = *pIter; iter != *pEnd; ++iter) + { + assert(iter->nStart != iter->nEnd); // TODO possible? + assert(iter->pNode->GetIndex() == nIndex); + if (rAnchor.nContent.GetIndex() < iter->nStart) + { + return false; + } + if (rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR) + { + // if there is an extent then obviously the node was not + // deleted fully... + // show if start <= pos <= end + // *or* if first-node/0 *and* not StartOfSection + // *or* if last-node/Len *and* not EndOfSection + + // first determine the extent to compare to, then + // construct start/end positions for the deletion *before* the + // extent and compare once. + // the interesting corner cases are on the edge of the extent! + // no need to check for > the last extent because those + // are never visible. + if (rAnchor.nContent.GetIndex() <= iter->nEnd) + { + if (iter->nStart == 0) + { + return true; + } + else + { + SwPosition const start( + const_cast( + iter == *pIter + ? *pFirstNode // simplification + : *iter->pNode), + iter == *pIter // first extent? + ? iter->pNode == pFirstNode + ? 0 // at start of 1st node + : pFirstNode->Len() // previous node; simplification but should get right result + : (iter-1)->nEnd); // previous extent + SwPosition const end(*iter->pNode, iter->nStart); + return !IsDestroyFrameAnchoredAtChar(rAnchor, start, end); + } + } + else if (iter == *pEnd - 1) // special case: after last extent + { + if (iter->nEnd == iter->pNode->Len()) + { + return true; // special case: end of node + } + else + { + SwPosition const start(*iter->pNode, iter->nEnd); + SwPosition const end( + const_cast(*pLastNode), // simplification + iter->pNode == pLastNode + ? iter->pNode->Len() + : 0); + return !IsDestroyFrameAnchoredAtChar(rAnchor, start, end); + } + } + } + else + { + assert(rAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR); + // for AS_CHAR obviously must be < + if (rAnchor.nContent.GetIndex() < iter->nEnd) + { + return true; + } + } + } + return false; + } + else + { + return true; + } +} + +void RemoveHiddenObjsOfNode(SwTextNode const& rNode, + std::vector::const_iterator const*const pIter, + std::vector::const_iterator const*const pEnd, + SwTextNode const*const pFirstNode, SwTextNode const*const pLastNode) +{ + std::vector const*const pFlys(rNode.GetAnchoredFlys()); + if (!pFlys) + { + return; + } + for (SwFrameFormat * pFrameFormat : *pFlys) + { + SwFormatAnchor const& rAnchor = pFrameFormat->GetAnchor(); + if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR + || (rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR + && RES_DRAWFRMFMT == pFrameFormat->Which())) + { + assert(rAnchor.GetContentAnchor()->nNode.GetIndex() == rNode.GetIndex()); + if (!IsShown(rNode.GetIndex(), rAnchor, pIter, pEnd, pFirstNode, pLastNode)) + { + pFrameFormat->DelFrames(); + } + } + } +} + +void AppendObjsOfNode(SwFrameFormats const*const pTable, sal_uLong const nIndex, + SwFrame *const pFrame, SwPageFrame *const pPage, SwDoc *const pDoc, + std::vector::const_iterator const*const pIter, + std::vector::const_iterator const*const pEnd, + SwTextNode const*const pFirstNode, SwTextNode const*const pLastNode) +{ +#if OSL_DEBUG_LEVEL > 0 + std::vector checkFormats; + for ( size_t i = 0; i < pTable->size(); ++i ) + { + SwFrameFormat *pFormat = (*pTable)[i]; + const SwFormatAnchor &rAnch = pFormat->GetAnchor(); + if ( rAnch.GetContentAnchor() && + IsShown(nIndex, rAnch, pIter, pEnd, pFirstNode, pLastNode)) + { + checkFormats.push_back( pFormat ); + } + } +#else + (void)pTable; +#endif + + SwNode const& rNode(*pDoc->GetNodes()[nIndex]); + std::vector const*const pFlys(rNode.GetAnchoredFlys()); + for (size_t it = 0; pFlys && it != pFlys->size(); ) + { + SwFrameFormat *const pFormat = (*pFlys)[it]; + const SwFormatAnchor &rAnch = pFormat->GetAnchor(); + if ( rAnch.GetContentAnchor() && + IsShown(nIndex, rAnch, pIter, pEnd, pFirstNode, pLastNode)) + { +#if OSL_DEBUG_LEVEL > 0 + std::vector::iterator checkPos = std::find( checkFormats.begin(), checkFormats.end(), pFormat ); + assert( checkPos != checkFormats.end()); + checkFormats.erase( checkPos ); +#endif + AppendObj(pFrame, pPage, pFormat, rAnch); + } + ++it; + } + +#if OSL_DEBUG_LEVEL > 0 + assert( checkFormats.empty()); +#endif +} + + +void AppendObjs(const SwFrameFormats *const pTable, sal_uLong const nIndex, + SwFrame *const pFrame, SwPageFrame *const pPage, SwDoc *const pDoc) +{ + if (pFrame->IsTextFrame()) + { + SwTextFrame const*const pTextFrame(static_cast(pFrame)); + if (sw::MergedPara const*const pMerged = pTextFrame->GetMergedPara()) + { + std::vector::const_iterator iterFirst(pMerged->extents.begin()); + std::vector::const_iterator iter(iterFirst); + SwTextNode const* pNode(pMerged->pFirstNode); + for ( ; ; ++iter) + { + if (iter == pMerged->extents.end() + || iter->pNode != pNode) + { + AppendObjsOfNode(pTable, pNode->GetIndex(), pFrame, pPage, pDoc, + &iterFirst, &iter, pMerged->pFirstNode, pMerged->pLastNode); + sal_uLong const until = iter == pMerged->extents.end() + ? pMerged->pLastNode->GetIndex() + 1 + : iter->pNode->GetIndex(); + for (sal_uLong i = pNode->GetIndex() + 1; i < until; ++i) + { + // let's show at-para flys on nodes that contain start/end of + // redline too, even if there's no text there + SwNode const*const pTmp(pNode->GetNodes()[i]); + if (pTmp->GetRedlineMergeFlag() == SwNode::Merge::NonFirst) + { + AppendObjsOfNode(pTable, pTmp->GetIndex(), pFrame, pPage, pDoc, &iter, &iter, pMerged->pFirstNode, pMerged->pLastNode); + } + } + if (iter == pMerged->extents.end()) + { + break; + } + pNode = iter->pNode; + iterFirst = iter; + } + } + } + else + { + return AppendObjsOfNode(pTable, nIndex, pFrame, pPage, pDoc, nullptr, nullptr, nullptr, nullptr); + } + } + else + { + return AppendObjsOfNode(pTable, nIndex, pFrame, pPage, pDoc, nullptr, nullptr, nullptr, nullptr); + } +} + +bool IsAnchoredObjShown(SwTextFrame const& rFrame, SwFormatAnchor const& rAnchor) +{ + assert(rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA || + rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR || + rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR); + bool ret(true); + if (auto const pMergedPara = rFrame.GetMergedPara()) + { + ret = false; + auto const pAnchor(rAnchor.GetContentAnchor()); + auto iterFirst(pMergedPara->extents.cbegin()); + if (iterFirst == pMergedPara->extents.end() + && (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA + || rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)) + { + ret = (&pAnchor->nNode.GetNode() == pMergedPara->pFirstNode + && (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA + || pAnchor->nContent == 0)) + || (&pAnchor->nNode.GetNode() == pMergedPara->pLastNode + && (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA + || pAnchor->nContent == pMergedPara->pLastNode->Len())); + } + auto iter(iterFirst); + SwTextNode const* pNode(pMergedPara->pFirstNode); + for ( ; ; ++iter) + { + if (iter == pMergedPara->extents.end() + || iter->pNode != pNode) + { + assert(pNode->GetRedlineMergeFlag() != SwNode::Merge::Hidden); + if (pNode == &pAnchor->nNode.GetNode()) + { + ret = IsShown(pNode->GetIndex(), rAnchor, &iterFirst, &iter, + pMergedPara->pFirstNode, pMergedPara->pLastNode); + break; + } + if (iter == pMergedPara->extents.end()) + { + break; + } + pNode = iter->pNode; + if (pAnchor->nNode.GetIndex() < pNode->GetIndex()) + { + break; + } + iterFirst = iter; + } + } + } + return ret; +} + +void AppendAllObjs(const SwFrameFormats* pTable, const SwFrame* pSib) +{ + //Connecting of all Objects, which are described in the SpzTable with the + //layout. + + boost::circular_buffer vFormatsToConnect(pTable->size()); + for(const auto& pFormat : *pTable) + { + const auto& rAnch = pFormat->GetAnchor(); + // Formats can still remain, because we neither use character bound + // frames nor objects which are anchored to character bounds. + if ((rAnch.GetAnchorId() != RndStdIds::FLY_AT_PAGE) && (rAnch.GetAnchorId() != RndStdIds::FLY_AS_CHAR)) + { + auto pContentAnchor = rAnch.GetContentAnchor(); + // formats in header/footer have no dependencies + if(pContentAnchor && pFormat->GetDoc()->IsInHeaderFooter(pContentAnchor->nNode)) + pFormat->MakeFrames(); + else + vFormatsToConnect.push_back(pFormat); + } + } + const SwRootFrame* pRoot = pSib ? pSib->getRootFrame() : nullptr; + const SwFrameFormat* pFirstRequeued(nullptr); + while(!vFormatsToConnect.empty()) + { + auto& pFormat = vFormatsToConnect.front(); + bool isConnected(false); + pFormat->CallSwClientNotify(sw::GetObjectConnectedHint(isConnected, pRoot)); + if(!isConnected) + { + pFormat->MakeFrames(); + pFormat->CallSwClientNotify(sw::GetObjectConnectedHint(isConnected, pRoot)); + } + // do this *before* push_back! the circular_buffer can be "full"! + vFormatsToConnect.pop_front(); + if (!isConnected) + { + if(pFirstRequeued == pFormat) + // If nothing happens anymore we can stop. + break; + if(!pFirstRequeued) + pFirstRequeued = pFormat; + assert(!vFormatsToConnect.full()); + vFormatsToConnect.push_back(pFormat); + } + else + { + pFirstRequeued = nullptr; + } + } +} + +namespace sw { + +void RecreateStartTextFrames(SwTextNode & rNode) +{ + std::vector frames; + SwIterator aIter(rNode); + for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next()) + { + if (pFrame->getRootFrame()->IsHideRedlines()) + { + frames.push_back(pFrame); + } + } + auto eMode(sw::FrameMode::Existing); + for (SwTextFrame * pFrame : frames) + { + // SplitNode could have moved the original frame to the start node + // & created a new one on end, or could have created new frame on + // start node... grab start node's frame and recreate MergedPara. + SwTextNode & rFirstNode(pFrame->GetMergedPara() + ? *pFrame->GetMergedPara()->pFirstNode + : rNode); + assert(rFirstNode.GetIndex() <= rNode.GetIndex()); + // clear old one first to avoid DelFrames confusing updates & asserts... + pFrame->SetMergedPara(nullptr); + pFrame->SetMergedPara(sw::CheckParaRedlineMerge( + *pFrame, rFirstNode, eMode)); + eMode = sw::FrameMode::New; // Existing is not idempotent! + // note: this may or may not delete frames on the end node + } +} + +} // namespace sw + +/** local method to set 'working' position for newly inserted frames + + OD 12.08.2003 #i17969# +*/ +static void lcl_SetPos( SwFrame& _rNewFrame, + const SwLayoutFrame& _rLayFrame ) +{ + SwRectFnSet aRectFnSet(&_rLayFrame); + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(_rNewFrame); + aRectFnSet.SetPos( aFrm, aRectFnSet.GetPos(_rLayFrame.getFrameArea()) ); + + // move position by one SwTwip in text flow direction in order to get + // notifications for a new calculated position after its formatting. + if ( aRectFnSet.IsVert() ) + { + aFrm.Pos().AdjustX( -1 ); + } + else + { + aFrm.Pos().AdjustY(1 ); + } +} + +void InsertCnt_( SwLayoutFrame *pLay, SwDoc *pDoc, + sal_uLong nIndex, bool bPages, sal_uLong nEndIndex, + SwFrame *pPrv, sw::FrameMode const eMode ) +{ + pDoc->getIDocumentTimerAccess().BlockIdling(); + SwRootFrame* pLayout = pLay->getRootFrame(); + const bool bOldCallbackActionEnabled = pLayout && pLayout->IsCallbackActionEnabled(); + if( bOldCallbackActionEnabled ) + pLayout->SetCallbackActionEnabled( false ); + + //In the generation of the Layout bPages=true will be handed over. + //Then will be new pages generated all x paragraphs already times in advance. + //On breaks and/or pagedescriptorchanges the corresponding will be generated + //immediately. + //The advantage is, that on one hand already a nearly realistic number of + //pages are created, but above all there are no almost endless long chain + //of paragraphs, which must be moved expensively until it reaches a tolerable + //reduced level. + //We'd like to think that 20 Paragraphs fit on one page. + //So that it does not become in extreme situations so violent we calculate depending + //on the node something to it. + //If in the DocStatistic a usable given pagenumber + //(Will be cared for while writing), so it will be presumed that this will be + //number of pages. + const bool bStartPercent = bPages && !nEndIndex; + + SwPageFrame *pPage = pLay->FindPageFrame(); + const SwFrameFormats *pTable = pDoc->GetSpzFrameFormats(); + SwFrame *pFrame = nullptr; + std::unique_ptr pActualSection; + std::unique_ptr pPageMaker; + + //If the layout will be created (bPages == true) we do head on the progress + //Flys and DrawObjects are not connected immediately, this + //happens only at the end of the function. + if ( bPages ) + { + // Attention: the SwLayHelper class uses references to the content-, + // page-, layout-frame etc. and may change them! + pPageMaker.reset(new SwLayHelper( pDoc, pFrame, pPrv, pPage, pLay, + pActualSection, nIndex, 0 == nEndIndex )); + if( bStartPercent ) + { + const sal_uLong nPageCount = pPageMaker->CalcPageCount(); + if( nPageCount ) + bObjsDirect = false; + } + } + else + pPageMaker = nullptr; + + if( pLay->IsInSct() && + ( pLay->IsSctFrame() || pLay->GetUpper() ) ) // Hereby will newbies + // be intercepted, of which flags could not determined yet, + // for e.g. while inserting a table + { + SwSectionFrame* pSct = pLay->FindSctFrame(); + // If content will be inserted in a footnote, which in a column area, + // the column area it is not allowed to be broken up. + // Only if in the inner of the footnote lies an area, is this a candidate + // for pActualSection. + // The same applies for areas in tables, if inside the table will be + // something inserted, it's only allowed to break up areas, which + // lies in the inside also. + if( ( !pLay->IsInFootnote() || pSct->IsInFootnote() ) && + ( !pLay->IsInTab() || pSct->IsInTab() ) ) + { + pActualSection.reset(new SwActualSection(nullptr, pSct, pSct->GetSection()->GetFormat()->GetSectionNode())); + // tdf#132236 for SwUndoDelete: find outer sections whose start + // nodes aren't contained in the range but whose end nodes are, + // because section frames may need to be created for them + SwActualSection * pUpperSection(pActualSection.get()); + while (pUpperSection->GetSectionNode()->EndOfSectionIndex() < nEndIndex) + { + SwStartNode *const pStart(pUpperSection->GetSectionNode()->StartOfSectionNode()); + if (!pStart->IsSectionNode()) + { + break; + } + // note: these don't have a section frame, check it in EndNode case! + auto const pTmp(new SwActualSection(nullptr, nullptr, static_cast(pStart))); + pUpperSection->SetUpper(pTmp); + pUpperSection = pTmp; + } + OSL_ENSURE( !pLay->Lower() || !pLay->Lower()->IsColumnFrame(), + "InsertCnt_: Wrong Call" ); + } + } + + //If a section is "open", the pActualSection points to an SwActualSection. + //If the page breaks, for "open" sections a follow will created. + //For nested sections (which have, however, not a nested layout), + //the SwActualSection class has a member, which points to an upper(section). + //When the "inner" section finishes, the upper will used instead. + + // Do not consider the end node. The caller (Section/MakeFrames()) has to + // ensure that the end of this range is positioned before EndIndex! + for ( ; nEndIndex == 0 || nIndex < nEndIndex; ++nIndex) + { + SwNode *pNd = pDoc->GetNodes()[nIndex]; + if ( pNd->IsContentNode() ) + { + SwContentNode* pNode = static_cast(pNd); + if (pLayout->IsHideRedlines() && !pNd->IsCreateFrameWhenHidingRedlines()) + { + if (pNd->IsTextNode() + && pNd->GetRedlineMergeFlag() == SwNode::Merge::NonFirst) + { // must have a frame already + assert(static_cast(pNode->getLayoutFrame(pLayout))->GetMergedPara()); + } + continue; // skip it + } + pFrame = pNode->IsTextNode() + ? sw::MakeTextFrame(*pNode->GetTextNode(), pLay, eMode) + : pNode->MakeFrame(pLay); + if( pPageMaker ) + pPageMaker->CheckInsert( nIndex ); + + pFrame->InsertBehind( pLay, pPrv ); + // #i27138# + // notify accessibility paragraphs objects about changed + // CONTENT_FLOWS_FROM/_TO relation. + // Relation CONTENT_FLOWS_FROM for next paragraph will change + // and relation CONTENT_FLOWS_TO for previous paragraph will change. + if ( pFrame->IsTextFrame() ) + { + SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() ); + // no notification, if is in construction + if ( pViewShell && !pViewShell->IsInConstructor() && + pViewShell->GetLayout() && + pViewShell->GetLayout()->IsAnyShellAccessible() && + pFrame->FindPageFrame() != nullptr) + { + pViewShell->InvalidateAccessibleParaFlowRelation( + dynamic_cast(pFrame->FindNextCnt( true )), + dynamic_cast(pFrame->FindPrevCnt()) ); + // #i68958# + // The information flags of the text frame are validated + // in methods and . + // The information flags have to be invalidated, because + // it is possible, that the one of its upper frames + // isn't inserted into the layout. + pFrame->InvalidateInfFlags(); + } + } + // OD 12.08.2003 #i17969# - consider horizontal/vertical layout + // for setting position at newly inserted frame + lcl_SetPos( *pFrame, *pLay ); + pPrv = pFrame; + + if ( !pTable->empty() && bObjsDirect && !bDontCreateObjects ) + AppendObjs( pTable, nIndex, pFrame, pPage, pDoc ); + } + else if ( pNd->IsTableNode() ) + { //Should we have encountered a table? + SwTableNode *pTableNode = static_cast(pNd); + if (pLayout->IsHideRedlines()) + { + // in the problematic case, there can be only 1 redline... + SwPosition const tmp(*pNd); + SwRangeRedline const*const pRedline( + pDoc->getIDocumentRedlineAccess().GetRedline(tmp, nullptr)); + // pathology: redline that starts on a TableNode; cannot + // be created in UI but by import filters... + if (pRedline + && pRedline->GetType() == RedlineType::Delete + && &pRedline->Start()->nNode.GetNode() == pNd) + { + SAL_WARN("sw.pageframe", "skipping table frame creation on bizarre redline"); + while (true) + { + pTableNode->GetNodes()[nIndex]->SetRedlineMergeFlag(SwNode::Merge::Hidden); + if (nIndex == pTableNode->EndOfSectionIndex()) + { + break; + } + ++nIndex; + } + continue; + } + } + if (pLayout->IsHideRedlines() && !pNd->IsCreateFrameWhenHidingRedlines()) + { + assert(pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden); + nIndex = pTableNode->EndOfSectionIndex(); + continue; // skip it + } + + // #108116# loading may produce table structures that GCLines + // needs to clean up. To keep table formulas correct, change + // all table formulas to internal (BOXPTR) representation. + SwTableFormulaUpdate aMsgHint( &pTableNode->GetTable() ); + aMsgHint.m_eFlags = TBL_BOXPTR; + pDoc->getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint ); + pTableNode->GetTable().GCLines(); + + pFrame = pTableNode->MakeFrame( pLay ); + + if( pPageMaker ) + pPageMaker->CheckInsert( nIndex ); + + pFrame->InsertBehind( pLay, pPrv ); + if (pPage) // would null in SwCellFrame ctor + { // tdf#134931 call ResetTurbo(); not sure if Paste() would be + pFrame->InvalidatePage(pPage); // better than InsertBehind()? + } + // #i27138# + // notify accessibility paragraphs objects about changed + // CONTENT_FLOWS_FROM/_TO relation. + // Relation CONTENT_FLOWS_FROM for next paragraph will change + // and relation CONTENT_FLOWS_TO for previous paragraph will change. + { + SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() ); + // no notification, if is in construction + if ( pViewShell && !pViewShell->IsInConstructor() && + pViewShell->GetLayout() && + pViewShell->GetLayout()->IsAnyShellAccessible() && + pFrame->FindPageFrame() != nullptr) + { + pViewShell->InvalidateAccessibleParaFlowRelation( + dynamic_cast(pFrame->FindNextCnt( true )), + dynamic_cast(pFrame->FindPrevCnt()) ); + } + } + if ( bObjsDirect && !pTable->empty() ) + static_cast(pFrame)->RegistFlys(); + // OD 12.08.2003 #i17969# - consider horizontal/vertical layout + // for setting position at newly inserted frame + lcl_SetPos( *pFrame, *pLay ); + + pPrv = pFrame; + //Set the index to the endnode of the table section. + nIndex = pTableNode->EndOfSectionIndex(); + + SwTabFrame* pTmpFrame = static_cast(pFrame); + while ( pTmpFrame ) + { + pTmpFrame->CheckDirChange(); + pTmpFrame = pTmpFrame->IsFollow() ? pTmpFrame->FindMaster() : nullptr; + } + + } + else if ( pNd->IsSectionNode() ) + { + if (pLayout->IsHideRedlines() && !pNd->IsCreateFrameWhenHidingRedlines()) + { + assert(pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden); + continue; // skip it + } + SwSectionNode *pNode = static_cast(pNd); + if( pNode->GetSection().CalcHiddenFlag() ) + // is hidden, skip the area + nIndex = pNode->EndOfSectionIndex(); + else + { + pFrame = pNode->MakeFrame( pLay ); + pActualSection.reset( new SwActualSection( pActualSection.release(), + static_cast(pFrame), pNode ) ); + if ( pActualSection->GetUpper() ) + { + //Insert behind the Upper, the "Follow" of the Upper will be + //generated at the EndNode. + SwSectionFrame *pTmp = pActualSection->GetUpper()->GetSectionFrame(); + pFrame->InsertBehind( pTmp->GetUpper(), pTmp ); + // OD 25.03.2003 #108339# - direct initialization of section + // after insertion in the layout + static_cast(pFrame)->Init(); + } + else + { + pFrame->InsertBehind( pLay, pPrv ); + // OD 25.03.2003 #108339# - direct initialization of section + // after insertion in the layout + static_cast(pFrame)->Init(); + + // #i33963# + // Do not trust the IsInFootnote flag. If we are currently + // building up a table, the upper of pPrv may be a cell + // frame, but the cell frame does not have an upper yet. + if( pPrv && nullptr != pPrv->ImplFindFootnoteFrame() ) + { + if( pPrv->IsSctFrame() ) + pPrv = static_cast(pPrv)->ContainsContent(); + if( pPrv && pPrv->IsTextFrame() ) + static_cast(pPrv)->Prepare( PrepareHint::QuoVadis, nullptr, false ); + } + } + if (nIndex + 1 == nEndIndex) + { // tdf#131684 tdf#132236 fix upper of frame moved in + // SwUndoDelete; can't be done there unfortunately + // because empty section frames are deleted here + SwFrame *const pNext( + // if there's a parent section, it has been split + // into 2 SwSectionFrame already :( + ( pFrame->GetNext() + && pFrame->GetNext()->IsSctFrame() + && pActualSection->GetUpper() + && pActualSection->GetUpper()->GetSectionNode() == + static_cast(pFrame->GetNext())->GetSection()->GetFormat()->GetSectionNode()) + ? static_cast(pFrame->GetNext())->ContainsContent() + : pFrame->GetNext()); + if (pNext + && pNext->IsTextFrame() + && static_cast(pNext)->GetTextNodeFirst() == pDoc->GetNodes()[nEndIndex] + && (pNext->GetUpper() == pFrame->GetUpper() + || pFrame->GetNext()->IsSctFrame())) // checked above + { + pNext->Cut(); + pNext->InvalidateInfFlags(); // mbInfSct changed + // could have columns + SwSectionFrame *const pSection(static_cast(pFrame)); + assert(!pSection->Lower() || pSection->Lower()->IsLayoutFrame()); + SwLayoutFrame *const pParent(pSection->Lower() ? pSection->GetNextLayoutLeaf() : pSection); + assert(!pParent->Lower()); + // paste invalidates, section could have indent... + pNext->Paste(pParent, nullptr); + } + } + // #i27138# + // notify accessibility paragraphs objects about changed + // CONTENT_FLOWS_FROM/_TO relation. + // Relation CONTENT_FLOWS_FROM for next paragraph will change + // and relation CONTENT_FLOWS_TO for previous paragraph will change. + { + SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() ); + // no notification, if is in construction + if ( pViewShell && !pViewShell->IsInConstructor() && + pViewShell->GetLayout() && + pViewShell->GetLayout()->IsAnyShellAccessible() && + pFrame->FindPageFrame() != nullptr) + { + pViewShell->InvalidateAccessibleParaFlowRelation( + dynamic_cast(pFrame->FindNextCnt( true )), + dynamic_cast(pFrame->FindPrevCnt()) ); + } + } + pFrame->CheckDirChange(); + + // OD 12.08.2003 #i17969# - consider horizontal/vertical layout + // for setting position at newly inserted frame + lcl_SetPos( *pFrame, *pLay ); + + // OD 20.11.2002 #105405# - no page, no invalidate. + if ( pPage ) + { + // OD 18.09.2002 #100522# + // invalidate page in order to force format and paint of + // inserted section frame + pFrame->InvalidatePage( pPage ); + + // FME 10.11.2003 #112243# + // Invalidate fly content flag: + if ( pFrame->IsInFly() ) + pPage->InvalidateFlyContent(); + + // OD 14.11.2002 #104684# - invalidate page content in order to + // force format and paint of section content. + pPage->InvalidateContent(); + } + + pLay = static_cast(pFrame); + if ( pLay->Lower() && pLay->Lower()->IsLayoutFrame() ) + pLay = pLay->GetNextLayoutLeaf(); + pPrv = nullptr; + } + } + else if ( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsSectionNode() ) + { + if (pLayout->IsHideRedlines() && !pNd->IsCreateFrameWhenHidingRedlines()) + { + assert(pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden); + continue; // skip it + } + assert(pActualSection && "Section end without section start?"); + assert(pActualSection->GetSectionNode() == pNd->StartOfSectionNode()); + + //Close the section, where appropriate activate the surrounding + //section again. + pActualSection.reset(pActualSection->GetUpper()); + pLay = pLay->FindSctFrame(); + if ( pActualSection ) + { + //Could be, that the last SectionFrame remains empty. + //Then now is the time to remove them. + if ( !pLay->ContainsContent() ) + { + SwFrame *pTmpFrame = pLay; + pLay = pTmpFrame->GetUpper(); + pPrv = pTmpFrame->GetPrev(); + pTmpFrame->RemoveFromLayout(); + SwFrame::DestroyFrame(pTmpFrame); + } + else + { + pPrv = pLay; + pLay = pLay->GetUpper(); + } + + // new section frame + pFrame = pActualSection->GetSectionNode()->MakeFrame( pLay ); + pFrame->InsertBehind( pLay, pPrv ); + static_cast(pFrame)->Init(); + + // OD 12.08.2003 #i17969# - consider horizontal/vertical layout + // for setting position at newly inserted frame + lcl_SetPos( *pFrame, *pLay ); + + SwSectionFrame* pOuterSectionFrame = pActualSection->GetSectionFrame(); + + // a follow has to be appended to the new section frame + SwSectionFrame* pFollow = pOuterSectionFrame ? pOuterSectionFrame->GetFollow() : nullptr; + if ( pFollow ) + { + pOuterSectionFrame->SetFollow( nullptr ); + pOuterSectionFrame->InvalidateSize(); + static_cast(pFrame)->SetFollow( pFollow ); + } + + // We don't want to leave empty parts back. + if (pOuterSectionFrame && + ! pOuterSectionFrame->IsColLocked() && + ! pOuterSectionFrame->ContainsContent() ) + { + pOuterSectionFrame->DelEmpty( true ); + SwFrame::DestroyFrame(pOuterSectionFrame); + } + pActualSection->SetSectionFrame( static_cast(pFrame) ); + + pLay = static_cast(pFrame); + if ( pLay->Lower() && pLay->Lower()->IsLayoutFrame() ) + pLay = pLay->GetNextLayoutLeaf(); + pPrv = nullptr; + } + else + { + //Nothing more with sections, it goes on right behind + //the SectionFrame. + pPrv = pLay; + pLay = pLay->GetUpper(); + } + } + else if( pNd->IsStartNode() && + SwFlyStartNode == static_cast(pNd)->GetStartNodeType() ) + { + if (pLayout->IsHideRedlines() && !pNd->IsCreateFrameWhenHidingRedlines()) + { + assert(pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden); + assert(false); // actually a fly-section can't be deleted? + continue; // skip it + } + if ( !pTable->empty() && bObjsDirect && !bDontCreateObjects ) + { + SwFlyFrame* pFly = pLay->FindFlyFrame(); + if( pFly ) + AppendObjs( pTable, nIndex, pFly, pPage, pDoc ); + } + } + else + { + assert(!pLayout->IsHideRedlines() + || pNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden); + // Neither Content nor table nor section, so we are done. + break; + } + } + + if ( pActualSection ) + { + // Might happen that an empty (Follow-)Section is left over. + if ( !(pLay = pActualSection->GetSectionFrame())->ContainsContent() ) + { + pLay->RemoveFromLayout(); + SwFrame::DestroyFrame(pLay); + } + pActualSection.reset(); + } + + if ( bPages ) // let the Flys connect to each other + { + if ( !bDontCreateObjects ) + AppendAllObjs( pTable, pLayout ); + bObjsDirect = true; + } + + if( pPageMaker ) + { + pPageMaker->CheckFlyCache( pPage ); + pPageMaker.reset(); + if( pDoc->GetLayoutCache() ) + { +#ifdef DBG_UTIL + pDoc->GetLayoutCache()->CompareLayout( *pDoc ); +#endif + pDoc->GetLayoutCache()->ClearImpl(); + } + } + + pDoc->getIDocumentTimerAccess().UnblockIdling(); + if( bOldCallbackActionEnabled ) + pLayout->SetCallbackActionEnabled( bOldCallbackActionEnabled ); +} + +void MakeFrames( SwDoc *pDoc, const SwNodeIndex &rSttIdx, + const SwNodeIndex &rEndIdx ) +{ + bObjsDirect = false; + + SwNodeIndex aTmp( rSttIdx ); + sal_uLong nEndIdx = rEndIdx.GetIndex(); + SwNode* pNd = pDoc->GetNodes().FindPrvNxtFrameNode( aTmp, + pDoc->GetNodes()[ nEndIdx-1 ]); + if ( pNd ) + { + bool bApres = aTmp < rSttIdx; + SwNode2Layout aNode2Layout( *pNd, rSttIdx.GetIndex() ); + SwFrame* pFrame; + sw::FrameMode eMode = sw::FrameMode::Existing; + while( nullptr != (pFrame = aNode2Layout.NextFrame()) ) + { + SwLayoutFrame *pUpper = pFrame->GetUpper(); + SwFootnoteFrame* pFootnoteFrame = pUpper->FindFootnoteFrame(); + bool bOldLock, bOldFootnote; + if( pFootnoteFrame ) + { + bOldFootnote = pFootnoteFrame->IsColLocked(); + pFootnoteFrame->ColLock(); + } + else + bOldFootnote = true; + SwSectionFrame* pSct = pUpper->FindSctFrame(); + // Inside of footnotes only those areas are interesting that are inside of them. But + // not the ones (e.g. column areas) in which are the footnote containers positioned. + // #109767# Table frame is in section, insert section in cell frame. + if( pSct && ((pFootnoteFrame && !pSct->IsInFootnote()) || pUpper->IsCellFrame()) ) + pSct = nullptr; + if( pSct ) + { // to prevent pTmp->MoveFwd from destroying the SectionFrame + bOldLock = pSct->IsColLocked(); + pSct->ColLock(); + } + else + bOldLock = true; + + // If pFrame cannot be moved, it is not possible to move it to the next page. This applies + // also for frames (in the first column of a frame pFrame is moveable) and column + // sections of tables (also here pFrame is moveable). + bool bMoveNext = nEndIdx - rSttIdx.GetIndex() > 120; + bool bAllowMove = !pFrame->IsInFly() && pFrame->IsMoveable() && + (!pFrame->IsInTab() || pFrame->IsTabFrame() ); + if ( bMoveNext && bAllowMove ) + { + SwFrame *pMove = pFrame; + SwFrame *pPrev = pFrame->GetPrev(); + SwFlowFrame *pTmp = SwFlowFrame::CastFlowFrame( pMove ); + assert(pTmp); + + if ( bApres ) + { + // The rest of this page should be empty. Thus, the following one has to move to + // the next page (it might also be located in the following column). + assert(!pTmp->HasFollow() && "prev. node's frame is not last"); + pPrev = pFrame; + // If the surrounding SectionFrame has a "next" one, + // so this one needs to be moved as well. + pMove = pFrame->GetIndNext(); + SwColumnFrame* pCol = static_cast(pFrame->FindColFrame()); + if( pCol ) + pCol = static_cast(pCol->GetNext()); + do + { + if( pCol && !pMove ) + { // No successor so far, look into the next column + pMove = pCol->ContainsAny(); + if( pCol->GetNext() ) + pCol = static_cast(pCol->GetNext()); + else if( pCol->IsInSct() ) + { // If there is no following column but we are in a column frame, + // there might be (page) columns outside of it. + pCol = static_cast(pCol->FindSctFrame()->FindColFrame()); + if( pCol ) + pCol = static_cast(pCol->GetNext()); + } + else + pCol = nullptr; + } + // skip invalid SectionFrames + while( pMove && pMove->IsSctFrame() && + !static_cast(pMove)->GetSection() ) + pMove = pMove->GetNext(); + } while( !pMove && pCol ); + + if( pMove ) + { + if ( pMove->IsContentFrame() ) + pTmp = static_cast(pMove); + else if ( pMove->IsTabFrame() ) + pTmp = static_cast(pMove); + else if ( pMove->IsSctFrame() ) + { + pMove = static_cast(pMove)->ContainsAny(); + if( pMove ) + pTmp = SwFlowFrame::CastFlowFrame( pMove ); + else + pTmp = nullptr; + } + } + else + pTmp = nullptr; + } + else + { + assert(!pTmp->IsFollow() && "next node's frame is not master"); + // move the _content_ of a section frame + if( pMove->IsSctFrame() ) + { + while( pMove && pMove->IsSctFrame() && + !static_cast(pMove)->GetSection() ) + pMove = pMove->GetNext(); + if( pMove && pMove->IsSctFrame() ) + pMove = static_cast(pMove)->ContainsAny(); + if( pMove ) + pTmp = SwFlowFrame::CastFlowFrame( pMove ); + else + pTmp = nullptr; + } + } + + if( pTmp ) + { + SwFrame* pOldUp = pTmp->GetFrame().GetUpper(); + // MoveFwd==true means that we are still on the same page. + // But since we want to move if possible! + bool bTmpOldLock = pTmp->IsJoinLocked(); + pTmp->LockJoin(); + while( pTmp->MoveFwd( true, false, true ) ) + { + if( pOldUp == pTmp->GetFrame().GetUpper() ) + break; + pOldUp = pTmp->GetFrame().GetUpper(); + } + if( !bTmpOldLock ) + pTmp->UnlockJoin(); + } + ::InsertCnt_( pUpper, pDoc, rSttIdx.GetIndex(), + pFrame->IsInDocBody(), nEndIdx, pPrev, eMode ); + } + else + { + bool bSplit; + SwFrame* pPrv = bApres ? pFrame : pFrame->GetPrev(); + // If the section frame is inserted into another one, it must be split. + if( pSct && rSttIdx.GetNode().IsSectionNode() ) + { + bSplit = pSct->SplitSect( pFrame, bApres ); + if( !bSplit && !bApres ) + { + pUpper = pSct->GetUpper(); + pPrv = pSct->GetPrev(); + } + } + else + bSplit = false; + + ::InsertCnt_( pUpper, pDoc, rSttIdx.GetIndex(), false, + nEndIdx, pPrv, eMode ); + // OD 23.06.2003 #108784# - correction: append objects doesn't + // depend on value of + if( !bDontCreateObjects ) + { + const SwFrameFormats *pTable = pDoc->GetSpzFrameFormats(); + if( !pTable->empty() ) + AppendAllObjs( pTable, pUpper ); + } + + // If nothing was added (e.g. a hidden section), the split must be reversed. + if( bSplit && pSct && pSct->GetNext() + && pSct->GetNext()->IsSctFrame() ) + pSct->MergeNext( static_cast(pSct->GetNext()) ); + if( pFrame->IsInFly() ) + pFrame->FindFlyFrame()->Invalidate_(); + if( pFrame->IsInTab() ) + pFrame->InvalidateSize(); + } + + SwPageFrame *pPage = pUpper->FindPageFrame(); + SwFrame::CheckPageDescs( pPage, false ); + if( !bOldFootnote ) + pFootnoteFrame->ColUnlock(); + if( !bOldLock ) + { + pSct->ColUnlock(); + // pSct might be empty (e.g. when inserting linked section containing further + // sections) and can be destroyed in such cases. + if( !pSct->ContainsContent() ) + { + pSct->DelEmpty( true ); + pUpper->getRootFrame()->RemoveFromList( pSct ); + SwFrame::DestroyFrame(pSct); + } + } + eMode = sw::FrameMode::New; // use Existing only once! + } + } + + bObjsDirect = true; +} + +SwBorderAttrs::SwBorderAttrs(const SwModify *pMod, const SwFrame *pConstructor) + : SwCacheObj(pMod) + , m_rAttrSet(pConstructor->IsContentFrame() + ? pConstructor->IsTextFrame() + ? static_cast(pConstructor)->GetTextNodeForParaProps()->GetSwAttrSet() + : static_cast(pConstructor)->GetNode()->GetSwAttrSet() + : static_cast(pConstructor)->GetFormat()->GetAttrSet()) + , m_rUL(m_rAttrSet.GetULSpace()) + // #i96772# + // LRSpaceItem is copied due to the possibility that it is adjusted - see below + , m_rLR(m_rAttrSet.GetLRSpace().Clone()) + , m_rBox(m_rAttrSet.GetBox()) + , m_rShadow(m_rAttrSet.GetShadow()) + , m_aFrameSize(m_rAttrSet.GetFrameSize().GetSize()) + , m_bIsLine(false) + , m_bJoinedWithPrev(false) + , m_bJoinedWithNext(false) + , m_nTopLine(0) + , m_nBottomLine(0) + , m_nLeftLine(0) + , m_nRightLine(0) + , m_nTop(0) + , m_nBottom(0) + , m_nGetTopLine(0) + , m_nGetBottomLine(0) + , m_nLineSpacing(0) +{ + // #i96772# + const SwTextFrame* pTextFrame = dynamic_cast(pConstructor); + if ( pTextFrame ) + { + pTextFrame->GetTextNodeForParaProps()->ClearLRSpaceItemDueToListLevelIndents( m_rLR ); + } + else if ( pConstructor->IsNoTextFrame() ) + { + m_rLR = std::make_shared(RES_LR_SPACE); + } + + // Caution: The USHORTs for the cached values are not initialized by intention! + + // everything needs to be calculated at least once: + m_bTopLine = m_bBottomLine = m_bLeftLine = m_bRightLine = + m_bTop = m_bBottom = m_bLine = true; + + // except this one: calculate line spacing before cell border only for text frames + m_bLineSpacing = bool(pTextFrame); + + m_bCacheGetLine = m_bCachedGetTopLine = m_bCachedGetBottomLine = false; + // OD 21.05.2003 #108789# - init cache status for values + // and , which aren't initialized by default. + m_bCachedJoinedWithPrev = false; + m_bCachedJoinedWithNext = false; +} + +SwBorderAttrs::~SwBorderAttrs() +{ + const_cast(static_cast(m_pOwner))->SetInCache( false ); +} + +/* All calc methods calculate a safety distance in addition to the values given by the attributes. + * This safety distance is only added when working with borders and/or shadows to prevent that + * e.g. borders are painted over. + */ + +void SwBorderAttrs::CalcTop_() +{ + m_nTop = CalcTopLine() + m_rUL.GetUpper(); + m_bTop = false; +} + +void SwBorderAttrs::CalcBottom_() +{ + m_nBottom = CalcBottomLine() + m_rUL.GetLower(); + m_bBottom = false; +} + +long SwBorderAttrs::CalcRight( const SwFrame* pCaller ) const +{ + long nRight=0; + + if (!pCaller->IsTextFrame() || !static_cast(pCaller)->GetDoc().GetDocumentSettingManager().get(DocumentSettingId::INVERT_BORDER_SPACING)) { + // OD 23.01.2003 #106895# - for cell frame in R2L text direction the left + // and right border are painted on the right respectively left. + if ( pCaller->IsCellFrame() && pCaller->IsRightToLeft() ) + nRight = CalcLeftLine(); + else + nRight = CalcRightLine(); + + } + // for paragraphs, "left" is "before text" and "right" is "after text" + if ( pCaller->IsTextFrame() && pCaller->IsRightToLeft() ) + nRight += m_rLR->GetLeft(); + else + nRight += m_rLR->GetRight(); + + // correction: retrieve left margin for numbering in R2L-layout + if ( pCaller->IsTextFrame() && pCaller->IsRightToLeft() ) + { + nRight += static_cast(pCaller)->GetTextNodeForParaProps()->GetLeftMarginWithNum(); + } + + return nRight; +} + +/// Tries to detect if this paragraph has a floating table attached. +static bool lcl_hasTabFrame(const SwTextFrame* pTextFrame) +{ + if (pTextFrame->GetDrawObjs()) + { + const SwSortedObjs* pSortedObjs = pTextFrame->GetDrawObjs(); + if (pSortedObjs->size() > 0) + { + SwAnchoredObject* pObject = (*pSortedObjs)[0]; + if (dynamic_cast(pObject) != nullptr) + { + SwFlyFrame* pFly = static_cast(pObject); + if (pFly->Lower() && pFly->Lower()->IsTabFrame()) + return true; + } + } + } + return false; +} + +long SwBorderAttrs::CalcLeft( const SwFrame *pCaller ) const +{ + long nLeft=0; + + if (!pCaller->IsTextFrame() || !static_cast(pCaller)->GetDoc().GetDocumentSettingManager().get(DocumentSettingId::INVERT_BORDER_SPACING)) + { + // OD 23.01.2003 #106895# - for cell frame in R2L text direction the left + // and right border are painted on the right respectively left. + if ( pCaller->IsCellFrame() && pCaller->IsRightToLeft() ) + nLeft = CalcRightLine(); + else + nLeft = CalcLeftLine(); + } + + // for paragraphs, "left" is "before text" and "right" is "after text" + if ( pCaller->IsTextFrame() && pCaller->IsRightToLeft() ) + nLeft += m_rLR->GetRight(); + else + { + bool bIgnoreMargin = false; + if (pCaller->IsTextFrame()) + { + const SwTextFrame* pTextFrame = static_cast(pCaller); + if (pTextFrame->GetDoc().GetDocumentSettingManager().get(DocumentSettingId::FLOATTABLE_NOMARGINS)) + { + // If this is explicitly requested, ignore the margins next to the floating table. + if (lcl_hasTabFrame(pTextFrame)) + bIgnoreMargin = true; + // TODO here we only handle the first two paragraphs, would be nice to generalize this. + else if (pTextFrame->FindPrev() && pTextFrame->FindPrev()->IsTextFrame() && lcl_hasTabFrame(static_cast(pTextFrame->FindPrev()))) + bIgnoreMargin = true; + } + } + if (!bIgnoreMargin) + nLeft += m_rLR->GetLeft(); + } + + // correction: do not retrieve left margin for numbering in R2L-layout + if ( pCaller->IsTextFrame() && !pCaller->IsRightToLeft() ) + { + nLeft += static_cast(pCaller)->GetTextNodeForParaProps()->GetLeftMarginWithNum(); + } + + return nLeft; +} + +/* Calculated values for borders and shadows. + * It might be that a distance is wanted even without lines. This will be + * considered here and not by the attribute (e.g. bBorderDist for cells). + */ + +void SwBorderAttrs::CalcTopLine_() +{ + m_nTopLine = m_rBox.CalcLineSpace( SvxBoxItemLine::TOP, /*bEvenIfNoLine*/true ); + m_nTopLine = m_nTopLine + m_rShadow.CalcShadowSpace(SvxShadowItemSide::TOP); + m_bTopLine = false; +} + +void SwBorderAttrs::CalcBottomLine_() +{ + m_nBottomLine = m_rBox.CalcLineSpace( SvxBoxItemLine::BOTTOM, true ); + m_nBottomLine = m_nBottomLine + m_rShadow.CalcShadowSpace(SvxShadowItemSide::BOTTOM); + m_bBottomLine = false; +} + +void SwBorderAttrs::CalcLeftLine_() +{ + m_nLeftLine = m_rBox.CalcLineSpace( SvxBoxItemLine::LEFT, true); + m_nLeftLine = m_nLeftLine + m_rShadow.CalcShadowSpace(SvxShadowItemSide::LEFT); + m_bLeftLine = false; +} + +void SwBorderAttrs::CalcRightLine_() +{ + m_nRightLine = m_rBox.CalcLineSpace( SvxBoxItemLine::RIGHT, true ); + m_nRightLine = m_nRightLine + m_rShadow.CalcShadowSpace(SvxShadowItemSide::RIGHT); + m_bRightLine = false; +} + +void SwBorderAttrs::IsLine_() +{ + m_bIsLine = m_rBox.GetTop() || m_rBox.GetBottom() || + m_rBox.GetLeft()|| m_rBox.GetRight(); + m_bLine = false; +} + +/* The borders of neighboring paragraphs are condensed by following algorithm: + * + * 1. No top border if the predecessor has the same top border and (3) applies. + * In addition, the paragraph needs to have a border at least one side (left/right/bottom). + * 2. No bottom border if the successor has the same bottom border and (3) applies. + * In addition, the paragraph needs to have a border at least one side (left/right/top). + * 3. The borders on the left and right side are identical between the current and the + * pre-/succeeding paragraph. + */ + +static bool CmpLines( const editeng::SvxBorderLine *pL1, const editeng::SvxBorderLine *pL2 ) +{ + return ( ((pL1 && pL2) && (*pL1 == *pL2)) || (!pL1 && !pL2) ); +} + +// OD 21.05.2003 #108789# - change name of 1st parameter - "rAttrs" -> "rCmpAttrs" +// OD 21.05.2003 #108789# - compare and +// instead of only the right LR-spacing, because R2L-layout has to be +// considered. +bool SwBorderAttrs::CmpLeftRight( const SwBorderAttrs &rCmpAttrs, + const SwFrame *pCaller, + const SwFrame *pCmp ) const +{ + return ( CmpLines( rCmpAttrs.GetBox().GetLeft(), GetBox().GetLeft() ) && + CmpLines( rCmpAttrs.GetBox().GetRight(),GetBox().GetRight() ) && + CalcLeft( pCaller ) == rCmpAttrs.CalcLeft( pCmp ) && + // OD 21.05.2003 #108789# - compare with . + CalcRight( pCaller ) == rCmpAttrs.CalcRight( pCmp ) ); +} + +bool SwBorderAttrs::JoinWithCmp( const SwFrame& _rCallerFrame, + const SwFrame& _rCmpFrame ) const +{ + bool bReturnVal = false; + + SwBorderAttrAccess aCmpAccess( SwFrame::GetCache(), &_rCmpFrame ); + const SwBorderAttrs &rCmpAttrs = *aCmpAccess.Get(); + if ( m_rShadow == rCmpAttrs.GetShadow() && + CmpLines( m_rBox.GetTop(), rCmpAttrs.GetBox().GetTop() ) && + CmpLines( m_rBox.GetBottom(), rCmpAttrs.GetBox().GetBottom() ) && + CmpLeftRight( rCmpAttrs, &_rCallerFrame, &_rCmpFrame ) + ) + { + bReturnVal = true; + } + + return bReturnVal; +} + +// OD 21.05.2003 #108789# - method to determine, if borders are joined with +// previous frame. Calculated value saved in cached value +// OD 2004-02-26 #i25029# - add 2nd parameter <_pPrevFrame> +void SwBorderAttrs::CalcJoinedWithPrev( const SwFrame& _rFrame, + const SwFrame* _pPrevFrame ) +{ + // set default + m_bJoinedWithPrev = false; + + if ( _rFrame.IsTextFrame() ) + { + // text frame can potentially join with previous text frame, if + // corresponding attribute set is set at previous text frame. + // OD 2004-02-26 #i25029# - If parameter <_pPrevFrame> is set, take this + // one as previous frame. + const SwFrame* pPrevFrame = _pPrevFrame ? _pPrevFrame : _rFrame.GetPrev(); + // OD 2004-02-13 #i25029# - skip hidden text frames. + while ( pPrevFrame && pPrevFrame->IsTextFrame() && + static_cast(pPrevFrame)->IsHiddenNow() ) + { + pPrevFrame = pPrevFrame->GetPrev(); + } + if ( pPrevFrame && pPrevFrame->IsTextFrame() && + pPrevFrame->GetAttrSet()->GetParaConnectBorder().GetValue() + ) + { + m_bJoinedWithPrev = JoinWithCmp( _rFrame, *pPrevFrame ); + } + } + + // valid cache status, if demanded + // OD 2004-02-26 #i25029# - Do not validate cache, if parameter <_pPrevFrame> + // is set. + m_bCachedJoinedWithPrev = m_bCacheGetLine && !_pPrevFrame; +} + +// OD 21.05.2003 #108789# - method to determine, if borders are joined with +// next frame. Calculated value saved in cached value +void SwBorderAttrs::CalcJoinedWithNext( const SwFrame& _rFrame ) +{ + // set default + m_bJoinedWithNext = false; + + if ( _rFrame.IsTextFrame() ) + { + // text frame can potentially join with next text frame, if + // corresponding attribute set is set at current text frame. + // OD 2004-02-13 #i25029# - get next frame, but skip hidden text frames. + const SwFrame* pNextFrame = _rFrame.GetNext(); + while ( pNextFrame && pNextFrame->IsTextFrame() && + static_cast(pNextFrame)->IsHiddenNow() ) + { + pNextFrame = pNextFrame->GetNext(); + } + if ( pNextFrame && pNextFrame->IsTextFrame() && + _rFrame.GetAttrSet()->GetParaConnectBorder().GetValue() + ) + { + m_bJoinedWithNext = JoinWithCmp( _rFrame, *pNextFrame ); + } + } + + // valid cache status, if demanded + m_bCachedJoinedWithNext = m_bCacheGetLine; +} + +// OD 21.05.2003 #108789# - accessor for cached values +// OD 2004-02-26 #i25029# - add 2nd parameter <_pPrevFrame>, which is passed to +// method <_CalcJoindWithPrev(..)>. +bool SwBorderAttrs::JoinedWithPrev( const SwFrame& _rFrame, + const SwFrame* _pPrevFrame ) const +{ + if ( !m_bCachedJoinedWithPrev || _pPrevFrame ) + { + // OD 2004-02-26 #i25029# - pass <_pPrevFrame> as 2nd parameter + const_cast(this)->CalcJoinedWithPrev( _rFrame, _pPrevFrame ); + } + + return m_bJoinedWithPrev; +} + +bool SwBorderAttrs::JoinedWithNext( const SwFrame& _rFrame ) const +{ + if ( !m_bCachedJoinedWithNext ) + { + const_cast(this)->CalcJoinedWithNext( _rFrame ); + } + + return m_bJoinedWithNext; +} + +// OD 2004-02-26 #i25029# - added 2nd parameter <_pPrevFrame>, which is passed to +// method +void SwBorderAttrs::GetTopLine_( const SwFrame& _rFrame, + const SwFrame* _pPrevFrame ) +{ + sal_uInt16 nRet = CalcTopLine(); + + // OD 21.05.2003 #108789# - use new method + // OD 2004-02-26 #i25029# - add 2nd parameter + if ( JoinedWithPrev( _rFrame, _pPrevFrame ) ) + { + nRet = 0; + } + + m_bCachedGetTopLine = m_bCacheGetLine; + + m_nGetTopLine = nRet; +} + +void SwBorderAttrs::GetBottomLine_( const SwFrame& _rFrame ) +{ + sal_uInt16 nRet = CalcBottomLine(); + + // OD 21.05.2003 #108789# - use new method + if ( JoinedWithNext( _rFrame ) ) + { + nRet = 0; + } + + m_bCachedGetBottomLine = m_bCacheGetLine; + + m_nGetBottomLine = nRet; +} + +void SwBorderAttrs::CalcLineSpacing_() +{ + // tdf#125300 compatibility option AddParaLineSpacingToTableCells needs also line spacing + const SvxLineSpacingItem &rSpace = m_rAttrSet.GetLineSpacing(); + if ( rSpace.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop && rSpace.GetPropLineSpace() > 100 ) + { + sal_Int32 nFontSize = m_rAttrSet.Get(RES_CHRATR_FONTSIZE).GetHeight(); + m_nLineSpacing = nFontSize * (rSpace.GetPropLineSpace() - 100) * 1.15 / 100; + } + m_bLineSpacing = false; +} + +static SwModify const* GetCacheOwner(SwFrame const& rFrame) +{ + return rFrame.IsContentFrame() + ? static_cast(rFrame.IsTextFrame() + // sw_redlinehide: presumably this caches the border attrs at the model level and can be shared across different layouts so we want the ParaProps node here + ? static_cast(rFrame).GetTextNodeForParaProps() + : static_cast(rFrame).GetNode()) + : static_cast(static_cast(rFrame).GetFormat()); +} + +SwBorderAttrAccess::SwBorderAttrAccess( SwCache &rCach, const SwFrame *pFrame ) : + SwCacheAccess( rCach, + static_cast(GetCacheOwner(*pFrame)), + GetCacheOwner(*pFrame)->IsInCache()), + m_pConstructor( pFrame ) +{ +} + +SwCacheObj *SwBorderAttrAccess::NewObj() +{ + const_cast(static_cast(m_pOwner))->SetInCache( true ); + return new SwBorderAttrs( static_cast(m_pOwner), m_pConstructor ); +} + +SwBorderAttrs *SwBorderAttrAccess::Get() +{ + return static_cast(SwCacheAccess::Get()); +} + +SwOrderIter::SwOrderIter( const SwPageFrame *pPg ) : + m_pPage( pPg ), + m_pCurrent( nullptr ) +{ +} + +void SwOrderIter::Top() +{ + m_pCurrent = nullptr; + if ( m_pPage->GetSortedObjs() ) + { + const SwSortedObjs *pObjs = m_pPage->GetSortedObjs(); + if ( pObjs->size() ) + { + sal_uInt32 nTopOrd = 0; + (*pObjs)[0]->GetDrawObj()->GetOrdNum(); // force updating + for (SwAnchoredObject* i : *pObjs) + { + const SdrObject* pObj = i->GetDrawObj(); + if ( dynamic_cast( pObj) == nullptr ) + continue; + sal_uInt32 nTmp = pObj->GetOrdNumDirect(); + if ( nTmp >= nTopOrd ) + { + nTopOrd = nTmp; + m_pCurrent = pObj; + } + } + } + } +} + +const SdrObject *SwOrderIter::Bottom() +{ + m_pCurrent = nullptr; + if ( m_pPage->GetSortedObjs() ) + { + sal_uInt32 nBotOrd = USHRT_MAX; + const SwSortedObjs *pObjs = m_pPage->GetSortedObjs(); + if ( pObjs->size() ) + { + (*pObjs)[0]->GetDrawObj()->GetOrdNum(); // force updating + for (SwAnchoredObject* i : *pObjs) + { + const SdrObject* pObj = i->GetDrawObj(); + if ( dynamic_cast( pObj) == nullptr ) + continue; + sal_uInt32 nTmp = pObj->GetOrdNumDirect(); + if ( nTmp < nBotOrd ) + { + nBotOrd = nTmp; + m_pCurrent = pObj; + } + } + } + } + return m_pCurrent; +} + +const SdrObject *SwOrderIter::Next() +{ + const sal_uInt32 nCurOrd = m_pCurrent ? m_pCurrent->GetOrdNumDirect() : 0; + m_pCurrent = nullptr; + if ( m_pPage->GetSortedObjs() ) + { + sal_uInt32 nOrd = USHRT_MAX; + const SwSortedObjs *pObjs = m_pPage->GetSortedObjs(); + if ( pObjs->size() ) + { + (*pObjs)[0]->GetDrawObj()->GetOrdNum(); // force updating + for (SwAnchoredObject* i : *pObjs) + { + const SdrObject* pObj = i->GetDrawObj(); + if ( dynamic_cast( pObj) == nullptr ) + continue; + sal_uInt32 nTmp = pObj->GetOrdNumDirect(); + if ( nTmp > nCurOrd && nTmp < nOrd ) + { + nOrd = nTmp; + m_pCurrent = pObj; + } + } + } + } + return m_pCurrent; +} + +void SwOrderIter::Prev() +{ + const sal_uInt32 nCurOrd = m_pCurrent ? m_pCurrent->GetOrdNumDirect() : 0; + m_pCurrent = nullptr; + if ( m_pPage->GetSortedObjs() ) + { + const SwSortedObjs *pObjs = m_pPage->GetSortedObjs(); + if ( pObjs->size() ) + { + sal_uInt32 nOrd = 0; + (*pObjs)[0]->GetDrawObj()->GetOrdNum(); // force updating + for (SwAnchoredObject* i : *pObjs) + { + const SdrObject* pObj = i->GetDrawObj(); + if ( dynamic_cast( pObj) == nullptr ) + continue; + sal_uInt32 nTmp = pObj->GetOrdNumDirect(); + if ( nTmp < nCurOrd && nTmp >= nOrd ) + { + nOrd = nTmp; + m_pCurrent = pObj; + } + } + } + } +} + +/// Keep and restore the substructure of a layout frame for an action. +// New algorithm: +// Do not look at each neighbor one by one to set all pointers correctly. +// It is sufficient to detach a part of a chain and check if another chain needs to be added +// when attaching it again. Only the pointers necessary for the chain connection need to be +// adjusted. The correction happens in RestoreContent(). In between all access is restricted. +// During this action, the Flys are detached from the page. + +// #115759# - 'remove' also drawing object from page and +// at-fly anchored objects from page +static void lcl_RemoveObjsFromPage( SwFrame* _pFrame ) +{ + OSL_ENSURE( _pFrame->GetDrawObjs(), "no DrawObjs in lcl_RemoveObjsFromPage." ); + SwSortedObjs &rObjs = *_pFrame->GetDrawObjs(); + for (SwAnchoredObject* pObj : rObjs) + { + // #115759# - reset member, at which the anchored + // object orients its vertical position + pObj->ClearVertPosOrientFrame(); + // #i43913# + pObj->ResetLayoutProcessBools(); + // #115759# - remove also lower objects of as-character + // anchored Writer fly frames from page + if ( dynamic_cast( pObj) != nullptr ) + { + SwFlyFrame* pFlyFrame = static_cast(pObj); + + // #115759# - remove also direct lowers of Writer + // fly frame from page + if ( pFlyFrame->GetDrawObjs() ) + { + ::lcl_RemoveObjsFromPage( pFlyFrame ); + } + + SwContentFrame* pCnt = pFlyFrame->ContainsContent(); + while ( pCnt ) + { + if ( pCnt->GetDrawObjs() ) + ::lcl_RemoveObjsFromPage( pCnt ); + pCnt = pCnt->GetNextContentFrame(); + } + if ( pFlyFrame->IsFlyFreeFrame() ) + { + // #i28701# - use new method + pFlyFrame->GetPageFrame()->RemoveFlyFromPage( pFlyFrame ); + } + } + // #115759# - remove also drawing objects from page + else if ( dynamic_cast( pObj) != nullptr ) + { + if (pObj->GetFrameFormat().GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) + { + if (SwPageFrame *pPg = pObj->GetPageFrame()) + pPg->RemoveDrawObjFromPage( + *static_cast(pObj) ); + } + } + } +} + +SwFrame *SaveContent( SwLayoutFrame *pLay, SwFrame *pStart ) +{ + if( pLay->IsSctFrame() && pLay->Lower() && pLay->Lower()->IsColumnFrame() ) + sw_RemoveFootnotes( static_cast(pLay->Lower()), true, true ); + + SwFrame *pSav; + if ( nullptr == (pSav = pLay->ContainsAny()) ) + return nullptr; + + if( pSav->IsInFootnote() && !pLay->IsInFootnote() ) + { + do + pSav = pSav->FindNext(); + while( pSav && pSav->IsInFootnote() ); + if( !pSav || !pLay->IsAnLower( pSav ) ) + return nullptr; + } + + // Tables should be saved as a whole, exception: + // The contents of a section or a cell inside a table should be saved + if ( pSav->IsInTab() && !( ( pLay->IsSctFrame() || pLay->IsCellFrame() ) && pLay->IsInTab() ) ) + while ( !pSav->IsTabFrame() ) + pSav = pSav->GetUpper(); + + if( pSav->IsInSct() ) + { // search the upmost section inside of pLay + SwFrame* pSect = pLay->FindSctFrame(); + SwFrame *pTmp = pSav; + do + { + pSav = pTmp; + pTmp = (pSav && pSav->GetUpper()) ? pSav->GetUpper()->FindSctFrame() : nullptr; + } while ( pTmp != pSect ); + } + + SwFrame *pFloat = pSav; + if( !pStart ) + pStart = pSav; + bool bGo = pStart == pSav; + do + { + if( bGo ) + pFloat->GetUpper()->m_pLower = nullptr; // detach the chain part + + // search the end of the chain part, remove Flys on the way + do + { + if( bGo ) + { + if ( pFloat->IsContentFrame() ) + { + if ( pFloat->GetDrawObjs() ) + ::lcl_RemoveObjsFromPage( static_cast(pFloat) ); + } + else if ( pFloat->IsTabFrame() || pFloat->IsSctFrame() ) + { + SwContentFrame *pCnt = static_cast(pFloat)->ContainsContent(); + if( pCnt ) + { + do + { if ( pCnt->GetDrawObjs() ) + ::lcl_RemoveObjsFromPage( pCnt ); + pCnt = pCnt->GetNextContentFrame(); + } while ( pCnt && static_cast(pFloat)->IsAnLower( pCnt ) ); + } + } + else { + OSL_ENSURE( !pFloat, "new FloatFrame?" ); + } + } + if ( pFloat->GetNext() ) + { + if( bGo ) + pFloat->mpUpper = nullptr; + pFloat = pFloat->GetNext(); + if( !bGo && pFloat == pStart ) + { + bGo = true; + pFloat->mpPrev->mpNext = nullptr; + pFloat->mpPrev = nullptr; + } + } + else + break; + + } while ( pFloat ); + + // search next chain part and connect both chains + SwFrame *pTmp = pFloat->FindNext(); + if( bGo ) + pFloat->mpUpper = nullptr; + + if( !pLay->IsInFootnote() ) + while( pTmp && pTmp->IsInFootnote() ) + pTmp = pTmp->FindNext(); + + if ( !pLay->IsAnLower( pTmp ) ) + pTmp = nullptr; + + if ( pTmp && bGo ) + { + pFloat->mpNext = pTmp; // connect both chains + pFloat->mpNext->mpPrev = pFloat; + } + pFloat = pTmp; + bGo = bGo || ( pStart == pFloat ); + } while ( pFloat ); + + return bGo ? pStart : nullptr; +} + +// #115759# - add also drawing objects to page and at-fly +// anchored objects to page +static void lcl_AddObjsToPage( SwFrame* _pFrame, SwPageFrame* _pPage ) +{ + OSL_ENSURE( _pFrame->GetDrawObjs(), "no DrawObjs in lcl_AddObjsToPage." ); + SwSortedObjs &rObjs = *_pFrame->GetDrawObjs(); + for (SwAnchoredObject* pObj : rObjs) + { + // #115759# - unlock position of anchored object + // in order to get the object's position calculated. + pObj->UnlockPosition(); + // #115759# - add also lower objects of as-character + // anchored Writer fly frames from page + if ( dynamic_cast( pObj) != nullptr ) + { + SwFlyFrame* pFlyFrame = static_cast(pObj); + if ( dynamic_cast( pObj) != nullptr ) + { + _pPage->AppendFlyToPage( pFlyFrame ); + } + pFlyFrame->InvalidatePos_(); + pFlyFrame->InvalidateSize_(); + pFlyFrame->InvalidatePage( _pPage ); + + // #115759# - add also at-fly anchored objects + // to page + if ( pFlyFrame->GetDrawObjs() ) + { + ::lcl_AddObjsToPage( pFlyFrame, _pPage ); + } + + SwContentFrame *pCnt = pFlyFrame->ContainsContent(); + while ( pCnt ) + { + if ( pCnt->GetDrawObjs() ) + ::lcl_AddObjsToPage( pCnt, _pPage ); + pCnt = pCnt->GetNextContentFrame(); + } + } + // #115759# - remove also drawing objects from page + else if ( dynamic_cast( pObj) != nullptr ) + { + if (pObj->GetFrameFormat().GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) + { + pObj->InvalidateObjPos(); + _pPage->AppendDrawObjToPage( + *static_cast(pObj) ); + } + } + } +} + +void RestoreContent( SwFrame *pSav, SwLayoutFrame *pParent, SwFrame *pSibling ) +{ + OSL_ENSURE( pSav && pParent, "no Save or Parent provided for RestoreContent." ); + SwRectFnSet aRectFnSet(pParent); + + // If there are already FlowFrames below the new parent, so add the chain (starting with pSav) + // after the last one. The parts are inserted and invalidated if needed. + // On the way, the Flys of the ContentFrames are registered at the page. + + SwPageFrame *pPage = pParent->FindPageFrame(); + + if ( pPage ) + pPage->InvalidatePage( pPage ); + + // determine predecessor and establish connection or initialize + pSav->mpPrev = pSibling; + SwFrame* pNxt; + if ( pSibling ) + { + pNxt = pSibling->mpNext; + pSibling->mpNext = pSav; + pSibling->InvalidatePrt_(); + pSibling->InvalidatePage( pPage ); + SwFlowFrame *pFlowFrame = dynamic_cast(pSibling); + if (pFlowFrame && pFlowFrame->GetFollow()) + pSibling->Prepare( PrepareHint::Clear, nullptr, false ); + } + else + { pNxt = pParent->m_pLower; + pParent->m_pLower = pSav; + pSav->mpUpper = pParent; // set here already, so that it is explicit when invalidating + + if ( pSav->IsContentFrame() ) + static_cast(pSav)->InvalidatePage( pPage ); + else + { // pSav might be an empty SectFrame + SwContentFrame* pCnt = pParent->ContainsContent(); + if( pCnt ) + pCnt->InvalidatePage( pPage ); + } + } + + // the parent needs to grow appropriately + SwTwips nGrowVal = 0; + SwFrame* pLast; + do + { pSav->mpUpper = pParent; + nGrowVal += aRectFnSet.GetHeight(pSav->getFrameArea()); + pSav->InvalidateAll_(); + + // register Flys, if TextFrames than also invalidate appropriately + if ( pSav->IsContentFrame() ) + { + if ( pSav->IsTextFrame() && + static_cast(pSav)->GetCacheIdx() != USHRT_MAX ) + static_cast(pSav)->Init(); // I am its friend + + if ( pPage && pSav->GetDrawObjs() ) + ::lcl_AddObjsToPage( static_cast(pSav), pPage ); + } + else + { SwContentFrame *pBlub = static_cast(pSav)->ContainsContent(); + if( pBlub ) + { + do + { if ( pPage && pBlub->GetDrawObjs() ) + ::lcl_AddObjsToPage( pBlub, pPage ); + if( pBlub->IsTextFrame() && static_cast(pBlub)->HasFootnote() && + static_cast(pBlub)->GetCacheIdx() != USHRT_MAX ) + static_cast(pBlub)->Init(); // I am its friend + pBlub = pBlub->GetNextContentFrame(); + } while ( pBlub && static_cast(pSav)->IsAnLower( pBlub )); + } + } + pLast = pSav; + pSav = pSav->GetNext(); + + } while ( pSav ); + + if( pNxt ) + { + pLast->mpNext = pNxt; + pNxt->mpPrev = pLast; + } + + pParent->Grow( nGrowVal ); +} + +namespace sw { + +bool IsRightPageByNumber(SwRootFrame const& rLayout, sal_uInt16 const nPageNum) +{ + assert(rLayout.GetLower()); + // unfortunately can only get SwPageDesc, not SwFormatPageDesc here... + auto const nFirstVirtPageNum(rLayout.GetLower()->GetVirtPageNum()); + bool const isFirstPageOfLayoutOdd(nFirstVirtPageNum % 2 == 1); + return ((nPageNum % 2) == 1) == isFirstPageOfLayoutOdd; +} + +} // namespace sw + +SwPageFrame * InsertNewPage( SwPageDesc &rDesc, SwFrame *pUpper, + bool const isRightPage, bool const bFirst, bool bInsertEmpty, + bool const bFootnote, + SwFrame *pSibling ) +{ + assert(pUpper); + assert(pUpper->IsRootFrame()); + assert(!pSibling || static_cast(pUpper)->Lower() != pSibling); // currently no insert before 1st page + SwPageFrame *pRet; + SwDoc *pDoc = static_cast(pUpper)->GetFormat()->GetDoc(); + if (bFirst) + { + if (rDesc.IsFirstShared()) + { + // We need to fallback to left or right page format, decide it now. + // FIXME: is this still needed? + if (isRightPage) + { + rDesc.GetFirstMaster().SetFormatAttr( rDesc.GetMaster().GetHeader() ); + rDesc.GetFirstMaster().SetFormatAttr( rDesc.GetMaster().GetFooter() ); + // fdo#60250 copy margins for mirrored pages + rDesc.GetFirstMaster().SetFormatAttr( rDesc.GetMaster().GetLRSpace() ); + } + else + { + rDesc.GetFirstLeft().SetFormatAttr( rDesc.GetLeft().GetHeader() ); + rDesc.GetFirstLeft().SetFormatAttr( rDesc.GetLeft().GetFooter() ); + rDesc.GetFirstLeft().SetFormatAttr( rDesc.GetLeft().GetLRSpace() ); + } + } + } + SwFrameFormat *pFormat(isRightPage ? rDesc.GetRightFormat(bFirst) : rDesc.GetLeftFormat(bFirst)); + // If there is no FrameFormat for this page, add an empty page + if ( !pFormat ) + { + pFormat = isRightPage ? rDesc.GetLeftFormat() : rDesc.GetRightFormat(); + OSL_ENSURE( pFormat, "Descriptor without any format?!" ); + bInsertEmpty = !bInsertEmpty; + } + if( bInsertEmpty ) + { + SwPageDesc *pTmpDesc = pSibling && pSibling->GetPrev() ? + static_cast(pSibling->GetPrev())->GetPageDesc() : &rDesc; + pRet = new SwPageFrame( pDoc->GetEmptyPageFormat(), pUpper, pTmpDesc ); + SAL_INFO( "sw.pageframe", "InsertNewPage - insert empty p: " << pRet << " d: " << pTmpDesc ); + pRet->Paste( pUpper, pSibling ); + pRet->PreparePage( bFootnote ); + } + pRet = new SwPageFrame( pFormat, pUpper, &rDesc ); + SAL_INFO( "sw.pageframe", "InsertNewPage p: " << pRet << " d: " << &rDesc << " f: " << pFormat ); + pRet->Paste( pUpper, pSibling ); + pRet->PreparePage( bFootnote ); + if ( pRet->GetNext() ) + SwRootFrame::AssertPageFlys( pRet ); + return pRet; +} + +/* The following two methods search the layout structure recursively and + * register all Flys at the page that have a Frame in this structure as an anchor. + */ + +static void lcl_Regist( SwPageFrame *pPage, const SwFrame *pAnch ) +{ + SwSortedObjs *pObjs = const_cast(pAnch->GetDrawObjs()); + for (SwAnchoredObject* pObj : *pObjs) + { + if (SwFlyFrame* pFly = dynamic_cast(pObj)) + { + // register (not if already known) + // #i28701# - use new method + SwPageFrame *pPg = pFly->IsFlyFreeFrame() + ? pFly->GetPageFrame() : pFly->FindPageFrame(); + if ( pPg != pPage ) + { + if ( pPg ) + pPg->RemoveFlyFromPage( pFly ); + pPage->AppendFlyToPage( pFly ); + } + ::RegistFlys( pPage, pFly ); + } + else + { + // #i87493# + if ( pPage != pObj->GetPageFrame() ) + { + // #i28701# + if (SwPageFrame *pPg = pObj->GetPageFrame()) + pPg->RemoveDrawObjFromPage( *pObj ); + pPage->AppendDrawObjToPage( *pObj ); + } + } + + const SwFlyFrame* pFly = pAnch->FindFlyFrame(); + if ( pFly && + pObj->GetDrawObj()->GetOrdNum() < pFly->GetVirtDrawObj()->GetOrdNum() && + pObj->GetDrawObj()->getSdrPageFromSdrObject() ) + { + //#i119945# set pFly's OrdNum to pObj's. So when pFly is removed by Undo, the original OrdNum will not be changed. + pObj->DrawObj()->getSdrPageFromSdrObject()->SetObjectOrdNum( pFly->GetVirtDrawObj()->GetOrdNumDirect(), + pObj->GetDrawObj()->GetOrdNumDirect() ); + } + } +} + +void RegistFlys( SwPageFrame *pPage, const SwLayoutFrame *pLay ) +{ + if ( pLay->GetDrawObjs() ) + ::lcl_Regist( pPage, pLay ); + const SwFrame *pFrame = pLay->Lower(); + while ( pFrame ) + { + if ( pFrame->IsLayoutFrame() ) + ::RegistFlys( pPage, static_cast(pFrame) ); + else if ( pFrame->GetDrawObjs() ) + ::lcl_Regist( pPage, pFrame ); + pFrame = pFrame->GetNext(); + } +} + +/// Notify the background based on the difference between old and new rectangle +void Notify( SwFlyFrame *pFly, SwPageFrame *pOld, const SwRect &rOld, + const SwRect* pOldPrt ) +{ + const SwRect aFrame( pFly->GetObjRectWithSpaces() ); + if ( rOld.Pos() != aFrame.Pos() ) + { // changed position, invalidate old and new area + if ( rOld.HasArea() && + rOld.Left()+pFly->GetFormat()->GetLRSpace().GetLeft() < FAR_AWAY ) + { + pFly->NotifyBackground( pOld, rOld, PrepareHint::FlyFrameLeave ); + } + pFly->NotifyBackground( pFly->FindPageFrame(), aFrame, PrepareHint::FlyFrameArrive ); + } + else if ( rOld.SSize() != aFrame.SSize() ) + { // changed size, invalidate the area that was left or is now overlapped + // For simplicity, we purposely invalidate a Twip even if not needed. + + SwViewShell *pSh = pFly->getRootFrame()->GetCurrShell(); + if( pSh && rOld.HasArea() ) + pSh->InvalidateWindows( rOld ); + + // #i51941# - consider case that fly frame isn't + // registered at the old page + SwPageFrame* pPageFrame = pFly->FindPageFrame(); + if ( pOld != pPageFrame ) + { + pFly->NotifyBackground( pPageFrame, aFrame, PrepareHint::FlyFrameArrive ); + } + + if ( rOld.Left() != aFrame.Left() ) + { + SwRect aTmp( rOld ); + aTmp.Union( aFrame ); + aTmp.Left( std::min(aFrame.Left(), rOld.Left()) ); + aTmp.Right( std::max(aFrame.Left(), rOld.Left()) ); + pFly->NotifyBackground( pOld, aTmp, PrepareHint::FlyFrameSizeChanged ); + } + SwTwips nOld = rOld.Right(); + SwTwips nNew = aFrame.Right(); + if ( nOld != nNew ) + { + SwRect aTmp( rOld ); + aTmp.Union( aFrame ); + aTmp.Left( std::min(nNew, nOld) ); + aTmp.Right( std::max(nNew, nOld) ); + pFly->NotifyBackground( pOld, aTmp, PrepareHint::FlyFrameSizeChanged ); + } + if ( rOld.Top() != aFrame.Top() ) + { + SwRect aTmp( rOld ); + aTmp.Union( aFrame ); + aTmp.Top( std::min(aFrame.Top(), rOld.Top()) ); + aTmp.Bottom( std::max(aFrame.Top(), rOld.Top()) ); + pFly->NotifyBackground( pOld, aTmp, PrepareHint::FlyFrameSizeChanged ); + } + nOld = rOld.Bottom(); + nNew = aFrame.Bottom(); + if ( nOld != nNew ) + { + SwRect aTmp( rOld ); + aTmp.Union( aFrame ); + aTmp.Top( std::min(nNew, nOld) ); + aTmp.Bottom( std::max(nNew, nOld) ); + pFly->NotifyBackground( pOld, aTmp, PrepareHint::FlyFrameSizeChanged ); + } + } + else if(pOldPrt && *pOldPrt != pFly->getFramePrintArea()) + { + bool bNotifyBackground(pFly->GetFormat()->GetSurround().IsContour()); + + if(!bNotifyBackground && + pFly->IsFlyFreeFrame() && + static_cast< const SwFlyFreeFrame* >(pFly)->supportsAutoContour()) + { + // RotateFlyFrame3: Also notify for FlyFrames which allow AutoContour + bNotifyBackground = true; + } + + if(bNotifyBackground) + { + // #i24097# + pFly->NotifyBackground( pFly->FindPageFrame(), aFrame, PrepareHint::FlyFrameArrive ); + } + } +} + +static void lcl_CheckFlowBack( SwFrame* pFrame, const SwRect &rRect ) +{ + SwTwips nBottom = rRect.Bottom(); + while( pFrame ) + { + if( pFrame->IsLayoutFrame() ) + { + if( rRect.IsOver( pFrame->getFrameArea() ) ) + lcl_CheckFlowBack( static_cast(pFrame)->Lower(), rRect ); + } + else if( !pFrame->GetNext() && nBottom > pFrame->getFrameArea().Bottom() ) + { + if( pFrame->IsContentFrame() && static_cast(pFrame)->HasFollow() ) + pFrame->InvalidateSize(); + else + pFrame->InvalidateNextPos(); + } + pFrame = pFrame->GetNext(); + } +} + +static void lcl_NotifyContent( const SdrObject *pThis, SwContentFrame *pCnt, + const SwRect &rRect, const PrepareHint eHint ) +{ + if ( pCnt->IsTextFrame() ) + { + SwRect aCntPrt( pCnt->getFramePrintArea() ); + aCntPrt.Pos() += pCnt->getFrameArea().Pos(); + if ( eHint == PrepareHint::FlyFrameAttributesChanged ) + { + // #i35640# - use given rectangle instead + // of current bound rectangle + if ( aCntPrt.IsOver( rRect ) ) + pCnt->Prepare( PrepareHint::FlyFrameAttributesChanged ); + } + // #i23129# - only invalidate, if the text frame + // printing area overlaps with the given rectangle. + else if ( aCntPrt.IsOver( rRect ) ) + pCnt->Prepare( eHint, static_cast(&aCntPrt.Intersection_( rRect )) ); + if ( pCnt->GetDrawObjs() ) + { + const SwSortedObjs &rObjs = *pCnt->GetDrawObjs(); + for (SwAnchoredObject* pObj : rObjs) + { + if ( dynamic_cast( pObj) != nullptr ) + { + SwFlyFrame *pFly = static_cast(pObj); + if ( pFly->IsFlyInContentFrame() ) + { + SwContentFrame *pContent = pFly->ContainsContent(); + while ( pContent ) + { + ::lcl_NotifyContent( pThis, pContent, rRect, eHint ); + pContent = pContent->GetNextContentFrame(); + } + } + } + } + } + } +} + +void Notify_Background( const SdrObject* pObj, + SwPageFrame* pPage, + const SwRect& rRect, + const PrepareHint eHint, + const bool bInva ) +{ + // If the frame was positioned correctly for the first time, do not inform the old area + if ( eHint == PrepareHint::FlyFrameLeave && rRect.Top() == FAR_AWAY ) + return; + + SwLayoutFrame* pArea; + SwFlyFrame *pFlyFrame = nullptr; + SwFrame* pAnchor; + if( auto pVirtFlyDrawObj = dynamic_cast( pObj) ) + { + pFlyFrame = const_cast(pVirtFlyDrawObj)->GetFlyFrame(); + pAnchor = pFlyFrame->AnchorFrame(); + } + else + { + pFlyFrame = nullptr; + pAnchor = const_cast( + GetUserCall(pObj)->GetAnchoredObj( pObj )->GetAnchorFrame() ); + } + if( PrepareHint::FlyFrameLeave != eHint && pAnchor->IsInFly() ) + pArea = pAnchor->FindFlyFrame(); + else + pArea = pPage; + SwContentFrame *pCnt = nullptr; + if ( pArea ) + { + if( PrepareHint::FlyFrameArrive != eHint ) + lcl_CheckFlowBack( pArea, rRect ); + + // Only the Flys following this anchor are reacting. Thus, those do not + // need to be processed. + // An exception is LEAVE, since the Fly might come "from above". + // If the anchor is positioned on the previous page, the whole page + // needs to be processed (47722). + // OD 2004-05-13 #i28701# - If the wrapping style has to be considered + // on the object positioning, the complete area has to be processed, + // because content frames before the anchor frame also have to consider + // the object for the text wrapping. + // #i3317# - The complete area has always been + // processed. + { + pCnt = pArea->ContainsContent(); + } + } + SwFrame *pLastTab = nullptr; + + bool isValidTableBeforeAnchor(false); + while ( pCnt && pArea && pArea->IsAnLower( pCnt ) ) + { + ::lcl_NotifyContent( pObj, pCnt, rRect, eHint ); + if ( pCnt->IsInTab() ) + { + SwTabFrame *pTab = pCnt->FindTabFrame(); + if ( pTab != pLastTab ) + { + pLastTab = pTab; + isValidTableBeforeAnchor = false; + if (PrepareHint::FlyFrameArrive == eHint + && pFlyFrame // TODO: do it for draw objects too? + && pTab->IsFollow() // table starts on previous page? + // "through" means they will actually overlap anyway + && css::text::WrapTextMode_THROUGH != pFlyFrame->GetFormat()->GetSurround().GetSurround() + // if it's anchored in footer it can't move to other page + && !pAnchor->FindFooterOrHeader()) + { + SwFrame * pTmp(pAnchor->GetPrev()); + while (pTmp) + { + if (pTmp == pTab) + { + // tdf#99460 the table shouldn't be moved by the fly + isValidTableBeforeAnchor = true; + break; + } + pTmp = pTmp->GetPrev(); + } + } + // #i40606# - use + // instead of , because a recalculation + // of the bounding rectangle isn't intended here. + if (!isValidTableBeforeAnchor + && (pTab->getFrameArea().IsOver(pObj->GetLastBoundRect()) || + pTab->getFrameArea().IsOver(rRect))) + { + if ( !pFlyFrame || !pFlyFrame->IsLowerOf( pTab ) ) + pTab->InvalidatePrt(); + } + } + SwLayoutFrame* pCell = pCnt->GetUpper(); + // #i40606# - use + // instead of , because a recalculation + // of the bounding rectangle isn't intended here. + if (!isValidTableBeforeAnchor && pCell->IsCellFrame() && + ( pCell->getFrameArea().IsOver( pObj->GetLastBoundRect() ) || + pCell->getFrameArea().IsOver( rRect ) ) ) + { + const SwFormatVertOrient &rOri = pCell->GetFormat()->GetVertOrient(); + if ( text::VertOrientation::NONE != rOri.GetVertOrient() ) + pCell->InvalidatePrt(); + } + } + pCnt = pCnt->GetNextContentFrame(); + } + // #128702# - make code robust + if ( pPage && pPage->GetSortedObjs() ) + { + pObj->GetOrdNum(); + const SwSortedObjs &rObjs = *pPage->GetSortedObjs(); + for (SwAnchoredObject* pAnchoredObj : rObjs) + { + if ( dynamic_cast( pAnchoredObj) != nullptr ) + { + if( pAnchoredObj->GetDrawObj() == pObj ) + continue; + SwFlyFrame *pFly = static_cast(pAnchoredObj); + if ( pFly->getFrameArea().Top() == FAR_AWAY ) + continue; + + if ( !pFlyFrame || + (!pFly->IsLowerOf( pFlyFrame ) && + pFly->GetVirtDrawObj()->GetOrdNumDirect() < pObj->GetOrdNumDirect())) + { + pCnt = pFly->ContainsContent(); + while ( pCnt ) + { + ::lcl_NotifyContent( pObj, pCnt, rRect, eHint ); + pCnt = pCnt->GetNextContentFrame(); + } + } + if( pFly->IsFlyLayFrame() ) + { + if( pFly->Lower() && pFly->Lower()->IsColumnFrame() && + pFly->getFrameArea().Bottom() >= rRect.Top() && + pFly->getFrameArea().Top() <= rRect.Bottom() && + pFly->getFrameArea().Right() >= rRect.Left() && + pFly->getFrameArea().Left() <= rRect.Right() ) + { + pFly->InvalidateSize(); + } + } + // Flys above myself might sidestep if they have an automatic + // alignment. This happens independently of my attributes since + // this might have been changed as well. + else if ( pFly->IsFlyAtContentFrame() && + pObj->GetOrdNumDirect() < + pFly->GetVirtDrawObj()->GetOrdNumDirect() && + pFlyFrame && !pFly->IsLowerOf( pFlyFrame ) ) + { + const SwFormatHoriOrient &rH = pFly->GetFormat()->GetHoriOrient(); + if ( text::HoriOrientation::NONE != rH.GetHoriOrient() && + text::HoriOrientation::CENTER != rH.GetHoriOrient() && + ( !pFly->IsAutoPos() || text::RelOrientation::CHAR != rH.GetRelationOrient() ) && + (pFly->getFrameArea().Bottom() >= rRect.Top() && + pFly->getFrameArea().Top() <= rRect.Bottom()) ) + pFly->InvalidatePos(); + } + } + } + } + if ( pFlyFrame && pAnchor->GetUpper() && pAnchor->IsInTab() )//MA_FLY_HEIGHT + pAnchor->GetUpper()->InvalidateSize(); + + // #i82258# - make code robust + SwViewShell* pSh = nullptr; + if ( bInva && pPage && + nullptr != (pSh = pPage->getRootFrame()->GetCurrShell()) ) + { + pSh->InvalidateWindows( rRect ); + } +} + +/// Provides the Upper of an anchor in paragraph-bound objects. If the latter +/// is a chained border or a footnote, the "virtual" Upper might be returned. +const SwFrame* GetVirtualUpper( const SwFrame* pFrame, const Point& rPos ) +{ + if( pFrame->IsTextFrame() ) + { + pFrame = pFrame->GetUpper(); + if( !pFrame->getFrameArea().IsInside( rPos ) ) + { + if( pFrame->IsFootnoteFrame() ) + { + const SwFootnoteFrame* pTmp = static_cast(pFrame)->GetFollow(); + while( pTmp ) + { + if( pTmp->getFrameArea().IsInside( rPos ) ) + return pTmp; + pTmp = pTmp->GetFollow(); + } + } + else + { + SwFlyFrame* pTmp = const_cast(pFrame->FindFlyFrame()); + while( pTmp ) + { + if( pTmp->getFrameArea().IsInside( rPos ) ) + return pTmp; + pTmp = pTmp->GetNextLink(); + } + } + } + } + return pFrame; +} + +bool Is_Lower_Of(const SwFrame *pCurrFrame, const SdrObject* pObj) +{ + Point aPos; + const SwFrame* pFrame; + if (const SwVirtFlyDrawObj *pFlyDrawObj = dynamic_cast(pObj)) + { + const SwFlyFrame* pFly = pFlyDrawObj->GetFlyFrame(); + pFrame = pFly->GetAnchorFrame(); + aPos = pFly->getFrameArea().Pos(); + } + else + { + pFrame = static_cast(GetUserCall(pObj))->GetAnchorFrame(pObj); + aPos = pObj->GetCurrentBoundRect().TopLeft(); + } + OSL_ENSURE( pFrame, "8-( Fly is lost in Space." ); + pFrame = GetVirtualUpper( pFrame, aPos ); + do + { if ( pFrame == pCurrFrame ) + return true; + if( pFrame->IsFlyFrame() ) + { + aPos = pFrame->getFrameArea().Pos(); + pFrame = GetVirtualUpper( static_cast(pFrame)->GetAnchorFrame(), aPos ); + } + else + pFrame = pFrame->GetUpper(); + } while ( pFrame ); + return false; +} + +/// provides the area of a frame in that no Fly from another area can overlap +const SwFrame *FindContext( const SwFrame *pFrame, SwFrameType nAdditionalContextType ) +{ + const SwFrameType nTyp = SwFrameType::Root | SwFrameType::Header | SwFrameType::Footer | SwFrameType::FtnCont | + SwFrameType::Ftn | SwFrameType::Fly | + SwFrameType::Tab | SwFrameType::Row | SwFrameType::Cell | + nAdditionalContextType; + do + { if ( pFrame->GetType() & nTyp ) + break; + pFrame = pFrame->GetUpper(); + } while( pFrame ); + return pFrame; +} + +bool IsFrameInSameContext( const SwFrame *pInnerFrame, const SwFrame *pFrame ) +{ + const SwFrame *pContext = FindContext( pInnerFrame, SwFrameType::None ); + + const SwFrameType nTyp = SwFrameType::Root | SwFrameType::Header | SwFrameType::Footer | SwFrameType::FtnCont | + SwFrameType::Ftn | SwFrameType::Fly | + SwFrameType::Tab | SwFrameType::Row | SwFrameType::Cell; + do + { if ( pFrame->GetType() & nTyp ) + { + if( pFrame == pContext ) + return true; + if( pFrame->IsCellFrame() ) + return false; + } + if( pFrame->IsFlyFrame() ) + { + Point aPos( pFrame->getFrameArea().Pos() ); + pFrame = GetVirtualUpper( static_cast(pFrame)->GetAnchorFrame(), aPos ); + } + else + pFrame = pFrame->GetUpper(); + } while( pFrame ); + + return false; +} + +static SwTwips lcl_CalcCellRstHeight( SwLayoutFrame *pCell ) +{ + SwFrame *pLow = pCell->Lower(); + if ( pLow && (pLow->IsContentFrame() || pLow->IsSctFrame()) ) + { + long nHeight = 0, nFlyAdd = 0; + do + { + long nLow = pLow->getFrameArea().Height(); + if( pLow->IsTextFrame() && static_cast(pLow)->IsUndersized() ) + nLow += static_cast(pLow)->GetParHeight()-pLow->getFramePrintArea().Height(); + else if( pLow->IsSctFrame() && static_cast(pLow)->IsUndersized() ) + nLow += static_cast(pLow)->Undersize(); + nFlyAdd = std::max( 0L, nFlyAdd - nLow ); + nFlyAdd = std::max( nFlyAdd, ::CalcHeightWithFlys( pLow ) ); + nHeight += nLow; + pLow = pLow->GetNext(); + } while ( pLow ); + if ( nFlyAdd ) + nHeight += nFlyAdd; + + // The border cannot be calculated based on PrtArea and Frame, since both can be invalid. + SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCell ); + const SwBorderAttrs &rAttrs = *aAccess.Get(); + nHeight += rAttrs.CalcTop() + rAttrs.CalcBottom(); + + return pCell->getFrameArea().Height() - nHeight; + } + else + { + long nRstHeight = 0; + while (pLow && pLow->IsLayoutFrame()) + { + nRstHeight += ::CalcRowRstHeight(static_cast(pLow)); + pLow = pLow->GetNext(); + } + return nRstHeight; + } +} + +SwTwips CalcRowRstHeight( SwLayoutFrame *pRow ) +{ + SwFrame *pLow = pRow->Lower(); + if (!(pLow && pLow->IsLayoutFrame())) + { + return 0; + } + SwTwips nRstHeight = LONG_MAX; + while (pLow && pLow->IsLayoutFrame()) + { + nRstHeight = std::min(nRstHeight, ::lcl_CalcCellRstHeight(static_cast(pLow))); + pLow = pLow->GetNext(); + } + return nRstHeight; +} + +const SwFrame* FindPage( const SwRect &rRect, const SwFrame *pPage ) +{ + if ( !rRect.IsOver( pPage->getFrameArea() ) ) + { + const SwRootFrame* pRootFrame = static_cast(pPage->GetUpper()); + const SwFrame* pTmpPage = pRootFrame ? pRootFrame->GetPageAtPos( rRect.TopLeft(), &rRect.SSize(), true ) : nullptr; + if ( pTmpPage ) + pPage = pTmpPage; + } + + return pPage; +} + +namespace { + +class SwFrameHolder : private SfxListener +{ + SwFrame* pFrame; + bool bSet; + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; +public: + SwFrameHolder() : pFrame(nullptr), bSet(false) {} + void SetFrame( SwFrame* pHold ); + SwFrame* GetFrame() { return pFrame; } + void Reset(); + bool IsSet() const { return bSet; } +}; + +} + +void SwFrameHolder::SetFrame( SwFrame* pHold ) +{ + bSet = true; + if (pFrame != pHold) + { + if (pFrame) + EndListening(*pFrame); + StartListening(*pHold); + pFrame = pHold; + } +} + +void SwFrameHolder::Reset() +{ + if (pFrame) + EndListening(*pFrame); + bSet = false; + pFrame = nullptr; +} + +void SwFrameHolder::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + if ( rHint.GetId() == SfxHintId::Dying && &rBC == pFrame ) + { + pFrame = nullptr; + } +} + +SwFrame* GetFrameOfModify(SwRootFrame const*const pLayout, SwModify const& rMod, + SwFrameType const nFrameType, SwPosition const*const pPos, + std::pair const*const pViewPosAndCalcFrame) +{ + SwFrame *pMinFrame = nullptr, *pTmpFrame; + SwFrameHolder aHolder; + SwRect aCalcRect; + bool bClientIterChanged = false; + + SwIterator aIter(rMod); + do { + pMinFrame = nullptr; + aHolder.Reset(); + sal_uInt64 nMinDist = 0; + bClientIterChanged = false; + + for( pTmpFrame = aIter.First(); pTmpFrame; pTmpFrame = aIter.Next() ) + { + if( pTmpFrame->GetType() & nFrameType && + ( !pLayout || pLayout == pTmpFrame->getRootFrame() ) && + (!pTmpFrame->IsFlowFrame() || + !SwFlowFrame::CastFlowFrame( pTmpFrame )->IsFollow() )) + { + if (pViewPosAndCalcFrame) + { + // watch for Frame being deleted + if ( pMinFrame ) + aHolder.SetFrame( pMinFrame ); + else + aHolder.Reset(); + + if (pViewPosAndCalcFrame->second) + { + // tdf#108118 prevent recursion + DisableCallbackAction a(*pTmpFrame->getRootFrame()); + // - format parent Writer + // fly frame, if it isn't been formatted yet. + // Note: The Writer fly frame could be the frame itself. + SwFlyFrame* pFlyFrame( pTmpFrame->FindFlyFrame() ); + if ( pFlyFrame && + pFlyFrame->getFrameArea().Pos().X() == FAR_AWAY && + pFlyFrame->getFrameArea().Pos().Y() == FAR_AWAY ) + { + SwObjectFormatter::FormatObj( *pFlyFrame ); + } + pTmpFrame->Calc(pLayout ? pLayout->GetCurrShell()->GetOut() : nullptr); + } + + // aIter.IsChanged checks if the current pTmpFrame has been deleted while + // it is the current iterator + // FrameHolder watches for deletion of the current pMinFrame + if( aIter.IsChanged() || ( aHolder.IsSet() && !aHolder.GetFrame() ) ) + { + // restart iteration + bClientIterChanged = true; + break; + } + + // for Flys go via the parent if the Fly is not yet "formatted" + if (!pViewPosAndCalcFrame->second && + pTmpFrame->GetType() & SwFrameType::Fly && + static_cast(pTmpFrame)->GetAnchorFrame() && + FAR_AWAY == pTmpFrame->getFrameArea().Pos().getX() && + FAR_AWAY == pTmpFrame->getFrameArea().Pos().getY() ) + aCalcRect = static_cast(pTmpFrame)->GetAnchorFrame()->getFrameArea(); + else + aCalcRect = pTmpFrame->getFrameArea(); + + if (aCalcRect.IsInside(pViewPosAndCalcFrame->first)) + { + pMinFrame = pTmpFrame; + break; + } + + // Point not in rectangle. Compare distances: + const Point aCalcRectCenter = aCalcRect.Center(); + const Point aDiff = aCalcRectCenter - pViewPosAndCalcFrame->first; + const sal_uInt64 nCurrentDist = sal_Int64(aDiff.getX()) * sal_Int64(aDiff.getX()) + sal_Int64(aDiff.getY()) * sal_Int64(aDiff.getY()); // opt: no sqrt + if ( !pMinFrame || nCurrentDist < nMinDist ) + { + pMinFrame = pTmpFrame; + nMinDist = nCurrentDist; + } + } + else + { + // if no pViewPosAndCalcFrame is provided, take the first one + pMinFrame = pTmpFrame; + break; + } + } + } + } while( bClientIterChanged ); + + if( pPos && pMinFrame && pMinFrame->IsTextFrame() ) + return static_cast(pMinFrame)->GetFrameAtPos( *pPos ); + + return pMinFrame; +} + +bool IsExtraData( const SwDoc *pDoc ) +{ + const SwLineNumberInfo &rInf = pDoc->GetLineNumberInfo(); + return rInf.IsPaintLineNumbers() || + rInf.IsCountInFlys() || + (static_cast(SW_MOD()->GetRedlineMarkPos()) != text::HoriOrientation::NONE && + !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty()); +} + +// OD 22.09.2003 #110978# +SwRect SwPageFrame::PrtWithoutHeaderAndFooter() const +{ + SwRect aPrtWithoutHeaderFooter( getFramePrintArea() ); + aPrtWithoutHeaderFooter.Pos() += getFrameArea().Pos(); + + const SwFrame* pLowerFrame = Lower(); + while ( pLowerFrame ) + { + // Note: independent on text direction page header and page footer are + // always at top respectively at bottom of the page frame. + if ( pLowerFrame->IsHeaderFrame() ) + { + aPrtWithoutHeaderFooter.AddTop( pLowerFrame->getFrameArea().Height() ); + } + if ( pLowerFrame->IsFooterFrame() ) + { + aPrtWithoutHeaderFooter.AddBottom( - pLowerFrame->getFrameArea().Height() ); + } + + pLowerFrame = pLowerFrame->GetNext(); + } + + return aPrtWithoutHeaderFooter; +} + +/** method to determine the spacing values of a frame + + OD 2004-03-10 #i28701# + OD 2009-08-28 #i102458# + Add output parameter +*/ +void GetSpacingValuesOfFrame( const SwFrame& rFrame, + SwTwips& onLowerSpacing, + SwTwips& onLineSpacing, + bool& obIsLineSpacingProportional ) +{ + if ( !rFrame.IsFlowFrame() ) + { + onLowerSpacing = 0; + onLineSpacing = 0; + } + else + { + const SvxULSpaceItem& rULSpace = rFrame.GetAttrSet()->GetULSpace(); + onLowerSpacing = rULSpace.GetLower(); + + onLineSpacing = 0; + obIsLineSpacingProportional = false; + if ( rFrame.IsTextFrame() ) + { + onLineSpacing = static_cast(rFrame).GetLineSpace(); + obIsLineSpacingProportional = + onLineSpacing != 0 && + static_cast(rFrame).GetLineSpace( true ) == 0; + } + + OSL_ENSURE( onLowerSpacing >= 0 && onLineSpacing >= 0, + " - spacing values aren't positive!" ); + } +} + +/// get the content of the table cell, skipping content from nested tables +const SwContentFrame* GetCellContent( const SwLayoutFrame& rCell ) +{ + const SwContentFrame* pContent = rCell.ContainsContent(); + const SwTabFrame* pTab = rCell.FindTabFrame(); + + while ( pContent && rCell.IsAnLower( pContent ) ) + { + const SwTabFrame* pTmpTab = pContent->FindTabFrame(); + if ( pTmpTab != pTab ) + { + SwFrame const*const pTmp = pTmpTab->FindLastContentOrTable(); + if (pTmp) + { + pContent = pTmp->FindNextCnt(); + } + else + { + pContent = nullptr; + } + } + else + break; + } + return pContent; +} + +SwDeletionChecker::SwDeletionChecker(const SwFrame* pFrame) + : mpFrame( pFrame ) + , mpRegIn( pFrame + ? pFrame->IsTextFrame() + // sw_redlinehide: GetDep() may be a member of SwTextFrame! + ? static_cast(pFrame)->GetTextNodeFirst() + : const_cast(pFrame)->GetDep() + : nullptr ) +{ +} + +/// Can be used to check if a frame has been deleted +bool SwDeletionChecker::HasBeenDeleted() const +{ + if ( !mpFrame || !mpRegIn ) + return false; + + SwIterator aIter(*mpRegIn); + SwFrame* pLast = aIter.First(); + while ( pLast ) + { + if ( pLast == mpFrame ) + return false; + pLast = aIter.Next(); + } + + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3